优化分批执行点击菜单

This commit is contained in:
dengqichen 2025-03-07 13:50:01 +08:00
parent 0dfad65b57
commit 82fb7ebe96
6 changed files with 91 additions and 87 deletions

View File

@ -1,41 +1,41 @@
{ {
"name": "playwright-automation", "name": "playwright-automation",
"version": "1.0.0", "version": "1.0.0",
"description": "基于Playwright的自动化测试工具", "description": "基于Playwright的自动化测试工具",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "playwright test", "test": "playwright test",
"test:ui": "playwright test --ui", "test:ui": "playwright test --ui",
"report": "playwright show-report", "report": "playwright show-report",
"codegen": "playwright codegen", "codegen": "playwright codegen",
"debug": "playwright test --debug", "debug": "playwright test --debug",
"test:menu": "cross-env NODE_ENV=dev playwright test tests/e2e/menu.spec.js", "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: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: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: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: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 \"批量测试菜单\"" "test:menu:batch": "cross-env NODE_ENV=dev playwright test tests/e2e/menu.spec.js --grep \"批量测试菜单\""
}, },
"keywords": [ "keywords": [
"playwright", "playwright",
"automation", "automation",
"testing", "testing",
"e2e" "e2e"
], ],
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@playwright/test": "^1.40.0", "@playwright/test": "^1.40.0",
"chalk": "^4.1.2", "chalk": "^4.1.2",
"commander": "^11.1.0", "commander": "^11.1.0",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"faker": "^5.5.3" "faker": "^5.5.3"
}, },
"devDependencies": { "devDependencies": {
"allure-playwright": "^2.9.2", "allure-playwright": "^2.9.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"dotenv-flow": "^4.1.0", "dotenv-flow": "^4.1.0",
"eslint": "^8.54.0", "eslint": "^8.54.0",
"eslint-plugin-playwright": "^0.18.0" "eslint-plugin-playwright": "^0.18.0"
} }
} }

View File

