优化分批执行点击菜单
This commit is contained in:
parent
03e91a0ff9
commit
6221a18dd3
@ -1,62 +1,10 @@
|
||||
const {chromium} = require('@playwright/test');
|
||||
const LongiMainPage = require('../pages/LongiMainPage');
|
||||
const LongiLoginPage = require('../pages/LongiLoginPage');
|
||||
const menuDataService = require('../services/LongiTestService');
|
||||
|
||||
/**
|
||||
* 测试控制器
|
||||
* 负责协调页面操作和数据管理
|
||||
* 负责协调测试流程
|
||||
*/
|
||||
class LongiTestController {
|
||||
constructor() {
|
||||
this.initializeConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化配置
|
||||
* @private
|
||||
*/
|
||||
initializeConfig() {
|
||||
// 从环境变量获取测试相关配置
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建浏览器实例
|
||||
* @returns {Promise<{browser: import('@playwright/test').Browser, page: import('@playwright/test').Page}>}
|
||||
* @private
|
||||
*/
|
||||
async createBrowser() {
|
||||
try {
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage();
|
||||
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) {
|
||||
try {
|
||||
const loginPage = new LongiLoginPage(page);
|
||||
await loginPage.navigateToLoginPage();
|
||||
return await loginPage.clickLoginButton();
|
||||
} catch (error) {
|
||||
console.error('登录失败:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并保存菜单数据
|
||||
* @returns {Promise<Array>} 菜单数据数组
|
||||
@ -65,102 +13,12 @@ class LongiTestController {
|
||||
return await menuDataService.fetchAndSaveMenuData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行一批菜单的测试
|
||||
* @param {Array} menuBatch - 要测试的菜单数组
|
||||
* @private
|
||||
*/
|
||||
async runBatchTest(menuBatch) {
|
||||
if (!menuBatch?.length) return;
|
||||
|
||||
console.log(`开始执行批次测试,包含 ${menuBatch.length} 个菜单项:`);
|
||||
console.log(menuBatch.map(m => m.text).join(', '));
|
||||
|
||||
let browser, 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 completedBatch) {
|
||||
if (!newProgress.includes(menuItem.id)) {
|
||||
newProgress.push(menuItem.id);
|
||||
}
|
||||
}
|
||||
menuDataService.saveProgress(newProgress);
|
||||
} catch (error) {
|
||||
console.error('更新测试进度失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一批要测试的菜单
|
||||
* @returns {Array|null} - 下一批要测试的菜单,如果没有则返回null
|
||||
* @private
|
||||
*/
|
||||
getNextBatch() {
|
||||
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);
|
||||
} catch (error) {
|
||||
console.error('获取下一批测试菜单失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取测试进度信息
|
||||
* @returns {Object} - 进度信息
|
||||
*/
|
||||
getTestProgress() {
|
||||
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
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取测试进度失败:', error);
|
||||
throw error;
|
||||
}
|
||||
return menuDataService.getTestProgress();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,47 +26,7 @@ class LongiTestController {
|
||||
* @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;
|
||||
}
|
||||
return await menuDataService.runAllTests();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,21 +6,121 @@ const {chromium} = require('@playwright/test');
|
||||
|
||||
/**
|
||||
* 菜单数据服务
|
||||
* 负责菜单数据的存储和检索
|
||||
* 负责菜单数据的存储和检索,以及浏览器操作
|
||||
*/
|
||||
class LongiTestService {
|
||||
constructor() {
|
||||
// 从环境变量获取路径配置
|
||||
this.initializeConfig();
|
||||
this.initializePaths();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化配置
|
||||
* @private
|
||||
*/
|
||||
initializeConfig() {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化路径
|
||||
* @private
|
||||
*/
|
||||
initializePaths() {
|
||||
this.dataDir = path.join(process.cwd(), process.env.TEST_DATA_DIR);
|
||||
this.menuDataPath = process.env.MENU_DATA_FILE_PATH;
|
||||
this.progressPath = process.env.TEST_PROGRESS_FILE_PATH;
|
||||
|
||||
// 确保数据目录存在
|
||||
if (!fs.existsSync(this.dataDir)) {
|
||||
fs.mkdirSync(this.dataDir, {recursive: true});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建浏览器实例
|
||||
* @returns {Promise<{browser: Browser, page: Page}>}
|
||||
* @private
|
||||
*/
|
||||
async createBrowser() {
|
||||
const browser = await chromium.launch();
|
||||
const page = await browser.newPage();
|
||||
return {browser, page};
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行登录操作
|
||||
* @param {Page} page - Playwright页面对象
|
||||
* @returns {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async performLogin(page) {
|
||||
const loginPage = new LongiLoginPage(page);
|
||||
await loginPage.navigateToLoginPage();
|
||||
const loginSuccess = await loginPage.clickLoginButton();
|
||||
|
||||
if (!loginSuccess) {
|
||||
throw new Error('登录失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行一批菜单的测试
|
||||
* @param {Array} menuBatch - 要测试的菜单数组
|
||||
*/
|
||||
async runBatchTest(menuBatch) {
|
||||
if (!menuBatch?.length) return;
|
||||
|
||||
console.log(`开始执行批次测试,包含 ${menuBatch.length} 个菜单项:`);
|
||||
console.log(menuBatch.map(m => m.text).join(', '));
|
||||
|
||||
let browser, page;
|
||||
try {
|
||||
({browser, page} = await this.createBrowser());
|
||||
await this.performLogin(page);
|
||||
|
||||
const mainPage = new LongiMainPage(page);
|
||||
await mainPage.handleAllMenuClicks(menuBatch);
|
||||
await this.updateTestProgress(menuBatch);
|
||||
} finally {
|
||||
if (browser) await browser.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新测试进度
|
||||
* @param {Array} completedBatch - 完成测试的菜单批次
|
||||
* @private
|
||||
*/
|
||||
async updateTestProgress(completedBatch) {
|
||||
const progress = this.getProgress();
|
||||
const newProgress = [...progress];
|
||||
for (const menuItem of completedBatch) {
|
||||
if (!newProgress.includes(menuItem.id)) {
|
||||
newProgress.push(menuItem.id);
|
||||
}
|
||||
}
|
||||
this.saveProgress(newProgress);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一批要测试的菜单
|
||||
* @returns {Array|null} - 下一批要测试的菜单,如果没有则返回null
|
||||
*/
|
||||
getNextBatch() {
|
||||
const menuData = this.getMenuData();
|
||||
const progress = this.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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取并保存菜单数据
|
||||
* @returns {Promise<Array>} 菜单数据数组
|
||||
@ -116,6 +216,68 @@ class LongiTestService {
|
||||
fs.unlinkSync(this.progressPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行所有菜单的测试
|
||||
* @returns {Promise<Object>} - 测试结果
|
||||
*/
|
||||
async runAllTests() {
|
||||
// 清理之前的进度
|
||||
this.saveProgress([]);
|
||||
let retryCount = 0;
|
||||
|
||||
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 progress;
|
||||
}
|
||||
|
||||
await this.runBatchTest(batch);
|
||||
await new Promise(resolve => setTimeout(resolve, this.batchInterval));
|
||||
} catch (error) {
|
||||
console.error('测试执行失败:', error);
|
||||
if (error.message.includes('登录失败')) {
|
||||
throw error; // 登录失败直接终止
|
||||
}
|
||||
// 其他错误继续下一批次
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取测试进度信息
|
||||
* @returns {Object} - 进度信息,包含总数、已完成数和剩余数
|
||||
*/
|
||||
getTestProgress() {
|
||||
try {
|
||||
const menuData = this.getMenuData();
|
||||
const progress = this.getProgress();
|
||||
|
||||
if (!menuData) {
|
||||
return {total: 0, completed: 0, remaining: 0};
|
||||
}
|
||||
|
||||
return {
|
||||
total: menuData.length,
|
||||
completed: progress.length,
|
||||
remaining: menuData.length - progress.length
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取测试进度失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 导出单例实例
|
||||
|
||||
@ -18,8 +18,8 @@ test.describe('测试所有隆基需求计划是否可用', () => {
|
||||
});
|
||||
|
||||
test('访问并点击所有页面', async () => {
|
||||
// 开始批量测试
|
||||
const result = await controller.runAllTests();
|
||||
// 执行所有测试
|
||||
await controller.runAllTests();
|
||||
|
||||
// 验证测试结果
|
||||
const progress = controller.getTestProgress();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user