284 lines
8.4 KiB
JavaScript
284 lines
8.4 KiB
JavaScript
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(); |