@ -4,13 +4,16 @@ const {defineConfig, devices} = require('@playwright/test');
/** /**
* @see https://playwright.dev/docs/test-configuration * @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: { expect: {
timeout: parseInt(process.env.EXPECT_TIMEOUT || '30000', 10) timeout: parseInt(process.env.EXPECT_TIMEOUT)
}, },
/* 测试运行并发数 */ /* 测试运行并发数 */
fullyParallel: false, fullyParallel: false,
@ -24,7 +27,6 @@ module.exports = defineConfig({
use: { use: {
/* 基础URL */ /* 基础URL */
baseURL: process.env.BASE_URL, baseURL: process.env.BASE_URL,
/* 收集测试追踪信息 */ /* 收集测试追踪信息 */
trace: 'on-first-retry', trace: 'on-first-retry',
/* 自动截图 */ /* 自动截图 */
@ -34,13 +36,13 @@ module.exports = defineConfig({
/* 浏览器配置 */ /* 浏览器配置 */
headless: process.env.BROWSER_HEADLESS === 'true', headless: process.env.BROWSER_HEADLESS === 'true',
viewport: { viewport: {
width: parseInt(process.env.VIEWPORT_WIDTH || '1920', 10), width: parseInt(process.env.VIEWPORT_WIDTH),
height: parseInt(process.env.VIEWPORT_HEIGHT || '1080', 10) height: parseInt(process.env.VIEWPORT_HEIGHT)
}, },
/* 浏览器启动选项 */ /* 浏览器启动选项 */
launchOptions: { launchOptions: {
slowMo: parseInt(process.env.BROWSER_SLOW_MO || '50', 10), slowMo: parseInt(process.env.BROWSER_SLOW_MO),
timeout: parseInt(process.env.BROWSER_TIMEOUT || '30000', 10) timeout: parseInt(process.env.BROWSER_TIMEOUT)
} }
}, },
@ -58,4 +60,6 @@ module.exports = defineConfig({
// port: 3000, // port: 3000,
// reuseExistingServer: !process.env.CI, // reuseExistingServer: !process.env.CI,
// }, // },
}); };
module.exports = config;

View File

@ -1,13 +1,13 @@
const { chromium } = require('@playwright/test'); const {chromium} = require('@playwright/test');
const LongiMainPage = require('../pages/LongiMainPage'); const LongiMainPage = require('../pages/LongiMainPage');
const LongiLoginPage = require('../pages/LongiLoginPage'); const LongiLoginPage = require('../pages/LongiLoginPage');
const menuDataService = require('../services/MenuDataService'); const menuDataService = require('../services/LongiTestService');
/** /**
* 测试控制器 * 测试控制器
* 负责协调页面操作和数据管理 * 负责协调页面操作和数据管理
*/ */
class TestController { class LongiTestController {
constructor() { constructor() {
this.initializeConfig(); this.initializeConfig();
} }
@ -18,10 +18,10 @@ class TestController {
*/ */
initializeConfig() { initializeConfig() {
// 从环境变量获取测试相关配置 // 从环境变量获取测试相关配置
this.batchSize = parseInt(process.env.TEST_BATCH_SIZE || '5', 10); this.batchSize = parseInt(process.env.TEST_BATCH_SIZE);
this.retryCount = parseInt(process.env.TEST_RETRY_COUNT || '3', 10); this.retryCount = parseInt(process.env.TEST_RETRY_COUNT);
this.batchInterval = parseInt(process.env.TEST_BATCH_INTERVAL || '2000', 10); this.batchInterval = parseInt(process.env.TEST_BATCH_INTERVAL);
this.maxRetryDelay = parseInt(process.env.TEST_MAX_RETRY_DELAY || '5000', 10); this.maxRetryDelay = parseInt(process.env.TEST_MAX_RETRY_DELAY);
} }
/** /**
@ -33,7 +33,7 @@ class TestController {
try { try {
const browser = await chromium.launch(); const browser = await chromium.launch();
const page = await browser.newPage(); const page = await browser.newPage();
return { browser, page }; return {browser, page};
} catch (error) { } catch (error) {
console.error('创建浏览器实例失败:', error); console.error('创建浏览器实例失败:', error);
throw new Error('无法创建浏览器实例'); throw new Error('无法创建浏览器实例');
@ -66,7 +66,7 @@ class TestController {
let browser, page; let browser, page;
try { try {
({ browser, page } = await this.createBrowser()); ({browser, page} = await this.createBrowser());
const mainPage = new LongiMainPage(page); const mainPage = new LongiMainPage(page);
if (!await this.performLogin(page)) { if (!await this.performLogin(page)) {
@ -97,7 +97,7 @@ class TestController {
let browser, page; let browser, page;
try { try {
({ browser, page } = await this.createBrowser()); ({browser, page} = await this.createBrowser());
const mainPage = new LongiMainPage(page); const mainPage = new LongiMainPage(page);
if (!await this.performLogin(page)) { if (!await this.performLogin(page)) {
@ -167,7 +167,7 @@ class TestController {
const progress = menuDataService.getProgress(); const progress = menuDataService.getProgress();
if (!menuData) { if (!menuData) {
return { total: 0, completed: 0, remaining: 0 }; return {total: 0, completed: 0, remaining: 0};
} }
return { return {
@ -230,4 +230,4 @@ class TestController {
} }
} }
module.exports = TestController; module.exports = LongiTestController;

View File

@ -5,7 +5,7 @@ const path = require('path');
* 菜单数据服务 * 菜单数据服务
* 负责菜单数据的存储和检索 * 负责菜单数据的存储和检索
*/ */
class MenuDataService { class LongiTestService {
constructor() { constructor() {
// 从环境变量获取路径配置 // 从环境变量获取路径配置
this.dataDir = path.join(process.cwd(), process.env.TEST_DATA_DIR || 'test-data'); this.dataDir = path.join(process.cwd(), process.env.TEST_DATA_DIR || 'test-data');
@ -79,4 +79,4 @@ class MenuDataService {
} }
// 导出单例实例 // 导出单例实例
module.exports = new MenuDataService(); module.exports = new LongiTestService();

View File

@ -30,7 +30,7 @@ class FileUtils {
static ensureDirectoryExists(dirPath) { static ensureDirectoryExists(dirPath) {
try { try {
if (!fs.existsSync(dirPath)) { if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true }); fs.mkdirSync(dirPath, {recursive: true});
console.log(`目录已创建: ${dirPath}`); console.log(`目录已创建: ${dirPath}`);
} }
return true; return true;
@ -51,7 +51,7 @@ class FileUtils {
*/ */
static saveToJsonFile(data, filePath, options = {}) { static saveToJsonFile(data, filePath, options = {}) {
try { try {
const { pretty = true, encoding = 'utf8' } = options; const {pretty = true, encoding = 'utf8'} = options;
const indent = pretty ? 2 : 0; const indent = pretty ? 2 : 0;
// 确保目录存在 // 确保目录存在
@ -77,7 +77,7 @@ class FileUtils {
*/ */
static loadFromJsonFile(filePath, options = {}) { static loadFromJsonFile(filePath, options = {}) {
try { try {
const { encoding = 'utf8' } = options; const {encoding = 'utf8'} = options;
if (!fs.existsSync(filePath)) { if (!fs.existsSync(filePath)) {
console.error(`文件不存在: ${filePath}`); console.error(`文件不存在: ${filePath}`);
@ -102,7 +102,7 @@ class FileUtils {
*/ */
static saveTextToFile(text, filePath, options = {}) { static saveTextToFile(text, filePath, options = {}) {
try { try {
const { encoding = 'utf8' } = options; const {encoding = 'utf8'} = options;
// 确保目录存在 // 确保目录存在
const dirPath = path.dirname(filePath); const dirPath = path.dirname(filePath);
@ -127,7 +127,7 @@ class FileUtils {
*/ */
static loadTextFromFile(filePath, options = {}) { static loadTextFromFile(filePath, options = {}) {
try { try {
const { encoding = 'utf8' } = options; const {encoding = 'utf8'} = options;
if (!fs.existsSync(filePath)) { if (!fs.existsSync(filePath)) {
console.error(`文件不存在: ${filePath}`); console.error(`文件不存在: ${filePath}`);

View File

@ -2,7 +2,7 @@
require('../../config/env'); require('../../config/env');
const { test } = require('@playwright/test'); const { test } = require('@playwright/test');
const TestController = require('../../src/controllers/TestController'); const TestController = require('../../src/controllers/LongiTestController');
test.describe('菜单可访问性测试', () => { test.describe('菜单可访问性测试', () => {
let controller; let controller;