Files
novalon-manage-system/docs/superpowers/plans/2026-04-04-e2e-test-optimization.md

868 lines
22 KiB
Markdown
Raw Permalink 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.
# E2E测试优化实现计划
> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。
**目标:** 将E2E测试通过率从17.3%提升至100%,并优化测试执行时间至12分钟以内
**架构:** 采用分阶段实施策略,第一阶段修复基础导航问题(目标50%通过率),第二阶段精准化选择器(目标90%通过率),第三阶段优化性能(目标100%通过率)
**技术栈:** Playwright, TypeScript, Vue 3, Element Plus
---
## 文件结构
### 将要修改的文件
**Page Object类**(优化导航和选择器):
- `novalon-manage-web/e2e/pages/UserManagementPage.ts` - 用户管理页面
- `novalon-manage-web/e2e/pages/RoleManagementPage.ts` - 角色管理页面
- `novalon-manage-web/e2e/pages/MenuManagementPage.ts` - 菜单管理页面
- `novalon-manage-web/e2e/pages/SystemConfigPage.ts` - 系统配置页面
- `novalon-manage-web/e2e/pages/DictionaryManagementPage.ts` - 字典管理页面
- `novalon-manage-web/e2e/pages/FileManagementPage.ts` - 文件管理页面
- `novalon-manage-web/e2e/pages/OperationLogPage.ts` - 操作日志页面
- `novalon-manage-web/e2e/pages/LoginLogPage.ts` - 登录日志页面
- `novalon-manage-web/e2e/pages/ExceptionLogPage.ts` - 异常日志页面
**测试配置文件**(优化性能):
- `novalon-manage-web/e2e/global-setup.ts` - 全局setup优化
- `novalon-manage-web/playwright.config.ts` - 测试配置优化
**测试用例文件**(修复选择器):
- `novalon-manage-web/e2e/system-integration-test.spec.ts` - 系统集成测试
---
## 第一阶段:基础导航修复
**目标:** 测试通过率提升至50%以上(至少26个测试用例通过)
---
### 任务 1:验证页面存在性
**文件:**
- 修改:`novalon-manage-web/src/router/index.ts`(验证路由配置)
- [ ] **步骤 1:检查路由配置文件**
读取路由配置文件,确认所有测试用例涉及的页面都已配置:
```bash
cat novalon-manage-web/src/router/index.ts
```
预期:看到所有路由配置(/users, /roles, /menus, /sys/config, /dict, /files, /loginlog, /oplog, /exceptionlog
- [ ] **步骤 2:验证页面组件是否存在**
检查每个路由对应的Vue组件是否存在:
```bash
ls -la novalon-manage-web/src/views/system/
ls -la novalon-manage-web/src/views/config/
ls -la novalon-manage-web/src/views/file/
ls -la novalon-manage-web/src/views/audit/
```
预期:所有组件文件都存在
- [ ] **步骤 3:记录缺失的页面**
如果发现缺失的页面,记录下来:
```markdown
# 缺失页面列表
- 无(所有页面都已实现)
```
---
### 任务 2:优化UserManagementPage导航
**文件:**
- 修改:`novalon-manage-web/e2e/pages/UserManagementPage.ts`
- [ ] **步骤 1:读取当前UserManagementPage代码**
```bash
cat novalon-manage-web/e2e/pages/UserManagementPage.ts
```
- [ ] **步骤 2:优化goto方法**
修改`goto`方法,添加更健壮的导航逻辑:
```typescript
async goto() {
try {
console.log('导航到用户管理页面...');
await this.page.goto('/users');
// 等待页面加载完成
await this.page.waitForLoadState('networkidle');
// 等待关键元素出现
await this.table.waitFor({ state: 'visible', timeout: 10000 });
// 验证页面URL
await expect(this.page).toHaveURL(/.*users/);
console.log('用户管理页面加载完成');
} catch (error) {
// 截图保存错误状态
await this.page.screenshot({ path: `test-results/user-management-error-${Date.now()}.png` });
// 记录错误信息
console.error('导航到用户管理页面失败:', error);
// 抛出更详细的错误信息
throw new Error(`导航到用户管理页面失败: ${error instanceof Error ? error.message : String(error)}`);
}
}
```
- [ ] **步骤 3:添加waitForTableReady方法**
添加智能等待表格加载的方法:
```typescript
async waitForTableReady() {
// 等待表格出现
await this.table.waitFor({ state: 'visible', timeout: 10000 });
// 等待表格数据加载完成(至少有一行数据)
await this.page.waitForFunction(
() => {
const rows = document.querySelectorAll('.el-table__body-wrapper tbody tr');
return rows.length > 0;
},
{ timeout: 5000 }
).catch(() => {
// 如果没有数据,也继续执行(可能是空表格)
console.log('表格没有数据,继续执行');
});
}
```
- [ ] **步骤 4:提交修改**
```bash
git add novalon-manage-web/e2e/pages/UserManagementPage.ts
git commit -m "fix: optimize UserManagementPage navigation with better error handling"
```
---
### 任务 3:优化RoleManagementPage导航
**文件:**
- 修改:`novalon-manage-web/e2e/pages/RoleManagementPage.ts`
- [ ] **步骤 1:读取当前RoleManagementPage代码**
```bash
cat novalon-manage-web/e2e/pages/RoleManagementPage.ts
```
- [ ] **步骤 2:优化goto方法**
```typescript
async goto() {
try {
console.log('导航到角色管理页面...');
await this.page.goto('/roles');
await this.page.waitForLoadState('networkidle');
await this.table.waitFor({ state: 'visible', timeout: 10000 });
await expect(this.page).toHaveURL(/.*roles/);
console.log('角色管理页面加载完成');
} catch (error) {
await this.page.screenshot({ path: `test-results/role-management-error-${Date.now()}.png` });
console.error('导航到角色管理页面失败:', error);
throw new Error(`导航到角色管理页面失败: ${error instanceof Error ? error.message : String(error)}`);
}
}
```
- [ ] **步骤 3:添加waitForTableReady方法**
```typescript
async waitForTableReady() {
await this.table.waitFor({ state: 'visible', timeout: 10000 });
await this.page.waitForFunction(
() => {
const rows = document.querySelectorAll('.el-table__body-wrapper tbody tr');
return rows.length > 0;
},
{ timeout: 5000 }
).catch(() => {
console.log('表格没有数据,继续执行');
});
}
```
- [ ] **步骤 4:提交修改**
```bash
git add novalon-manage-web/e2e/pages/RoleManagementPage.ts
git commit -m "fix: optimize RoleManagementPage navigation with better error handling"
```
---
### 任务 4:优化MenuManagementPage导航
**文件:**
- 修改:`novalon-manage-web/e2e/pages/MenuManagementPage.ts`
- [ ] **步骤 1:读取当前MenuManagementPage代码**
```bash
cat novalon-manage-web/e2e/pages/MenuManagementPage.ts
```
- [ ] **步骤 2:优化goto方法**
```typescript
async goto() {
try {
console.log('导航到菜单管理页面...');
await this.page.goto('/menus');
await this.page.waitForLoadState('networkidle');
// 菜单管理页面可能是树形结构,等待树形组件
await this.page.waitForSelector('.el-tree', { timeout: 10000 }).catch(() => {
// 如果没有树形组件,等待表格
return this.page.waitForSelector('.el-table', { timeout: 5000 });
});
await expect(this.page).toHaveURL(/.*menus/);
console.log('菜单管理页面加载完成');
} catch (error) {
await this.page.screenshot({ path: `test-results/menu-management-error-${Date.now()}.png` });
console.error('导航到菜单管理页面失败:', error);
throw new Error(`导航到菜单管理页面失败: ${error instanceof Error ? error.message : String(error)}`);
}
}
```
- [ ] **步骤 3:提交修改**
```bash
git add novalon-manage-web/e2e/pages/MenuManagementPage.ts
git commit -m "fix: optimize MenuManagementPage navigation with better error handling"
```
---
### 任务 5:优化SystemConfigPage导航
**文件:**
- 修改:`novalon-manage-web/e2e/pages/SystemConfigPage.ts`
- [ ] **步骤 1:读取当前SystemConfigPage代码**
```bash
cat novalon-manage-web/e2e/pages/SystemConfigPage.ts
```
- [ ] **步骤 2:优化goto方法**
```typescript
async goto() {
try {
console.log('导航到系统配置页面...');
await this.page.goto('/sys/config');
await this.page.waitForLoadState('networkidle');
await this.table.waitFor({ state: 'visible', timeout: 10000 });
await expect(this.page).toHaveURL(/.*config/);
console.log('系统配置页面加载完成');
} catch (error) {
await this.page.screenshot({ path: `test-results/system-config-error-${Date.now()}.png` });
console.error('导航到系统配置页面失败:', error);
throw new Error(`导航到系统配置页面失败: ${error instanceof Error ? error.message : String(error)}`);
}
}
```
- [ ] **步骤 3:提交修改**
```bash
git add novalon-manage-web/e2e/pages/SystemConfigPage.ts
git commit -m "fix: optimize SystemConfigPage navigation with better error handling"
```
---
### 任务 6:优化DictionaryManagementPage导航
**文件:**
- 修改:`novalon-manage-web/e2e/pages/DictionaryManagementPage.ts`
- [ ] **步骤 1:读取当前DictionaryManagementPage代码**
```bash
cat novalon-manage-web/e2e/pages/DictionaryManagementPage.ts
```
- [ ] **步骤 2:优化goto方法**
```typescript
async goto() {
try {
console.log('导航到字典管理页面...');
await this.page.goto('/dict');
await this.page.waitForLoadState('networkidle');
await this.table.waitFor({ state: 'visible', timeout: 10000 });
await expect(this.page).toHaveURL(/.*dict/);
console.log('字典管理页面加载完成');
} catch (error) {
await this.page.screenshot({ path: `test-results/dict-management-error-${Date.now()}.png` });
console.error('导航到字典管理页面失败:', error);
throw new Error(`导航到字典管理页面失败: ${error instanceof Error ? error.message : String(error)}`);
}
}
```
- [ ] **步骤 3:提交修改**
```bash
git add novalon-manage-web/e2e/pages/DictionaryManagementPage.ts
git commit -m "fix: optimize DictionaryManagementPage navigation with better error handling"
```
---
### 任务 7:优化FileManagementPage导航
**文件:**
- 修改:`novalon-manage-web/e2e/pages/FileManagementPage.ts`
- [ ] **步骤 1:读取当前FileManagementPage代码**
```bash
cat novalon-manage-web/e2e/pages/FileManagementPage.ts
```
- [ ] **步骤 2:优化goto方法**
```typescript
async goto() {
try {
console.log('导航到文件管理页面...');
await this.page.goto('/files');
await this.page.waitForLoadState('networkidle');
await this.table.waitFor({ state: 'visible', timeout: 10000 });
await expect(this.page).toHaveURL(/.*files/);
console.log('文件管理页面加载完成');
} catch (error) {
await this.page.screenshot({ path: `test-results/file-management-error-${Date.now()}.png` });
console.error('导航到文件管理页面失败:', error);
throw new Error(`导航到文件管理页面失败: ${error instanceof Error ? error.message : String(error)}`);
}
}
```
- [ ] **步骤 3:提交修改**
```bash
git add novalon-manage-web/e2e/pages/FileManagementPage.ts
git commit -m "fix: optimize FileManagementPage navigation with better error handling"
```
---
### 任务 8:优化OperationLogPage导航
**文件:**
- 修改:`novalon-manage-web/e2e/pages/OperationLogPage.ts`
- [ ] **步骤 1:读取当前OperationLogPage代码**
```bash
cat novalon-manage-web/e2e/pages/OperationLogPage.ts
```
- [ ] **步骤 2:优化goto方法**
```typescript
async goto() {
try {
console.log('导航到操作日志页面...');
await this.page.goto('/oplog');
await this.page.waitForLoadState('networkidle');
await this.table.waitFor({ state: 'visible', timeout: 10000 });
await expect(this.page).toHaveURL(/.*oplog/);
console.log('操作日志页面加载完成');
} catch (error) {
await this.page.screenshot({ path: `test-results/operation-log-error-${Date.now()}.png` });
console.error('导航到操作日志页面失败:', error);
throw new Error(`导航到操作日志页面失败: ${error instanceof Error ? error.message : String(error)}`);
}
}
```
- [ ] **步骤 3:提交修改**
```bash
git add novalon-manage-web/e2e/pages/OperationLogPage.ts
git commit -m "fix: optimize OperationLogPage navigation with better error handling"
```
---
### 任务 9:优化LoginLogPage导航
**文件:**
- 修改:`novalon-manage-web/e2e/pages/LoginLogPage.ts`
- [ ] **步骤 1:读取当前LoginLogPage代码**
```bash
cat novalon-manage-web/e2e/pages/LoginLogPage.ts
```
- [ ] **步骤 2:优化goto方法**
```typescript
async goto() {
try {
console.log('导航到登录日志页面...');
await this.page.goto('/loginlog');
await this.page.waitForLoadState('networkidle');
await this.table.waitFor({ state: 'visible', timeout: 10000 });
await expect(this.page).toHaveURL(/.*loginlog/);
console.log('登录日志页面加载完成');
} catch (error) {
await this.page.screenshot({ path: `test-results/login-log-error-${Date.now()}.png` });
console.error('导航到登录日志页面失败:', error);
throw new Error(`导航到登录日志页面失败: ${error instanceof Error ? error.message : String(error)}`);
}
}
```
- [ ] **步骤 3:提交修改**
```bash
git add novalon-manage-web/e2e/pages/LoginLogPage.ts
git commit -m "fix: optimize LoginLogPage navigation with better error handling"
```
---
### 任务 10:优化ExceptionLogPage导航
**文件:**
- 修改:`novalon-manage-web/e2e/pages/ExceptionLogPage.ts`
- [ ] **步骤 1:读取当前ExceptionLogPage代码**
```bash
cat novalon-manage-web/e2e/pages/ExceptionLogPage.ts
```
- [ ] **步骤 2:优化goto方法**
```typescript
async goto() {
try {
console.log('导航到异常日志页面...');
await this.page.goto('/exceptionlog');
await this.page.waitForLoadState('networkidle');
await this.table.waitFor({ state: 'visible', timeout: 10000 });
await expect(this.page).toHaveURL(/.*exceptionlog/);
console.log('异常日志页面加载完成');
} catch (error) {
await this.page.screenshot({ path: `test-results/exception-log-error-${Date.now()}.png` });
console.error('导航到异常日志页面失败:', error);
throw new Error(`导航到异常日志页面失败: ${error instanceof Error ? error.message : String(error)}`);
}
}
```
- [ ] **步骤 3:提交修改**
```bash
git add novalon-manage-web/e2e/pages/ExceptionLogPage.ts
git commit -m "fix: optimize ExceptionLogPage navigation with better error handling"
```
---
### 任务 11:运行第一阶段测试验证
**文件:**
- 无文件修改
- [ ] **步骤 1:运行完整测试套件**
```bash
cd novalon-manage-web
npx playwright test system-integration-test.spec.ts --project=chromium --retries=0
```
预期:测试通过率提升至50%以上(至少26个测试用例通过)
- [ ] **步骤 2:收集测试结果**
查看测试报告:
```bash
cat test-results/results.json | jq '.suites[0].suites[0].suites[] | {title: .title, passed: [.specs[] | select(.ok == true)] | length, total: (.specs | length)}'
```
- [ ] **步骤 3:分析剩余失败原因**
记录第一阶段修复后的测试通过率和剩余失败原因:
```markdown
# 第一阶段测试结果
- 总测试数:52
- 通过数:[实际通过数]
- 失败数:[实际失败数]
- 通过率:[实际通过率]%
# 剩余失败原因分析
1. 选择器问题:[数量]个
2. 其他问题:[数量]个
```
---
## 第二阶段:选择器精准化
**目标:** 测试通过率提升至90%以上(至少47个测试用例通过)
---
### 任务 12:启用Playwright trace功能
**文件:**
- 修改:`novalon-manage-web/playwright.config.ts`
- [ ] **步骤 1:读取当前playwright配置**
```bash
cat novalon-manage-web/playwright.config.ts
```
- [ ] **步骤 2:启用trace功能**
`use`配置中添加trace选项:
```typescript
use: {
// ... 其他配置
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
}
```
- [ ] **步骤 3:提交修改**
```bash
git add novalon-manage-web/playwright.config.ts
git commit -m "feat: enable Playwright trace for debugging"
```
---
### 任务 13:诊断失败测试的选择器问题
**文件:**
- 无文件修改
- [ ] **步骤 1:运行失败测试并生成trace**
选择一个失败的测试用例运行:
```bash
cd novalon-manage-web
npx playwright test system-integration-test.spec.ts:104 --project=chromium --trace on
```
- [ ] **步骤 2:查看trace报告**
```bash
npx playwright show-trace test-results/[trace-file].zip
```
- [ ] **步骤 3:记录选择器问题**
根据trace报告,记录选择器问题:
```markdown
# 选择器问题列表
1. `.user-table` - 实际选择器应该是`.el-table`
2. `.user-row` - 实际选择器应该是`.el-table__body-wrapper tbody tr`
3. ...
```
---
### 任务 14:更新UserManagementPage选择器
**文件:**
- 修改:`novalon-manage-web/e2e/pages/UserManagementPage.ts`
- [ ] **步骤 1:更新构造函数中的选择器**
根据诊断结果,更新选择器:
```typescript
constructor(page: Page) {
this.page = page;
// 使用更健壮的选择器
this.table = page.locator('.el-table').first();
this.createUserButton = page.getByRole('button', { name: '新增用户' });
this.searchInput = page.getByPlaceholder('搜索用户名或邮箱').or(page.locator('input[placeholder*="搜索"]'));
this.searchButton = page.getByRole('button', { name: '搜索' });
this.successMessage = page.locator('.el-message--success').or(page.locator('.el-message'));
this.pagination = page.locator('.el-pagination');
this.nextPageButton = page.locator('.el-pagination .btn-next');
this.prevPageButton = page.locator('.el-pagination .btn-prev');
}
```
- [ ] **步骤 2:更新其他方法中的选择器**
检查并更新所有使用选择器的方法,确保使用最佳实践。
- [ ] **步骤 3:提交修改**
```bash
git add novalon-manage-web/e2e/pages/UserManagementPage.ts
git commit -m "fix: update UserManagementPage selectors for better reliability"
```
---
### 任务 15:更新其他Page Object类的选择器
**文件:**
- 修改:所有其他Page Object类
- [ ] **步骤 1:批量更新选择器**
按照任务14的模式,更新所有其他Page Object类的选择器。
- [ ] **步骤 2:提交修改**
```bash
git add novalon-manage-web/e2e/pages/*.ts
git commit -m "fix: update all Page Object selectors for better reliability"
```
---
### 任务 16:运行第二阶段测试验证
**文件:**
- 无文件修改
- [ ] **步骤 1:运行完整测试套件**
```bash
cd novalon-manage-web
npx playwright test system-integration-test.spec.ts --project=chromium --retries=0
```
预期:测试通过率提升至90%以上(至少47个测试用例通过)
- [ ] **步骤 2:收集测试结果**
```bash
cat test-results/results.json | jq '.suites[0].suites[0].suites[] | {title: .title, passed: [.specs[] | select(.ok == true)] | length, total: (.specs | length)}'
```
- [ ] **步骤 3:分析剩余失败原因**
记录第二阶段修复后的测试通过率和剩余失败原因。
---
## 第三阶段:性能优化
**目标:** 测试通过率达到100%,执行时间减少30%以上
---
### 任务 17:优化全局setup时间
**文件:**
- 修改:`novalon-manage-web/e2e/global-setup.ts`
- [ ] **步骤 1:读取当前global-setup代码**
```bash
cat novalon-manage-web/e2e/global-setup.ts
```
- [ ] **步骤 2:优化健康检查间隔**
将健康检查间隔从1000ms改为500ms
```typescript
// 优化前
await new Promise(resolve => setTimeout(resolve, 1000));
// 优化后
await new Promise(resolve => setTimeout(resolve, 500));
```
- [ ] **步骤 3:减少最大等待时间**
将最大等待时间从60秒改为30秒:
```typescript
const maxAttempts = 30; // 从60改为30
```
- [ ] **步骤 4:提交修改**
```bash
git add novalon-manage-web/e2e/global-setup.ts
git commit -m "perf: optimize global setup time"
```
---
### 任务 18:优化页面加载等待策略
**文件:**
- 修改:`novalon-manage-web/e2e/system-integration-test.spec.ts`
- [ ] **步骤 1:查找所有waitForTimeout调用**
```bash
grep -n "waitForTimeout" novalon-manage-web/e2e/system-integration-test.spec.ts
```
- [ ] **步骤 2:替换固定等待为智能等待**
将所有`waitForTimeout`替换为更智能的等待策略:
```typescript
// 优化前
await page.waitForTimeout(2000);
// 优化后
await page.waitForLoadState('domcontentloaded');
await page.waitForSelector('.el-table', { state: 'visible' });
```
- [ ] **步骤 3:提交修改**
```bash
git add novalon-manage-web/e2e/system-integration-test.spec.ts
git commit -m "perf: replace fixed waits with smart waits"
```
---
### 任务 19:运行最终测试验证
**文件:**
- 无文件修改
- [ ] **步骤 1:运行完整测试套件**
```bash
cd novalon-manage-web
npx playwright test system-integration-test.spec.ts --project=chromium --retries=0
```
预期:所有52个测试用例通过,执行时间在12分钟以内
- [ ] **步骤 2:生成最终测试报告**
```bash
cat test-results/results.json | jq '.suites[0].suites[0].suites[] | {title: .title, passed: [.specs[] | select(.ok == true)] | length, total: (.specs | length)}'
```
- [ ] **步骤 3:记录最终结果**
```markdown
# 最终测试结果
- 总测试数:52
- 通过数:[实际通过数]
- 失败数:[实际失败数]
- 通过率:[实际通过率]%
- 执行时间:[实际执行时间]
# 性能提升
- 执行时间减少:[百分比]%
- Setup时间减少:[百分比]%
```
---
### 任务 20:生成最终报告并提交
**文件:**
- 创建:`docs/superpowers/reports/2026-04-04-e2e-test-optimization-report.md`
- [ ] **步骤 1:创建测试报告**
创建详细的测试报告,包含:
- 测试通过率统计
- 执行时间统计
- 修复的问题列表
- 性能提升数据
- [ ] **步骤 2:提交最终报告**
```bash
git add docs/superpowers/reports/2026-04-04-e2e-test-optimization-report.md
git commit -m "docs: add E2E test optimization final report"
```
- [ ] **步骤 3:推送所有修改到远程仓库**
```bash
git push origin feature/operation-log-optimization
```
---
## 验收标准
### 功能验收
- ✅ 所有52个测试用例100%通过
- ✅ 测试覆盖所有核心业务流程
- ✅ 测试报告清晰展示测试结果
### 性能验收
- ✅ 测试执行时间在12分钟以内
- ✅ 全局setup时间在30秒以内
- ✅ 单个测试用例平均执行时间在20秒以内
### 质量验收
- ✅ 所有Page Object类有完善的错误处理
- ✅ 所有选择器使用最佳实践
- ✅ 测试代码有清晰的注释和文档
---
**计划结束**