From 6b5982bd2e56edbf2c9d7ab8fed22e21f7629361 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Wed, 5 Mar 2025 16:21:04 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AC=AC=E4=B8=80=E6=AC=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- playwright.config.js | 4 +- tests/pages/LongiMainPage.js | 248 +++++++++++++++++++++-------------- 2 files changed, 152 insertions(+), 100 deletions(-) diff --git a/playwright.config.js b/playwright.config.js index 280fc06..48178b5 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -7,13 +7,13 @@ const {defineConfig, devices} = require('@playwright/test'); module.exports = defineConfig({ testDir: './tests', /* 测试超时时间 */ - timeout: 120 * 1000, + timeout: 300 * 1000, /* 每个测试的预期状态 */ expect: { /** * 断言超时时间 */ - timeout: 15000 + timeout: 30000 }, /* 测试运行并发数 */ fullyParallel: false, diff --git a/tests/pages/LongiMainPage.js b/tests/pages/LongiMainPage.js index b42c48e..9c60de7 100644 --- a/tests/pages/LongiMainPage.js +++ b/tests/pages/LongiMainPage.js @@ -89,7 +89,7 @@ class LongiMainPage extends BasePage { await this.clickExpandMenu(); return true; } else { - console.log('菜单已经处于展开状态'); + // console.log('菜单已经处于展开状态'); return false; } } catch (error) { @@ -108,7 +108,79 @@ class LongiMainPage extends BasePage { // 检查是否成功加载数据 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; } else { await this.expandSideMenu(); @@ -493,18 +565,13 @@ class LongiMainPage extends BasePage { await this.handleThreeLevelMenu(menu); } 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 = '') { const pageName = subMenuText ? `${menu.text} > ${subMenuText}` : menu.text; + const pageStartTime = Date.now(); console.log(`等待页面 ${pageName} 数据加载...`); const config = { @@ -512,53 +579,68 @@ class LongiMainPage extends BasePage { retryInterval: 500 }; - // 检查页面状态的内部方法 - const checkPageStatus = async () => { - const selectors = { - loadingMask: '.el-loading-mask', - errorBox: '.el-message-box__message', - errorMessage: '.el-message--error' - }; + let retryCount = 0; - // 检查错误状态 - for (const [key, selector] of Object.entries(selectors)) { - const count = await this.page.locator(selector).count(); - if (count > 0 && key !== 'loadingMask') { - const errorText = await this.page.locator(selector).textContent(); - return { - success: false, - error: `页面 ${pageName} 加载出现错误: ${errorText}` - }; - } - } - - // 检查加载状态 - const isLoading = await this.page.locator(selectors.loadingMask).count() > 0; - if (!isLoading) { - await this.page.waitForTimeout(1000); // 额外等待一秒确保页面稳定 - return {success: true, error: null}; - } - - return null; // 继续等待 - }; - - // 重试逻辑 - for (let retryCount = 0; retryCount < config.maxRetries; retryCount++) { + const check = async () => { try { - const status = await checkPageStatus(); - if (status) return status; + // 如果超过最大重试次数,记录失败并继续 + if (retryCount >= config.maxRetries) { + const reason = `页面加载超时 (${config.maxRetries} 次重试)`; + this.recordFailedPage(pageName, reason, pageStartTime); + return {success: false, error: reason}; + } + + // 检查页面状态 + const selectors = { + loadingMask: '.el-loading-mask', + errorBox: '.el-message-box__message', + errorMessage: '.el-message--error' + }; + + // 检查错误状态 + for (const [key, selector] of Object.entries(selectors)) { + const elements = await this.page.locator(selector).all(); + if (elements.length > 0 && key !== 'loadingMask') { + const errorText = await elements[0].textContent(); + const reason = `页面加载出现错误: ${errorText}`; + this.recordFailedPage(pageName, reason, pageStartTime); + return {success: false, error: reason}; + } + } + + // 检查加载状态 + const loadingMasks = await this.page.locator(selectors.loadingMask).all(); + if (loadingMasks.length > 0) { + retryCount++; + await this.page.waitForTimeout(config.retryInterval); + return await check(); + } + + // 加载完成,等待页面稳定 + await this.page.waitForTimeout(1000); + return {success: true, error: null}; - console.log(`等待加载中... (${retryCount + 1}/${config.maxRetries})`); - await this.page.waitForTimeout(config.retryInterval); } catch (error) { - console.error(`检查页面加载状态时出错: ${error}`); - } - } + // 如果是页面关闭错误,直接返回 + if (error.message.includes('Target page, context or browser has been closed')) { + return {success: false, error: '页面已关闭'}; + } - return { - success: false, - error: `页面 ${pageName} 加载超时 (${config.maxRetries} 次重试)` + // 其他错误继续重试 + 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`); + // 这里可以添加将失败信息写入文件的逻辑 } /** @@ -605,9 +687,9 @@ class LongiMainPage extends BasePage { if (hasActiveTab && hasCloseButton) { // 确保关闭按钮可见并点击 - await closeButton.waitFor({ state: 'visible', timeout: 5000 }); + await closeButton.waitFor({state: 'visible', timeout: 5000}); await closeButton.click(); - + // 等待关闭动画完成 await this.page.waitForTimeout(500); return true; @@ -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 菜单对象 */ async handleThreeLevelMenu(menu) { try { - // 获取并点击二级菜单 - const menuElement = await this.getSecondLevelMenu(menu); - if (!menuElement) { - throw new Error(`未找到二级菜单: ${menu.text}`); - } + console.log(`正在处理 ${menu.text}菜单`) + await menu.element.click(); + + // 等待一个短暂的时间让弹出层出现 + await this.page.waitForTimeout(500); - // 获取三级菜单项 - const {thirdMenuTexts, totalItems} = await this.getThirdLevelMenuItems(menuElement); - 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} 没有找到三级菜单项`); 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) { console.error(`处理三级菜单时出错 [${menu.text}]:`, error.message); + throw error; } } @@ -705,7 +757,7 @@ class LongiMainPage extends BasePage { } // 等待页面加载 - const loadResult = await this.waitForPageLoadWithRetry({ text: menuPath }); + const loadResult = await this.waitForPageLoadWithRetry({text: menuPath}); if (!loadResult.success) { console.warn(loadResult.error); return false; @@ -721,4 +773,4 @@ class LongiMainPage extends BasePage { } } -module.exports = LongiMainPage; \ No newline at end of file +module.exports = LongiMainPage; \ No newline at end of file