优化分批执行点击菜单
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');
|
const menuDataService = require('../services/LongiTestService');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 测试控制器
|
* 测试控制器
|
||||||
* 负责协调页面操作和数据管理
|
* 负责协调测试流程
|
||||||
*/
|
*/
|
||||||
class LongiTestController {
|
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>} 菜单数据数组
|
* @returns {Promise<Array>} 菜单数据数组
|
||||||
@ -65,102 +13,12 @@ class LongiTestController {
|
|||||||
return await menuDataService.fetchAndSaveMenuData();
|
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} - 进度信息
|
* @returns {Object} - 进度信息
|
||||||
*/
|
*/
|
||||||
getTestProgress() {
|
getTestProgress() {
|
||||||
try {
|
return menuDataService.getTestProgress();
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -168,47 +26,7 @@ class LongiTestController {
|
|||||||
* @returns {Promise<Object>} - 测试结果
|
* @returns {Promise<Object>} - 测试结果
|
||||||
*/
|
*/
|
||||||
async runAllTests() {
|
async runAllTests() {
|
||||||
try {
|
return await menuDataService.runAllTests();
|
||||||
// 清理之前的进度
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,21 +6,121 @@ const {chromium} = require('@playwright/test');
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 菜单数据服务
|
* 菜单数据服务
|
||||||
* 负责菜单数据的存储和检索
|
* 负责菜单数据的存储和检索,以及浏览器操作
|
||||||
*/
|
*/
|
||||||
class LongiTestService {
|
class LongiTestService {
|
||||||
constructor() {
|
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.dataDir = path.join(process.cwd(), process.env.TEST_DATA_DIR);
|
||||||
this.menuDataPath = process.env.MENU_DATA_FILE_PATH;
|
this.menuDataPath = process.env.MENU_DATA_FILE_PATH;
|
||||||
this.progressPath = process.env.TEST_PROGRESS_FILE_PATH;
|
this.progressPath = process.env.TEST_PROGRESS_FILE_PATH;
|
||||||
|
|
||||||
// 确保数据目录存在
|
|
||||||
if (!fs.existsSync(this.dataDir)) {
|
if (!fs.existsSync(this.dataDir)) {
|
||||||
fs.mkdirSync(this.dataDir, { recursive: true });
|
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>} 菜单数据数组
|
* @returns {Promise<Array>} 菜单数据数组
|
||||||
@ -32,12 +132,12 @@ class LongiTestService {
|
|||||||
try {
|
try {
|
||||||
browser = await chromium.launch();
|
browser = await chromium.launch();
|
||||||
page = await browser.newPage();
|
page = await browser.newPage();
|
||||||
|
|
||||||
// 登录
|
// 登录
|
||||||
const loginPage = new LongiLoginPage(page);
|
const loginPage = new LongiLoginPage(page);
|
||||||
await loginPage.navigateToLoginPage();
|
await loginPage.navigateToLoginPage();
|
||||||
const loginSuccess = await loginPage.clickLoginButton();
|
const loginSuccess = await loginPage.clickLoginButton();
|
||||||
|
|
||||||
if (!loginSuccess) {
|
if (!loginSuccess) {
|
||||||
throw new Error('登录失败,无法获取菜单数据');
|
throw new Error('登录失败,无法获取菜单数据');
|
||||||
}
|
}
|
||||||
@ -45,10 +145,10 @@ class LongiTestService {
|
|||||||
// 获取菜单数据
|
// 获取菜单数据
|
||||||
const mainPage = new LongiMainPage(page);
|
const mainPage = new LongiMainPage(page);
|
||||||
const menuItems = await mainPage.getMenuItems();
|
const menuItems = await mainPage.getMenuItems();
|
||||||
|
|
||||||
// 保存数据
|
// 保存数据
|
||||||
await this.saveMenuData(menuItems);
|
await this.saveMenuData(menuItems);
|
||||||
|
|
||||||
return menuItems;
|
return menuItems;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`获取并保存菜单数据时出错: ${error}`);
|
console.error(`获取并保存菜单数据时出错: ${error}`);
|
||||||
@ -116,6 +216,68 @@ class LongiTestService {
|
|||||||
fs.unlinkSync(this.progressPath);
|
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 () => {
|
test('访问并点击所有页面', async () => {
|
||||||
// 开始批量测试
|
// 执行所有测试
|
||||||
const result = await controller.runAllTests();
|
await controller.runAllTests();
|
||||||
|
|
||||||
// 验证测试结果
|
// 验证测试结果
|
||||||
const progress = controller.getTestProgress();
|
const progress = controller.getTestProgress();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user