700 lines
22 KiB
JavaScript
700 lines
22 KiB
JavaScript
const { chromium } = require('playwright');
|
||
|
||
// 测试配置
|
||
const CONFIG = {
|
||
baseUrl: 'http://localhost:5175',
|
||
apiUrl: 'http://localhost:8080',
|
||
credentials: {
|
||
username: 'admin',
|
||
password: 'Admin@123'
|
||
},
|
||
timeout: 30000,
|
||
screenshotDir: '/tmp/e2e-project-test'
|
||
};
|
||
|
||
// 测试结果记录
|
||
const testResults = {
|
||
passed: [],
|
||
failed: [],
|
||
warnings: []
|
||
};
|
||
|
||
// 工具函数
|
||
const log = {
|
||
info: (msg) => console.log(`[INFO] ${msg}`),
|
||
success: (msg) => console.log(`\x1b[32m[SUCCESS]\x1b[0m ${msg}`),
|
||
error: (msg) => console.log(`\x1b[31m[ERROR]\x1b[0m ${msg}`),
|
||
warning: (msg) => console.log(`\x1b[33m[WARNING]\x1b[0m ${msg}`),
|
||
test: (name, status) => {
|
||
const icon = status === 'PASS' ? '✓' : '✗';
|
||
const color = status === 'PASS' ? '\x1b[32m' : '\x1b[31m';
|
||
console.log(` ${color}${icon}\x1b[0m ${name}`);
|
||
if (status === 'PASS') {
|
||
testResults.passed.push(name);
|
||
} else {
|
||
testResults.failed.push(name);
|
||
}
|
||
}
|
||
};
|
||
|
||
// 等待应用初始化
|
||
async function waitForAppInit(page, timeout = 30000) {
|
||
// 等待页面完全加载
|
||
await page.waitForLoadState('networkidle');
|
||
|
||
// 等待关键元素出现
|
||
try {
|
||
await page.waitForSelector('.ant-layout', { timeout: 5000 });
|
||
await page.waitForTimeout(1000); // 额外等待确保稳定
|
||
return true;
|
||
} catch (e) {
|
||
// 如果没有 layout,可能是登录页
|
||
await page.waitForTimeout(1000);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// 登录函数
|
||
async function login(page) {
|
||
log.info('执行登录...');
|
||
|
||
await page.goto(`${CONFIG.baseUrl}/login`, { waitUntil: 'networkidle' });
|
||
await waitForAppInit(page);
|
||
|
||
// 等待登录表单加载
|
||
await page.waitForSelector('.login-form', { timeout: 10000 });
|
||
|
||
// 填写用户名
|
||
const usernameInput = await page.$('.login-form input[type="text"]');
|
||
if (usernameInput) {
|
||
await usernameInput.fill(CONFIG.credentials.username);
|
||
} else {
|
||
// 尝试其他选择器
|
||
const inputs = await page.$$('.login-form input');
|
||
if (inputs.length > 0) {
|
||
await inputs[0].fill(CONFIG.credentials.username);
|
||
}
|
||
}
|
||
|
||
// 填写密码
|
||
const passwordInput = await page.$('.login-form input[type="password"]');
|
||
if (passwordInput) {
|
||
await passwordInput.fill(CONFIG.credentials.password);
|
||
}
|
||
|
||
// 点击登录按钮
|
||
const loginButton = await page.$('.login-btn');
|
||
if (loginButton) {
|
||
await loginButton.click();
|
||
} else {
|
||
// 尝试其他选择器
|
||
const buttons = await page.$$('.login-form button');
|
||
if (buttons.length > 0) {
|
||
await buttons[0].click();
|
||
}
|
||
}
|
||
|
||
// 等待登录成功
|
||
await page.waitForURL('**/dashboard', { timeout: 10000 });
|
||
|
||
const url = page.url();
|
||
if (url.includes('/login')) {
|
||
throw new Error('登录失败,仍在登录页');
|
||
}
|
||
|
||
log.success('登录成功');
|
||
await page.screenshot({ path: `${CONFIG.screenshotDir}/01-login-success.png` });
|
||
}
|
||
|
||
// 测试1: 项目列表页面功能
|
||
async function testProjectList(page) {
|
||
log.info('测试项目列表页面功能...');
|
||
|
||
// 导航到项目列表
|
||
await page.goto(`${CONFIG.baseUrl}/project/list`, { waitUntil: 'networkidle' });
|
||
await waitForAppInit(page);
|
||
await page.waitForTimeout(3000); // 等待数据加载
|
||
await page.screenshot({ path: `${CONFIG.screenshotDir}/02-project-list.png` });
|
||
|
||
// 测试1.1: 验证页面标题
|
||
try {
|
||
const pageTitle = await page.textContent('.page-title');
|
||
if (pageTitle && pageTitle.includes('项目管理')) {
|
||
log.test('项目列表 - 页面标题显示正确', 'PASS');
|
||
} else {
|
||
log.test('项目列表 - 页面标题显示正确', 'FAIL');
|
||
testResults.warnings.push('页面标题不匹配');
|
||
}
|
||
} catch (e) {
|
||
log.test('项目列表 - 页面标题显示正确', 'FAIL');
|
||
testResults.warnings.push('无法找到页面标题元素');
|
||
}
|
||
|
||
// 测试1.2: 验证搜索功能
|
||
try {
|
||
const searchInput = await page.$('input[placeholder*="项目名称"]');
|
||
if (searchInput) {
|
||
await searchInput.fill('测试项目');
|
||
await page.waitForTimeout(500);
|
||
|
||
// 点击查询按钮
|
||
const searchButton = await page.$('button:has-text("查询")');
|
||
if (searchButton) {
|
||
await searchButton.click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
log.test('项目列表 - 搜索功能可用', 'PASS');
|
||
} else {
|
||
log.test('项目列表 - 搜索功能可用', 'FAIL');
|
||
}
|
||
} catch (e) {
|
||
log.test('项目列表 - 搜索功能可用', 'FAIL');
|
||
testResults.warnings.push(`搜索功能异常: ${e.message}`);
|
||
}
|
||
|
||
// 测试1.3: 验证状态筛选功能
|
||
try {
|
||
// 重置搜索条件
|
||
const resetButton = await page.$('button:has-text("重置")');
|
||
if (resetButton) {
|
||
await resetButton.click();
|
||
await page.waitForTimeout(1000);
|
||
}
|
||
|
||
// 查找状态下拉框
|
||
const statusSelect = await page.$('.ant-select');
|
||
if (statusSelect) {
|
||
await statusSelect.click();
|
||
await page.waitForTimeout(500);
|
||
|
||
// 检查下拉选项
|
||
const options = await page.$$('.ant-select-item');
|
||
if (options.length > 0) {
|
||
log.test('项目列表 - 状态筛选功能可用', 'PASS');
|
||
// 关闭下拉框
|
||
await page.keyboard.press('Escape');
|
||
await page.waitForTimeout(300);
|
||
} else {
|
||
log.test('项目列表 - 状态筛选功能可用', 'FAIL');
|
||
}
|
||
} else {
|
||
log.test('项目列表 - 状态筛选功能可用', 'FAIL');
|
||
}
|
||
} catch (e) {
|
||
log.test('项目列表 - 状态筛选功能可用', 'FAIL');
|
||
testResults.warnings.push(`状态筛选异常: ${e.message}`);
|
||
}
|
||
|
||
// 测试1.4: 验证表格显示
|
||
try {
|
||
const table = await page.$('.ant-table');
|
||
if (table) {
|
||
// 等待表格数据加载
|
||
await page.waitForTimeout(2000);
|
||
|
||
const rows = await page.$$('.ant-table-tbody tr');
|
||
const rowCount = rows.length;
|
||
|
||
// 检查是否有空数据提示
|
||
const emptyText = await page.textContent('.ant-empty-description').catch(() => null);
|
||
if (emptyText && emptyText.includes('No data')) {
|
||
log.test('项目列表 - 表格显示正常 (无数据)', 'PASS');
|
||
testResults.warnings.push('项目列表无数据,可能需要检查API或数据');
|
||
} else if (rowCount > 0) {
|
||
// 验证表格内容
|
||
const firstRowText = await page.textContent('.ant-table-tbody tr:first-child').catch(() => '');
|
||
if (firstRowText && firstRowText.includes('PRJ')) {
|
||
log.test(`项目列表 - 表格显示正常 (共${rowCount}条记录)`, 'PASS');
|
||
} else {
|
||
log.test(`项目列表 - 表格显示正常 (共${rowCount}条记录)`, 'PASS');
|
||
}
|
||
} else {
|
||
log.test('项目列表 - 表格显示正常', 'FAIL');
|
||
}
|
||
} else {
|
||
log.test('项目列表 - 表格显示正常', 'FAIL');
|
||
}
|
||
} catch (e) {
|
||
log.test('项目列表 - 表格显示正常', 'FAIL');
|
||
testResults.warnings.push(`表格显示异常: ${e.message}`);
|
||
}
|
||
|
||
// 测试1.5: 验证分页功能(只有数据超过一页时才显示)
|
||
try {
|
||
const pagination = await page.$('.ant-pagination');
|
||
if (pagination) {
|
||
log.test('项目列表 - 分页组件显示正常', 'PASS');
|
||
} else {
|
||
// 检查是否因为数据太少而不显示分页
|
||
const rows = await page.$$('.ant-table-tbody tr');
|
||
if (rows.length <= 10) {
|
||
log.test('项目列表 - 分页组件显示正常 (数据少于10条,不显示分页)', 'PASS');
|
||
} else {
|
||
log.test('项目列表 - 分页组件显示正常', 'FAIL');
|
||
}
|
||
}
|
||
} catch (e) {
|
||
log.test('项目列表 - 分页组件显示正常', 'FAIL');
|
||
}
|
||
|
||
await page.screenshot({ path: `${CONFIG.screenshotDir}/03-project-list-features.png` });
|
||
}
|
||
|
||
// 测试2: 项目详情页面功能
|
||
async function testProjectDetail(page) {
|
||
log.info('测试项目详情页面功能...');
|
||
|
||
// 返回项目列表
|
||
await page.goto(`${CONFIG.baseUrl}/project/list`, { waitUntil: 'networkidle' });
|
||
await waitForAppInit(page);
|
||
|
||
// 等待表格加载
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 查找第一个项目的详情按钮
|
||
try {
|
||
// 先检查是否有数据
|
||
const rows = await page.$$('.ant-table-tbody tr');
|
||
if (rows.length === 0) {
|
||
log.warning('项目列表为空,跳过详情测试');
|
||
testResults.warnings.push('项目列表为空,无法测试详情页');
|
||
return;
|
||
}
|
||
|
||
// 尝试多种方式查找详情按钮
|
||
let detailButton = await page.$('button:has-text("详情")');
|
||
|
||
if (!detailButton) {
|
||
// 尝试在操作列中查找
|
||
detailButton = await page.$('.ant-table-tbody tr:first-child button:has-text("详情")');
|
||
}
|
||
|
||
if (!detailButton) {
|
||
// 尝试查找包含"详情"文本的按钮
|
||
const allButtons = await page.$$('.ant-table-tbody tr:first-child button');
|
||
for (const btn of allButtons) {
|
||
const text = await btn.textContent();
|
||
if (text && text.includes('详情')) {
|
||
detailButton = btn;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (detailButton) {
|
||
await detailButton.click();
|
||
await page.waitForTimeout(2000);
|
||
} else {
|
||
log.warning('未找到详情按钮,尝试从表格获取项目ID');
|
||
|
||
// 从表格获取项目ID
|
||
const firstRow = await page.$('.ant-table-tbody tr:first-child');
|
||
if (firstRow) {
|
||
const cells = await firstRow.$$('td');
|
||
if (cells.length > 0) {
|
||
const projectCode = await cells[0].textContent();
|
||
log.info(`找到项目编码: ${projectCode}`);
|
||
}
|
||
}
|
||
|
||
// 跳过详情测试
|
||
log.warning('无法访问详情页,跳过详情测试');
|
||
testResults.warnings.push('无法访问项目详情页');
|
||
return;
|
||
}
|
||
|
||
await page.screenshot({ path: `${CONFIG.screenshotDir}/04-project-detail.png` });
|
||
|
||
// 测试2.1: 验证统计卡片显示
|
||
try {
|
||
const statCards = await page.$$('.ant-statistic');
|
||
if (statCards.length >= 6) {
|
||
log.test('项目详情 - 统计卡片显示正常', 'PASS');
|
||
} else {
|
||
log.test('项目详情 - 统计卡片显示正常', 'FAIL');
|
||
testResults.warnings.push(`统计卡片数量不足: ${statCards.length}`);
|
||
}
|
||
} catch (e) {
|
||
log.test('项目详情 - 统计卡片显示正常', 'FAIL');
|
||
}
|
||
|
||
// 测试2.2: 验证Tab页切换
|
||
try {
|
||
const tabs = await page.$$('.ant-tabs-tab');
|
||
if (tabs.length >= 4) {
|
||
log.test('项目详情 - Tab页显示正常', 'PASS');
|
||
|
||
// 测试切换到成员管理
|
||
for (const tab of tabs) {
|
||
const text = await tab.textContent();
|
||
if (text && text.includes('成员管理')) {
|
||
await tab.click();
|
||
await page.waitForTimeout(1000);
|
||
await page.screenshot({ path: `${CONFIG.screenshotDir}/05-member-tab.png` });
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
log.test('项目详情 - Tab页显示正常', 'FAIL');
|
||
}
|
||
} catch (e) {
|
||
log.test('项目详情 - Tab页显示正常', 'FAIL');
|
||
testResults.warnings.push(`Tab页异常: ${e.message}`);
|
||
}
|
||
|
||
// 测试2.3: 验证基本信息显示
|
||
try {
|
||
// 等待数据加载
|
||
await page.waitForTimeout(1000);
|
||
|
||
// 尝试多种选择器
|
||
let found = false;
|
||
|
||
// 方式1: 检查 descriptions-item
|
||
const descriptions = await page.$$('.ant-descriptions-item');
|
||
if (descriptions.length > 0) {
|
||
found = true;
|
||
}
|
||
|
||
// 方式2: 检查内容是否包含项目信息
|
||
if (!found) {
|
||
const infoContent = await page.textContent('.ant-tabs-tabpane-active').catch(() => '');
|
||
if (infoContent && (infoContent.includes('项目编码') || infoContent.includes('PRJ') || infoContent.includes('项目名称'))) {
|
||
found = true;
|
||
}
|
||
}
|
||
|
||
// 方式3: 检查是否有描述列表
|
||
if (!found) {
|
||
const descList = await page.$('.ant-descriptions');
|
||
if (descList) {
|
||
found = true;
|
||
}
|
||
}
|
||
|
||
log.test('项目详情 - 基本信息显示正常', found ? 'PASS' : 'FAIL');
|
||
} catch (e) {
|
||
log.test('项目详情 - 基本信息显示正常', 'FAIL');
|
||
}
|
||
|
||
} catch (e) {
|
||
log.error(`项目详情测试失败: ${e.message}`);
|
||
testResults.warnings.push(`项目详情测试异常: ${e.message}`);
|
||
}
|
||
}
|
||
|
||
// 测试3: 项目成员管理
|
||
async function testProjectMember(page) {
|
||
log.info('测试项目成员管理功能...');
|
||
|
||
try {
|
||
// 确保在成员管理Tab
|
||
const memberTab = await page.$('.ant-tabs-tab:has-text("成员管理")');
|
||
if (!memberTab) {
|
||
// 尝试点击成员管理Tab
|
||
const tabs = await page.$$('.ant-tabs-tab');
|
||
for (const tab of tabs) {
|
||
const text = await tab.textContent();
|
||
if (text && text.includes('成员管理')) {
|
||
await tab.click();
|
||
await page.waitForTimeout(1000);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 测试3.1: 验证成员列表显示
|
||
try {
|
||
const memberTable = await page.$('.ant-tabs-tabpane-active .ant-table');
|
||
if (memberTable) {
|
||
const rows = await page.$$('.ant-tabs-tabpane-active .ant-table-tbody tr');
|
||
log.test('成员管理 - 成员列表显示正常', 'PASS');
|
||
} else {
|
||
log.test('成员管理 - 成员列表显示正常', 'FAIL');
|
||
}
|
||
} catch (e) {
|
||
log.test('成员管理 - 成员列表显示正常', 'FAIL');
|
||
}
|
||
|
||
// 测试3.2: 验证添加成员按钮
|
||
try {
|
||
const addButton = await page.$('button:has-text("添加成员")');
|
||
if (addButton) {
|
||
log.test('成员管理 - 添加成员按钮存在', 'PASS');
|
||
|
||
// 点击添加成员按钮
|
||
await addButton.click();
|
||
await page.waitForTimeout(500);
|
||
|
||
// 检查弹窗是否显示
|
||
const modal = await page.$('.ant-modal');
|
||
if (modal) {
|
||
log.test('成员管理 - 添加成员弹窗显示正常', 'PASS');
|
||
await page.screenshot({ path: `${CONFIG.screenshotDir}/06-add-member-modal.png` });
|
||
|
||
// 关闭弹窗
|
||
const cancelButton = await page.$('.ant-modal button:has-text("取消")');
|
||
if (cancelButton) {
|
||
await cancelButton.click();
|
||
await page.waitForTimeout(300);
|
||
}
|
||
} else {
|
||
log.test('成员管理 - 添加成员弹窗显示正常', 'FAIL');
|
||
}
|
||
} else {
|
||
log.test('成员管理 - 添加成员按钮存在', 'FAIL');
|
||
}
|
||
} catch (e) {
|
||
log.test('成员管理 - 添加成员功能测试', 'FAIL');
|
||
testResults.warnings.push(`添加成员异常: ${e.message}`);
|
||
}
|
||
|
||
} catch (e) {
|
||
log.error(`成员管理测试失败: ${e.message}`);
|
||
testResults.warnings.push(`成员管理测试异常: ${e.message}`);
|
||
}
|
||
}
|
||
|
||
// 测试4: 项目状态管理
|
||
async function testProjectStatus(page) {
|
||
log.info('测试项目状态管理功能...');
|
||
|
||
// 返回项目列表
|
||
await page.goto(`${CONFIG.baseUrl}/project/list`, { waitUntil: 'networkidle' });
|
||
await waitForAppInit(page);
|
||
await page.waitForTimeout(2000);
|
||
|
||
try {
|
||
// 检查是否有数据
|
||
const rows = await page.$$('.ant-table-tbody tr');
|
||
if (rows.length === 0) {
|
||
log.warning('项目列表为空,跳过状态测试');
|
||
testResults.warnings.push('项目列表为空,无法测试状态管理');
|
||
return;
|
||
}
|
||
|
||
// 测试4.1: 验证状态标签显示
|
||
try {
|
||
const statusTags = await page.$$('.ant-table-tbody .ant-tag');
|
||
if (statusTags.length > 0) {
|
||
log.test('项目状态 - 状态标签显示正常', 'PASS');
|
||
} else {
|
||
log.test('项目状态 - 状态标签显示正常', 'FAIL');
|
||
}
|
||
} catch (e) {
|
||
log.test('项目状态 - 状态标签显示正常', 'FAIL');
|
||
}
|
||
|
||
// 测试4.2: 验证状态切换按钮
|
||
try {
|
||
const firstRowButtons = await page.$$('.ant-table-tbody tr:first-child button');
|
||
let hasToggle = false;
|
||
|
||
for (const button of firstRowButtons) {
|
||
const text = await button.textContent();
|
||
if (text && (text.includes('禁用') || text.includes('启用'))) {
|
||
hasToggle = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
log.test('项目状态 - 状态切换按钮存在', hasToggle ? 'PASS' : 'FAIL');
|
||
} catch (e) {
|
||
log.test('项目状态 - 状态切换按钮存在', 'FAIL');
|
||
}
|
||
|
||
await page.screenshot({ path: `${CONFIG.screenshotDir}/07-project-status.png` });
|
||
|
||
} catch (e) {
|
||
log.error(`项目状态测试失败: ${e.message}`);
|
||
testResults.warnings.push(`项目状态测试异常: ${e.message}`);
|
||
}
|
||
}
|
||
|
||
// 测试5: 项目配置管理
|
||
async function testProjectConfig(page) {
|
||
log.info('测试项目配置管理功能...');
|
||
|
||
// 导航到项目详情
|
||
await page.goto(`${CONFIG.baseUrl}/project/list`, { waitUntil: 'networkidle' });
|
||
await waitForAppInit(page);
|
||
await page.waitForTimeout(2000);
|
||
|
||
try {
|
||
// 检查是否有数据
|
||
const rows = await page.$$('.ant-table-tbody tr');
|
||
if (rows.length === 0) {
|
||
log.warning('项目列表为空,跳过配置测试');
|
||
testResults.warnings.push('项目列表为空,无法测试项目配置');
|
||
return;
|
||
}
|
||
|
||
// 点击第一个项目的详情按钮
|
||
let detailButton = await page.$('.ant-table-tbody tr:first-child button:has-text("详情")');
|
||
|
||
if (!detailButton) {
|
||
const allButtons = await page.$$('.ant-table-tbody tr:first-child button');
|
||
for (const btn of allButtons) {
|
||
const text = await btn.textContent();
|
||
if (text && text.includes('详情')) {
|
||
detailButton = btn;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!detailButton) {
|
||
log.warning('未找到详情按钮,跳过配置测试');
|
||
testResults.warnings.push('无法访问项目配置页');
|
||
return;
|
||
}
|
||
|
||
await detailButton.click();
|
||
await page.waitForTimeout(2000);
|
||
|
||
// 切换到配置Tab
|
||
const tabs = await page.$$('.ant-tabs-tab');
|
||
for (const tab of tabs) {
|
||
const text = await tab.textContent();
|
||
if (text && text.includes('项目配置')) {
|
||
await tab.click();
|
||
await page.waitForTimeout(1000);
|
||
break;
|
||
}
|
||
}
|
||
|
||
await page.screenshot({ path: `${CONFIG.screenshotDir}/08-project-config.png` });
|
||
|
||
// 测试5.1: 验证配置项显示
|
||
try {
|
||
const switches = await page.$$('.ant-switch');
|
||
if (switches.length >= 8) {
|
||
log.test('项目配置 - 配置项显示正常', 'PASS');
|
||
} else {
|
||
log.test('项目配置 - 配置项显示正常', 'FAIL');
|
||
testResults.warnings.push(`配置项数量不足: ${switches.length}`);
|
||
}
|
||
} catch (e) {
|
||
log.test('项目配置 - 配置项显示正常', 'FAIL');
|
||
}
|
||
|
||
// 测试5.2: 验证保存按钮
|
||
try {
|
||
const saveButton = await page.$('button:has-text("保存配置")');
|
||
if (saveButton) {
|
||
log.test('项目配置 - 保存按钮存在', 'PASS');
|
||
} else {
|
||
log.test('项目配置 - 保存按钮存在', 'FAIL');
|
||
}
|
||
} catch (e) {
|
||
log.test('项目配置 - 保存按钮存在', 'FAIL');
|
||
}
|
||
|
||
} catch (e) {
|
||
log.error(`项目配置测试失败: ${e.message}`);
|
||
testResults.warnings.push(`项目配置测试异常: ${e.message}`);
|
||
}
|
||
}
|
||
|
||
// 主测试流程
|
||
async function runTests() {
|
||
log.info('========================================');
|
||
log.info('Ether 项目管理功能 E2E 测试');
|
||
log.info('========================================');
|
||
log.info(`测试时间: ${new Date().toLocaleString('zh-CN')}`);
|
||
log.info(`前端地址: ${CONFIG.baseUrl}`);
|
||
log.info(`后端地址: ${CONFIG.apiUrl}`);
|
||
log.info('========================================\n');
|
||
|
||
const browser = await chromium.launch({
|
||
headless: true
|
||
});
|
||
|
||
const context = await browser.newContext({
|
||
viewport: { width: 1920, height: 1080 }
|
||
});
|
||
|
||
const page = await context.newPage();
|
||
|
||
// 监听控制台输出
|
||
page.on('console', msg => {
|
||
const text = msg.text();
|
||
if (msg.type() === 'error') {
|
||
testResults.warnings.push(`浏览器控制台错误: ${text}`);
|
||
} else if (text.includes('API') || text.includes('请求') || text.includes('失败') || text.includes('登录')) {
|
||
log.info(`控制台: ${text}`);
|
||
}
|
||
});
|
||
|
||
// 监听网络请求
|
||
page.on('response', async (response) => {
|
||
const url = response.url();
|
||
if (url.includes('/api/mdm/projects')) {
|
||
try {
|
||
const status = response.status();
|
||
log.info(`API请求: ${url} - 状态: ${status}`);
|
||
if (status === 200) {
|
||
const body = await response.json();
|
||
log.info(`API响应数据: ${JSON.stringify(body).substring(0, 200)}...`);
|
||
}
|
||
} catch (e) {
|
||
// 忽略解析错误
|
||
}
|
||
}
|
||
});
|
||
|
||
page.on('pageerror', error => {
|
||
testResults.warnings.push(`页面错误: ${error.message}`);
|
||
});
|
||
|
||
try {
|
||
// 登录
|
||
await login(page);
|
||
|
||
// 执行测试
|
||
await testProjectList(page);
|
||
await testProjectDetail(page);
|
||
await testProjectMember(page);
|
||
await testProjectStatus(page);
|
||
await testProjectConfig(page);
|
||
|
||
} catch (error) {
|
||
log.error(`测试执行失败: ${error.message}`);
|
||
await page.screenshot({ path: `${CONFIG.screenshotDir}/error.png` });
|
||
} finally {
|
||
await browser.close();
|
||
}
|
||
|
||
// 输出测试报告
|
||
log.info('\n========================================');
|
||
log.info('测试报告');
|
||
log.info('========================================');
|
||
log.info(`通过: ${testResults.passed.length}`);
|
||
log.info(`失败: ${testResults.failed.length}`);
|
||
log.info(`警告: ${testResults.warnings.length}`);
|
||
|
||
if (testResults.failed.length > 0) {
|
||
log.error('\n失败的测试项:');
|
||
testResults.failed.forEach(item => log.error(` - ${item}`));
|
||
}
|
||
|
||
if (testResults.warnings.length > 0) {
|
||
log.warning('\n警告信息:');
|
||
testResults.warnings.forEach(item => log.warning(` - ${item}`));
|
||
}
|
||
|
||
log.info('\n========================================');
|
||
log.info(`测试完成! 截图保存在: ${CONFIG.screenshotDir}`);
|
||
log.info('========================================\n');
|
||
|
||
// 返回退出码
|
||
process.exit(testResults.failed.length > 0 ? 1 : 0);
|
||
}
|
||
|
||
// 执行测试
|
||
runTests().catch(error => {
|
||
log.error(`测试执行异常: ${error.message}`);
|
||
process.exit(1);
|
||
});
|