playwright/src/services/LongiTestService.js
2025-03-07 14:52:52 +08:00

284 lines
8.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require('fs');
const path = require('path');
const LongiMainPage = require('../pages/LongiMainPage');
const LongiLoginPage = require('../pages/LongiLoginPage');
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>} 菜单数据数组
*/
async fetchAndSaveMenuData() {
console.log('开始获取菜单数据...');
let browser, page;
try {
browser = await chromium.launch();
page = await browser.newPage();
// 登录
const loginPage = new LongiLoginPage(page);
await loginPage.navigateToLoginPage();
const loginSuccess = await loginPage.clickLoginButton();
if (!loginSuccess) {
throw new Error('登录失败,无法获取菜单数据');
}
// 获取菜单数据
const mainPage = new LongiMainPage(page);
const menuItems = await mainPage.getMenuItems();
// 保存数据
await this.saveMenuData(menuItems);
return menuItems;
} catch (error) {
console.error(`获取并保存菜单数据时出错: ${error}`);
return null;
} finally {
if (browser) await browser.close();
}
}
/**
* 保存菜单数据
* @param {Array} menuItems - 从页面获取的原始菜单项
* @returns {Array} - 处理后的菜单数据
*/
saveMenuData(menuItems) {
const menuData = menuItems.map((menu, index) => ({
id: index + 1,
text: menu.text,
path: menu.path || menu.text,
hasThirdMenu: menu.hasThirdMenu,
parentMenu: menu.parentMenu
}));
fs.writeFileSync(this.menuDataPath, JSON.stringify(menuData, null, 2));
return menuData;
}
/**
* 读取菜单数据
* @returns {Array|null} - 菜单数据数组如果文件不存在则返回null
*/
getMenuData() {
if (!fs.existsSync(this.menuDataPath)) {
return null;
}
return JSON.parse(fs.readFileSync(this.menuDataPath, 'utf8'));
}
/**
* 保存测试进度
* @param {Array} completedMenus - 已完成测试的菜单ID数组
*/
saveProgress(completedMenus) {
fs.writeFileSync(this.progressPath, JSON.stringify(completedMenus, null, 2));
}
/**
* 读取测试进度
* @returns {Array} - 已完成测试的菜单ID数组
*/
getProgress() {
if (!fs.existsSync(this.progressPath)) {
return [];
}
return JSON.parse(fs.readFileSync(this.progressPath, 'utf8'));
}
/**
* 清理所有数据文件
*/
clearAll() {
if (fs.existsSync(this.menuDataPath)) {
fs.unlinkSync(this.menuDataPath);
}
if (fs.existsSync(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;
}
}
}
// 导出单例实例
module.exports = new LongiTestService();