优化分批执行点击菜单
This commit is contained in:
parent
c8bd981cc4
commit
ff891e82b0
12
.env.dev
12
.env.dev
@ -12,4 +12,14 @@ TEST_PROGRESS_FILE_PATH=test-data/test-progress.json
|
||||
# 测试批次配置
|
||||
TEST_BATCH_SIZE=5
|
||||
TEST_RETRY_COUNT=3
|
||||
TEST_BATCH_INTERVAL=2000
|
||||
TEST_BATCH_INTERVAL=2000
|
||||
TEST_MAX_RETRY_DELAY=5000
|
||||
|
||||
# 浏览器配置
|
||||
BROWSER_HEADLESS=false
|
||||
BROWSER_SLOW_MO=50
|
||||
BROWSER_TIMEOUT=30000
|
||||
|
||||
# 视窗配置
|
||||
VIEWPORT_WIDTH=1920
|
||||
VIEWPORT_HEIGHT=1080
|
||||
@ -1,6 +1,6 @@
|
||||
const { chromium } = require('@playwright/test');
|
||||
const LongiMainPage = require('../../tests/pages/LongiMainPage');
|
||||
const LongiLoginPage = require('../../tests/pages/LongiLoginPage');
|
||||
const LongiMainPage = require('../pages/LongiMainPage');
|
||||
const LongiLoginPage = require('../pages/LongiLoginPage');
|
||||
const menuDataService = require('../services/MenuDataService');
|
||||
|
||||
/**
|
||||
@ -9,28 +9,67 @@ const menuDataService = require('../services/MenuDataService');
|
||||
*/
|
||||
class TestController {
|
||||
constructor() {
|
||||
this.initializeConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化配置
|
||||
* @private
|
||||
*/
|
||||
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.browserConfig = {
|
||||
headless: false,
|
||||
headless: process.env.BROWSER_HEADLESS === 'true',
|
||||
args: ['--start-maximized'],
|
||||
slowMo: 50
|
||||
slowMo: parseInt(process.env.BROWSER_SLOW_MO || '50', 10),
|
||||
timeout: parseInt(process.env.BROWSER_TIMEOUT || '30000', 10)
|
||||
};
|
||||
|
||||
// 视窗配置
|
||||
this.viewportConfig = {
|
||||
width: parseInt(process.env.VIEWPORT_WIDTH || '1920', 10),
|
||||
height: parseInt(process.env.VIEWPORT_HEIGHT || '1080', 10)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建浏览器实例
|
||||
* @returns {Promise<{browser: import('@playwright/test').Browser, page: import('@playwright/test').Page}>}
|
||||
* @private
|
||||
*/
|
||||
async createBrowser() {
|
||||
try {
|
||||
const browser = await chromium.launch(this.browserConfig);
|
||||
const page = await browser.newPage();
|
||||
await page.setViewportSize(this.viewportConfig);
|
||||
return { browser, page };
|
||||
} catch (error) {
|
||||
console.error('创建浏览器实例失败:', error);
|
||||
throw new Error('无法创建浏览器实例');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行登录操作
|
||||
* @param {import('@playwright/test').Page} page - Playwright页面对象
|
||||
* @returns {Promise<boolean>} 登录是否成功
|
||||
* @private
|
||||
*/
|
||||
async performLogin(page) {
|
||||
const loginPage = new LongiLoginPage(page);
|
||||
await loginPage.navigateToLoginPage();
|
||||
return await loginPage.clickLoginButton();
|
||||
try {
|
||||
const loginPage = new LongiLoginPage(page);
|
||||
await loginPage.navigateToLoginPage();
|
||||
return await loginPage.clickLoginButton();
|
||||
} catch (error) {
|
||||
console.error('登录失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -39,73 +78,98 @@ class TestController {
|
||||
*/
|
||||
async collectMenuData() {
|
||||
console.log('开始收集菜单数据...');
|
||||
const browser = await chromium.launch(this.browserConfig);
|
||||
const page = await browser.newPage();
|
||||
await page.setViewportSize({ width: 1920, height: 1080 });
|
||||
let browser, page;
|
||||
|
||||
const mainPage = new LongiMainPage(page);
|
||||
|
||||
try {
|
||||
({ browser, page } = await this.createBrowser());
|
||||
const mainPage = new LongiMainPage(page);
|
||||
|
||||
if (!await this.performLogin(page)) {
|
||||
throw new Error('登录失败,无法收集菜单数据');
|
||||
}
|
||||
|
||||
const menuItems = await mainPage.checkAndLoadMenuItems();
|
||||
return menuDataService.saveMenuData(menuItems);
|
||||
} catch (error) {
|
||||
console.error('收集菜单数据失败:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
await browser.close();
|
||||
if (browser) await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行一批菜单的测试
|
||||
* @param {Array} menuBatch - 要测试的菜单数组
|
||||
* @private
|
||||
*/
|
||||
async runBatchTest(menuBatch) {
|
||||
if (!menuBatch?.length) return;
|
||||
|
||||
console.log(`开始执行批次测试,包含 ${menuBatch.length} 个菜单项:`);
|
||||
console.log(menuBatch.map(m => m.text).join(', '));
|
||||
|
||||
const browser = await chromium.launch(this.browserConfig);
|
||||
const page = await browser.newPage();
|
||||
await page.setViewportSize({ width: 1920, height: 1080 });
|
||||
let browser, page;
|
||||
|
||||
const mainPage = new LongiMainPage(page);
|
||||
|
||||
try {
|
||||
({ browser, page } = await this.createBrowser());
|
||||
const mainPage = new LongiMainPage(page);
|
||||
|
||||
if (!await this.performLogin(page)) {
|
||||
throw new Error('登录失败,无法执行菜单测试');
|
||||
}
|
||||
|
||||
await mainPage.handleAllMenuClicks(menuBatch);
|
||||
await this.updateTestProgress(menuBatch);
|
||||
} catch (error) {
|
||||
console.error('批次测试执行失败:', error);
|
||||
throw error;
|
||||
} finally {
|
||||
if (browser) await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
// 更新进度
|
||||
/**
|
||||
* 更新测试进度
|
||||
* @param {Array} completedBatch - 完成测试的菜单批次
|
||||
* @private
|
||||
*/
|
||||
async updateTestProgress(completedBatch) {
|
||||
try {
|
||||
const progress = menuDataService.getProgress();
|
||||
const newProgress = [...progress];
|
||||
for (const menuItem of menuBatch) {
|
||||
for (const menuItem of completedBatch) {
|
||||
if (!newProgress.includes(menuItem.id)) {
|
||||
newProgress.push(menuItem.id);
|
||||
}
|
||||
}
|
||||
menuDataService.saveProgress(newProgress);
|
||||
} finally {
|
||||
await browser.close();
|
||||
} catch (error) {
|
||||
console.error('更新测试进度失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一批要测试的菜单
|
||||
* @returns {Array|null} - 下一批要测试的菜单,如果没有则返回null
|
||||
* @private
|
||||
*/
|
||||
getNextBatch() {
|
||||
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;
|
||||
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;
|
||||
|
||||
return remainingMenus.slice(0, this.batchSize);
|
||||
return remainingMenus.slice(0, this.batchSize);
|
||||
} catch (error) {
|
||||
console.error('获取下一批测试菜单失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,18 +177,71 @@ class TestController {
|
||||
* @returns {Object} - 进度信息
|
||||
*/
|
||||
getTestProgress() {
|
||||
const menuData = menuDataService.getMenuData();
|
||||
const progress = menuDataService.getProgress();
|
||||
|
||||
if (!menuData) {
|
||||
return { total: 0, completed: 0, remaining: 0 };
|
||||
}
|
||||
try {
|
||||
const menuData = menuDataService.getMenuData();
|
||||
const progress = menuDataService.getProgress();
|
||||
|
||||
if (!menuData) {
|
||||
return { total: 0, completed: 0, remaining: 0 };
|
||||
}
|
||||
|
||||
return {
|
||||
total: menuData.length,
|
||||
completed: progress.length,
|
||||
remaining: menuData.length - progress.length
|
||||
};
|
||||
return {
|
||||
total: menuData.length,
|
||||
completed: progress.length,
|
||||
remaining: menuData.length - progress.length
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取测试进度失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行所有菜单的测试
|
||||
* @returns {Promise<Object>} - 测试结果
|
||||
*/
|
||||
async runAllTests() {
|
||||
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++;
|
||||
// 使用指数退避策略
|
||||
const delay = Math.min(this.batchInterval * Math.pow(2, retryCount), this.maxRetryDelay);
|
||||
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) {
|
||||
lastError = error;
|
||||
console.error('批次执行失败:', error);
|
||||
// 继续下一个批次
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('执行所有测试失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,55 +3,32 @@ require('../../config/env');
|
||||
|
||||
const { test } = require('@playwright/test');
|
||||
const TestController = require('../../src/controllers/TestController');
|
||||
const menuDataService = require('../../src/services/MenuDataService');
|
||||
|
||||
test.describe('菜单可访问性测试', () => {
|
||||
let controller;
|
||||
|
||||
test.beforeAll(async () => {
|
||||
controller = new TestController();
|
||||
// 打印环境变量,用于调试
|
||||
console.log('BASE_URL:', process.env.BASE_URL);
|
||||
});
|
||||
|
||||
test('收集菜单数据', async () => {
|
||||
test('应该能成功收集所有菜单数据', async () => {
|
||||
const menuData = await controller.collectMenuData();
|
||||
console.log(`成功收集 ${menuData.length} 个菜单项`);
|
||||
test.expect(menuData.length).toBeGreaterThan(0);
|
||||
console.log(`✓ 成功收集 ${menuData.length} 个菜单项`);
|
||||
});
|
||||
|
||||
test('批量测试菜单', async () => {
|
||||
// 清理之前的进度
|
||||
menuDataService.saveProgress([]);
|
||||
|
||||
let retryCount = 0;
|
||||
const maxRetries = parseInt(process.env.TEST_RETRY_COUNT || '3', 10);
|
||||
|
||||
while (true) {
|
||||
const batch = controller.getNextBatch();
|
||||
if (!batch || batch.length === 0) {
|
||||
const finalProgress = controller.getTestProgress();
|
||||
|
||||
if (finalProgress.completed < finalProgress.total && retryCount < maxRetries) {
|
||||
console.log(`\n还有未完成的测试,尝试重试 (${retryCount + 1}/${maxRetries})...`);
|
||||
retryCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log('\n所有测试完成!');
|
||||
console.log(`最终进度: ${finalProgress.completed}/${finalProgress.total}`);
|
||||
break;
|
||||
}
|
||||
|
||||
// 显示当前进度
|
||||
const currentProgress = controller.getTestProgress();
|
||||
console.log(`\n当前进度: ${currentProgress.completed}/${currentProgress.total}`);
|
||||
|
||||
// 执行当前批次
|
||||
await controller.runBatchTest(batch);
|
||||
|
||||
// 批次间暂停
|
||||
const batchInterval = parseInt(process.env.TEST_BATCH_INTERVAL || '2000', 10);
|
||||
await new Promise(resolve => setTimeout(resolve, batchInterval));
|
||||
}
|
||||
test('应该能访问所有菜单页面', async () => {
|
||||
// 开始批量测试
|
||||
const result = await controller.runAllTests();
|
||||
|
||||
// 验证测试结果
|
||||
const progress = controller.getTestProgress();
|
||||
test.expect(progress.completed).toBe(progress.total);
|
||||
|
||||
// 输出测试统计
|
||||
console.log('\n测试完成!');
|
||||
console.log(`✓ 总计测试: ${progress.total} 个菜单`);
|
||||
console.log(`✓ 成功完成: ${progress.completed} 个菜单`);
|
||||
console.log(`✓ 成功率: ${((progress.completed / progress.total) * 100).toFixed(2)}%`);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user