内存优化2

This commit is contained in:
dengqichen 2025-03-03 12:30:01 +08:00
parent 22d2d993aa
commit 83117e8169

View File

@ -12,6 +12,33 @@ describe('隆基系统全部页面验证', () => {
cleanupMemory(); cleanupMemory();
}); });
// 创建通用对话框模板函数
const createDialogTemplate = (options) => {
const {
dialogClass,
title,
width,
bodyContent,
footerContent
} = options;
return `
<div class="el-dialog__wrapper ${dialogClass}" style="position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow: auto; margin: 0; z-index: 2000; background: rgba(0, 0, 0, 0.5);">
<div class="el-dialog" style="margin: 15vh auto 50px; width: ${width || '500px'}; background: #fff; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);">
<div class="el-dialog__header" style="padding: 20px; border-bottom: 1px solid #eee;">
<span class="el-dialog__title" style="font-size: 18px; color: #303133;">${title}</span>
</div>
<div class="el-dialog__body" style="padding: 20px; max-height: 50vh; overflow-y: auto;">
${bodyContent}
</div>
<div class="el-dialog__footer" style="padding: 20px; text-align: right; border-top: 1px solid #eee;">
${footerContent}
</div>
</div>
</div>
`;
};
// 格式化时间的辅助函数 // 格式化时间的辅助函数
const formatTime = (date) => { const formatTime = (date) => {
return date.toLocaleString('zh-CN', { return date.toLocaleString('zh-CN', {
@ -84,29 +111,28 @@ describe('隆基系统全部页面验证', () => {
Cypress.on('uncaught:exception', (err, runnable) => false); Cypress.on('uncaught:exception', (err, runnable) => false);
// 创建环境选择对话框 // 创建环境选择对话框
const dialogHtml = ` const envDialogBodyContent = `
<div class="el-dialog__wrapper env-select-dialog" style="position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow: auto; margin: 0; z-index: 2000; background: rgba(0, 0, 0, 0.5);"> <div class="env-selection-status" style="margin-bottom: 15px; padding: 10px; background-color: #f0f9eb; border-radius: 4px; color: #67c23a;">
<div class="el-dialog" style="margin: 15vh auto 50px; width: 400px; background: #fff; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);"> 请选择要测试的环境
<div class="el-dialog__header" style="padding: 20px; border-bottom: 1px solid #eee;"> </div>
<span class="el-dialog__title" style="font-size: 18px; color: #303133;">选择测试环境</span> <div class="env-list" style="display: flex; gap: 10px;">
</div> <button class="env-btn el-button el-button--primary" data-env="DEV" style="flex: 1; padding: 12px; height: 40px; line-height: 1;">
<div class="el-dialog__body" style="padding: 20px;"> DEV环境
<div class="env-selection-status" style="margin-bottom: 15px; padding: 10px; background-color: #f0f9eb; border-radius: 4px; color: #67c23a;"> </button>
请选择要测试的环境 <button class="env-btn el-button el-button--success" data-env="UAT" style="flex: 1; padding: 12px; height: 40px; line-height: 1;">
</div> UAT环境
<div class="env-list" style="display: flex; gap: 10px;"> </button>
<button class="env-btn el-button el-button--primary" data-env="DEV" style="flex: 1; padding: 12px; height: 40px; line-height: 1;">
DEV环境
</button>
<button class="env-btn el-button el-button--success" data-env="UAT" style="flex: 1; padding: 12px; height: 40px; line-height: 1;">
UAT环境
</button>
</div>
</div>
</div>
</div> </div>
`; `;
const dialogHtml = createDialogTemplate({
dialogClass: 'env-select-dialog',
title: '选择测试环境',
width: '400px',
bodyContent: envDialogBodyContent,
footerContent: ''
});
// 添加对话框到页面 // 添加对话框到页面
cy.get('body').then($body => { cy.get('body').then($body => {
$body.append(dialogHtml); $body.append(dialogHtml);
@ -159,35 +185,101 @@ describe('隆基系统全部页面验证', () => {
} }
}; };
// 添加内存清理的公共方法 // 优化内存清理函数确保与Cypress命令链兼容
const cleanupMemory = () => { const cleanupMemory = () => {
// 执行垃圾回收 cy.log('🧹 执行内存清理...');
cy.task('clearMemory', null, {log: false});
// 记录清理前的内存使用情况
// 清理DOM快照 cy.window({log: false}).then(win => {
cy.get('body').then(() => { if (win.performance && win.performance.memory) {
const beforeMemory = Math.round(win.performance.memory.usedJSHeapSize / 1024 / 1024);
cy.log(`清理前内存使用: ${beforeMemory}MB`);
}
// 执行内存清理操作
try { try {
// 移除一些可能导致内存泄漏的元素 // 1. 清理DOM元素
Cypress.$('.el-loading-mask').remove(); const elementsToRemove = [
Cypress.$('.el-message').remove(); '.el-loading-mask',
Cypress.$('.el-message-box').remove(); '.el-message',
'.el-message-box:not(.is-message)',
'.el-notification',
'.el-drawer__wrapper:not(.active)',
'.el-tooltip__popper',
'.el-select-dropdown',
'.el-popover',
'.el-dropdown-menu',
'.v-modal'
];
// 清理事件监听器 - 只清理特定元素的事件 elementsToRemove.forEach(selector => {
Cypress.$('.el-dialog__wrapper').off(); if (Cypress.$(selector).length > 0) {
Cypress.$('.el-notification').off(); Cypress.$(selector).remove();
Cypress.$('.el-drawer__wrapper').off(); }
});
// 清理控制台 // 2. 清理iframe内容
Cypress.$('iframe').each((_, el) => {
try {
el.src = 'about:blank';
} catch (e) {
// 忽略错误
}
});
// 3. 清理大型表格内容
Cypress.$('.el-table__body tr').each((index, el) => {
if (index > 20) {
Cypress.$(el).remove();
}
});
// 4. 清理事件监听器(保留菜单相关元素)
Cypress.$('*:not(.el-menu):not(.el-menu-item):not(.el-submenu):not(.sidebar-menu):not(.el-scrollbar__view)').each((_, el) => {
try {
const element = Cypress.$(el);
// 只清理非菜单元素的事件
if (!element.closest('.el-menu, .sidebar-menu').length) {
element.off();
}
} catch (e) {
// 忽略错误
}
});
// 5. 清理定时器
let id = window.setTimeout(() => {}, 0);
while (id--) {
window.clearTimeout(id);
window.clearInterval(id);
}
// 6. 清理Vue相关引用
if (win.__VUE_DEVTOOLS_GLOBAL_HOOK__) {
win.__VUE_DEVTOOLS_GLOBAL_HOOK__.Vue = null;
}
// 7. 手动触发一些可能的内存释放
win.dispatchEvent(new Event('beforeunload'));
// 8. 清理控制台
console.clear(); console.clear();
} catch (e) { } catch (e) {
console.error('清理DOM时出错:', e); console.error('清理DOM时出错:', e);
} }
}).then(() => {
// 等待一段时间让垃圾回收生效
return cy.wait(1000, {log: false});
}).then(() => {
// 再次检查内存使用情况
return cy.window({log: false}).then(win => {
if (win.performance && win.performance.memory) {
const afterMemory = Math.round(win.performance.memory.usedJSHeapSize / 1024 / 1024);
cy.log(`清理后内存使用: ${afterMemory}MB`);
}
cy.log('🧹 内存清理完成');
});
}); });
// 手动清理一些可能的引用
if (window.gc) {
window.gc();
}
}; };
// 添加关闭tab的公共方法 // 添加关闭tab的公共方法
@ -311,18 +403,19 @@ describe('隆基系统全部页面验证', () => {
cy.log(`\n📑 页面 "${currentPage.name}" 发现 ${$inactiveTabs.length} 个待访问的tab页`); cy.log(`\n📑 页面 "${currentPage.name}" 发现 ${$inactiveTabs.length} 个待访问的tab页`);
testReport.summary.totalTabs += $inactiveTabs.length; testReport.summary.totalTabs += $inactiveTabs.length;
const processTab = (index = 0) => { // 使用迭代方式处理Tab页避免深度递归
if (index >= $inactiveTabs.length) { const processTabsSequentially = (tabs, startIndex = 0) => {
if (startIndex >= tabs.length) {
cy.log(`✅ 页面 "${currentPage.name}" 的所有tab页处理完成`); cy.log(`✅ 页面 "${currentPage.name}" 的所有tab页处理完成`);
return; return cy.wrap(null);
} }
const tabProgress = ((index + 1) / $inactiveTabs.length * 100).toFixed(1); const $currentTab = Cypress.$(tabs[startIndex]);
const $currentTab = Cypress.$($inactiveTabs[index]);
const tabText = $currentTab.text().trim(); const tabText = $currentTab.text().trim();
const tabProgress = ((startIndex + 1) / tabs.length * 100).toFixed(1);
cy.log(`🔸 正在处理tab页 [${index + 1}/${$inactiveTabs.length}] (${tabProgress}%): ${currentPage.name} > ${tabText}`);
cy.log(`🔸 正在处理tab页 [${startIndex + 1}/${tabs.length}] (${tabProgress}%): ${currentPage.name} > ${tabText}`);
const tabStartTime = new Date(); const tabStartTime = new Date();
const tabInfo = { const tabInfo = {
name: tabText, name: tabText,
@ -331,53 +424,59 @@ describe('隆基系统全部页面验证', () => {
duration: '', duration: '',
status: 'pending' status: 'pending'
}; };
if (!currentPage.tabs) { if (!currentPage.tabs) {
currentPage.tabs = []; currentPage.tabs = [];
} }
currentPage.tabs.push(tabInfo); currentPage.tabs.push(tabInfo);
cy.wrap($currentTab).click({force: true}).then(() => { // 处理单个Tab然后链式处理下一个
cy.log(`⏳ [${currentPage.name}] 已点击tab页: ${tabText},等待加载...`); return cy.wrap($currentTab)
.click({force: true})
let tabLoadRetry = 0; .then(() => {
const waitForTabLoad = () => { cy.log(`⏳ [${currentPage.name}] 已点击tab页: ${tabText},等待加载...`);
if (tabLoadRetry >= 30) { return waitForTabLoadWithTimeout(currentPage, tabInfo, tabStartTime, 30);
cy.log(`❌ [${currentPage.name}] Tab页 ${tabText} 加载超时 [${tabLoadRetry}次重试],继续下一个`); })
updateTabInfo(tabInfo, tabStartTime, 'timeout'); .then(() => {
processTab(index + 1); // 处理下一个Tab
return; return processTabsSequentially(tabs, startIndex + 1);
} });
};
cy.get('@pageBody').then($body => {
const isLoading = $body.find('.el-loading-mask').length > 0; // 等待Tab加载的函数使用超时机制
const hasTabError = $body.find('.el-message-box__message').length > 0 || const waitForTabLoadWithTimeout = (currentPage, tabInfo, tabStartTime, maxRetries, retryCount = 0) => {
$body.find('.el-message--error').length > 0; if (retryCount >= maxRetries) {
cy.log(`❌ [${currentPage.name}] Tab页 ${tabInfo.name} 加载超时 [${retryCount}次重试]`);
if (hasTabError) { updateTabInfo(tabInfo, tabStartTime, 'timeout');
cy.log(`❌ [${currentPage.name}] Tab页 ${tabText} 加载出现错误`); return cy.wrap(null);
updateTabInfo(tabInfo, tabStartTime, 'error'); }
processTab(index + 1);
return; return cy.get('@pageBody').then($body => {
} const isLoading = $body.find('.el-loading-mask').length > 0;
const hasTabError = $body.find('.el-message-box__message').length > 0 ||
if (isLoading) { $body.find('.el-message--error').length > 0;
tabLoadRetry++;
cy.log(`⏳ [${currentPage.name}] Tab页 ${tabText}${tabLoadRetry} 次检查加载状态...`); if (hasTabError) {
cy.wait(500).then(waitForTabLoad); cy.log(`❌ [${currentPage.name}] Tab页 ${tabInfo.name} 加载出现错误`);
} else { updateTabInfo(tabInfo, tabStartTime, 'error');
updateTabInfo(tabInfo, tabStartTime, 'success'); return cy.wrap(null);
cy.log(`✅ [${currentPage.name}] Tab页 ${tabText} 加载完成 (耗时: ${tabInfo.duration}秒)`); }
cy.wait(1000).then(() => processTab(index + 1));
} if (isLoading) {
cy.log(`⏳ [${currentPage.name}] Tab页 ${tabInfo.name}${retryCount + 1} 次检查加载状态...`);
return cy.wait(500).then(() => {
return waitForTabLoadWithTimeout(currentPage, tabInfo, tabStartTime, maxRetries, retryCount + 1);
}); });
}; } else {
updateTabInfo(tabInfo, tabStartTime, 'success');
waitForTabLoad(); cy.log(`✅ [${currentPage.name}] Tab页 ${tabInfo.name} 加载完成 (耗时: ${tabInfo.duration}秒)`);
return cy.wait(1000);
}
}); });
}; };
processTab(); // 开始处理所有Tab
return processTabsSequentially($inactiveTabs);
}; };
// 处理页面中的tab页 // 处理页面中的tab页
@ -510,38 +609,50 @@ describe('隆基系统全部页面验证', () => {
pageInfo.hasThirdMenu = true; pageInfo.hasThirdMenu = true;
cy.log(`${menuText} 包含 ${$thirdMenuItems.length} 个三级菜单`); cy.log(`${menuText} 包含 ${$thirdMenuItems.length} 个三级菜单`);
const processThirdMenu = (thirdIndex = 0) => { // 使用迭代方式处理三级菜单,避免深度递归
if (thirdIndex >= $thirdMenuItems.length) { const processThirdMenuSequentially = (items, startIndex = 0) => {
if (startIndex >= items.length) {
pageInfo.endTime = formatTime(new Date()); pageInfo.endTime = formatTime(new Date());
const thirdMenuProgress = (100).toFixed(1); const thirdMenuProgress = (100).toFixed(1);
cy.log(`✅ 完成菜单 "${menuText}" 的所有三级菜单处理 (${thirdMenuProgress}%)`); cy.log(`✅ 完成菜单 "${menuText}" 的所有三级菜单处理 (${thirdMenuProgress}%)`);
closeActiveTab(menuText); closeActiveTab(menuText);
cy.wait(1000).then(() => processMenuItems(index + 1)); return cy.wait(1000).then(() => processMenuItems(index + 1));
return;
} }
const thirdMenuProgress = ((thirdIndex + 1) / $thirdMenuItems.length * 100).toFixed(1); const thirdMenuProgress = ((startIndex + 1) / items.length * 100).toFixed(1);
if (thirdIndex > 0) { if (startIndex > 0) {
cy.wrap($menuItem).click(); return cy.wrap($menuItem).click()
cy.wait(1000); .wait(1000)
.then(() => {
return cy.get('@thirdMenuItems')
.eq(startIndex)
.then(processCurrentThirdMenuItem);
});
} else {
return cy.get('@thirdMenuItems')
.eq(startIndex)
.then(processCurrentThirdMenuItem);
} }
cy.get('@thirdMenuItems') function processCurrentThirdMenuItem($thirdMenu) {
.eq(thirdIndex) const thirdMenuText = $thirdMenu.text().trim();
.then($thirdMenu => { cy.log(`\n🔸 处理三级菜单 [${startIndex + 1}/${items.length}] (${thirdMenuProgress}%): ${menuText} > ${thirdMenuText}`);
const thirdMenuText = $thirdMenu.text().trim();
cy.log(`\n🔸 处理三级菜单 [${thirdIndex + 1}/${$thirdMenuItems.length}] (${thirdMenuProgress}%): ${menuText} > ${thirdMenuText}`);
const thirdMenuPage = new PageInfo(thirdMenuText, true, menuText).addToBatch(); const thirdMenuPage = new PageInfo(thirdMenuText, true, menuText).addToBatch();
cy.wrap($thirdMenu).click(); return cy.wrap($thirdMenu).click()
waitForPageLoad(thirdMenuPage, new Date()); .then(() => {
cy.wait(1000).then(() => processThirdMenu(thirdIndex + 1)); waitForPageLoad(thirdMenuPage, new Date());
}); return cy.wait(1000);
})
.then(() => {
return processThirdMenuSequentially(items, startIndex + 1);
});
}
}; };
processThirdMenu(); return processThirdMenuSequentially($thirdMenuItems);
}; };
// 处理没有三级菜单的情况 // 处理没有三级菜单的情况
@ -609,35 +720,35 @@ describe('隆基系统全部页面验证', () => {
cy.log(`🔍 找到 ${menuItems.length} 个可测试的菜单项`); cy.log(`🔍 找到 ${menuItems.length} 个可测试的菜单项`);
// 创建菜单选择对话框 // 创建菜单选择对话框
const dialogHtml = ` const menuDialogBodyContent = `
<div class="el-dialog__wrapper menu-select-dialog" style="position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow: auto; margin: 0; z-index: 2000; background: rgba(0, 0, 0, 0.5);"> <div class="menu-selection-status" style="margin-bottom: 15px; padding: 10px; background-color: #f0f9eb; border-radius: 4px; color: #67c23a;">
<div class="el-dialog" style="margin: 15vh auto 50px; width: 600px; background: #fff; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);"> 请选择需要测试的菜单项然后点击"开始测试"按钮开始测试
<div class="el-dialog__header" style="padding: 20px; border-bottom: 1px solid #eee;"> </div>
<span class="el-dialog__title" style="font-size: 18px; color: #303133;">选择要测试的菜单</span> <div class="menu-list">
${menuItems.map((item, idx) => `
<div class="menu-item" style="margin-bottom: 10px;">
<label style="display: flex; align-items: center;">
<input type="checkbox" value="${idx}" style="margin-right: 8px;">
<span>${item.text}</span>
</label>
</div> </div>
<div class="el-dialog__body" style="padding: 20px; max-height: 50vh; overflow-y: auto;"> `).join('')}
<div class="menu-selection-status" style="margin-bottom: 15px; padding: 10px; background-color: #f0f9eb; border-radius: 4px; color: #67c23a;">
请选择需要测试的菜单项然后点击"开始测试"按钮开始测试
</div>
<div class="menu-list">
${menuItems.map((item, idx) => `
<div class="menu-item" style="margin-bottom: 10px;">
<label style="display: flex; align-items: center;">
<input type="checkbox" value="${idx}" style="margin-right: 8px;">
<span>${item.text}</span>
</label>
</div>
`).join('')}
</div>
</div>
<div class="el-dialog__footer" style="padding: 20px; text-align: right; border-top: 1px solid #eee;">
<button class="select-all-btn el-button el-button--default" style="margin-right: 10px;">全选</button>
<button class="start-test-btn el-button el-button--primary" disabled>开始测试</button>
</div>
</div>
</div> </div>
`; `;
const menuDialogFooterContent = `
<button class="select-all-btn el-button el-button--default" style="margin-right: 10px;">全选</button>
<button class="start-test-btn el-button el-button--primary" disabled>开始测试</button>
`;
const dialogHtml = createDialogTemplate({
dialogClass: 'menu-select-dialog',
title: '选择要测试的菜单',
width: '600px',
bodyContent: menuDialogBodyContent,
footerContent: menuDialogFooterContent
});
// 添加对话框到页面 // 添加对话框到页面
Cypress.$('body').append(dialogHtml); Cypress.$('body').append(dialogHtml);
@ -798,152 +909,172 @@ describe('隆基系统全部页面验证', () => {
cy.task('readAllReports', environment).then((allBatches) => { cy.task('readAllReports', environment).then((allBatches) => {
cy.log(`📊 读取到 ${allBatches.length} 个批次报告`); cy.log(`📊 读取到 ${allBatches.length} 个批次报告`);
// 合并所有批次数据 // 使用更高效的方式合并批次数据
const combinedReport = { const combinedReport = {
pages: [], pages: [],
failedPages: [] failedPages: []
}; };
// 预计算总页面数和失败页面数,提高性能
let totalPages = 0;
let totalFailedPages = 0;
// 使用for循环替代forEach减少函数调用和闭包
if (Array.isArray(allBatches)) { if (Array.isArray(allBatches)) {
allBatches.forEach(batch => { for (let i = 0; i < allBatches.length; i++) {
if (batch.pages) { const batch = allBatches[i];
cy.log(`合并批次报告: ${batch.pages.length} 个页面`); if (batch.pages && Array.isArray(batch.pages)) {
combinedReport.pages.push(...batch.pages); totalPages += batch.pages.length;
} }
if (batch.failedPages) { if (batch.failedPages && Array.isArray(batch.failedPages)) {
combinedReport.failedPages.push(...batch.failedPages); totalFailedPages += batch.failedPages.length;
} }
}); }
} }
// 添加当前批次的数据 // 添加当前批次
if (testReport.currentBatch.pages.length > 0) { if (testReport.currentBatch.pages && Array.isArray(testReport.currentBatch.pages)) {
cy.log(`添加当前批次: ${testReport.currentBatch.pages.length} 个页面`); totalPages += testReport.currentBatch.pages.length;
combinedReport.pages.push(...testReport.currentBatch.pages);
} }
if (testReport.currentBatch.failedPages.length > 0) { if (testReport.currentBatch.failedPages && Array.isArray(testReport.currentBatch.failedPages)) {
combinedReport.failedPages.push(...testReport.currentBatch.failedPages); totalFailedPages += testReport.currentBatch.failedPages.length;
} }
// 预分配数组大小,避免动态扩容
combinedReport.pages = new Array(totalPages);
combinedReport.failedPages = new Array(totalFailedPages);
// 填充数据使用for循环替代forEach
let pageIndex = 0;
let failedPageIndex = 0;
if (Array.isArray(allBatches)) {
for (let i = 0; i < allBatches.length; i++) {
const batch = allBatches[i];
if (batch.pages && Array.isArray(batch.pages)) {
for (let j = 0; j < batch.pages.length; j++) {
combinedReport.pages[pageIndex++] = batch.pages[j];
}
}
if (batch.failedPages && Array.isArray(batch.failedPages)) {
for (let j = 0; j < batch.failedPages.length; j++) {
combinedReport.failedPages[failedPageIndex++] = batch.failedPages[j];
}
}
}
}
// 添加当前批次使用for循环
if (testReport.currentBatch.pages && Array.isArray(testReport.currentBatch.pages)) {
for (let i = 0; i < testReport.currentBatch.pages.length; i++) {
combinedReport.pages[pageIndex++] = testReport.currentBatch.pages[i];
}
}
if (testReport.currentBatch.failedPages && Array.isArray(testReport.currentBatch.failedPages)) {
for (let i = 0; i < testReport.currentBatch.failedPages.length; i++) {
combinedReport.failedPages[failedPageIndex++] = testReport.currentBatch.failedPages[i];
}
}
cy.log(`📊 总计: ${combinedReport.pages.length} 个页面, ${testReport.summary.totalTabs} 个Tab页`); cy.log(`📊 总计: ${combinedReport.pages.length} 个页面, ${testReport.summary.totalTabs} 个Tab页`);
const htmlReport = ` // 使用字符串拼接而不是模板字符串生成HTML提高性能
<!DOCTYPE html> let htmlReport = '<!DOCTYPE html>\n<html>\n<head>\n';
<html> htmlReport += ' <meta charset="UTF-8">\n';
<head> htmlReport += ' <title>系统页面可用性测试报告</title>\n';
<meta charset="UTF-8"> htmlReport += ' <style>\n';
<title>系统页面可用性测试报告</title> htmlReport += ' body { font-family: Arial, sans-serif; margin: 20px; }\n';
<style> htmlReport += ' .summary { margin-bottom: 20px; }\n';
body { font-family: Arial, sans-serif; margin: 20px; } htmlReport += ' table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }\n';
.summary { margin-bottom: 20px; } htmlReport += ' th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }\n';
table { border-collapse: collapse; width: 100%; margin-bottom: 20px; } htmlReport += ' th { background-color: #f4f4f4; }\n';
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; } htmlReport += ' .success { color: green; }\n';
th { background-color: #f4f4f4; } htmlReport += ' .error { color: red; }\n';
.success { color: green; } htmlReport += ' .timeout { color: orange; }\n';
.error { color: red; } htmlReport += ' .pending { color: gray; }\n';
.timeout { color: orange; } htmlReport += ' .tab-row { background-color: #f9f9f9; }\n';
.pending { color: gray; } htmlReport += ' .failed-pages { margin-top: 20px; }\n';
.tab-row { background-color: #f9f9f9; } htmlReport += ' .failed-page { background-color: #fff0f0; margin-bottom: 10px; padding: 10px; border-left: 4px solid #ff0000; }\n';
.failed-pages { margin-top: 20px; } htmlReport += ' </style>\n';
.failed-page { background-color: #fff0f0; margin-bottom: 10px; padding: 10px; border-left: 4px solid #ff0000; } htmlReport += '</head>\n<body>\n';
</style> htmlReport += ' <h1>系统页面可用性测试报告</h1>\n';
</head> htmlReport += ' <div class="summary">\n';
<body> htmlReport += ' <h2>测试摘要</h2>\n';
<h1>系统页面可用性测试报告</h1> htmlReport += ' <p>测试环境: ' + environment + '</p>\n';
<div class="summary"> htmlReport += ' <p>总页面数: ' + combinedReport.pages.length + '</p>\n';
<h2>测试摘要</h2> htmlReport += ' <p>总Tab页数: ' + testReport.summary.totalTabs + '</p>\n';
<p>测试环境: ${environment}</p> htmlReport += ' <p>失败页面数: ' + combinedReport.failedPages.length + '</p>\n';
<p>总页面数: ${combinedReport.pages.length}</p> htmlReport += ' <p>开始时间: ' + testReport.summary.startTime + '</p>\n';
<p>总Tab页数: ${testReport.summary.totalTabs}</p> htmlReport += ' <p>结束时间: ' + testReport.summary.endTime + '</p>\n';
<p>失败页面数: ${combinedReport.failedPages.length}</p> htmlReport += ' <p>总耗时: ' + calculateDuration(new Date(testReport.summary.startTime), new Date(testReport.summary.endTime)) + '秒</p>\n';
<p>开始时间: ${testReport.summary.startTime}</p> htmlReport += ' </div>\n\n';
<p>结束时间: ${testReport.summary.endTime}</p>
<p>总耗时: ${calculateDuration(new Date(testReport.summary.startTime), new Date(testReport.summary.endTime))}</p> // 生成失败页面列表使用for循环
</div> if (combinedReport.failedPages.length > 0) {
htmlReport += ' <div class="failed-pages">\n';
${combinedReport.failedPages.length > 0 ? ` htmlReport += ' <h2>失败页面列表</h2>\n';
<div class="failed-pages">
<h2>失败页面列表</h2>
${combinedReport.failedPages.map(page => `
<div class="failed-page">
<h3>${page.name}</h3>
<p>失败时间: ${page.time}</p>
<p>错误信息: ${page.error}</p>
<p>耗时: ${page.duration}</p>
</div>
`).join('')}
</div>
` : ''}
<h2>详细测试结果</h2>
<table>
<tr>
<th>页面名称</th>
<th>开始时间</th>
<th>Tab页名称</th>
<th>Tab开始时间</th>
<th>Tab结束时间</th>
<th>加载时长()</th>
<th>状态</th>
</tr>
${combinedReport.pages.map(page => {
if (!page.tabs || page.tabs.length === 0) {
return `
<tr>
<td>${page.name}</td>
<td>${page.startTime}</td>
<td colspan="5">无Tab页</td>
</tr>`;
}
return page.tabs.map((tab, index) => `
<tr class="tab-row">
<td>${index === 0 ? page.name : ''}</td>
<td>${index === 0 ? page.startTime : ''}</td>
<td>${tab.name}</td>
<td>${tab.startTime}</td>
<td>${tab.endTime}</td>
<td>${tab.duration}</td>
<td class="${tab.status}">${tab.status}</td>
</tr>`).join('')
}).join('')}
</table>
</body>
</html>`;
// 将报告写入到时间戳目录中
cy.writeFile(`${reportDir}/${environment}-test-report-${timestamp}.html`, htmlReport).then(() => {
cy.log(`✅ 已生成测试报告: ${reportDir}/${environment}-test-report-${timestamp}.html`);
// 添加完成提示对话框 for (let i = 0; i < combinedReport.failedPages.length; i++) {
const completionDialogHtml = ` const page = combinedReport.failedPages[i];
<div class="el-dialog__wrapper completion-dialog" style="position: fixed; top: 0; right: 0; bottom: 0; left: 0; overflow: auto; margin: 0; z-index: 2001; background: rgba(0, 0, 0, 0.5);"> htmlReport += ' <div class="failed-page">\n';
<div class="el-dialog" style="margin: 15vh auto 50px; width: 500px; background: #fff; border-radius: 4px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);"> htmlReport += ' <h3>' + page.name + '</h3>\n';
<div class="el-dialog__header" style="padding: 20px; border-bottom: 1px solid #eee;"> htmlReport += ' <p>失败时间: ' + page.time + '</p>\n';
<span class="el-dialog__title" style="font-size: 18px; color: #303133;"> 测试完成</span> htmlReport += ' <p>错误信息: ' + page.error + '</p>\n';
</div> htmlReport += ' <p>耗时: ' + page.duration + '秒</p>\n';
<div class="el-dialog__body" style="padding: 30px 20px;"> htmlReport += ' </div>\n';
<div style="margin-bottom: 15px;"> }
<p style="margin: 0 0 15px 0;">测试报告已生成完成</p>
<p style="margin: 0; color: #666;">报告目录<br><span style="color: #409EFF; word-break: break-all;">${reportDir}</span></p> htmlReport += ' </div>\n';
<p style="margin: 10px 0 0 0; color: #666;">报告文件<br><span style="color: #409EFF; word-break: break-all;">${environment}-test-report-${timestamp}.html</span></p> }
</div>
</div> // 生成详细测试结果表格使用for循环
<div class="el-dialog__footer" style="padding: 20px; text-align: right; border-top: 1px solid #eee;"> htmlReport += ' <h2>详细测试结果</h2>\n';
<button class="close-dialog-btn el-button el-button--primary" style="padding: 9px 15px;">确定</button> htmlReport += ' <table>\n';
</div> htmlReport += ' <tr>\n';
</div> htmlReport += ' <th>页面名称</th>\n';
</div> htmlReport += ' <th>开始时间</th>\n';
`; htmlReport += ' <th>Tab页名称</th>\n';
htmlReport += ' <th>Tab开始时间</th>\n';
// 添加对话框到页面 htmlReport += ' <th>Tab结束时间</th>\n';
cy.get('body').then($body => { htmlReport += ' <th>加载时长(秒)</th>\n';
$body.append(completionDialogHtml); htmlReport += ' <th>状态</th>\n';
htmlReport += ' </tr>\n';
// 绑定关闭按钮事件
Cypress.$('.close-dialog-btn').on('click', function() { // 生成表格内容使用for循环
Cypress.$('.completion-dialog').remove(); for (let i = 0; i < combinedReport.pages.length; i++) {
}); const page = combinedReport.pages[i];
}); if (!page.tabs || page.tabs.length === 0) {
htmlReport += ' <tr>\n';
htmlReport += ' <td>' + page.name + '</td>\n';
htmlReport += ' <td>' + page.startTime + '</td>\n';
htmlReport += ' <td colspan="5">无Tab页</td>\n';
htmlReport += ' </tr>\n';
} else {
for (let j = 0; j < page.tabs.length; j++) {
const tab = page.tabs[j];
htmlReport += ' <tr class="tab-row">\n';
htmlReport += ' <td>' + (j === 0 ? page.name : '') + '</td>\n';
htmlReport += ' <td>' + (j === 0 ? page.startTime : '') + '</td>\n';
htmlReport += ' <td>' + tab.name + '</td>\n';
htmlReport += ' <td>' + tab.startTime + '</td>\n';
htmlReport += ' <td>' + tab.endTime + '</td>\n';
htmlReport += ' <td>' + tab.duration + '</td>\n';
htmlReport += ' <td class="' + tab.status + '">' + tab.status + '</td>\n';
htmlReport += ' </tr>\n';
}
}
}
htmlReport += ' </table>\n';
htmlReport += '</body>\n</html>';
// 将报告写入到时间戳目录中
const reportFile = `${environment}-test-report-${timestamp}.html`;
cy.writeFile(`${reportDir}/${reportFile}`, htmlReport).then(() => {
cy.log(`✅ 已生成测试报告: ${reportDir}/${reportFile}`);
// 显示完成对话框
showCompletionDialog(reportDir, reportFile);
}); });
// 清理批次报告文件 // 清理批次报告文件
@ -955,4 +1086,38 @@ describe('隆基系统全部页面验证', () => {
after(() => { after(() => {
cleanupMemory(); cleanupMemory();
}); });
// 添加完成对话框
const showCompletionDialog = (reportDir, reportFile) => {
// 创建完成对话框的HTML
const completionDialogBodyContent = `
<div style="margin-bottom: 15px;">
<p style="margin: 0 0 15px 0;">测试报告已生成完成</p>
<p style="margin: 0; color: #666;">报告目录<br><span style="color: #409EFF; word-break: break-all;">${reportDir}</span></p>
<p style="margin: 10px 0 0 0; color: #666;">报告文件<br><span style="color: #409EFF; word-break: break-all;">${reportFile}</span></p>
</div>
`;
const completionDialogFooterContent = `
<button class="close-dialog-btn el-button el-button--primary" style="padding: 9px 15px;">确定</button>
`;
const completionDialogHtml = createDialogTemplate({
dialogClass: 'completion-dialog',
title: '✅ 测试完成',
width: '500px',
bodyContent: completionDialogBodyContent,
footerContent: completionDialogFooterContent
});
// 添加对话框到页面
cy.get('body').then($body => {
$body.append(completionDialogHtml);
// 绑定关闭按钮事件
Cypress.$('.close-dialog-btn').on('click', function() {
Cypress.$('.completion-dialog').remove();
});
});
};
}); });