第一次
This commit is contained in:
parent
84f9d72471
commit
6b5982bd2e
@ -7,13 +7,13 @@ const {defineConfig, devices} = require('@playwright/test');
|
|||||||
module.exports = defineConfig({
|
module.exports = defineConfig({
|
||||||
testDir: './tests',
|
testDir: './tests',
|
||||||
/* 测试超时时间 */
|
/* 测试超时时间 */
|
||||||
timeout: 120 * 1000,
|
timeout: 300 * 1000,
|
||||||
/* 每个测试的预期状态 */
|
/* 每个测试的预期状态 */
|
||||||
expect: {
|
expect: {
|
||||||
/**
|
/**
|
||||||
* 断言超时时间
|
* 断言超时时间
|
||||||
*/
|
*/
|
||||||
timeout: 15000
|
timeout: 30000
|
||||||
},
|
},
|
||||||
/* 测试运行并发数 */
|
/* 测试运行并发数 */
|
||||||
fullyParallel: false,
|
fullyParallel: false,
|
||||||
|
|||||||
@ -89,7 +89,7 @@ class LongiMainPage extends BasePage {
|
|||||||
await this.clickExpandMenu();
|
await this.clickExpandMenu();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
console.log('菜单已经处于展开状态');
|
// console.log('菜单已经处于展开状态');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -108,7 +108,79 @@ class LongiMainPage extends BasePage {
|
|||||||
|
|
||||||
// 检查是否成功加载数据
|
// 检查是否成功加载数据
|
||||||
if (menuItems && Array.isArray(menuItems) && menuItems.length > 0) {
|
if (menuItems && Array.isArray(menuItems) && menuItems.length > 0) {
|
||||||
console.log(`从文件 ${process.env.BASE_URL} 成功加载了 ${menuItems.length} 个菜单项`);
|
console.log(`从文件 ${process.env.MENU_DATA_FILE_PATH} 成功加载了 ${menuItems.length} 个菜单项`);
|
||||||
|
|
||||||
|
//这里因为是序列化回来的,所以没有对应每个menu的element元素,需要补充回来。
|
||||||
|
console.log('开始恢复菜单项的element元素...');
|
||||||
|
|
||||||
|
// 等待菜单加载完成
|
||||||
|
await this.page.waitForSelector(this.selectors.sideNav, {
|
||||||
|
timeout: parseInt(process.env.MENU_TIME_OUT || '30000', 10)
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取当前页面上的所有菜单元素
|
||||||
|
const currentMenuElements = await this.page.locator(this.selectors.menuItems).all();
|
||||||
|
console.log(`当前页面上找到 ${currentMenuElements.length} 个菜单元素`);
|
||||||
|
|
||||||
|
// 为每个菜单项恢复element元素
|
||||||
|
for (const menuItem of menuItems) {
|
||||||
|
let foundElement = null;
|
||||||
|
let sameTextElements = [];
|
||||||
|
|
||||||
|
// 首先找到所有文本匹配的元素
|
||||||
|
for (const element of currentMenuElements) {
|
||||||
|
const elementText = await element.textContent();
|
||||||
|
if (elementText.trim() === menuItem.text) {
|
||||||
|
// 检查是否是一级菜单
|
||||||
|
const isTopMenu = await element.locator(this.selectors.firstLevelIndicator).count() > 0;
|
||||||
|
if (!isTopMenu) {
|
||||||
|
sameTextElements.push(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果找到多个同名菜单,根据是否有三级菜单来区分
|
||||||
|
if (sameTextElements.length > 0) {
|
||||||
|
for (const element of sameTextElements) {
|
||||||
|
// 检查是否有三级菜单指示器
|
||||||
|
const hasThirdMenu = await element.evaluate(el => {
|
||||||
|
return el.classList.contains('el-sub-menu__title') ||
|
||||||
|
el.querySelector('.el-submenu__icon-arrow') !== null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasThirdMenu === menuItem.hasThirdMenu) {
|
||||||
|
foundElement = element;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果还没找到,就用第一个匹配的元素
|
||||||
|
if (!foundElement && sameTextElements.length > 0) {
|
||||||
|
foundElement = sameTextElements[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundElement) {
|
||||||
|
menuItem.element = foundElement;
|
||||||
|
console.log(`✅ 成功恢复菜单项 "${menuItem.text}" (${menuItem.hasThirdMenu ? '有' : '无'}三级菜单) 的element元素`);
|
||||||
|
} else {
|
||||||
|
console.warn(`⚠️ 无法恢复菜单项 "${menuItem.text}" (${menuItem.hasThirdMenu ? '有' : '无'}三级菜单) 的element元素`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查是否所有菜单项都恢复了element元素
|
||||||
|
const missingElementCount = menuItems.filter(item => !item.element).length;
|
||||||
|
if (missingElementCount > 0) {
|
||||||
|
console.warn(`⚠️ 有 ${missingElementCount} 个菜单项未能恢复element元素`);
|
||||||
|
|
||||||
|
// 如果大部分菜单项都没有恢复element元素,可能需要重新查找
|
||||||
|
if (missingElementCount > menuItems.length / 2) {
|
||||||
|
console.log('由于大部分菜单项未能恢复element元素,将重新查找所有菜单项');
|
||||||
|
await this.expandSideMenu();
|
||||||
|
return await this.findAndSaveMenuItems();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return menuItems;
|
return menuItems;
|
||||||
} else {
|
} else {
|
||||||
await this.expandSideMenu();
|
await this.expandSideMenu();
|
||||||
@ -493,18 +565,13 @@ class LongiMainPage extends BasePage {
|
|||||||
await this.handleThreeLevelMenu(menu);
|
await this.handleThreeLevelMenu(menu);
|
||||||
} else {
|
} else {
|
||||||
// 处理二级菜单点击
|
// 处理二级菜单点击
|
||||||
await this.handleMenuClick(menu);
|
// await this.handleMenuClick(menu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 等待页面加载完成,包括检查加载遮罩和错误提示
|
|
||||||
* @param {Object} menu 菜单对象,包含text等信息
|
|
||||||
* @param {string} [subMenuText] 子菜单文本,可选
|
|
||||||
* @returns {Promise<{success: boolean, error: string|null}>}
|
|
||||||
*/
|
|
||||||
async waitForPageLoadWithRetry(menu, subMenuText = '') {
|
async waitForPageLoadWithRetry(menu, subMenuText = '') {
|
||||||
const pageName = subMenuText ? `${menu.text} > ${subMenuText}` : menu.text;
|
const pageName = subMenuText ? `${menu.text} > ${subMenuText}` : menu.text;
|
||||||
|
const pageStartTime = Date.now();
|
||||||
console.log(`等待页面 ${pageName} 数据加载...`);
|
console.log(`等待页面 ${pageName} 数据加载...`);
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
@ -512,8 +579,18 @@ class LongiMainPage extends BasePage {
|
|||||||
retryInterval: 500
|
retryInterval: 500
|
||||||
};
|
};
|
||||||
|
|
||||||
// 检查页面状态的内部方法
|
let retryCount = 0;
|
||||||
const checkPageStatus = async () => {
|
|
||||||
|
const check = async () => {
|
||||||
|
try {
|
||||||
|
// 如果超过最大重试次数,记录失败并继续
|
||||||
|
if (retryCount >= config.maxRetries) {
|
||||||
|
const reason = `页面加载超时 (${config.maxRetries} 次重试)`;
|
||||||
|
this.recordFailedPage(pageName, reason, pageStartTime);
|
||||||
|
return {success: false, error: reason};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查页面状态
|
||||||
const selectors = {
|
const selectors = {
|
||||||
loadingMask: '.el-loading-mask',
|
loadingMask: '.el-loading-mask',
|
||||||
errorBox: '.el-message-box__message',
|
errorBox: '.el-message-box__message',
|
||||||
@ -522,43 +599,48 @@ class LongiMainPage extends BasePage {
|
|||||||
|
|
||||||
// 检查错误状态
|
// 检查错误状态
|
||||||
for (const [key, selector] of Object.entries(selectors)) {
|
for (const [key, selector] of Object.entries(selectors)) {
|
||||||
const count = await this.page.locator(selector).count();
|
const elements = await this.page.locator(selector).all();
|
||||||
if (count > 0 && key !== 'loadingMask') {
|
if (elements.length > 0 && key !== 'loadingMask') {
|
||||||
const errorText = await this.page.locator(selector).textContent();
|
const errorText = await elements[0].textContent();
|
||||||
return {
|
const reason = `页面加载出现错误: ${errorText}`;
|
||||||
success: false,
|
this.recordFailedPage(pageName, reason, pageStartTime);
|
||||||
error: `页面 ${pageName} 加载出现错误: ${errorText}`
|
return {success: false, error: reason};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查加载状态
|
// 检查加载状态
|
||||||
const isLoading = await this.page.locator(selectors.loadingMask).count() > 0;
|
const loadingMasks = await this.page.locator(selectors.loadingMask).all();
|
||||||
if (!isLoading) {
|
if (loadingMasks.length > 0) {
|
||||||
await this.page.waitForTimeout(1000); // 额外等待一秒确保页面稳定
|
retryCount++;
|
||||||
return {success: true, error: null};
|
|
||||||
}
|
|
||||||
|
|
||||||
return null; // 继续等待
|
|
||||||
};
|
|
||||||
|
|
||||||
// 重试逻辑
|
|
||||||
for (let retryCount = 0; retryCount < config.maxRetries; retryCount++) {
|
|
||||||
try {
|
|
||||||
const status = await checkPageStatus();
|
|
||||||
if (status) return status;
|
|
||||||
|
|
||||||
console.log(`等待加载中... (${retryCount + 1}/${config.maxRetries})`);
|
|
||||||
await this.page.waitForTimeout(config.retryInterval);
|
await this.page.waitForTimeout(config.retryInterval);
|
||||||
} catch (error) {
|
return await check();
|
||||||
console.error(`检查页面加载状态时出错: ${error}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
// 加载完成,等待页面稳定
|
||||||
success: false,
|
await this.page.waitForTimeout(1000);
|
||||||
error: `页面 ${pageName} 加载超时 (${config.maxRetries} 次重试)`
|
return {success: true, error: null};
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
// 如果是页面关闭错误,直接返回
|
||||||
|
if (error.message.includes('Target page, context or browser has been closed')) {
|
||||||
|
return {success: false, error: '页面已关闭'};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他错误继续重试
|
||||||
|
retryCount++;
|
||||||
|
await this.page.waitForTimeout(config.retryInterval);
|
||||||
|
return await check();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
return await check();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加记录失败页面的方法
|
||||||
|
recordFailedPage(pageName, reason, startTime) {
|
||||||
|
const duration = Date.now() - startTime;
|
||||||
|
console.error(`页面加载失败: ${pageName}, 原因: ${reason}, 耗时: ${duration}ms`);
|
||||||
|
// 这里可以添加将失败信息写入文件的逻辑
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -621,66 +703,36 @@ class LongiMainPage extends BasePage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 点击并等待三级菜单项
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
async clickAndWaitThirdMenuItem(menu, currentMenuText) {
|
|
||||||
try {
|
|
||||||
const currentThirdMenuItem = {
|
|
||||||
text: currentMenuText,
|
|
||||||
element: await this.page.locator(this.selectors.thirdLevelMenu)
|
|
||||||
.filter({hasText: currentMenuText})
|
|
||||||
.first()
|
|
||||||
};
|
|
||||||
|
|
||||||
await this.handleMenuClick(currentThirdMenuItem, menu.text);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`点击三级菜单项失败 [${currentMenuText}]:`, error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理三级菜单
|
* 处理三级菜单
|
||||||
* @param {Object} menu 菜单对象
|
* @param {Object} menu 菜单对象
|
||||||
*/
|
*/
|
||||||
async handleThreeLevelMenu(menu) {
|
async handleThreeLevelMenu(menu) {
|
||||||
try {
|
try {
|
||||||
// 获取并点击二级菜单
|
console.log(`正在处理 ${menu.text}菜单`)
|
||||||
const menuElement = await this.getSecondLevelMenu(menu);
|
await menu.element.click();
|
||||||
if (!menuElement) {
|
|
||||||
throw new Error(`未找到二级菜单: ${menu.text}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取三级菜单项
|
// 等待一个短暂的时间让弹出层出现
|
||||||
const {thirdMenuTexts, totalItems} = await this.getThirdLevelMenuItems(menuElement);
|
await this.page.waitForTimeout(500);
|
||||||
if (totalItems === 0) {
|
|
||||||
|
// 检查三级菜单是否存在
|
||||||
|
const hasThirdMenu = await this.page.evaluate(() => {
|
||||||
|
const elements = document.querySelectorAll('.el-popper.is-light.el-popover .menuTitle.canClick');
|
||||||
|
return elements.length > 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasThirdMenu) {
|
||||||
console.log(`⚠️ ${menu.text} 没有找到三级菜单项`);
|
console.log(`⚠️ ${menu.text} 没有找到三级菜单项`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`✓ ${menu.text} 包含 ${totalItems} 个三级菜单`);
|
console.log(`✓ ${menu.text} 找到三级菜单`);
|
||||||
|
|
||||||
// 处理每个三级菜单项
|
|
||||||
for (let i = 0; i < totalItems; i++) {
|
|
||||||
const progress = (((i + 1) / totalItems) * 100).toFixed(1);
|
|
||||||
const currentMenuText = thirdMenuTexts[i];
|
|
||||||
|
|
||||||
console.log(`\n🔸 处理三级菜单 [${i + 1}/${totalItems}] (${progress}%): ${menu.text} > ${currentMenuText}`);
|
|
||||||
|
|
||||||
// 重新点击二级菜单(仅从第二个开始)
|
|
||||||
if (i > 0) {
|
|
||||||
await menuElement.click();
|
|
||||||
await this.page.waitForTimeout(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.clickAndWaitThirdMenuItem(menu, currentMenuText);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`✅ 完成菜单 "${menu.text}" 的所有三级菜单处理`);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`处理三级菜单时出错 [${menu.text}]:`, error.message);
|
console.error(`处理三级菜单时出错 [${menu.text}]:`, error.message);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user