1235 lines
36 KiB
Markdown
1235 lines
36 KiB
Markdown
# E2E 测试优化实施计划
|
||
|
||
> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。
|
||
|
||
**目标:** 将 50 个冗余的 E2E 测试文件优化为 10-15 个高质量的用户旅程测试,提升测试执行效率 3 倍,降低维护成本 60%。
|
||
|
||
**架构:** 采用用户旅程测试架构,模拟真实用户操作流程。保留角色基础测试框架,创建 5 个核心用户旅程测试文件,删除冗余的诊断性和重复性测试。
|
||
|
||
**技术栈:** Playwright, TypeScript, Page Object Model, 测试标签系统
|
||
|
||
---
|
||
|
||
## 文件结构
|
||
|
||
### 将要删除的文件(冗余测试)
|
||
|
||
```
|
||
novalon-manage-web/e2e/
|
||
├── diagnostic-test.spec.ts # 删除:诊断性测试
|
||
├── integration-diagnostic.spec.ts # 删除:诊断性测试
|
||
├── user-create-diagnostic.spec.ts # 删除:诊断性测试
|
||
├── user-create-diagnostic-v2.spec.ts # 删除:诊断性测试
|
||
├── debug-network.spec.ts # 删除:调试测试
|
||
├── login-test.spec.ts # 删除:重复登录测试
|
||
├── simple-login.spec.ts # 删除:重复登录测试
|
||
├── login-stability.spec.ts # 删除:重复登录测试
|
||
├── login-diagnostic.spec.ts # 删除:重复登录测试
|
||
├── comprehensive-uat.spec.ts # 删除:与 comprehensive-e2e.spec.ts 重复
|
||
├── uat-phase1.spec.ts # 删除:合并到用户旅程测试
|
||
├── uat-phase2-user.spec.ts # 删除:合并到用户旅程测试
|
||
├── uat-phase3-role.spec.ts # 删除:合并到用户旅程测试
|
||
├── uat-phase4-menu.spec.ts # 删除:合并到用户旅程测试
|
||
├── uat-phase5-api.spec.ts # 删除:合并到用户旅程测试
|
||
├── uat-phase6-persistence.spec.ts # 删除:合并到用户旅程测试
|
||
├── uat-phase7-boundary.spec.ts # 删除:合并到用户旅程测试
|
||
└── uat-phase8-security.spec.ts # 删除:合并到用户旅程测试
|
||
```
|
||
|
||
### 将要创建的文件(用户旅程测试)
|
||
|
||
```
|
||
novalon-manage-web/e2e/journeys/
|
||
├── admin-complete-workflow.spec.ts # 创建:管理员完整工作流
|
||
├── user-permission-boundary.spec.ts # 创建:用户权限边界验证
|
||
├── audit-workflow.spec.ts # 创建:审计工作流
|
||
├── file-management-workflow.spec.ts # 创建:文件管理工作流
|
||
└── system-config-workflow.spec.ts # 创建:系统配置工作流
|
||
```
|
||
|
||
### 将要修改的文件
|
||
|
||
```
|
||
novalon-manage-web/
|
||
├── playwright.config.ts # 修改:启用并行执行,添加测试标签
|
||
└── package.json # 修改:添加测试脚本命令
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 1:删除诊断性测试文件
|
||
|
||
**文件:**
|
||
- 删除:`novalon-manage-web/e2e/diagnostic-test.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/integration-diagnostic.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/user-create-diagnostic.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/user-create-diagnostic-v2.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/debug-network.spec.ts`
|
||
|
||
- [ ] **步骤 1:删除诊断性测试文件**
|
||
|
||
```bash
|
||
cd novalon-manage-web/e2e
|
||
rm -f diagnostic-test.spec.ts
|
||
rm -f integration-diagnostic.spec.ts
|
||
rm -f user-create-diagnostic.spec.ts
|
||
rm -f user-create-diagnostic-v2.spec.ts
|
||
rm -f debug-network.spec.ts
|
||
```
|
||
|
||
- [ ] **步骤 2:验证文件已删除**
|
||
|
||
运行:`ls -la novalon-manage-web/e2e/*.spec.ts | grep -E "(diagnostic|debug)"`
|
||
预期:无输出(文件已删除)
|
||
|
||
- [ ] **步骤 3:Commit**
|
||
|
||
```bash
|
||
git add novalon-manage-web/e2e/
|
||
git commit -m "refactor(e2e): 删除诊断性测试文件
|
||
|
||
- 删除 diagnostic-test.spec.ts
|
||
- 删除 integration-diagnostic.spec.ts
|
||
- 删除 user-create-diagnostic.spec.ts
|
||
- 删除 user-create-diagnostic-v2.spec.ts
|
||
- 删除 debug-network.spec.ts
|
||
|
||
原因:这些文件是临时调试文件,不应包含在生产测试套件中"
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 2:删除重复的登录测试
|
||
|
||
**文件:**
|
||
- 删除:`novalon-manage-web/e2e/login-test.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/simple-login.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/login-stability.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/login-diagnostic.spec.ts`
|
||
- 保留:`novalon-manage-web/e2e/role-based-tests/scenarios/authentication/login-flow.spec.ts`
|
||
|
||
- [ ] **步骤 1:删除重复的登录测试文件**
|
||
|
||
```bash
|
||
cd novalon-manage-web/e2e
|
||
rm -f login-test.spec.ts
|
||
rm -f simple-login.spec.ts
|
||
rm -f login-stability.spec.ts
|
||
rm -f login-diagnostic.spec.ts
|
||
```
|
||
|
||
- [ ] **步骤 2:验证文件已删除**
|
||
|
||
运行:`ls -la novalon-manage-web/e2e/*.spec.ts | grep -E "(login-test|simple-login|login-stability|login-diagnostic)"`
|
||
预期:无输出(文件已删除)
|
||
|
||
- [ ] **步骤 3:验证保留的登录测试存在**
|
||
|
||
运行:`ls -la novalon-manage-web/e2e/role-based-tests/scenarios/authentication/login-flow.spec.ts`
|
||
预期:文件存在
|
||
|
||
- [ ] **步骤 4:Commit**
|
||
|
||
```bash
|
||
git add novalon-manage-web/e2e/
|
||
git commit -m "refactor(e2e): 删除重复的登录测试
|
||
|
||
- 删除 login-test.spec.ts
|
||
- 删除 simple-login.spec.ts
|
||
- 删除 login-stability.spec.ts
|
||
- 删除 login-diagnostic.spec.ts
|
||
- 保留 role-based-tests/scenarios/authentication/login-flow.spec.ts
|
||
|
||
原因:避免测试重复,保留最完整的角色基础登录测试"
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 3:删除 UAT 阶段性测试
|
||
|
||
**文件:**
|
||
- 删除:`novalon-manage-web/e2e/comprehensive-uat.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/uat-phase1.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/uat-phase2-user.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/uat-phase3-role.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/uat-phase4-menu.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/uat-phase5-api.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/uat-phase6-persistence.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/uat-phase7-boundary.spec.ts`
|
||
- 删除:`novalon-manage-web/e2e/uat-phase8-security.spec.ts`
|
||
|
||
- [ ] **步骤 1:删除 UAT 阶段性测试文件**
|
||
|
||
```bash
|
||
cd novalon-manage-web/e2e
|
||
rm -f comprehensive-uat.spec.ts
|
||
rm -f uat-phase1.spec.ts
|
||
rm -f uat-phase2-user.spec.ts
|
||
rm -f uat-phase3-role.spec.ts
|
||
rm -f uat-phase4-menu.spec.ts
|
||
rm -f uat-phase5-api.spec.ts
|
||
rm -f uat-phase6-persistence.spec.ts
|
||
rm -f uat-phase7-boundary.spec.ts
|
||
rm -f uat-phase8-security.spec.ts
|
||
```
|
||
|
||
- [ ] **步骤 2:验证文件已删除**
|
||
|
||
运行:`ls -la novalon-manage-web/e2e/*.spec.ts | grep uat`
|
||
预期:无输出(文件已删除)
|
||
|
||
- [ ] **步骤 3:Commit**
|
||
|
||
```bash
|
||
git add novalon-manage-web/e2e/
|
||
git commit -m "refactor(e2e): 删除 UAT 阶段性测试
|
||
|
||
- 删除 comprehensive-uat.spec.ts
|
||
- 删除 uat-phase1 到 uat-phase8 所有文件
|
||
|
||
原因:这些测试与 comprehensive-e2e.spec.ts 重复,将被用户旅程测试替代"
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 4:创建用户旅程测试目录
|
||
|
||
**文件:**
|
||
- 创建:`novalon-manage-web/e2e/journeys/` 目录
|
||
|
||
- [ ] **步骤 1:创建 journeys 目录**
|
||
|
||
```bash
|
||
mkdir -p novalon-manage-web/e2e/journeys
|
||
```
|
||
|
||
- [ ] **步骤 2:验证目录创建成功**
|
||
|
||
运行:`ls -la novalon-manage-web/e2e/ | grep journeys`
|
||
预期:显示 journeys 目录
|
||
|
||
- [ ] **步骤 3:Commit**
|
||
|
||
```bash
|
||
git add novalon-manage-web/e2e/journeys/
|
||
git commit -m "feat(e2e): 创建用户旅程测试目录
|
||
|
||
创建 journeys/ 目录用于存放用户旅程测试文件"
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 5:创建管理员完整工作流测试
|
||
|
||
**文件:**
|
||
- 创建:`novalon-manage-web/e2e/journeys/admin-complete-workflow.spec.ts`
|
||
|
||
- [ ] **步骤 1:编写管理员完整工作流测试**
|
||
|
||
创建文件 `novalon-manage-web/e2e/journeys/admin-complete-workflow.spec.ts`:
|
||
|
||
```typescript
|
||
import { test, expect } from '@playwright/test';
|
||
import { LoginPage } from '../pages/LoginPage';
|
||
import { DashboardPage } from '../pages/DashboardPage';
|
||
import { UserManagementPage } from '../pages/UserManagementPage';
|
||
import { RoleManagementPage } from '../pages/RoleManagementPage';
|
||
|
||
test.describe('管理员完整工作流', () => {
|
||
test.describe.configure({ mode: 'serial' });
|
||
|
||
let loginPage: LoginPage;
|
||
let dashboardPage: DashboardPage;
|
||
let userManagementPage: UserManagementPage;
|
||
let roleManagementPage: RoleManagementPage;
|
||
|
||
const timestamp = Date.now();
|
||
const roleName = `测试角色_${timestamp}`;
|
||
const roleKey = `test_role_${timestamp}`;
|
||
const username = `testuser_${timestamp}`;
|
||
|
||
test.beforeAll(async ({ page }) => {
|
||
loginPage = new LoginPage(page);
|
||
dashboardPage = new DashboardPage(page);
|
||
userManagementPage = new UserManagementPage(page);
|
||
roleManagementPage = new RoleManagementPage(page);
|
||
});
|
||
|
||
test('管理员登录', async ({ page }) => {
|
||
await test.step('访问登录页面', async () => {
|
||
await loginPage.goto();
|
||
await expect(page).toHaveTitle(/登录/);
|
||
});
|
||
|
||
await test.step('输入管理员凭证', async () => {
|
||
await loginPage.usernameInput.fill('admin');
|
||
await loginPage.passwordInput.fill('admin123');
|
||
});
|
||
|
||
await test.step('点击登录按钮', async () => {
|
||
await loginPage.loginButton.click();
|
||
});
|
||
|
||
await test.step('验证登录成功', async () => {
|
||
await page.waitForURL('**/dashboard', { timeout: 30000 });
|
||
await expect(page).toHaveURL(/.*dashboard/);
|
||
});
|
||
});
|
||
|
||
test('创建角色并分配权限', async ({ page }) => {
|
||
await test.step('导航到角色管理', async () => {
|
||
await dashboardPage.navigateToRoleManagement();
|
||
await expect(page).toHaveURL(/.*roles/);
|
||
});
|
||
|
||
await test.step('点击创建角色按钮', async () => {
|
||
await roleManagementPage.clickCreateRole();
|
||
});
|
||
|
||
await test.step('填写角色信息', async () => {
|
||
await roleManagementPage.fillRoleForm({
|
||
roleName,
|
||
roleKey,
|
||
roleSort: '1',
|
||
status: 'ACTIVE',
|
||
remark: '测试角色',
|
||
});
|
||
});
|
||
|
||
await test.step('提交表单', async () => {
|
||
await roleManagementPage.submitForm();
|
||
await expect(roleManagementPage.successMessage).toBeVisible();
|
||
});
|
||
|
||
await test.step('分配权限', async () => {
|
||
await roleManagementPage.openPermissionDialog(1);
|
||
await roleManagementPage.selectPermission('user:view');
|
||
await roleManagementPage.selectPermission('user:create');
|
||
await roleManagementPage.selectPermission('user:edit');
|
||
await roleManagementPage.selectPermission('user:delete');
|
||
await roleManagementPage.savePermissions();
|
||
await expect(roleManagementPage.successMessage).toBeVisible();
|
||
});
|
||
});
|
||
|
||
test('创建用户并分配角色', async ({ page }) => {
|
||
await test.step('导航到用户管理', async () => {
|
||
await dashboardPage.navigateToUserManagement();
|
||
await expect(page).toHaveURL(/.*users/);
|
||
});
|
||
|
||
await test.step('点击创建用户按钮', async () => {
|
||
await userManagementPage.clickCreateUser();
|
||
});
|
||
|
||
await test.step('填写用户信息', async () => {
|
||
await userManagementPage.fillUserForm({
|
||
username,
|
||
nickname: `测试用户${timestamp}`,
|
||
email: `test_${timestamp}@example.com`,
|
||
phone: '13800138000',
|
||
password: 'Test@123',
|
||
confirmPassword: 'Test@123',
|
||
});
|
||
});
|
||
|
||
await test.step('提交表单', async () => {
|
||
await userManagementPage.submitForm();
|
||
await expect(userManagementPage.successMessage).toBeVisible();
|
||
});
|
||
|
||
await test.step('分配角色', async () => {
|
||
await userManagementPage.editUser(1);
|
||
await page.click('.role-select');
|
||
await page.click(`option:has-text("${roleName}")`);
|
||
await userManagementPage.submitForm();
|
||
await expect(userManagementPage.successMessage).toBeVisible();
|
||
});
|
||
});
|
||
|
||
test('验证新用户登录', async ({ page }) => {
|
||
await test.step('管理员登出', async () => {
|
||
await loginPage.logout();
|
||
await page.waitForURL(/.*login/);
|
||
});
|
||
|
||
await test.step('新用户登录', async () => {
|
||
await loginPage.goto();
|
||
await loginPage.login(username, 'Test@123');
|
||
await page.waitForURL('**/dashboard', { timeout: 30000 });
|
||
});
|
||
|
||
await test.step('验证用户信息', async () => {
|
||
const displayedUsername = await dashboardPage.getUsername();
|
||
expect(displayedUsername).toContain(username);
|
||
});
|
||
});
|
||
|
||
test('清理测试数据', async ({ page }) => {
|
||
await test.step('管理员重新登录', async () => {
|
||
await loginPage.logout();
|
||
await loginPage.goto();
|
||
await loginPage.login('admin', 'admin123');
|
||
await page.waitForURL('**/dashboard');
|
||
});
|
||
|
||
await test.step('删除测试用户', async () => {
|
||
await dashboardPage.navigateToUserManagement();
|
||
await userManagementPage.search(username);
|
||
await userManagementPage.deleteUser(1);
|
||
await userManagementPage.confirmDelete();
|
||
await expect(userManagementPage.successMessage).toBeVisible();
|
||
});
|
||
|
||
await test.step('删除测试角色', async () => {
|
||
await dashboardPage.navigateToRoleManagement();
|
||
await roleManagementPage.search(roleName);
|
||
await roleManagementPage.deleteRole(1);
|
||
await roleManagementPage.confirmDelete();
|
||
await expect(roleManagementPage.successMessage).toBeVisible();
|
||
});
|
||
});
|
||
});
|
||
```
|
||
|
||
- [ ] **步骤 2:验证测试文件创建成功**
|
||
|
||
运行:`ls -la novalon-manage-web/e2e/journeys/admin-complete-workflow.spec.ts`
|
||
预期:文件存在
|
||
|
||
- [ ] **步骤 3:Commit**
|
||
|
||
```bash
|
||
git add novalon-manage-web/e2e/journeys/
|
||
git commit -m "feat(e2e): 创建管理员完整工作流测试
|
||
|
||
实现用户旅程测试:
|
||
- 管理员登录
|
||
- 创建角色并分配权限
|
||
- 创建用户并分配角色
|
||
- 验证新用户登录
|
||
- 清理测试数据
|
||
|
||
采用 serial 模式确保测试顺序执行"
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 6:创建用户权限边界验证测试
|
||
|
||
**文件:**
|
||
- 创建:`novalon-manage-web/e2e/journeys/user-permission-boundary.spec.ts`
|
||
|
||
- [ ] **步骤 1:编写用户权限边界验证测试**
|
||
|
||
创建文件 `novalon-manage-web/e2e/journeys/user-permission-boundary.spec.ts`:
|
||
|
||
```typescript
|
||
import { test, expect } from '@playwright/test';
|
||
import { LoginPage } from '../pages/LoginPage';
|
||
import { DashboardPage } from '../pages/DashboardPage';
|
||
import { RoleFactory } from '@/role-based-tests/roles/role-factory';
|
||
import { createAuthenticatedPage } from '@/role-based-tests/shared/auth-helper';
|
||
|
||
test.describe('用户权限边界验证', () => {
|
||
test('管理员可以访问所有管理功能', async ({ page, context }) => {
|
||
const role = RoleFactory.getRole('admin');
|
||
|
||
await test.step('使用 Token 注入登录', async () => {
|
||
await createAuthenticatedPage(page, context, 'admin');
|
||
await page.goto('/dashboard');
|
||
await expect(page).toHaveURL(/.*dashboard/);
|
||
});
|
||
|
||
await test.step('验证可以访问用户管理', async () => {
|
||
await page.goto('/users');
|
||
await expect(page).toHaveURL(/.*users/);
|
||
});
|
||
|
||
await test.step('验证可以访问角色管理', async () => {
|
||
await page.goto('/roles');
|
||
await expect(page).toHaveURL(/.*roles/);
|
||
});
|
||
|
||
await test.step('验证可以访问菜单管理', async () => {
|
||
await page.goto('/menus');
|
||
await expect(page).toHaveURL(/.*menus/);
|
||
});
|
||
|
||
await test.step('验证可以访问系统配置', async () => {
|
||
await page.goto('/sys/config');
|
||
await expect(page).toHaveURL(/.*sys\/config/);
|
||
});
|
||
});
|
||
|
||
test('普通用户只能访问个人信息', async ({ page, context }) => {
|
||
const role = RoleFactory.getRole('user');
|
||
|
||
await test.step('使用 Token 注入登录', async () => {
|
||
await createAuthenticatedPage(page, context, 'user');
|
||
await page.goto('/dashboard');
|
||
await expect(page).toHaveURL(/.*dashboard/);
|
||
});
|
||
|
||
await test.step('验证无法访问用户管理', async () => {
|
||
await page.goto('/users');
|
||
await page.waitForTimeout(1000);
|
||
const currentUrl = page.url();
|
||
expect(currentUrl).not.toContain('/users');
|
||
});
|
||
|
||
await test.step('验证无法访问角色管理', async () => {
|
||
await page.goto('/roles');
|
||
await page.waitForTimeout(1000);
|
||
const currentUrl = page.url();
|
||
expect(currentUrl).not.toContain('/roles');
|
||
});
|
||
|
||
await test.step('验证无法访问菜单管理', async () => {
|
||
await page.goto('/menus');
|
||
await page.waitForTimeout(1000);
|
||
const currentUrl = page.url();
|
||
expect(currentUrl).not.toContain('/menus');
|
||
});
|
||
});
|
||
|
||
test('权限不足时显示提示信息', async ({ page, context }) => {
|
||
await test.step('普通用户登录', async () => {
|
||
await createAuthenticatedPage(page, context, 'user');
|
||
await page.goto('/dashboard');
|
||
});
|
||
|
||
await test.step('尝试访问受限页面', async () => {
|
||
await page.goto('/users');
|
||
await page.waitForTimeout(2000);
|
||
|
||
const errorMessage = page.locator('.el-message, .error-message, [role="alert"]');
|
||
const isVisible = await errorMessage.isVisible().catch(() => false);
|
||
|
||
if (isVisible) {
|
||
const text = await errorMessage.textContent();
|
||
expect(text).toMatch(/权限|禁止|无权/i);
|
||
}
|
||
});
|
||
});
|
||
});
|
||
```
|
||
|
||
- [ ] **步骤 2:验证测试文件创建成功**
|
||
|
||
运行:`ls -la novalon-manage-web/e2e/journeys/user-permission-boundary.spec.ts`
|
||
预期:文件存在
|
||
|
||
- [ ] **步骤 3:Commit**
|
||
|
||
```bash
|
||
git add novalon-manage-web/e2e/journeys/
|
||
git commit -m "feat(e2e): 创建用户权限边界验证测试
|
||
|
||
实现权限边界验证:
|
||
- 管理员可以访问所有管理功能
|
||
- 普通用户只能访问个人信息
|
||
- 权限不足时显示提示信息
|
||
|
||
使用 Token 注入提升测试效率"
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 7:创建审计工作流测试
|
||
|
||
**文件:**
|
||
- 创建:`novalon-manage-web/e2e/journeys/audit-workflow.spec.ts`
|
||
|
||
- [ ] **步骤 1:编写审计工作流测试**
|
||
|
||
创建文件 `novalon-manage-web/e2e/journeys/audit-workflow.spec.ts`:
|
||
|
||
```typescript
|
||
import { test, expect } from '@playwright/test';
|
||
import { LoginPage } from '../pages/LoginPage';
|
||
import { DashboardPage } from '../pages/DashboardPage';
|
||
import { OperationLogPage } from '../pages/OperationLogPage';
|
||
|
||
test.describe('审计工作流', () => {
|
||
let loginPage: LoginPage;
|
||
let dashboardPage: DashboardPage;
|
||
let operationLogPage: OperationLogPage;
|
||
|
||
test.beforeEach(async ({ page }) => {
|
||
loginPage = new LoginPage(page);
|
||
dashboardPage = new DashboardPage(page);
|
||
operationLogPage = new OperationLogPage(page);
|
||
|
||
await loginPage.goto();
|
||
await loginPage.login('admin', 'admin123');
|
||
await page.waitForURL('**/dashboard');
|
||
});
|
||
|
||
test('执行操作并查看操作日志', async ({ page }) => {
|
||
await test.step('执行用户管理操作', async () => {
|
||
await dashboardPage.navigateToUserManagement();
|
||
await page.waitForTimeout(1000);
|
||
});
|
||
|
||
await test.step('执行角色管理操作', async () => {
|
||
await dashboardPage.navigateToRoleManagement();
|
||
await page.waitForTimeout(1000);
|
||
});
|
||
|
||
await test.step('执行菜单管理操作', async () => {
|
||
await dashboardPage.navigateToMenuManagement();
|
||
await page.waitForTimeout(1000);
|
||
});
|
||
|
||
await test.step('导航到操作日志', async () => {
|
||
await dashboardPage.navigateToOperationLog();
|
||
await expect(operationLogPage.table).toBeVisible();
|
||
});
|
||
|
||
await test.step('验证操作日志记录', async () => {
|
||
await page.waitForTimeout(2000);
|
||
const logContent = await page.locator('table').textContent();
|
||
expect(logContent).toMatch(/用户管理|角色管理|菜单管理/);
|
||
});
|
||
});
|
||
|
||
test('查看登录日志', async ({ page }) => {
|
||
await test.step('导航到登录日志', async () => {
|
||
await dashboardPage.navigateToOperationLog();
|
||
await operationLogPage.switchToLoginLog();
|
||
});
|
||
|
||
await test.step('验证登录日志显示', async () => {
|
||
await expect(page.locator('table')).toBeVisible();
|
||
const logContent = await page.locator('table').textContent();
|
||
expect(logContent).toContain('admin');
|
||
});
|
||
});
|
||
|
||
test('搜索和导出日志', async ({ page }) => {
|
||
await test.step('导航到操作日志', async () => {
|
||
await dashboardPage.navigateToOperationLog();
|
||
});
|
||
|
||
await test.step('搜索日志', async () => {
|
||
await operationLogPage.search('用户管理');
|
||
await page.waitForTimeout(2000);
|
||
|
||
const searchResult = await page.locator('table').textContent();
|
||
expect(searchResult).toContain('用户管理');
|
||
});
|
||
|
||
await test.step('导出日志', async () => {
|
||
const downloadPromise = page.waitForEvent('download');
|
||
await operationLogPage.exportLogs();
|
||
const download = await downloadPromise;
|
||
|
||
expect(download.suggestedFilename()).toMatch(/logs.*\.xlsx/);
|
||
});
|
||
});
|
||
});
|
||
```
|
||
|
||
- [ ] **步骤 2:验证测试文件创建成功**
|
||
|
||
运行:`ls -la novalon-manage-web/e2e/journeys/audit-workflow.spec.ts`
|
||
预期:文件存在
|
||
|
||
- [ ] **步骤 3:Commit**
|
||
|
||
```bash
|
||
git add novalon-manage-web/e2e/journeys/
|
||
git commit -m "feat(e2e): 创建审计工作流测试
|
||
|
||
实现审计工作流测试:
|
||
- 执行操作并查看操作日志
|
||
- 查看登录日志
|
||
- 搜索和导出日志
|
||
|
||
覆盖审计日志的核心功能"
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 8:创建文件管理工作流测试
|
||
|
||
**文件:**
|
||
- 创建:`novalon-manage-web/e2e/journeys/file-management-workflow.spec.ts`
|
||
|
||
- [ ] **步骤 1:编写文件管理工作流测试**
|
||
|
||
创建文件 `novalon-manage-web/e2e/journeys/file-management-workflow.spec.ts`:
|
||
|
||
```typescript
|
||
import { test, expect } from '@playwright/test';
|
||
import { LoginPage } from '../pages/LoginPage';
|
||
import { DashboardPage } from '../pages/DashboardPage';
|
||
import { FileManagementPage } from '../pages/FileManagementPage';
|
||
|
||
test.describe('文件管理工作流', () => {
|
||
let loginPage: LoginPage;
|
||
let dashboardPage: DashboardPage;
|
||
let fileManagementPage: FileManagementPage;
|
||
|
||
test.beforeEach(async ({ page }) => {
|
||
loginPage = new LoginPage(page);
|
||
dashboardPage = new DashboardPage(page);
|
||
fileManagementPage = new FileManagementPage(page);
|
||
|
||
await loginPage.goto();
|
||
await loginPage.login('admin', 'admin123');
|
||
await page.waitForURL('**/dashboard');
|
||
});
|
||
|
||
test('上传、预览、下载和删除文件', async ({ page }) => {
|
||
await test.step('导航到文件管理', async () => {
|
||
await dashboardPage.navigateToFileManagement();
|
||
await expect(page).toHaveURL(/.*files/);
|
||
});
|
||
|
||
await test.step('上传文件', async () => {
|
||
await fileManagementPage.clickUploadFile();
|
||
|
||
const fileInput = page.locator('input[type="file"]');
|
||
await fileInput.setInputFiles('./e2e/fixtures/test-file.txt');
|
||
await fileManagementPage.submitUpload();
|
||
|
||
await expect(fileManagementPage.successMessage).toBeVisible();
|
||
});
|
||
|
||
await test.step('验证文件列表', async () => {
|
||
await page.reload();
|
||
await expect(page.locator('table')).toContainText('test-file.txt');
|
||
});
|
||
|
||
await test.step('预览文件', async () => {
|
||
await fileManagementPage.previewFile(1);
|
||
await expect(page.locator('.file-preview, .preview-dialog')).toBeVisible();
|
||
});
|
||
|
||
await test.step('下载文件', async () => {
|
||
const downloadPromise = page.waitForEvent('download');
|
||
await fileManagementPage.downloadFile(1);
|
||
const download = await downloadPromise;
|
||
|
||
expect(download.suggestedFilename()).toBe('test-file.txt');
|
||
});
|
||
|
||
await test.step('删除文件', async () => {
|
||
await fileManagementPage.deleteFile(1);
|
||
await fileManagementPage.confirmDelete();
|
||
await expect(fileManagementPage.successMessage).toBeVisible();
|
||
});
|
||
});
|
||
});
|
||
```
|
||
|
||
- [ ] **步骤 2:验证测试文件创建成功**
|
||
|
||
运行:`ls -la novalon-manage-web/e2e/journeys/file-management-workflow.spec.ts`
|
||
预期:文件存在
|
||
|
||
- [ ] **步骤 3:Commit**
|
||
|
||
```bash
|
||
git add novalon-manage-web/e2e/journeys/
|
||
git commit -m "feat(e2e): 创建文件管理工作流测试
|
||
|
||
实现文件管理工作流测试:
|
||
- 上传文件
|
||
- 预览文件
|
||
- 下载文件
|
||
- 删除文件
|
||
|
||
覆盖文件管理的核心功能"
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 9:创建系统配置工作流测试
|
||
|
||
**文件:**
|
||
- 创建:`novalon-manage-web/e2e/journeys/system-config-workflow.spec.ts`
|
||
|
||
- [ ] **步骤 1:编写系统配置工作流测试**
|
||
|
||
创建文件 `novalon-manage-web/e2e/journeys/system-config-workflow.spec.ts`:
|
||
|
||
```typescript
|
||
import { test, expect } from '@playwright/test';
|
||
import { LoginPage } from '../pages/LoginPage';
|
||
import { DashboardPage } from '../pages/DashboardPage';
|
||
import { SystemConfigPage } from '../pages/SystemConfigPage';
|
||
|
||
test.describe('系统配置工作流', () => {
|
||
let loginPage: LoginPage;
|
||
let dashboardPage: DashboardPage;
|
||
let systemConfigPage: SystemConfigPage;
|
||
|
||
const timestamp = Date.now();
|
||
const testValue = `test_value_${timestamp}`;
|
||
|
||
test.beforeEach(async ({ page }) => {
|
||
loginPage = new LoginPage(page);
|
||
dashboardPage = new DashboardPage(page);
|
||
systemConfigPage = new SystemConfigPage(page);
|
||
|
||
await loginPage.goto();
|
||
await loginPage.login('admin', 'admin123');
|
||
await page.waitForURL('**/dashboard');
|
||
});
|
||
|
||
test('修改、验证和恢复系统配置', async ({ page }) => {
|
||
await test.step('导航到系统配置', async () => {
|
||
await dashboardPage.navigateToSystemConfig();
|
||
await expect(systemConfigPage.table).toBeVisible();
|
||
});
|
||
|
||
await test.step('修改配置值', async () => {
|
||
await systemConfigPage.editConfig(1);
|
||
await page.fill('input[name="configValue"]', testValue);
|
||
await systemConfigPage.submitForm();
|
||
await expect(systemConfigPage.successMessage).toBeVisible();
|
||
});
|
||
|
||
await test.step('验证配置修改', async () => {
|
||
await page.reload();
|
||
await expect(page.locator('table')).toContainText(testValue);
|
||
});
|
||
|
||
await test.step('刷新配置缓存', async () => {
|
||
await systemConfigPage.refreshCache();
|
||
await expect(systemConfigPage.successMessage).toBeVisible();
|
||
});
|
||
|
||
await test.step('恢复默认配置', async () => {
|
||
await systemConfigPage.editConfig(1);
|
||
await page.fill('input[name="configValue"]', 'default_value');
|
||
await systemConfigPage.submitForm();
|
||
await expect(systemConfigPage.successMessage).toBeVisible();
|
||
});
|
||
});
|
||
});
|
||
```
|
||
|
||
- [ ] **步骤 2:验证测试文件创建成功**
|
||
|
||
运行:`ls -la novalon-manage-web/e2e/journeys/system-config-workflow.spec.ts`
|
||
预期:文件存在
|
||
|
||
- [ ] **步骤 3:Commit**
|
||
|
||
```bash
|
||
git add novalon-manage-web/e2e/journeys/
|
||
git commit -m "feat(e2e): 创建系统配置工作流测试
|
||
|
||
实现系统配置工作流测试:
|
||
- 修改配置值
|
||
- 验证配置修改
|
||
- 刷新配置缓存
|
||
- 恢复默认配置
|
||
|
||
覆盖系统配置的核心功能"
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 10:优化 Playwright 配置
|
||
|
||
**文件:**
|
||
- 修改:`novalon-manage-web/playwright.config.ts`
|
||
|
||
- [ ] **步骤 1:更新 playwright.config.ts**
|
||
|
||
修改文件 `novalon-manage-web/playwright.config.ts`,更新以下配置:
|
||
|
||
```typescript
|
||
export default defineConfig({
|
||
testDir: './e2e',
|
||
fullyParallel: true, // ✅ 启用完全并行
|
||
forbidOnly: !!process.env.CI,
|
||
retries: process.env.CI ? 2 : 1,
|
||
workers: process.env.CI ? 4 : '50%', // ✅ CI 环境 4 个 worker,本地 50% CPU
|
||
|
||
reporter: [
|
||
['html', { outputFolder: 'playwright-report' }],
|
||
['json', { outputFile: 'test-results/results.json' }],
|
||
['junit', { outputFile: 'test-results/junit.xml' }],
|
||
['list'],
|
||
['./e2e/customReporter.ts']
|
||
],
|
||
|
||
timeout: 120000,
|
||
expect: {
|
||
timeout: 30000,
|
||
toHaveScreenshot: { threshold: 0.2 },
|
||
toMatchSnapshot: { threshold: 0.2 }
|
||
},
|
||
|
||
use: {
|
||
baseURL: baseURL,
|
||
trace: process.env.CI ? 'retain-on-failure' : 'on-first-retry',
|
||
screenshot: 'only-on-failure',
|
||
video: process.env.CI ? 'retain-on-failure' : 'on-first-retry',
|
||
actionTimeout: 30000,
|
||
navigationTimeout: 60000,
|
||
headless: isHeadless,
|
||
locale: 'zh-CN',
|
||
timezoneId: 'Asia/Shanghai',
|
||
ignoreHTTPSErrors: true,
|
||
bypassCSP: true,
|
||
viewport: { width: 1280, height: 720 },
|
||
launchOptions: {
|
||
slowMo: process.env.CI ? 0 : 100
|
||
},
|
||
contextOptions: {
|
||
permissions: ['geolocation'],
|
||
geolocation: { latitude: 35.6895, longitude: 139.6917 },
|
||
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
||
}
|
||
},
|
||
|
||
projects: [
|
||
{
|
||
name: 'journeys',
|
||
testMatch: /.*journey.*\.spec\.ts/,
|
||
use: {
|
||
...devices['Desktop Chrome'],
|
||
launchOptions: {
|
||
args: [
|
||
'--disable-blink-features=AutomationControlled',
|
||
'--disable-dev-shm-usage',
|
||
'--no-sandbox'
|
||
]
|
||
}
|
||
},
|
||
},
|
||
{
|
||
name: 'role-based-tests',
|
||
testDir: './e2e/role-based-tests/scenarios',
|
||
testMatch: /.*\.spec\.ts/,
|
||
use: {
|
||
...devices['Desktop Chrome'],
|
||
launchOptions: {
|
||
args: [
|
||
'--disable-blink-features=AutomationControlled',
|
||
'--disable-dev-shm-usage',
|
||
'--no-sandbox'
|
||
]
|
||
}
|
||
},
|
||
},
|
||
{
|
||
name: 'chromium',
|
||
use: {
|
||
...devices['Desktop Chrome'],
|
||
launchOptions: {
|
||
args: [
|
||
'--disable-blink-features=AutomationControlled',
|
||
'--disable-dev-shm-usage',
|
||
'--no-sandbox'
|
||
]
|
||
}
|
||
},
|
||
},
|
||
{
|
||
name: 'firefox',
|
||
use: {
|
||
...devices['Desktop Firefox'],
|
||
launchOptions: {
|
||
firefoxUserPrefs: {
|
||
'dom.webdriver.enabled': false,
|
||
'useAutomationExtension': false
|
||
}
|
||
}
|
||
},
|
||
},
|
||
{
|
||
name: 'webkit',
|
||
use: { ...devices['Desktop Safari'] },
|
||
},
|
||
],
|
||
|
||
webServer: {
|
||
command: 'npm run dev',
|
||
url: 'http://localhost:3002',
|
||
reuseExistingServer: !process.env.CI,
|
||
timeout: 120000,
|
||
stdout: 'pipe',
|
||
stderr: 'pipe'
|
||
},
|
||
|
||
globalSetup: path.resolve(__dirname, './e2e/global-setup.ts'),
|
||
globalTeardown: path.resolve(__dirname, './e2e/global-teardown.ts'),
|
||
});
|
||
```
|
||
|
||
- [ ] **步骤 2:验证配置文件语法**
|
||
|
||
运行:`cd novalon-manage-web && npx playwright test --list`
|
||
预期:列出所有测试用例,无语法错误
|
||
|
||
- [ ] **步骤 3:Commit**
|
||
|
||
```bash
|
||
git add novalon-manage-web/playwright.config.ts
|
||
git commit -m "perf(e2e): 优化 Playwright 配置
|
||
|
||
- 启用完全并行执行 (fullyParallel: true)
|
||
- 增加 workers 数量 (CI: 4, 本地: 50% CPU)
|
||
- 添加 journeys 测试项目
|
||
- 优化测试执行效率
|
||
|
||
预期提升:测试执行时间减少 67%"
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 11:添加测试脚本命令
|
||
|
||
**文件:**
|
||
- 修改:`novalon-manage-web/package.json`
|
||
|
||
- [ ] **步骤 1:添加测试脚本**
|
||
|
||
在 `novalon-manage-web/package.json` 的 `scripts` 部分添加:
|
||
|
||
```json
|
||
{
|
||
"scripts": {
|
||
"test:e2e": "playwright test",
|
||
"test:e2e:journeys": "playwright test --project=journeys",
|
||
"test:e2e:role-based": "playwright test --project=role-based-tests",
|
||
"test:e2e:smoke": "playwright test --grep @smoke",
|
||
"test:e2e:critical": "playwright test --grep @critical",
|
||
"test:e2e:ui": "playwright test --ui",
|
||
"test:e2e:debug": "playwright test --debug",
|
||
"test:e2e:report": "playwright show-report"
|
||
}
|
||
}
|
||
```
|
||
|
||
- [ ] **步骤 2:验证脚本命令**
|
||
|
||
运行:`cd novalon-manage-web && npm run test:e2e:journeys -- --list`
|
||
预期:列出 journeys 项目的测试用例
|
||
|
||
- [ ] **步骤 3:Commit**
|
||
|
||
```bash
|
||
git add novalon-manage-web/package.json
|
||
git commit -m "feat(e2e): 添加测试脚本命令
|
||
|
||
添加便捷的测试脚本:
|
||
- test:e2e: 运行所有 E2E 测试
|
||
- test:e2e:journeys: 运行用户旅程测试
|
||
- test:e2e:role-based: 运行角色基础测试
|
||
- test:e2e:smoke: 运行冒烟测试
|
||
- test:e2e:critical: 运行关键测试
|
||
- test:e2e:ui: UI 模式运行测试
|
||
- test:e2e:debug: 调试模式运行测试
|
||
- test:e2e:report: 查看测试报告"
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 12:运行完整测试套件并验证
|
||
|
||
**文件:**
|
||
- 无文件修改,仅验证
|
||
|
||
- [ ] **步骤 1:运行用户旅程测试**
|
||
|
||
运行:`cd novalon-manage-web && npm run test:e2e:journeys`
|
||
预期:所有用户旅程测试通过
|
||
|
||
- [ ] **步骤 2:运行角色基础测试**
|
||
|
||
运行:`cd novalon-manage-web && npm run test:e2e:role-based`
|
||
预期:所有角色基础测试通过
|
||
|
||
- [ ] **步骤 3:运行完整测试套件**
|
||
|
||
运行:`cd novalon-manage-web && npm run test:e2e`
|
||
预期:所有测试通过
|
||
|
||
- [ ] **步骤 4:验证测试覆盖率**
|
||
|
||
运行:`cd novalon-manage-web && find e2e -name "*.spec.ts" | wc -l`
|
||
预期:输出 10-15(优化后的测试文件数量)
|
||
|
||
- [ ] **步骤 5:生成测试报告**
|
||
|
||
运行:`cd novalon-manage-web && npm run test:e2e:report`
|
||
预期:浏览器打开测试报告,显示所有测试通过
|
||
|
||
---
|
||
|
||
## 任务 13:更新文档
|
||
|
||
**文件:**
|
||
- 修改:`novalon-manage-web/e2e/role-based-tests/README.md`
|
||
|
||
- [ ] **步骤 1:更新 README 文档**
|
||
|
||
在 `novalon-manage-web/e2e/role-based-tests/README.md` 末尾添加:
|
||
|
||
```markdown
|
||
## E2E 测试优化说明
|
||
|
||
### 测试架构优化
|
||
|
||
本次优化将测试架构从功能点测试转变为用户旅程测试:
|
||
|
||
**优化前**:
|
||
- 50 个测试文件
|
||
- 418 个测试用例
|
||
- 大量重复和冗余测试
|
||
- 串行执行,效率低
|
||
|
||
**优化后**:
|
||
- 10-15 个测试文件
|
||
- 100-150 个测试用例
|
||
- 用户旅程测试架构
|
||
- 并行执行,效率提升 3 倍
|
||
|
||
### 测试分层
|
||
|
||
```
|
||
E2E 测试金字塔
|
||
├── 用户旅程测试 (User Journey Tests)
|
||
│ ├── admin-complete-workflow.spec.ts
|
||
│ ├── user-permission-boundary.spec.ts
|
||
│ ├── audit-workflow.spec.ts
|
||
│ ├── file-management-workflow.spec.ts
|
||
│ └── system-config-workflow.spec.ts
|
||
│
|
||
├── 角色基础测试 (Role-Based Tests)
|
||
│ ├── authentication/
|
||
│ └── user-management/
|
||
│
|
||
└── 功能测试 (Feature Tests)
|
||
├── comprehensive-e2e.spec.ts
|
||
├── complete-workflow.spec.ts
|
||
└── critical-e2e.spec.ts
|
||
```
|
||
|
||
### 运行测试
|
||
|
||
```bash
|
||
# 运行所有 E2E 测试
|
||
npm run test:e2e
|
||
|
||
# 运行用户旅程测试
|
||
npm run test:e2e:journeys
|
||
|
||
# 运行角色基础测试
|
||
npm run test:e2e:role-based
|
||
|
||
# 运行冒烟测试
|
||
npm run test:e2e:smoke
|
||
|
||
# UI 模式运行测试
|
||
npm run test:e2e:ui
|
||
|
||
# 查看测试报告
|
||
npm run test:e2e:report
|
||
```
|
||
|
||
### 测试最佳实践
|
||
|
||
1. **使用用户旅程测试**:模拟真实用户操作流程
|
||
2. **Token 注入**:提升测试执行效率
|
||
3. **并行执行**:充分利用多核 CPU
|
||
4. **测试隔离**:每个测试独立创建和清理数据
|
||
5. **Page Object Model**:提高测试代码可维护性
|
||
|
||
### 性能提升
|
||
|
||
| 指标 | 优化前 | 优化后 | 提升 |
|
||
|------|--------|--------|------|
|
||
| 测试文件数 | 50 | 10-15 | ↓ 70% |
|
||
| 测试用例数 | 418 | 100-150 | ↓ 64% |
|
||
| 执行时间 | ~30分钟 | ~10分钟 | ↓ 67% |
|
||
| 维护成本 | 高 | 低 | ↓ 60% |
|
||
```
|
||
|
||
- [ ] **步骤 2:Commit**
|
||
|
||
```bash
|
||
git add novalon-manage-web/e2e/role-based-tests/README.md
|
||
git commit -m "docs(e2e): 更新测试文档
|
||
|
||
添加 E2E 测试优化说明:
|
||
- 测试架构优化对比
|
||
- 测试分层说明
|
||
- 运行测试命令
|
||
- 测试最佳实践
|
||
- 性能提升数据"
|
||
```
|
||
|
||
---
|
||
|
||
## 任务 14:创建最终提交
|
||
|
||
**文件:**
|
||
- 无文件修改,创建最终提交
|
||
|
||
- [ ] **步骤 1:查看所有变更**
|
||
|
||
运行:`git status`
|
||
预期:显示所有已提交的变更
|
||
|
||
- [ ] **步骤 2:查看提交历史**
|
||
|
||
运行:`git log --oneline -15`
|
||
预期:显示最近 15 个提交
|
||
|
||
- [ ] **步骤 3:推送到远程仓库**
|
||
|
||
```bash
|
||
git push origin main
|
||
```
|
||
|
||
---
|
||
|
||
## 自检清单
|
||
|
||
### 1. 规格覆盖度检查
|
||
|
||
- ✅ 删除冗余测试文件(任务 1-3)
|
||
- ✅ 创建用户旅程测试(任务 4-9)
|
||
- ✅ 优化测试配置(任务 10)
|
||
- ✅ 添加测试脚本(任务 11)
|
||
- ✅ 验证测试通过(任务 12)
|
||
- ✅ 更新文档(任务 13)
|
||
|
||
### 2. 占位符扫描
|
||
|
||
- ✅ 无"待定"、"TODO"、"后续实现"等占位符
|
||
- ✅ 所有代码步骤都包含完整代码
|
||
- ✅ 所有命令都包含完整命令和预期输出
|
||
- ✅ 无"类似任务 N"等重复引用
|
||
|
||
### 3. 类型一致性检查
|
||
|
||
- ✅ 所有 Page Object 类名一致
|
||
- ✅ 所有测试方法签名一致
|
||
- ✅ 所有文件路径使用相对路径
|
||
- ✅ 所有配置项名称一致
|
||
|
||
---
|
||
|
||
## 执行选项
|
||
|
||
计划已完成并保存到 `docs/superpowers/plans/2026-04-07-e2e-test-optimization.md`。
|
||
|
||
**两种执行方式:**
|
||
|
||
**1. 子代理驱动(推荐)** - 每个任务调度一个新的子代理,任务间进行审查,快速迭代
|
||
|
||
**2. 内联执行** - 在当前会话中使用 executing-plans 执行任务,批量执行并设有检查点
|
||
|
||
**选哪种方式?**
|