From 82fb7ebe962eed12834e8823c27e3c212b044f43 Mon Sep 17 00:00:00 2001 From: dengqichen Date: Fri, 7 Mar 2025 13:50:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=88=86=E6=89=B9=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E7=82=B9=E5=87=BB=E8=8F=9C=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 78 +++++++++---------- playwright.config.js | 24 +++--- ...stController.js => LongiTestController.js} | 44 +++++------ ...MenuDataService.js => LongiTestService.js} | 4 +- src/utils/FileUtils.js | 26 +++---- tests/e2e/menu.spec.js | 2 +- 6 files changed, 91 insertions(+), 87 deletions(-) rename src/controllers/{TestController.js => LongiTestController.js} (90%) rename src/services/{MenuDataService.js => LongiTestService.js} (97%) diff --git a/package.json b/package.json index 5ac0ea0..0cfd5aa 100644 --- a/package.json +++ b/package.json @@ -1,41 +1,41 @@ { - "name": "playwright-automation", - "version": "1.0.0", - "description": "基于Playwright的自动化测试工具", - "main": "index.js", - "scripts": { - "test": "playwright test", - "test:ui": "playwright test --ui", - "report": "playwright show-report", - "codegen": "playwright codegen", - "debug": "playwright test --debug", - "test:menu": "cross-env NODE_ENV=dev playwright test tests/e2e/menu.spec.js", - "test:menu:ui": "cross-env NODE_ENV=dev playwright test tests/e2e/menu.spec.js --ui", - "test:menu:debug": "cross-env NODE_ENV=dev playwright test tests/e2e/menu.spec.js --debug", - "test:menu:clean": "cross-env NODE_ENV=dev rimraf test-data/* && npm run test:menu", - "test:menu:collect": "cross-env NODE_ENV=dev playwright test tests/e2e/menu.spec.js --grep \"收集菜单数据\"", - "test:menu:batch": "cross-env NODE_ENV=dev playwright test tests/e2e/menu.spec.js --grep \"批量测试菜单\"" - }, - "keywords": [ - "playwright", - "automation", - "testing", - "e2e" - ], - "author": "", - "license": "MIT", - "dependencies": { - "@playwright/test": "^1.40.0", - "chalk": "^4.1.2", - "commander": "^11.1.0", - "dotenv": "^16.3.1", - "faker": "^5.5.3" - }, - "devDependencies": { - "allure-playwright": "^2.9.2", - "cross-env": "^7.0.3", - "dotenv-flow": "^4.1.0", - "eslint": "^8.54.0", - "eslint-plugin-playwright": "^0.18.0" - } + "name": "playwright-automation", + "version": "1.0.0", + "description": "基于Playwright的自动化测试工具", + "main": "index.js", + "scripts": { + "test": "playwright test", + "test:ui": "playwright test --ui", + "report": "playwright show-report", + "codegen": "playwright codegen", + "debug": "playwright test --debug", + "test:menu": "cross-env NODE_ENV=dev playwright test tests/e2e/menu.spec.js", + "test:menu:ui": "cross-env NODE_ENV=dev playwright test tests/e2e/menu.spec.js --ui", + "test:menu:debug": "cross-env NODE_ENV=dev playwright test tests/e2e/menu.spec.js --debug", + "test:menu:clean": "cross-env NODE_ENV=dev rimraf test-data/* && npm run test:menu", + "test:menu:collect": "cross-env NODE_ENV=dev playwright test tests/e2e/menu.spec.js --grep \"收集菜单数据\"", + "test:menu:batch": "cross-env NODE_ENV=dev playwright test tests/e2e/menu.spec.js --grep \"批量测试菜单\"" + }, + "keywords": [ + "playwright", + "automation", + "testing", + "e2e" + ], + "author": "", + "license": "MIT", + "dependencies": { + "@playwright/test": "^1.40.0", + "chalk": "^4.1.2", + "commander": "^11.1.0", + "dotenv": "^16.3.1", + "faker": "^5.5.3" + }, + "devDependencies": { + "allure-playwright": "^2.9.2", + "cross-env": "^7.0.3", + "dotenv-flow": "^4.1.0", + "eslint": "^8.54.0", + "eslint-plugin-playwright": "^0.18.0" + } } diff --git a/playwright.config.js b/playwright.config.js index 896656a..21ebccb 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -4,13 +4,16 @@ const {defineConfig, devices} = require('@playwright/test'); /** * @see https://playwright.dev/docs/test-configuration */ -module.exports = defineConfig({ - testDir: './tests/e2e', +/** + * @type {import('@playwright/test').PlaywrightTestConfig} + */ +const config = { + testDir: './tests', /* 测试超时时间 */ - timeout: 60 * 60 * 1000, // 1小时 + timeout: parseInt(process.env.EXPECT_TIMEOUT), /* 每个测试的预期状态 */ expect: { - timeout: parseInt(process.env.EXPECT_TIMEOUT || '30000', 10) + timeout: parseInt(process.env.EXPECT_TIMEOUT) }, /* 测试运行并发数 */ fullyParallel: false, @@ -24,7 +27,6 @@ module.exports = defineConfig({ use: { /* 基础URL */ baseURL: process.env.BASE_URL, - /* 收集测试追踪信息 */ trace: 'on-first-retry', /* 自动截图 */ @@ -34,13 +36,13 @@ module.exports = defineConfig({ /* 浏览器配置 */ headless: process.env.BROWSER_HEADLESS === 'true', viewport: { - width: parseInt(process.env.VIEWPORT_WIDTH || '1920', 10), - height: parseInt(process.env.VIEWPORT_HEIGHT || '1080', 10) + width: parseInt(process.env.VIEWPORT_WIDTH), + height: parseInt(process.env.VIEWPORT_HEIGHT) }, /* 浏览器启动选项 */ launchOptions: { - slowMo: parseInt(process.env.BROWSER_SLOW_MO || '50', 10), - timeout: parseInt(process.env.BROWSER_TIMEOUT || '30000', 10) + slowMo: parseInt(process.env.BROWSER_SLOW_MO), + timeout: parseInt(process.env.BROWSER_TIMEOUT) } }, @@ -58,4 +60,6 @@ module.exports = defineConfig({ // port: 3000, // reuseExistingServer: !process.env.CI, // }, -}); \ No newline at end of file +}; + +module.exports = config; \ No newline at end of file diff --git a/src/controllers/TestController.js b/src/controllers/LongiTestController.js similarity index 90% rename from src/controllers/TestController.js rename to src/controllers/LongiTestController.js index 62be412..eb43373 100644 --- a/src/controllers/TestController.js +++ b/src/controllers/LongiTestController.js @@ -1,13 +1,13 @@ -const { chromium } = require('@playwright/test'); +const {chromium} = require('@playwright/test'); const LongiMainPage = require('../pages/LongiMainPage'); const LongiLoginPage = require('../pages/LongiLoginPage'); -const menuDataService = require('../services/MenuDataService'); +const menuDataService = require('../services/LongiTestService'); /** * 测试控制器 * 负责协调页面操作和数据管理 */ -class TestController { +class LongiTestController { constructor() { this.initializeConfig(); } @@ -18,10 +18,10 @@ class TestController { */ initializeConfig() { // 从环境变量获取测试相关配置 - this.batchSize = parseInt(process.env.TEST_BATCH_SIZE || '5', 10); - this.retryCount = parseInt(process.env.TEST_RETRY_COUNT || '3', 10); - this.batchInterval = parseInt(process.env.TEST_BATCH_INTERVAL || '2000', 10); - this.maxRetryDelay = parseInt(process.env.TEST_MAX_RETRY_DELAY || '5000', 10); + this.batchSize = parseInt(process.env.TEST_BATCH_SIZE); + this.retryCount = parseInt(process.env.TEST_RETRY_COUNT); + this.batchInterval = parseInt(process.env.TEST_BATCH_INTERVAL); + this.maxRetryDelay = parseInt(process.env.TEST_MAX_RETRY_DELAY); } /** @@ -33,7 +33,7 @@ class TestController { try { const browser = await chromium.launch(); const page = await browser.newPage(); - return { browser, page }; + return {browser, page}; } catch (error) { console.error('创建浏览器实例失败:', error); throw new Error('无法创建浏览器实例'); @@ -64,9 +64,9 @@ class TestController { async collectMenuData() { console.log('开始收集菜单数据...'); let browser, page; - + try { - ({ browser, page } = await this.createBrowser()); + ({browser, page} = await this.createBrowser()); const mainPage = new LongiMainPage(page); if (!await this.performLogin(page)) { @@ -95,9 +95,9 @@ class TestController { console.log(menuBatch.map(m => m.text).join(', ')); let browser, page; - + try { - ({ browser, page } = await this.createBrowser()); + ({browser, page} = await this.createBrowser()); const mainPage = new LongiMainPage(page); if (!await this.performLogin(page)) { @@ -144,9 +144,9 @@ class TestController { try { const menuData = menuDataService.getMenuData(); const progress = menuDataService.getProgress(); - + if (!menuData) return null; - + const remainingMenus = menuData.filter(menu => !progress.includes(menu.id)); if (remainingMenus.length === 0) return null; @@ -165,9 +165,9 @@ class TestController { try { const menuData = menuDataService.getMenuData(); const progress = menuDataService.getProgress(); - + if (!menuData) { - return { total: 0, completed: 0, remaining: 0 }; + return {total: 0, completed: 0, remaining: 0}; } return { @@ -189,16 +189,16 @@ class TestController { try { // 清理之前的进度 menuDataService.saveProgress([]); - + let retryCount = 0; let lastError = null; - + while (true) { try { const batch = this.getNextBatch(); if (!batch?.length) { const progress = this.getTestProgress(); - + if (progress.completed < progress.total && retryCount < this.retryCount) { console.log(`\n还有未完成的测试,尝试重试 (${retryCount + 1}/${this.retryCount})...`); retryCount++; @@ -207,14 +207,14 @@ class TestController { await new Promise(resolve => setTimeout(resolve, delay)); continue; } - + return this.getTestProgress(); } // 执行当前批次 await this.runBatchTest(batch); lastError = null; - + // 批次间暂停 await new Promise(resolve => setTimeout(resolve, this.batchInterval)); } catch (error) { @@ -230,4 +230,4 @@ class TestController { } } -module.exports = TestController; \ No newline at end of file +module.exports = LongiTestController; \ No newline at end of file diff --git a/src/services/MenuDataService.js b/src/services/LongiTestService.js similarity index 97% rename from src/services/MenuDataService.js rename to src/services/LongiTestService.js index 577a6ff..1973611 100644 --- a/src/services/MenuDataService.js +++ b/src/services/LongiTestService.js @@ -5,7 +5,7 @@ const path = require('path'); * 菜单数据服务 * 负责菜单数据的存储和检索 */ -class MenuDataService { +class LongiTestService { constructor() { // 从环境变量获取路径配置 this.dataDir = path.join(process.cwd(), process.env.TEST_DATA_DIR || 'test-data'); @@ -79,4 +79,4 @@ class MenuDataService { } // 导出单例实例 -module.exports = new MenuDataService(); \ No newline at end of file +module.exports = new LongiTestService(); \ No newline at end of file diff --git a/src/utils/FileUtils.js b/src/utils/FileUtils.js index a24dd30..fde2c13 100644 --- a/src/utils/FileUtils.js +++ b/src/utils/FileUtils.js @@ -30,7 +30,7 @@ class FileUtils { static ensureDirectoryExists(dirPath) { try { if (!fs.existsSync(dirPath)) { - fs.mkdirSync(dirPath, { recursive: true }); + fs.mkdirSync(dirPath, {recursive: true}); console.log(`目录已创建: ${dirPath}`); } return true; @@ -51,13 +51,13 @@ class FileUtils { */ static saveToJsonFile(data, filePath, options = {}) { try { - const { pretty = true, encoding = 'utf8' } = options; + const {pretty = true, encoding = 'utf8'} = options; const indent = pretty ? 2 : 0; - + // 确保目录存在 const dirPath = path.dirname(filePath); this.ensureDirectoryExists(dirPath); - + // 将对象转换为JSON字符串并写入文件 fs.writeFileSync(filePath, JSON.stringify(data, null, indent), encoding); console.log(`数据已保存到: ${filePath}`); @@ -77,13 +77,13 @@ class FileUtils { */ static loadFromJsonFile(filePath, options = {}) { try { - const { encoding = 'utf8' } = options; - + const {encoding = 'utf8'} = options; + if (!fs.existsSync(filePath)) { console.error(`文件不存在: ${filePath}`); return null; } - + const data = fs.readFileSync(filePath, encoding); return JSON.parse(data); } catch (error) { @@ -102,12 +102,12 @@ class FileUtils { */ static saveTextToFile(text, filePath, options = {}) { try { - const { encoding = 'utf8' } = options; - + const {encoding = 'utf8'} = options; + // 确保目录存在 const dirPath = path.dirname(filePath); this.ensureDirectoryExists(dirPath); - + // 写入文件 fs.writeFileSync(filePath, text, encoding); console.log(`文本已保存到: ${filePath}`); @@ -127,13 +127,13 @@ class FileUtils { */ static loadTextFromFile(filePath, options = {}) { try { - const { encoding = 'utf8' } = options; - + const {encoding = 'utf8'} = options; + if (!fs.existsSync(filePath)) { console.error(`文件不存在: ${filePath}`); return null; } - + return fs.readFileSync(filePath, encoding); } catch (error) { console.error(`加载文本文件失败: ${filePath}`, error); diff --git a/tests/e2e/menu.spec.js b/tests/e2e/menu.spec.js index b9681bb..02b1393 100644 --- a/tests/e2e/menu.spec.js +++ b/tests/e2e/menu.spec.js @@ -2,7 +2,7 @@ require('../../config/env'); const { test } = require('@playwright/test'); -const TestController = require('../../src/controllers/TestController'); +const TestController = require('../../src/controllers/LongiTestController'); test.describe('菜单可访问性测试', () => { let controller;