refactor(backend): 重命名后端项目为 gym-manage-api,修改包名为 cn.novalon.gym.manage

This commit is contained in:
张翔
2026-04-17 18:35:50 +08:00
parent 666189b676
commit deb961c427
916 changed files with 108360 additions and 38328 deletions
@@ -0,0 +1,320 @@
# E2E测试用例全面修复设计文档
**日期**: 2026-04-04
**作者**: 张翔
**状态**: 待审查
## 概述
本文档描述了Novalon管理系统的E2E测试用例全面修复方案,包括立即修复和短期目标两个阶段。
## 背景
### 当前状态
- **总测试用例数**: 52个
- **已验证测试用例**: 6个
- **通过测试用例**: 2个(33.3%通过率)
- **失败测试用例**: 4个
### 已完成的工作
1. ✅ 禁用测试并行执行,避免状态混乱
2. ✅ 统一URL匹配模式为`/\/(dashboard|\/)$/`
3. ✅ 修复Dashboard元素选择器
4. ✅ 修复登录失败测试用例设计
### 发现的问题
1. **错误消息选择器不正确**
- 当前:`.el-message--error`
- 实际:Element Plus的ElMessage组件使用`.el-message .el-message__content`
2. **登出按钮选择器不正确**
- 当前:`[data-testid="user-menu"]``[data-testid="logout-button"]`
- 实际:使用`el-dropdown`组件,需要点击`.el-avatar`后选择"退出登录"
3. **测试用例覆盖不完整**
- 剩余46个测试用例未验证
- 可能存在类似的选择器问题
## 设计方案
### 第一部分:立即修复
#### 1.1 错误消息选择器修复
**问题分析**
- Element Plus的ElMessage组件生成的DOM结构为:
```html
<div class="el-message el-message--error">
<p class="el-message__content">错误消息内容</p>
</div>
```
- 当前选择器`.el-message--error`无法匹配到可见元素
**修复方案**
```typescript
// 修复前
await expect(page.locator('.el-message--error')).toBeVisible({ timeout: 5000 });
// 修复后
await expect(page.locator('.el-message .el-message__content')).toBeVisible({ timeout: 5000 });
```
**影响范围**
- 测试用例1.2:错误的密码登录失败
- 测试用例1.3:不存在的用户登录失败
- 测试用例1.5:禁用用户登录失败
#### 1.2 登出功能修复
**问题分析**
- DefaultLayout.vue使用`el-dropdown`组件实现用户菜单
- 点击`.el-avatar`后显示下拉菜单
- 下拉菜单中包含"退出登录"选项
**修复方案**
```typescript
// 修复前
await page.click('[data-testid="user-menu"]');
await page.click('[data-testid="logout-button"]');
// 修复后
await page.locator('.el-avatar').click();
await page.waitForTimeout(500);
await page.locator('.el-dropdown-menu').getByText('退出登录').click();
```
**影响范围**
- 测试用例1.6:登出功能正常
### 第二部分:短期目标
#### 2.1 测试用例分类
剩余的46个测试用例分布在以下模块:
| 模块 | 测试用例数 | 主要验证内容 | 预期问题 |
|------|-----------|-------------|---------|
| 用户管理流程测试 | 5 | 用户列表、创建、编辑、删除、状态切换 | 表格选择器、表单选择器、按钮选择器 |
| 角色管理流程测试 | 5 | 角色列表、创建、编辑、删除、权限分配 | 类似用户管理 |
| 菜单管理流程测试 | 4 | 菜单树、创建、编辑、删除 | 树形结构选择器 |
| 权限验证测试 | 3 | 管理员权限、普通用户权限、未登录用户 | 权限提示选择器 |
| 字典管理流程测试 | 5 | 字典列表、创建、编辑、删除 | 表格选择器 |
| 系统配置流程测试 | 4 | 配置列表、创建、编辑、删除 | 表格选择器 |
| 文件管理流程测试 | 5 | 文件列表、上传、下载、删除 | 文件选择器 |
| 操作日志流程测试 | 5 | 日志列表、查询、详情 | 表格选择器 |
| 登录日志流程测试 | 4 | 日志列表、查询、详情 | 表格选择器 |
| 异常日志流程测试 | 4 | 日志列表、查询、详情 | 表格选择器 |
| 通知公告流程测试 | 5 | 公告列表、创建、编辑、删除、发布 | 表格选择器 |
| 性能和稳定性测试 | 3 | 并发登录、大数据量、长时间运行 | 性能指标验证 |
#### 2.2 执行策略
**阶段1:批量修复常见问题**(预计30分钟)
目标:统一修复所有常见的选择器问题
修复内容:
1. **错误消息选择器**:所有`.el-message--error`改为`.el-message .el-message__content`
2. **成功消息选择器**:所有`.success-message`改为`.el-message--success .el-message__content`
3. **表格选择器**:统一使用`.el-table`相关选择器
4. **表单选择器**:统一使用`[name="fieldName"]`或`input[placeholder="..."]`
5. **按钮选择器**:统一使用`button:has-text("按钮文本")`或`[data-testid="..."]`(如果存在)
**阶段2:逐模块验证**(预计1小时)
目标:按模块顺序运行测试,记录并修复问题
执行步骤:
1. 运行用户管理流程测试(5个测试用例)
2. 记录失败原因
3. 针对性修复
4. 重复步骤1-3,直到所有模块测试通过
**阶段3:全面测试**(预计30分钟)
目标:运行完整测试套件,验证所有修复
执行步骤:
1. 运行完整测试套件(52个测试用例)
2. 记录所有失败的测试用例
3. 分析失败原因
4. 针对性修复
5. 重新运行测试套件
6. 生成最终测试报告
#### 2.3 常见选择器映射表
| 功能 | 错误选择器 | 正确选择器 |
|------|-----------|-----------|
| 错误消息 | `.el-message--error` | `.el-message .el-message__content` |
| 成功消息 | `.success-message` | `.el-message--success .el-message__content` |
| 表格 | `.user-table`, `.role-table` | `.el-table` |
| 表格行 | `.user-row`, `.role-row` | `.el-table__row` |
| 用户头像 | `[data-testid="user-menu"]` | `.el-avatar` |
| 登出按钮 | `[data-testid="logout-button"]` | `.el-dropdown-menu`).getByText('退出登录') |
| 欢迎消息 | `.welcome-message` | `.dashboard` |
## 技术约束
### 前端技术栈
- Vue 3 + TypeScript
- Element Plus UI组件库
- Vite构建工具
### 测试技术栈
- Playwright测试框架
- TypeScript测试脚本
- 自定义报告器
### 浏览器环境
- Chromium(主要测试浏览器)
- Firefox(可选)
- WebKit(可选)
## 成功标准
### 立即修复成功标准
- ✅ 测试用例1.2、1.3、1.5通过
- ✅ 测试用例1.6通过
- ✅ 用户认证流程测试模块通过率达到100%
### 短期目标成功标准
- ✅ 所有52个测试用例运行完成
- ✅ 测试通过率达到90%以上(至少47个测试用例通过)
- ✅ 所有失败测试用例有明确的失败原因记录
- ✅ 生成完整的测试报告
## 风险与缓解措施
### 风险1:前端代码与测试用例不匹配
**影响**: 测试用例可能使用了前端不存在的元素选择器
**缓解措施**:
- 检查前端组件代码,确认实际DOM结构
- 使用Playwright的代码生成工具验证选择器
- 添加截图功能,记录测试失败时的页面状态
### 风险2:测试数据不足
**影响**: 部分测试用例可能因缺少测试数据而失败
**缓解措施**:
- 检查测试数据库初始化脚本
- 确保包含各种测试场景的数据(禁用用户、不同角色等)
- 在测试用例中动态创建测试数据
### 风险3:测试环境不稳定
**影响**: 测试可能因网络、服务启动等问题而失败
**缓解措施**:
- 使用JAR文件启动后端,减少启动时间
- 添加健康检查,确保服务就绪后再运行测试
- 设置合理的超时时间
## 后续优化建议
### 测试框架优化
1. 创建Page Object Model的基类,统一常见操作
2. 添加测试数据管理模块,支持动态创建和清理测试数据
3. 实现测试报告自动生成和发送
### 测试用例优化
1. 添加更多边界条件测试
2. 添加性能测试用例
3. 添加安全测试用例
### CI/CD集成
1. 将E2E测试集成到CI/CD流水线
2. 设置测试质量门禁(如90%通过率)
3. 自动发布测试报告
## 时间估算
| 阶段 | 预计时间 | 说明 |
|------|---------|------|
| 立即修复 | 15分钟 | 修复错误消息和登出按钮选择器 |
| 阶段1:批量修复 | 30分钟 | 统一修复常见选择器问题 |
| 阶段2:逐模块验证 | 60分钟 | 按模块运行测试并修复问题 |
| 阶段3:全面测试 | 30分钟 | 运行完整测试套件并生成报告 |
| **总计** | **2小时15分钟** | |
## 附录
### A. 前端组件选择器参考
#### Login.vue
```vue
<el-input v-model="formState.username" placeholder="请输入用户名" />
<el-input v-model="formState.password" placeholder="请输入密码" />
<el-button type="primary" native-type="submit">登录</el-button>
```
选择器:
- 用户名输入框:`input[placeholder="请输入用户名"]`
- 密码输入框:`input[placeholder="请输入密码"]`
- 登录按钮:`button:has-text("登录")`
#### DefaultLayout.vue
```vue
<el-dropdown @command="handleCommand">
<el-avatar :size="32">{{ username }}</el-avatar>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="profile">个人中心</el-dropdown-item>
<el-dropdown-item command="logout" divided>退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
```
选择器:
- 用户头像:`.el-avatar`
- 登出按钮:`.el-dropdown-menu`).getByText('退出登录')
### B. Element Plus组件选择器参考
#### ElMessage
```html
<div class="el-message el-message--error">
<p class="el-message__content">错误消息</p>
</div>
```
选择器:
- 错误消息:`.el-message .el-message__content`
- 成功消息:`.el-message--success .el-message__content`
#### ElTable
```html
<div class="el-table">
<div class="el-table__body-wrapper">
<table>
<tbody>
<tr class="el-table__row">
<td class="el-table__cell">...</td>
</tr>
</tbody>
</table>
</div>
</div>
```
选择器:
- 表格:`.el-table`
- 表格行:`.el-table__row`
- 表格单元格:`.el-table__cell`
### C. 测试执行命令参考
```bash
# 运行单个测试用例
npx playwright test system-integration-test.spec.ts:33 --project=chromium
# 运行指定模块测试
npx playwright test system-integration-test.spec.ts --grep "1. 用户认证流程测试"
# 运行完整测试套件
npx playwright test system-integration-test.spec.ts --project=chromium
# 生成HTML报告
npx playwright show-report
```
@@ -0,0 +1,535 @@
# E2E测试优化设计方案
**文档版本**: 1.0
**创建日期**: 2026-04-04
**作者**: 张翔
**目标**: 将E2E测试通过率从17.3%提升至100%,并优化测试执行时间
---
## 1. 背景与目标
### 1.1 当前状态
- **总测试数**: 52个测试用例
- **通过**: 9个测试用例 (17.3%)
- **失败**: 43个测试用例 (82.7%)
- **执行时间**: 17.2分钟
### 1.2 主要问题
1. **页面导航问题**: 大部分测试用例无法正确导航到目标页面
2. **选择器问题**: 测试用例使用的选择器无法找到对应的页面元素
3. **测试执行时间**: 当前执行时间较长,需要优化
### 1.3 目标
- **测试通过率**: 100% (所有52个测试用例通过)
- **执行时间**: 减少30%以上 (从17.2分钟降至12分钟以内)
- **测试稳定性**: 所有测试用例稳定可重复执行
---
## 2. 实施策略
采用**分阶段实施**策略,按照问题的影响范围,从基础到高级逐步修复。
### 2.1 为什么选择分阶段实施?
- **风险可控**: 每个阶段都可以验证效果,及时调整方案
- **效率最高**: 先解决基础问题,再解决复杂问题,避免重复工作
- **符合测试金字塔**: 从基础功能到高级功能,逐步提高测试覆盖率
- **易于管理**: 每个阶段都有明确的目标和验收标准
---
## 3. 第一阶段:基础导航修复
**预计时间**: 2-3天
**目标**: 测试通过率提升至50%以上(至少26个测试用例通过)
### 3.1 问题分析
43个失败的测试用例中,大部分都是因为无法正确导航到目标页面。主要原因包括:
1. **页面不存在**: 某些管理页面可能还未实现
2. **路由配置问题**: 路由路径与测试用例中的路径不一致
3. **页面加载超时**: 页面加载时间过长,导致测试超时
4. **权限问题**: 某些页面需要特定权限才能访问
### 3.2 修复策略
#### 3.2.1 页面存在性验证
首先验证所有测试用例涉及的页面是否都已经实现:
-`/users` - 用户管理页面
-`/roles` - 角色管理页面
-`/menus` - 菜单管理页面
-`/sys/config` - 系统配置页面
-`/dict` - 字典管理页面
-`/files` - 文件管理页面
-`/loginlog` - 登录日志页面
-`/oplog` - 操作日志页面
-`/exceptionlog` - 异常日志页面
#### 3.2.2 Page Object类优化
为每个Page Object类添加更健壮的导航逻辑:
```typescript
async goto() {
await this.page.goto('/users');
// 等待页面加载完成
await this.page.waitForLoadState('networkidle');
// 等待关键元素出现
await this.page.waitForSelector('.el-table', { timeout: 10000 });
// 验证页面标题或URL
await expect(this.page).toHaveURL(/.*users/);
}
```
#### 3.2.3 错误处理机制
添加完善的错误处理机制:
```typescript
async goto() {
try {
await this.page.goto('/users');
await this.page.waitForLoadState('networkidle');
await this.page.waitForSelector('.el-table', { timeout: 10000 });
} catch (error) {
// 截图保存错误状态
await this.page.screenshot({ path: `test-results/error-${Date.now()}.png` });
// 记录错误信息
console.error('页面导航失败:', error);
// 抛出更详细的错误信息
throw new Error(`导航到用户管理页面失败: ${error.message}`);
}
}
```
### 3.3 任务清单
1. **验证页面存在性**0.5天)
- 检查所有测试用例涉及的页面是否已实现
- 确认路由配置是否正确
- 验证页面权限设置
2. **优化Page Object类**1天)
- 为每个Page Object类添加健壮的导航方法
- 添加错误处理机制
- 添加页面加载验证逻辑
3. **运行测试验证**0.5天)
- 运行完整测试套件
- 收集通过率数据
- 分析剩余失败原因
### 3.4 验收标准
- ✅ 测试通过率提升至50%以上(至少26个测试用例通过)
- ✅ 所有页面都能正确导航
- ✅ 页面加载错误有清晰的错误信息
---
## 4. 第二阶段:选择器精准化
**预计时间**: 2-3天
**目标**: 测试通过率提升至90%以上(至少47个测试用例通过)
### 4.1 问题分析
测试用例中使用的选择器无法找到对应的页面元素,主要原因包括:
1. **选择器过时**: 前端代码修改后,选择器未同步更新
2. **选择器不够健壮**: 使用class选择器,容易受CSS变化影响
3. **动态元素**: 某些元素是动态生成的,需要更灵活的定位方式
4. **异步加载**: 元素加载有延迟,需要添加等待逻辑
### 4.2 修复策略
#### 4.2.1 选择器诊断工具
使用Playwright的trace功能,捕获实际页面元素:
```typescript
// 在测试配置中启用trace
use: {
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
}
```
#### 4.2.2 选择器优化原则
优先使用以下选择器(按优先级排序):
1. **data-testid属性**(最推荐)
```typescript
page.getByTestId('submit-button')
```
2. **角色和文本组合**
```typescript
page.getByRole('button', { name: '确定' })
page.getByText('用户管理')
```
3. **CSS选择器**(最后选择)
```typescript
page.locator('.el-button--primary')
```
#### 4.2.3 Page Object类选择器更新
为每个Page Object类更新选择器:
```typescript
export class UserManagementPage {
readonly page: Page;
readonly table: Locator;
readonly createUserButton: Locator;
readonly searchInput: Locator;
readonly searchButton: Locator;
constructor(page: Page) {
this.page = page;
// 使用更健壮的选择器
this.table = page.locator('.el-table').first();
this.createUserButton = page.getByRole('button', { name: '新增用户' });
this.searchInput = page.getByPlaceholder('搜索用户名或邮箱');
this.searchButton = page.getByRole('button', { name: '搜索' });
}
}
```
#### 4.2.4 等待策略优化
添加智能等待逻辑:
```typescript
async waitForTableReady() {
// 等待表格出现
await this.table.waitFor({ state: 'visible', timeout: 10000 });
// 等待表格数据加载完成
await this.page.waitForFunction(
() => document.querySelectorAll('.el-table__body tr').length > 0,
{ timeout: 5000 }
);
}
```
#### 4.2.5 动态元素处理
处理动态生成的元素:
```typescript
async clickDynamicButton(buttonText: string) {
// 使用文本内容定位动态按钮
await this.page.getByRole('button', { name: buttonText }).click();
// 或者使用正则表达式匹配
await this.page.getByRole('button', { name: /确定|确认/ }).click();
}
```
### 4.3 任务清单
1. **选择器诊断**0.5天)
- 使用Playwright trace捕获实际页面元素
- 分析所有失败测试的选择器问题
- 生成选择器诊断报告
2. **批量更新选择器**(1.5天)
- 更新所有Page Object类的选择器
- 添加智能等待逻辑
- 处理动态元素
3. **运行测试验证**0.5天)
- 运行完整测试套件
- 收集通过率数据
- 分析剩余失败原因
### 4.4 验收标准
- ✅ 测试通过率提升至90%以上(至少47个测试用例通过)
- ✅ 所有选择器都能正确找到元素
- ✅ 动态元素有稳定的处理逻辑
---
## 5. 第三阶段:性能优化
**预计时间**: 1-2天
**目标**: 测试通过率达到100%,执行时间减少30%以上
### 5.1 问题分析
当前测试套件执行时间为17.2分钟,主要耗时在:
1. **全局setup/teardown**: 启动后端服务、数据库初始化等
2. **页面加载等待**: 每个测试用例都等待页面加载完成
3. **固定等待时间**: 使用`waitForTimeout`固定等待,不够智能
4. **串行执行**: 测试用例逐个执行,无法并行
### 5.2 优化策略
#### 5.2.1 全局setup优化
优化后端服务启动时间:
```typescript
// global-setup.ts
export default async function globalSetup() {
console.log('🚀 开始全局测试环境设置...');
// 使用JAR文件启动(比Maven快50%
const jarFile = path.join(backendDir, 'target/manage-app-1.0.0.jar');
// 减少健康检查间隔(从1秒改为0.5秒)
const healthCheckInterval = 500;
// 减少最大等待时间(从60秒改为30秒)
const maxWaitTime = 30;
// 并行启动多个服务(如果需要)
await Promise.all([
startBackendService(),
startFrontendService(),
]);
}
```
#### 5.2.2 页面加载等待优化
使用更智能的等待策略:
```typescript
// 优化前
await page.waitForTimeout(2000);
// 优化后:等待特定条件
await page.waitForLoadState('domcontentloaded'); // 只等待DOM加载
await page.waitForSelector('.el-table', { state: 'visible' }); // 等待关键元素
```
#### 5.2.3 测试用例并行执行
在确保测试独立性的前提下,启用并行执行:
```typescript
// playwright.config.ts
export default defineConfig({
// 项目级并行(不同项目并行执行)
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
],
// 文件级并行(同一项目内,不同文件并行执行)
workers: process.env.CI ? 1 : 4, // CI环境串行,本地并行
// 完全并行(需要确保测试完全独立)
fullyParallel: false, // 暂不启用,避免localStorage冲突
});
```
#### 5.2.4 测试数据缓存
缓存测试数据,避免重复创建:
```typescript
// 使用全局状态存储测试数据
let testUserId: string | null = null;
test.beforeAll(async ({ request }) => {
if (!testUserId) {
// 只创建一次测试用户
const response = await request.post('/api/users', {
data: { username: 'testuser', password: 'Test@123' }
});
testUserId = (await response.json()).id;
}
});
test.afterAll(async ({ request }) => {
if (testUserId) {
// 清理测试数据
await request.delete(`/api/users/${testUserId}`);
testUserId = null;
}
});
```
#### 5.2.5 智能重试机制
为不稳定的测试用例添加智能重试:
```typescript
// playwright.config.ts
export default defineConfig({
// 失败后重试2次
retries: process.env.CI ? 2 : 1,
// 只重试失败的测试用例
retryOnlyFailed: true,
});
```
#### 5.2.6 测试报告优化
生成更详细的测试报告:
```typescript
// 自定义报告器
export default class CustomReporter {
onTestEnd(test: TestCase, result: TestResult) {
const duration = result.duration;
const status = result.status;
// 记录慢测试
if (duration > 10000) {
console.log(`⚠️ 慢测试: ${test.title} (${duration}ms)`);
}
// 记录失败测试的详细信息
if (status === 'failed') {
console.log(`❌ 失败: ${test.title}`);
console.log(` 错误: ${result.error?.message}`);
}
}
}
```
### 5.3 任务清单
1. **优化全局setup/teardown**0.5天)
- 使用JAR文件启动后端服务
- 减少健康检查等待时间
- 并行启动多个服务
2. **优化页面加载等待**0.5天)
- 移除固定等待时间
- 使用智能等待策略
- 优化关键元素等待逻辑
3. **生成最终报告**0.5天)
- 运行完整测试套件
- 生成详细的测试报告
- 分析性能指标
### 5.4 验收标准
- ✅ 测试通过率达到100%(所有52个测试用例通过)
- ✅ 测试执行时间减少30%以上(从17.2分钟降至12分钟以内)
- ✅ 生成完整的测试报告和性能分析
---
## 6. 总体验收标准
### 6.1 功能验收
- ✅ 所有52个测试用例100%通过
- ✅ 测试覆盖所有核心业务流程
- ✅ 测试报告清晰展示测试结果
### 6.2 性能验收
- ✅ 测试执行时间在12分钟以内
- ✅ 全局setup时间在30秒以内
- ✅ 单个测试用例平均执行时间在20秒以内
### 6.3 质量验收
- ✅ 所有Page Object类有完善的错误处理
- ✅ 所有选择器使用最佳实践
- ✅ 测试代码有清晰的注释和文档
---
## 7. 风险与应对
### 7.1 页面未实现风险
**风险**: 某些测试页面可能还未实现
**应对**:
- 优先检查页面存在性
- 如果页面未实现,暂时跳过相关测试用例
- 记录未实现页面的测试用例,后续补充
### 7.2 选择器不稳定风险
**风险**: 某些选择器可能不稳定,导致测试时好时坏
**应对**:
- 使用多个备选选择器
- 添加重试机制
- 使用更健壮的等待策略
### 7.3 测试数据冲突风险
**风险**: 多个测试用例共享测试数据,可能导致冲突
**应对**:
- 每个测试用例使用唯一的测试数据(如时间戳)
- 测试完成后清理测试数据
- 使用独立的测试数据库
### 7.4 执行时间过长风险
**风险**: 即使优化后,执行时间可能仍然较长
**应对**:
- 进一步优化等待策略
- 考虑并行执行更多测试用例
- 减少不必要的测试步骤
---
## 8. 后续优化建议
### 8.1 短期优化(1-2周)
1. **添加更多测试用例**: 覆盖更多边界场景
2. **优化测试数据管理**: 使用测试数据工厂模式
3. **集成到CI/CD**: 配置Woodpecker CI自动运行E2E测试
### 8.2 中期优化(2-4周)
1. **添加可视化测试**: 使用Percy或Applitools进行视觉回归测试
2. **性能监控**: 集成Lighthouse进行性能监控
3. **测试报告优化**: 生成更详细的HTML报告
### 8.3 长期优化(1-2个月)
1. **测试框架升级**: 考虑使用更先进的测试框架
2. **AI辅助测试**: 使用AI工具自动生成测试用例
3. **持续优化**: 定期审查测试用例,优化测试执行速度
---
## 9. 参考资料
- [Playwright官方文档](https://playwright.dev/)
- [Playwright最佳实践](https://playwright.dev/docs/best-practices)
- [Vue 3测试指南](https://vuejs.org/guide/scaling-up/testing.html)
- [E2E测试最佳实践](https://testingjavascript.com/)
---
**文档结束**
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,294 @@
# 角色测试框架迁移设计文档
**日期**: 2026-04-05
**作者**: 张翔
**状态**: 已批准
## 1. 背景
### 问题描述
运行完整E2E测试套件时遇到错误:
```
TypeError: Cannot redefine property: Symbol($$jest-matchers-object)
```
### 根本原因
- `e2e/role-based-tests/`目录下存在vitest单元测试文件(`.test.ts`
- Playwright运行时会加载这些文件,导致与Playwright的expect冲突
- 单元测试文件位置不当,不符合项目结构最佳实践
### 受影响的文件
**单元测试文件**6个):
- `e2e/role-based-tests/shared/__tests__/permission-helper.test.ts`
- `e2e/role-based-tests/shared/__tests__/role-auth-manager.test.ts`
- `e2e/role-based-tests/shared/__tests__/test-data-manager.test.ts`
- `e2e/role-based-tests/roles/__tests__/admin.role.test.ts`
- `e2e/role-based-tests/roles/__tests__/base.role.test.ts`
- `e2e/role-based-tests/roles/__tests__/role-factory.test.ts`
**工具类文件**8个):
- `e2e/role-based-tests/shared/auth-helper.ts`
- `e2e/role-based-tests/shared/permission-helper.ts`
- `e2e/role-based-tests/shared/role-auth-manager.ts`
- `e2e/role-based-tests/shared/test-data-manager.ts`
- `e2e/role-based-tests/roles/admin.role.ts`
- `e2e/role-based-tests/roles/base.role.ts`
- `e2e/role-based-tests/roles/role-factory.ts`
- `e2e/role-based-tests/roles/user.role.ts`
**E2E测试文件**4个,需要更新导入路径):
- `e2e/role-based-tests/scenarios/authentication/login-flow.spec.ts`
- `e2e/role-based-tests/scenarios/authentication/logout-flow.spec.ts`
- `e2e/role-based-tests/scenarios/user-management/admin-creates-user.spec.ts`
- `e2e/role-based-tests/scenarios/user-management/permission-boundary.spec.ts`
## 2. 解决方案
### 设计原则
1. **职责分离**:单元测试和E2E测试应该分开存放
2. **符合最佳实践**:单元测试放在`src/`目录,E2E测试放在`e2e/`目录
3. **便于维护**:工具类和单元测试在同一目录,便于查找和修改
4. **避免冲突**:彻底解决Playwright与Vitest的冲突问题
### 文件结构变更
**迁移前**
```
e2e/role-based-tests/
├── shared/
│ ├── __tests__/
│ │ ├── permission-helper.test.ts
│ │ ├── role-auth-manager.test.ts
│ │ └── test-data-manager.test.ts
│ ├── auth-helper.ts
│ ├── permission-helper.ts
│ ├── role-auth-manager.ts
│ └── test-data-manager.ts
├── roles/
│ ├── __tests__/
│ │ ├── admin.role.test.ts
│ │ ├── base.role.test.ts
│ │ └── role-factory.test.ts
│ ├── admin.role.ts
│ ├── base.role.ts
│ ├── role-factory.ts
│ └── user.role.ts
└── scenarios/
├── authentication/
│ ├── login-flow.spec.ts
│ └── logout-flow.spec.ts
└── user-management/
├── admin-creates-user.spec.ts
└── permission-boundary.spec.ts
```
**迁移后**
```
src/role-based-tests/
├── shared/
│ ├── __tests__/
│ │ ├── permission-helper.test.ts
│ │ ├── role-auth-manager.test.ts
│ │ └── test-data-manager.test.ts
│ ├── auth-helper.ts
│ ├── permission-helper.ts
│ ├── role-auth-manager.ts
│ └── test-data-manager.ts
└── roles/
├── __tests__/
│ ├── admin.role.test.ts
│ ├── base.role.test.ts
│ └── role-factory.test.ts
├── admin.role.ts
├── base.role.ts
├── role-factory.ts
└── user.role.ts
e2e/role-based-tests/
└── scenarios/
├── authentication/
│ ├── login-flow.spec.ts
│ └── logout-flow.spec.ts
└── user-management/
├── admin-creates-user.spec.ts
└── permission-boundary.spec.ts
```
## 3. 实施步骤
### 步骤1:创建目标目录结构
```bash
mkdir -p src/role-based-tests/shared/__tests__
mkdir -p src/role-based-tests/roles/__tests__
```
### 步骤2:迁移shared目录
```bash
# 迁移工具类
mv e2e/role-based-tests/shared/*.ts src/role-based-tests/shared/
# 迁移单元测试
mv e2e/role-based-tests/shared/__tests__/*.test.ts src/role-based-tests/shared/__tests__/
```
### 步骤3:迁移roles目录
```bash
# 迁移角色定义
mv e2e/role-based-tests/roles/*.ts src/role-based-tests/roles/
# 迁移单元测试
mv e2e/role-based-tests/roles/__tests__/*.test.ts src/role-based-tests/roles/__tests__/
```
### 步骤4:删除空目录
```bash
rm -rf e2e/role-based-tests/shared
rm -rf e2e/role-based-tests/roles
```
### 步骤5:更新vitest配置
**文件**: `vitest.config.ts`
**变更前**
```typescript
include: [
'src/test/**/*.{test,spec}.{js,ts,jsx,tsx}',
'e2e/role-based-tests/**/__tests__/*.{test,spec}.{js,ts,jsx,tsx}'
]
```
**变更后**
```typescript
include: [
'src/test/**/*.{test,spec}.{js,ts,jsx,tsx}',
'src/__tests__/**/*.{test,spec}.{js,ts,jsx,tsx}'
]
```
**完整配置更新**
```typescript
export default defineConfig({
plugins: [vue()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: ['./src/test/setup.ts'],
include: [
'src/test/**/*.{test,spec}.{js,ts,jsx,tsx}',
'src/__tests__/**/*.{test,spec}.{js,ts,jsx,tsx}'
],
exclude: [
'node_modules/',
'dist/',
'e2e/**/*.spec.ts',
'**/*.d.ts',
'**/*.config.*',
'**/mockData',
],
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html', 'lcov'],
exclude: [
'node_modules/',
'src/test/',
'src/__tests__/',
'**/*.d.ts',
'**/*.config.*',
'**/mockData',
'e2e/',
],
lines: 80,
functions: 80,
branches: 80,
statements: 80,
},
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url)),
},
},
})
```
### 步骤6:更新E2E测试导入路径
**文件**: `e2e/role-based-tests/scenarios/authentication/login-flow.spec.ts`
**变更前**
```typescript
import { RoleFactory } from '../../roles/role-factory';
import { createAuthenticatedPage } from '../../shared/auth-helper';
```
**变更后**
```typescript
import { RoleFactory } from '@/role-based-tests/roles/role-factory';
import { createAuthenticatedPage } from '@/role-based-tests/shared/auth-helper';
```
**需要更新的文件**
1. `e2e/role-based-tests/scenarios/authentication/login-flow.spec.ts`
2. `e2e/role-based-tests/scenarios/authentication/logout-flow.spec.ts`
3. `e2e/role-based-tests/scenarios/user-management/admin-creates-user.spec.ts`
4. `e2e/role-based-tests/scenarios/user-management/permission-boundary.spec.ts`
## 4. 验证步骤
### 4.1 验证单元测试
```bash
npm run test:unit
```
**预期结果**
- 所有单元测试通过
- vitest能够正确找到`src/role-based-tests/`下的测试文件
### 4.2 验证E2E测试
```bash
npx playwright test e2e/role-based-tests --project=chromium
```
**预期结果**
- 无TypeError错误
- 所有E2E测试正常运行
### 4.3 验证导入路径
```bash
npm run type-check
```
**预期结果**
- 无类型错误
- TypeScript能够正确解析`@/`别名
## 5. 风险与缓解措施
### 风险1:导入路径遗漏
**描述**:可能有其他文件引用了迁移的文件
**缓解措施**
- 使用grep搜索所有引用
- 运行类型检查确保无遗漏
### 风险2Playwright配置冲突
**描述**Playwright可能无法正确解析`@/`别名
**缓解措施**
- Playwright使用自己的配置,不依赖tsconfig.json
- 如果出现问题,可以使用相对路径作为备选方案
### 风险3:单元测试依赖问题
**描述**:单元测试可能依赖E2E测试的某些资源
**缓解措施**
- 单元测试使用相对路径导入,不依赖别名
- 迁移后立即运行测试验证
## 6. 后续优化建议
1. **清理诊断代码**:移除`PasswordDiagnosticHandler.java`(生产环境不需要)
2. **完善测试文档**:更新README,说明单元测试和E2E测试的运行方式
3. **CI/CD集成**:确保CI流水线正确运行单元测试和E2E测试
## 7. 参考资料
- [Vitest配置文档](https://vitest.dev/config/)
- [Playwright配置文档](https://playwright.dev/docs/test-configuration)
- [TypeScript路径映射](https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping)
@@ -0,0 +1,255 @@
# E2E测试精简设计文档
**版本:** 1.0
**日期:** 2026-04-07
**作者:** 张翔
**状态:** 待审查
---
## 1. 背景与目标
### 1.1 当前问题
当前E2E测试套件存在以下问题:
- **测试文件过多**:38个测试文件,维护成本高
- **运行时间长**:预计完整运行需要20分钟
- **测试稳定性差**:存在flaky测试,影响CI/CD效率
- **测试重复**:多个测试文件覆盖相同功能
### 1.2 优化目标
- 减少测试文件数量至5个(减少87%)
- 缩短测试运行时间至5分钟以内(减少75%)
- 提升测试稳定性和可维护性
- 保留关键业务流程验证
---
## 2. 测试架构设计
### 2.1 分层测试策略
采用分层测试策略,将E2E测试分为两层:
| 层级 | 测试类型 | 文件数 | 运行时间 | 覆盖范围 |
|------|---------|--------|---------|---------|
| L1 | 冒烟测试 | 1 | ~30秒 | 登录/登出基础流程 |
| L2 | 核心旅程 | 4 | ~4分钟 | 关键业务端到端流程 |
### 2.2 目录结构
```
e2e/
├── journeys/ # 核心用户旅程(保留)
│ ├── admin-complete-workflow.spec.ts # 管理员完整工作流
│ ├── user-permission-boundary.spec.ts # 用户权限边界验证
│ ├── file-management-workflow.spec.ts # 文件上传下载流程
│ └── audit-workflow.spec.ts # 审计日志查看流程
├── smoke/ # 冒烟测试(新增)
│ └── login-logout.spec.ts # 登录登出基础流程
├── fixtures/ # 测试数据(保留)
├── helpers/ # 测试辅助工具(保留)
├── pages/ # Page Object(保留)
└── utils/ # 工具函数(保留)
```
---
## 3. 核心测试用例设计
### 3.1 冒烟测试(smoke/login-logout.spec.ts
**测试目标:** 验证基础登录登出流程
**测试用例:**
- 管理员登录和登出
**预期运行时间:** ~30秒
### 3.2 核心旅程测试
#### 3.2.1 管理员完整工作流(admin-complete-workflow.spec.ts
**测试目标:** 验证管理员的核心操作流程
**测试用例:**
- 创建角色并分配权限
- 创建用户并分配角色
- 编辑用户信息
- 删除用户
- 删除角色
**预期运行时间:** ~2分钟
#### 3.2.2 用户权限边界验证(user-permission-boundary.spec.ts
**测试目标:** 验证权限控制是否正确
**测试用例:**
- 普通用户不能访问用户管理页面
- 普通用户不能访问角色管理页面
- 管理员可以访问所有页面
**预期运行时间:** ~1分钟
#### 3.2.3 文件管理流程(file-management-workflow.spec.ts
**测试目标:** 验证文件上传下载流程
**测试用例:**
- 上传文件
- 下载文件
- 删除文件
**预期运行时间:** ~1分钟
#### 3.2.4 审计日志流程(audit-workflow.spec.ts
**测试目标:** 验证审计日志查看功能
**测试用例:**
- 查看操作日志
- 查看登录日志
- 查看异常日志
**预期运行时间:** ~30秒
---
## 4. 实施计划
### 4.1 实施步骤
1. **创建新目录结构**
- 创建 `e2e/smoke/` 目录
2. **创建冒烟测试**
- 新建 `e2e/smoke/login-logout.spec.ts`
3. **删除非核心测试文件**
- 删除34个非核心测试文件
- 只保留 `journeys/` 目录下的4个核心测试文件
### 4.2 测试配置更新
**package.json 脚本更新:**
```json
{
"scripts": {
"test:e2e:smoke": "playwright test smoke/",
"test:e2e:journeys": "playwright test journeys/",
"test:e2e": "playwright test"
}
}
```
### 4.3 CI/CD集成
- **PR验证**:运行 `npm run test:e2e`~5分钟)
- **发布前验证**:运行所有测试
---
## 5. 预期收益
| 指标 | 优化前 | 优化后 | 改善幅度 |
|------|--------|--------|---------|
| 测试文件数量 | 38个 | 5个 | ↓ 87% |
| 预计运行时间 | ~20分钟 | ~5分钟 | ↓ 75% |
| 维护成本 | 高 | 低 | ↓ 80% |
| 测试稳定性 | 中 | 高 | ↑ 显著提升 |
---
## 6. 风险控制
### 6.1 功能覆盖风险
**风险:** 删除测试后功能覆盖下降
**缓解措施:**
- 通过单元测试和集成测试补充覆盖率
- 单元测试覆盖率目标:80%
### 6.2 回归测试风险
**风险:** 可能遗漏部分边界情况
**缓解措施:**
- 核心旅程测试覆盖关键路径
- 定期人工回归测试
### 6.3 团队适应风险
**风险:** 团队需要适应新的测试策略
**缓解措施:**
- 更新测试文档
- 培训团队成员
---
## 7. 后续优化建议
1. **补充单元测试**
- 为核心业务逻辑补充单元测试
- 覆盖率目标:80%
2. **补充集成测试**
- 为API接口补充集成测试
- 覆盖所有REST API端点
3. **持续优化**
- 定期评估测试效果
- 持续优化测试用例
---
## 8. 待删除测试文件清单
以下34个测试文件将被删除:
1. auth.spec.ts
2. basic.spec.ts
3. complete-workflow.spec.ts
4. comprehensive-e2e.spec.ts
5. critical-e2e.spec.ts
6. dashboard-operation-log.spec.ts
7. dictionary-management.spec.ts
8. edge-cases.spec.ts
9. exception-log.spec.ts
10. file-management.spec.ts
11. form-test.spec.ts
12. login-log.spec.ts
13. menu-management.spec.ts
14. notification.spec.ts
15. operation-log.spec.ts
16. permission-validation.spec.ts
17. role-management.spec.ts
18. security-e2e.spec.ts
19. system-config.spec.ts
20. system-integration-test.spec.ts
21. test-config-api.spec.ts
22. test-stability.spec.ts
23. uat-file-workflow.spec.ts
24. uat-permission-workflow.spec.ts
25. uat-user-lifecycle.spec.ts
26. user-lifecycle.spec.ts
27. user-management.spec.ts
28. role-based-tests/scenarios/authentication/login-flow.spec.ts
29. role-based-tests/scenarios/authentication/logout-flow.spec.ts
30. role-based-tests/scenarios/user-management/admin-creates-user.spec.ts
31. role-based-tests/scenarios/user-management/permission-boundary.spec.ts
32. journeys/system-config-workflow.spec.ts
33. journeys/permission-boundary.spec.ts(与user-permission-boundary.spec.ts重复)
---
## 9. 审查记录
| 日期 | 审查人 | 状态 | 备注 |
|------|--------|------|------|
| 2026-04-07 | 张翔 | 待审查 | 初始版本 |
@@ -0,0 +1,538 @@
# 权限系统增强设计文档
**日期**: 2026-04-08
**作者**: 张翔
**版本**: 1.0
**状态**: 待审查
## 1. 概述
### 1.1 背景
当前系统已完成基础的路由权限控制,但存在以下优化空间:
1. **菜单硬编码** - 菜单在前端硬编码,无法根据用户角色动态显示
2. **权限数据分散** - 角色和权限信息存储在 localStorage,缺乏统一管理
3. **缺少按钮级权限控制** - 无法控制按钮级别的权限
4. **缺少 API 权限检查** - 前端调用 API 前未检查权限,可能发送无效请求
### 1.2 目标
实现完整的权限系统增强,包括:
1. **动态菜单渲染** - 从后端获取菜单数据,根据用户权限动态渲染
2. **权限缓存优化** - 使用 Pinia 统一管理权限数据,localStorage 持久化
3. **权限指令** - 提供 `v-permission` 指令实现按钮级权限控制
4. **API 权限检查** - 前端调用 API 前检查权限,减少无效请求
### 1.3 范围
**包含:**
- Permission Store (Pinia)
- v-permission 指令
- 动态菜单渲染
- API 权限检查工具
- 相关单元测试
**不包含:**
- 后端权限系统修改(仅需新增 API)
- 数据库权限表结构调整
- 其他业务功能开发
## 2. 架构设计
### 2.1 整体架构
```
┌─────────────────────────────────────────────────────────────┐
│ 前端应用 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ 路由守卫 │ │ 权限指令 │ │ 动态菜单 │ │
│ │ (已完成) │ │ v-permission │ │ 渲染 │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └──────────────────┼──────────────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Permission │ │
│ │ Store (Pinia) │ │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ localStorage │ │
│ │ (持久化) │ │
│ └─────────────────┘ │
│ │
└───────────────────────────┬─────────────────────────────────┘
HTTP API │
┌───────────────────────────▼─────────────────────────────────┐
│ 后端服务 │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ /auth/login │ │ /menus/user │ │ /permissions │ │
│ │ (已存在) │ │ (新增) │ │ (已存在) │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ RBAC 权限系统 (角色-权限-菜单) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
### 2.2 数据流
```
登录成功
解析 JWT token 获取角色
调用 fetchUserMenus() 获取菜单和权限
存入 Store + localStorage
页面刷新时从 localStorage 恢复
```
## 3. 详细设计
### 3.1 Permission Store
**文件位置**: `src/stores/permission.ts`
**状态定义**:
```typescript
interface PermissionState {
roles: string[] // 用户角色
permissions: string[] // 用户权限码
menus: MenuItem[] // 用户菜单
loaded: boolean // 是否已加载
}
interface MenuItem {
id: number
name: string
path: string
icon?: string
parentId?: number
sort: number
children?: MenuItem[]
}
```
**核心 Actions**:
```typescript
// 初始化权限数据(从 localStorage 恢复)
initFromStorage(): void
// 登录后设置权限数据
setPermissionData(data: {
roles: string[]
permissions: string[]
menus: MenuItem[]
}): void
// 从后端刷新权限数据
async fetchUserMenus(): Promise<void>
// 清除权限数据(退出登录)
clearPermissionData(): void
// 权限检查方法
hasRole(role: string | string[]): boolean
hasPermission(permission: string | string[]): boolean
```
**持久化策略**:
- 登录时:将角色、权限、菜单数据存入 localStorage
- 页面刷新:Pinia 从 localStorage 恢复数据,立即渲染菜单
- 权限变更:提供刷新机制,同步更新 localStorage 和 Pinia
- 退出登录:清除所有数据
### 3.2 v-permission 指令
**文件位置**: `src/directives/permission.ts`
**用法**:
```vue
<!-- 角色检查 -->
<button v-permission:role="'admin'">管理员按钮</button>
<!-- 权限码检查 -->
<button v-permission:permission="'user:delete'">删除用户</button>
<!-- 支持数组满足任一条件 -->
<button v-permission:role="['admin', 'manager']">导出数据</button>
<button v-permission:permission="['user:create', 'user:update']">编辑用户</button>
<!-- 简写形式默认权限检查 -->
<button v-permission="'user:delete'">删除</button>
```
**实现逻辑**:
```typescript
export const permissionDirective = {
mounted(el: HTMLElement, binding: DirectiveBinding) {
const permissionStore = usePermissionStore()
const { arg, value } = binding
const checkType = arg || 'permission' // 默认权限检查
let hasAccess = false
if (checkType === 'role') {
hasAccess = permissionStore.hasRole(value)
} else if (checkType === 'permission') {
hasAccess = permissionStore.hasPermission(value)
}
if (!hasAccess) {
el.style.display = 'none'
}
}
}
```
**注册方式**:
```typescript
// src/main.ts
import { permissionDirective } from '@/directives/permission'
app.directive('permission', permissionDirective)
```
### 3.3 动态菜单渲染
**后端 API**:
```
GET /api/menus/user
请求头:
Authorization: Bearer <token>
响应:
{
"code": 200,
"data": {
"menus": [
{
"id": 1,
"name": "仪表盘",
"path": "/dashboard",
"icon": "Odometer",
"parentId": null,
"sort": 1
},
{
"id": 2,
"name": "系统管理",
"path": "/system",
"icon": "Setting",
"parentId": null,
"sort": 2,
"children": [
{
"id": 3,
"name": "用户管理",
"path": "/users",
"icon": null,
"parentId": 2,
"sort": 1
}
]
}
],
"permissions": [
"user:read",
"user:create",
"user:update",
"user:delete"
]
}
}
```
**前端组件**:
```vue
<!-- src/layouts/DefaultLayout.vue -->
<template>
<el-menu
:default-active="activeMenu"
class="menu"
:collapse="collapsed"
router
>
<menu-item
v-for="menu in menuTree"
:key="menu.id"
:menu="menu"
/>
</el-menu>
</template>
<script setup lang="ts">
import { computed } from 'vue'
import { usePermissionStore } from '@/stores/permission'
import MenuItem from '@/components/MenuItem.vue'
const permissionStore = usePermissionStore()
const menuTree = computed(() => permissionStore.menus)
</script>
```
**递归菜单组件**:
```vue
<!-- src/components/MenuItem.vue -->
<template>
<!-- 有子菜单 -->
<el-sub-menu
v-if="menu.children && menu.children.length > 0"
:index="String(menu.id)"
>
<template #title>
<el-icon v-if="menu.icon">
<component :is="menu.icon" />
</el-icon>
<span>{{ menu.name }}</span>
</template>
<menu-item
v-for="child in menu.children"
:key="child.id"
:menu="child"
/>
</el-sub-menu>
<!-- 无子菜单 -->
<el-menu-item
v-else
:index="menu.path"
>
<el-icon v-if="menu.icon">
<component :is="menu.icon" />
</el-icon>
<span>{{ menu.name }}</span>
</el-menu-item>
</template>
```
### 3.4 API 权限检查
**文件位置**: `src/utils/permission-check.ts`
**权限映射配置**:
```typescript
const apiPermissionMap: Record<string, { permission: string; method: string }> = {
'/api/users:GET': { permission: 'user:read', method: 'GET' },
'/api/users:POST': { permission: 'user:create', method: 'POST' },
'/api/users/*:PUT': { permission: 'user:update', method: 'PUT' },
'/api/users/*:DELETE': { permission: 'user:delete', method: 'DELETE' },
'/api/roles:GET': { permission: 'role:read', method: 'GET' },
// ... 更多映射
}
```
**检查函数**:
```typescript
export function canAccessApi(path: string, method: string): boolean {
const permissionStore = usePermissionStore()
const required = findRequiredPermission(path, method, apiPermissionMap)
if (!required) {
return true // 未定义权限要求的 API 默认允许
}
return permissionStore.hasPermission(required.permission)
}
```
**集成到请求拦截器**:
```typescript
// src/utils/request.ts
import { canAccessApi } from './permission-check'
request.interceptors.request.use(
(config) => {
// 权限检查
const path = config.url || ''
const method = config.method?.toUpperCase() || 'GET'
if (!canAccessApi(path, method)) {
return Promise.reject(new Error('无权限访问此 API'))
}
// 原有的 token 和签名逻辑
// ...
return config
}
)
```
## 4. 测试策略
### 4.1 测试覆盖范围
1. **Permission Store 单元测试**
- 测试权限数据的存储和恢复
- 测试 hasRole 和 hasPermission 方法
- 测试 localStorage 持久化
- 测试数据清除功能
2. **v-permission 指令测试**
- 测试角色检查功能
- 测试权限码检查功能
- 测试数组参数处理
- 测试元素隐藏/显示逻辑
3. **动态菜单测试**
- 测试菜单数据获取
- 测试菜单树渲染
- 测试菜单缓存机制
- 测试菜单权限过滤
4. **API 权限检查测试**
- 测试权限映射匹配
- 测试通配符匹配
- 测试请求拦截逻辑
### 4.2 测试文件结构
```
src/
├── stores/
│ └── __tests__/
│ └── permission.test.ts
├── directives/
│ └── __tests__/
│ └── permission.test.ts
├── components/
│ └── __tests__/
│ └── MenuItem.test.ts
└── utils/
└── __tests__/
└── permission-check.test.ts
```
## 5. 实施计划
### 5.1 实施顺序
**第 1 步:Permission Store1-2 小时)**
- 创建 `src/stores/permission.ts`
- 实现 localStorage 持久化
- 编写单元测试
- 集成到登录流程
**第 2 步:v-permission 指令(1-2 小时)**
- 创建 `src/directives/permission.ts`
- 注册全局指令
- 编写单元测试
- 在现有页面应用示例
**第 3 步:后端 API 开发(2-3 小时)**
- 新增 `GET /api/menus/user` 接口
- 根据用户角色返回菜单树
- 返回用户权限列表
- 编写后端测试
**第 4 步:动态菜单渲染(2-3 小时)**
- 创建 `src/components/MenuItem.vue`
- 修改 `DefaultLayout.vue`
- 集成 Permission Store
- 编写组件测试
**第 5 步:API 权限检查(1-2 小时)**
- 创建 `src/utils/permission-check.ts`
- 集成到请求拦截器
- 编写单元测试
- 优化性能
### 5.2 后端 API 需求
**接口**: `GET /api/menus/user`
**功能**: 获取当前登录用户可访问的菜单和权限
**业务逻辑**:
1. 从 token 获取用户 ID
2. 查询用户角色
3. 根据角色查询菜单和权限
4. 构建菜单树结构
5. 返回菜单和权限列表
**预估时间**: 7-12 小时
## 6. 风险和约束
### 6.1 技术风险
1. **后端 API 开发时间** - 需要后端配合开发新 API
2. **菜单数据迁移** - 需要将硬编码菜单迁移到数据库
3. **权限数据同步** - 前后端权限数据需要保持一致
### 6.2 约束条件
1. **向后兼容** - 需要兼容现有的路由守卫逻辑
2. **性能要求** - 菜单加载不能影响页面首屏渲染速度
3. **测试覆盖** - 所有新增代码需要单元测试覆盖
## 7. 验收标准
### 7.1 功能验收
- [ ] Permission Store 正确管理权限数据
- [ ] v-permission 指令正确控制按钮显示
- [ ] 动态菜单根据用户权限正确渲染
- [ ] API 权限检查正确拦截无权限请求
### 7.2 质量验收
- [ ] 所有单元测试通过
- [ ] 代码覆盖率 ≥ 80%
- [ ] TypeScript 类型检查通过
- [ ] ESLint 检查通过
### 7.3 性能验收
- [ ] 菜单加载时间 < 500ms
- [ ] localStorage 读写不影响页面性能
- [ ] 权限检查不影响 API 请求速度
## 8. 后续优化
### 8.1 短期优化
1. **权限缓存过期** - 添加权限数据过期机制
2. **权限变更通知** - 实现权限变更后的实时通知
3. **权限日志** - 记录权限检查日志,便于调试
### 8.2 长期优化
1. **权限可视化配置** - 提供权限配置界面
2. **权限审计** - 记录用户权限变更历史
3. **权限模板** - 提供常用权限模板,简化配置
## 9. 参考资料
- [Vue 3 官方文档](https://vuejs.org/)
- [Pinia 官方文档](https://pinia.vuejs.org/)
- [Element Plus 文档](https://element-plus.org/)
- [RBAC 权限模型](https://en.wikipedia.org/wiki/Role-based_access_control)
@@ -0,0 +1,404 @@
# User Journey 测试改进设计文档
**文档日期**: 2026-04-08
**负责人**: 张翔
**版本**: 1.0
**状态**: 已验证
---
## 执行摘要
通过快速验证测试,我们确认了 **Playwright 本身是有效的**,问题在于测试方式。改进后的测试方法成功发现了真实问题,证明了方案的可行性。
**核心发现**
- ✅ Playwright 工具本身有效
- ❌ 旧测试方式存在假阳性问题
- ✅ 新测试方式能真实发现问题
- ✅ 三层验证策略可行
---
## 1. 问题分析
### 1.1 当前问题
**用户报告**
- 测试通过了,但实际运行时页面没有内容
- Console 有 Mock API 日志,但页面无内容
**根本原因**
```typescript
// ❌ 错误的测试方式
const dataStats = page.locator('[data-testid="data-stats"]')
if (await dataStats.isVisible()) { // 如果不可见,跳过验证!
const statsText = await dataStats.textContent()
expect(statsText).toBeTruthy() // 这行永远不会执行
}
// 测试通过!但实际上什么都没验证
```
**问题本质**
- 软验证:元素不存在就跳过验证
- 假阳性:测试通过但实际无效
- 缺乏强制验证:没有确保元素必须存在
---
### 1.2 验证测试结果
**测试文件**: `tests/e2e/specs/validation/test-improvement-validation.spec.ts`
**测试结果**
| 测试类型 | 结果 | 说明 |
|---------|------|------|
| ❌ 旧方式:软验证 | ✅ 通过 | **假阳性!** 元素不存在但测试通过 |
| ✅ 新方式:硬验证 | ❌ 失败 | **正确!** 元素不存在,测试失败 |
| ✅ 三层验证 | ❌ 失败 | API请求超时,暴露真实问题 |
| ✅ 完整用户旅程 | ❌ 失败 | 案件列表元素不存在(count = 0) |
| ✅ 诊断测试 | ✅ 通过 | 提供详细诊断信息 |
**关键发现**
- 页面上没有 `.ant-list-item` 元素(count = 0
- API请求超时(没有调用 `/api/cases`
- 页面根本没有加载案件数据
---
## 2. 解决方案
### 2.1 核心原则转变
#### ❌ 旧方式(软验证)
```typescript
// 软验证:元素不存在就跳过
if (await element.isVisible()) {
expect(await element.textContent()).toBeTruthy()
}
```
#### ✅ 新方式(硬验证)
```typescript
// 硬验证:元素必须存在且可见
await expect(element).toBeVisible()
const text = await element.textContent()
expect(text).toBeTruthy()
expect(text.length).toBeGreaterThan(0)
```
---
### 2.2 三层验证策略
```typescript
test('真实验证用户看到的内容', async ({ page }) => {
// Layer 1: API层验证
const response = await page.waitForResponse('**/api/cases')
expect(response.status()).toBe(200)
const data = await response.json()
expect(data.length).toBeGreaterThan(0)
// Layer 2: 状态层验证
const state = await page.evaluate(() => {
return {
cases: window.__CASE_STORE__?.getState().cases,
currentCase: window.__CASE_STORE__?.getState().currentCase
}
})
expect(state.cases.length).toBeGreaterThan(0)
// Layer 3: DOM层验证
const caseItems = page.locator('.ant-list-item')
await expect(caseItems.first()).toBeVisible({ timeout: 5000 })
const count = await caseItems.count()
expect(count).toBeGreaterThan(0)
// Layer 4: 内容验证
const firstCaseText = await caseItems.first().textContent()
expect(firstCaseText).toBeTruthy()
expect(firstCaseText.length).toBeGreaterThan(10)
})
```
---
### 2.3 增强的测试工具
#### 1. 状态验证器
```typescript
// tests/e2e/utils/state-validator.ts
export async function validatePageState(page: Page, expectedState: {
hasCase?: boolean
hasData?: boolean
activePage?: string
}) {
const state = await page.evaluate(() => ({
currentCase: window.__CASE_STORE__?.getState().currentCase,
transactions: window.__DATA_STORE__?.getState().transactions,
activePage: window.__PAGE_STORE__?.getState().activePageKey
}))
if (expectedState.hasCase) {
expect(state.currentCase).toBeTruthy()
}
if (expectedState.hasData) {
expect(state.transactions.length).toBeGreaterThan(0)
}
if (expectedState.activePage) {
expect(state.activePage).toBe(expectedState.activePage)
}
}
```
#### 2. 内容验证器
```typescript
// tests/e2e/utils/content-validator.ts
export async function validateContent(
page: Page,
selector: string,
options: {
mustBeVisible?: boolean
mustHaveText?: boolean
minLength?: number
exactText?: string
} = {}
) {
const element = page.locator(selector)
// 默认必须可见
if (options.mustBeVisible !== false) {
await expect(element).toBeVisible({ timeout: 5000 })
}
if (options.mustHaveText) {
const text = await element.textContent()
expect(text).toBeTruthy()
if (options.minLength) {
expect(text.length).toBeGreaterThanOrEqual(options.minLength)
}
if (options.exactText) {
expect(text.trim()).toBe(options.exactText)
}
}
}
```
#### 3. 截图验证器
```typescript
// tests/e2e/utils/screenshot-validator.ts
export async function takeScreenshotAndValidate(
page: Page,
testName: string,
step: string
) {
const screenshot = await page.screenshot({
fullPage: true,
path: `test-results/screenshots/${testName}-${step}.png`
})
// 验证截图不为空
expect(screenshot.length).toBeGreaterThan(1000)
console.log(`📸 Screenshot saved: ${testName}-${step}.png`)
}
```
---
## 3. 实施计划
### 3.1 短期(1周内)
**目标**: 修复现有测试用例
**任务清单**:
- [ ] 将所有软验证改为硬验证
- [ ] 添加三层验证策略
- [ ] 创建测试工具函数
- [ ] 修复发现的问题
**预计工作量**: 2-3 天
---
### 3.2 中期(2-4周)
**目标**: 建立完整的测试体系
**任务清单**:
- [ ] 添加视觉验证(截图对比)
- [ ] 建立测试报告机制
- [ ] 集成到CI/CD
- [ ] 建立测试数据管理
**预计工作量**: 5-7 天
---
### 3.3 长期(1-3个月)
**目标**: 持续优化和扩展
**任务清单**:
- [ ] 评估是否需要引入Storybook
- [ ] 考虑AI辅助测试
- [ ] 建立性能测试
- [ ] 建立安全测试
**预计工作量**: 10-15 天
---
## 4. 技术选型
### 4.1 核心工具
**Playwright****已验证有效**
- 优势:
- 强大的选择器和断言
- 支持API拦截和验证
- 内置截图和视频录制
- 跨浏览器支持
- 活跃的社区和文档
- 劣势:
- 需要正确的使用方式
- 学习曲线适中
**结论**: 继续使用Playwright,改进测试方式
---
### 4.2 辅助工具
| 工具 | 用途 | 优先级 |
|------|------|--------|
| Playwright Screenshot | 视觉验证 | P0 |
| Playwright Trace | 调试支持 | P0 |
| Playwright API Mocking | 数据模拟 | P1 |
| Percy / Chromatic | 视觉回归 | P2 |
| Storybook | 组件测试 | P3 |
---
## 5. 质量保障
### 5.1 测试原则
1. **硬验证优先**: 元素必须存在,否则测试失败
2. **多层验证**: API → 状态 → DOM → 内容
3. **快速失败**: 发现问题立即失败,不继续执行
4. **清晰诊断**: 提供详细的诊断信息
---
### 5.2 测试覆盖率目标
| 层级 | 当前覆盖率 | 目标覆盖率 |
|------|-----------|-----------|
| API层 | 0% | 100% |
| 状态层 | 0% | 100% |
| DOM层 | 50% | 100% |
| 内容层 | 30% | 100% |
| **总体** | **30%** | **100%** |
---
## 6. 风险评估
### 6.1 技术风险
| 风险 | 影响 | 概率 | 缓解措施 |
|------|------|------|----------|
| 测试用例维护成本高 | 中 | 中 | 建立测试工具库,提高可维护性 |
| 测试执行时间长 | 低 | 低 | 使用并行执行,优化测试用例 |
| 误报率高 | 高 | 低 | 使用硬验证,减少假阳性 |
---
### 6.2 业务风险
| 风险 | 影响 | 概率 | 缓解措施 |
|------|------|------|----------|
| 测试不通过影响交付 | 高 | 中 | 优先修复关键问题,建立分级测试 |
| 测试数据管理复杂 | 中 | 中 | 建立测试数据工厂,使用Mock数据 |
---
## 7. 成功标准
### 7.1 短期目标(1周内)
- ✅ 所有测试用例使用硬验证
- ✅ 测试覆盖率提升到 60%
- ✅ 无假阳性问题
- ✅ 发现并修复当前问题
---
### 7.2 中期目标(2-4周)
- ✅ 测试覆盖率提升到 80%
- ✅ 建立完整的测试报告
- ✅ 集成到CI/CD
- ✅ 测试执行时间 < 10分钟
---
### 7.3 长期目标(1-3个月)
- ✅ 测试覆盖率提升到 100%
- ✅ 建立视觉回归测试
- ✅ 建立性能测试
- ✅ 测试执行时间 < 5分钟
---
## 8. 附录
### 8.1 验证测试文件
**文件**: `tests/e2e/specs/validation/test-improvement-validation.spec.ts`
**测试结果**:
- ❌ 旧方式:软验证 - ✅ 通过(假阳性)
- ✅ 新方式:硬验证 - ❌ 失败(正确)
- ✅ 三层验证 - ❌ 失败(正确)
- ✅ 完整用户旅程 - ❌ 失败(正确)
- ✅ 诊断测试 - ✅ 通过
---
### 8.2 参考资料
- [Playwright 官方文档](https://playwright.dev/)
- [Playwright 最佳实践](https://playwright.dev/docs/best-practices)
- [测试驱动开发(TDD](https://en.wikipedia.org/wiki/Test-driven_development)
---
## 9. 总结
### 9.1 核心结论
1.**Playwright 工具本身有效**
2.**问题在于测试方式(软验证 vs 硬验证)**
3.**改进后的测试能真实发现问题**
4.**三层验证策略可行**
---
### 9.2 下一步行动
1. **立即行动**: 修复现有测试用例,使用硬验证
2. **短期计划**: 建立测试工具库,提高可维护性
3. **中期计划**: 集成到CI/CD,建立完整测试体系
4. **长期计划**: 持续优化,建立视觉回归测试
---
**文档状态**: ✅ 已验证
**下一步**: 用户审查书面规格
@@ -0,0 +1,306 @@
# User Journey Tests 设计文档
**日期:** 2026-04-08
**作者:** 张翔
**状态:** 已批准
---
## 1. 概述
### 1.1 背景
novalon-manage-system 项目当前有 11 个功能模块,但仅有 7 个模块(63.6%)被 user journey 测试覆盖。为了提高测试覆盖率和系统质量,需要补充缺失的 4 个模块的端到端测试。
### 1.2 目标
为以下 4 个功能模块补充 user journey 测试:
1. **异常日志** - 查看系统异常记录
2. **系统配置** - 系统参数配置管理
3. **字典管理** - 数据字典管理
4. **通知管理** - 系统通知公告
### 1.3 范围
**包含:**
- 基础覆盖:查看列表、搜索功能、基本操作(新增/编辑/删除)
- 使用时间戳隔离测试数据
- 遵循现有测试风格和模式
**不包含:**
- 边界情况测试
- 错误处理测试
- 权限验证测试
---
## 2. 架构设计
### 2.1 文件结构
```
novalon-manage-web/e2e/journeys/
├── exception-log-workflow.spec.ts # 异常日志测试
├── config-workflow.spec.ts # 系统配置测试
├── dict-workflow.spec.ts # 字典管理测试
└── notice-workflow.spec.ts # 通知管理测试
```
### 2.2 测试模式
- **测试框架:** Playwright
- **组织方式:** 使用 `test.describe` 组织测试套件
- **步骤组织:** 使用 `test.step` 组织测试步骤
- **数据隔离:** 使用时间戳生成唯一测试数据
- **命名规范:** 遵循现有测试的命名规范
### 2.3 测试策略
每个模块包含 3-5 个独立测试:
1. **查看列表** - 验证页面加载和数据展示
2. **搜索功能** - 验证搜索和筛选
3. **新增操作** - 验证创建功能
4. **编辑操作** - 验证更新功能
5. **删除操作** - 验证删除功能
---
## 3. 详细设计
### 3.1 异常日志测试
**文件:** `exception-log-workflow.spec.ts`
**测试场景:**
| 测试名称 | 测试步骤 | 验证点 |
|---------|---------|--------|
| 查看异常日志列表 | 1. 导航到异常日志页面<br>2. 等待数据加载 | 表格组件可见 |
| 搜索异常日志 | 1. 输入搜索关键词<br>2. 点击搜索按钮<br>3. 等待结果 | 搜索结果正确显示 |
| 查看异常日志详情 | 1. 点击查看详情按钮<br>2. 等待对话框打开 | 详情对话框可见 |
**关键选择器:**
- 页面路径:`/exception-log`
- 表格:`.el-table`
- 搜索框:`input[placeholder*="搜索"]`
- 详情按钮:`button:has-text("查看")`
---
### 3.2 系统配置测试
**文件:** `config-workflow.spec.ts`
**测试场景:**
| 测试名称 | 测试步骤 | 验证点 |
|---------|---------|--------|
| 查看系统配置列表 | 1. 导航到系统配置页面<br>2. 等待数据加载 | 表格组件可见 |
| 新增系统配置 | 1. 点击新增配置按钮<br>2. 填写表单<br>3. 提交表单 | 成功消息显示 |
| 搜索系统配置 | 1. 输入搜索关键词<br>2. 点击搜索按钮 | 搜索结果正确显示 |
| 编辑系统配置 | 1. 点击编辑按钮<br>2. 修改配置值<br>3. 提交表单 | 成功消息显示 |
| 删除系统配置 | 1. 点击删除按钮<br>2. 确认删除 | 成功消息显示 |
**测试数据:**
```typescript
const timestamp = Date.now();
const configKey = `test_config_${timestamp}`;
const configName = `测试配置_${timestamp}`;
const configValue = `测试值_${timestamp}`;
```
**关键选择器:**
- 页面路径:`/config`
- 新增按钮:`button:has-text("新增配置")`
- 表单输入:`.el-dialog input`
- 提交按钮:`.el-dialog button:has-text("确定")`
---
### 3.3 字典管理测试
**文件:** `dict-workflow.spec.ts`
**测试场景:**
| 测试名称 | 测试步骤 | 验证点 |
|---------|---------|--------|
| 查看字典列表 | 1. 导航到字典管理页面<br>2. 等待数据加载 | 表格组件可见 |
| 新增字典 | 1. 点击新增字典按钮<br>2. 填写表单<br>3. 提交表单 | 成功消息显示 |
| 搜索字典 | 1. 输入搜索关键词<br>2. 点击搜索按钮 | 搜索结果正确显示 |
| 编辑字典 | 1. 点击编辑按钮<br>2. 修改字典信息<br>3. 提交表单 | 成功消息显示 |
| 删除字典 | 1. 点击删除按钮<br>2. 确认删除 | 成功消息显示 |
**测试数据:**
```typescript
const timestamp = Date.now();
const dictType = `test_dict_${timestamp}`;
const dictName = `测试字典_${timestamp}`;
```
**关键选择器:**
- 页面路径:`/dict`
- 新增按钮:`button:has-text("新增字典")`
- 表单输入:`.el-dialog input`
- 提交按钮:`.el-dialog button:has-text("确定")`
---
### 3.4 通知管理测试
**文件:** `notice-workflow.spec.ts`
**测试场景:**
| 测试名称 | 测试步骤 | 验证点 |
|---------|---------|--------|
| 查看通知列表 | 1. 导航到通知管理页面<br>2. 等待数据加载 | 表格组件可见 |
| 新增通知 | 1. 点击新增通知按钮<br>2. 填写表单<br>3. 提交表单 | 成功消息显示 |
| 搜索通知 | 1. 输入搜索关键词<br>2. 点击搜索按钮 | 搜索结果正确显示 |
| 编辑通知 | 1. 点击编辑按钮<br>2. 修改通知内容<br>3. 提交表单 | 成功消息显示 |
| 删除通知 | 1. 点击删除按钮<br>2. 确认删除 | 成功消息显示 |
**测试数据:**
```typescript
const timestamp = Date.now();
const noticeTitle = `测试通知_${timestamp}`;
const noticeContent = `这是测试通知内容_${timestamp}`;
```
**关键选择器:**
- 页面路径:`/notice`
- 新增按钮:`button:has-text("新增通知")`
- 表单输入:`.el-dialog input`
- 提交按钮:`.el-dialog button:has-text("确定")`
---
## 4. 测试数据管理
### 4.1 数据隔离策略
使用时间戳生成唯一测试数据:
```typescript
const timestamp = Date.now();
const uniqueName = `测试数据_${timestamp}`;
```
**优势:**
- 无需清理测试数据
- 避免测试数据冲突
- 与现有测试风格一致
### 4.2 测试数据示例
| 模块 | 数据字段 | 生成规则 |
|------|---------|---------|
| 系统配置 | configKey, configName | `test_config_${timestamp}` |
| 字典管理 | dictType, dictName | `test_dict_${timestamp}` |
| 通知管理 | noticeTitle, noticeContent | `测试通知_${timestamp}` |
---
## 5. 测试执行
### 5.1 运行命令
```bash
# 运行所有 journey 测试
npm run test:e2e:journeys
# 运行特定测试文件
npx playwright test e2e/journeys/exception-log-workflow.spec.ts
# 运行所有新增测试
npx playwright test e2e/journeys/exception-log-workflow.spec.ts \
e2e/journeys/config-workflow.spec.ts \
e2e/journeys/dict-workflow.spec.ts \
e2e/journeys/notice-workflow.spec.ts
```
### 5.2 测试配置
测试将使用现有的 Playwright 配置:
- **项目:** `journeys`
- **依赖:** `setup` 项目(认证)
- **存储状态:** `playwright/.auth/user.json`
- **浏览器:** Desktop Chrome
- **超时:** 120000ms
---
## 6. 验收标准
### 6.1 功能验收
- [ ] 所有测试文件创建成功
- [ ] 所有测试通过
- [ ] 测试覆盖率提升至 100%(11/11 模块)
### 6.2 质量验收
- [ ] 测试代码遵循现有风格
- [ ] 测试步骤清晰可读
- [ ] 测试数据隔离有效
- [ ] 无测试数据冲突
### 6.3 文档验收
- [ ] 测试文件包含清晰的注释
- [ ] 测试描述准确反映测试内容
- [ ] 测试步骤命名规范
---
## 7. 风险与缓解
### 7.1 风险识别
| 风险 | 影响 | 概率 | 缓解措施 |
|------|------|------|---------|
| 测试数据污染 | 中 | 低 | 使用时间戳隔离 |
| 测试依赖环境 | 高 | 中 | 使用独立的测试环境 |
| 页面元素变化 | 中 | 低 | 使用稳定的选择器 |
| 测试超时 | 低 | 中 | 增加适当的等待时间 |
### 7.2 回滚计划
如果测试失败或影响现有测试,可以:
1. 删除新增的测试文件
2. 恢复到之前的测试状态
3. 分析失败原因后重新实施
---
## 8. 后续改进
### 8.1 短期改进
1. 修复 `admin-complete-workflow.spec.ts` 中被跳过的清理测试
2. 增强菜单管理的测试覆盖
3. 增强登录日志的测试覆盖
### 8.2 长期改进
1. 引入边界情况测试
2. 引入错误处理测试
3. 引入权限验证测试
4. 实现测试数据自动清理
---
## 9. 参考资料
- [Playwright 官方文档](https://playwright.dev/)
- [项目 E2E 测试 README](../../novalon-manage-web/e2e/README.md)
- [现有测试示例](../../novalon-manage-web/e2e/journeys/)
---
**批准人:** 用户
**批准日期:** 2026-04-08
@@ -0,0 +1,260 @@
# 本地开发环境集成测试方案设计
**日期**: 2026-04-15
**作者**: 张翔 (全栈质量保障与效能工程师)
**版本**: 1.0
## 1. 任务概述
### 1.1 目标
启动前后端系统(包含网关服务),确保前后端联通,在开发环境中使用已有的测试框架进行用户旅程测试。数据库部署在Docker中,应用直接在开发环境中运行。
### 1.2 成功标准
1. ✅ 数据库在Docker中成功启动并初始化
2. ✅ 后端网关和应用服务在本地成功启动
3. ✅ 前端应用在本地成功启动并连接到后端
4. ✅ 用户旅程测试(E2E测试)成功执行
5. ✅ 所有服务健康状态正常
## 2. 技术架构
### 2.1 系统架构
```
┌─────────────────────────────────────────────────────────────┐
│ 本地开发环境 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Vue 3 │ │ Spring Cloud│ │ Spring Boot │ │
│ │ 前端应用 │◄──►│ Gateway │◄──►│ 应用服务 │ │
│ │ (端口:3000)│ │ (端口:8080)│ │ (端口:8084) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ ▲ ▲ ▲ │
│ │ │ │ │
│ └─────────────────────────┴───────────────┘ │
│ HTTP/REST API 通信 │
├─────────────────────────────────────────────────────────────┤
│ Docker容器环境 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────┐ │
│ │ PostgreSQL 15数据库 │ │
│ │ (端口:55432) │ │
│ │ Flyway自动迁移 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
### 2.2 技术栈
- **后端**: Java 21 + Spring Boot 3.5.13 + Spring Cloud Gateway
- **前端**: Vue 3 + TypeScript + Vite + Element Plus
- **数据库**: PostgreSQL 15 (Docker容器)
- **测试框架**: Playwright (E2E测试)
- **构建工具**: Maven (后端) + pnpm/npm (前端)
## 3. 配置方案
### 3.1 数据库配置
- **容器服务**: PostgreSQL 15 (postgres:15-alpine)
- **端口映射**: 55432:5432 (避免与本地PostgreSQL冲突)
- **数据库名称**: manage_system
- **认证信息**: novalon/novalon123
- **数据卷**: postgres_data (持久化存储)
- **健康检查**: pg_isready命令验证
### 3.2 后端服务配置
- **网关服务**:
- 端口: 8080
- 路由配置: /api/** → localhost:8084
- 过滤器: JWT认证、RBAC授权、重试机制
- **应用服务**:
- 端口: 8084
- 数据库连接: r2dbc:postgresql://localhost:55432/manage_system
- 健康检查: /actuator/health端点
- **启动方式**: Maven多模块同时启动
### 3.3 前端服务配置
- **开发服务器**: Vite (端口:3000)
- **API代理**: 配置代理到网关服务 (localhost:8080)
- **环境变量**: 使用开发环境配置
- **构建工具**: pnpm (推荐) 或 npm
### 3.4 测试配置
- **测试框架**: Playwright
- **测试范围**: 冒烟测试 (login-logout.spec.ts)
- **测试数据**:
- 管理员账号: admin/Test@123
- 普通用户账号: user/Test@123
- **测试环境**: 连接到本地运行的服务
## 4. 实施步骤
### 4.1 阶段一:数据库容器启动
```bash
# 1. 启动PostgreSQL容器
docker-compose up -d postgres
# 2. 等待数据库就绪 (10秒)
sleep 10
# 3. 验证数据库连接
docker-compose exec postgres pg_isready -U novalon -d manage_system
```
### 4.2 阶段二:后端服务启动
```bash
# 1. 进入后端项目目录
cd /Users/zhangxiang/Codes/Novalon/novalon-manage-system/novalon-manage-api
# 2. 使用Maven同时启动网关和应用
mvn spring-boot:run -pl manage-gateway,manage-app -am
```
### 4.3 阶段三:前端服务启动
```bash
# 1. 进入前端项目目录
cd /Users/zhangxiang/Codes/Novalon/novalon-manage-system/novalon-manage-web
# 2. 安装依赖 (如果未安装)
pnpm install # 或 npm install
# 3. 启动开发服务器
pnpm run dev # 或 npm run dev
```
### 4.4 阶段四:执行E2E测试
```bash
# 1. 在另一个终端执行冒烟测试
cd /Users/zhangxiang/Codes/Novalon/novalon-manage-system/novalon-manage-web
pnpm run test:e2e:smoke # 或 npm run test:e2e:smoke
```
## 5. 验证检查点
### 5.1 数据库验证
- [ ] PostgreSQL容器运行状态正常 (`docker-compose ps`)
- [ ] 数据库端口55432可访问 (`telnet localhost 55432`)
- [ ] Flyway迁移脚本执行成功 (查看应用日志)
### 5.2 后端验证
- [ ] 网关服务在8080端口响应 (`curl http://localhost:8080/actuator/health`)
- [ ] 应用服务在8084端口响应 (`curl http://localhost:8084/actuator/health`)
- [ ] 健康检查端点返回UP状态
- [ ] 网关能正确路由到应用服务
### 5.3 前端验证
- [ ] 开发服务器在3000端口运行 (`curl http://localhost:3000`)
- [ ] 页面能正常加载 (浏览器访问 http://localhost:3000)
- [ ] API请求能正确代理到后端
### 5.4 测试验证
- [ ] 冒烟测试执行通过
- [ ] 登录登出流程正常
- [ ] 测试报告生成成功
## 6. 故障排除预案
### 6.1 常见问题及解决方案
#### 问题1:端口冲突
- **症状**: 服务启动失败,提示端口被占用
- **解决方案**:
1. 检查8080、8084、55432端口是否被占用: `lsof -i :8080`
2. 停止占用端口的进程或修改配置使用其他端口
3. 修改application.yml中的端口配置
#### 问题2:数据库连接失败
- **症状**: 应用启动时报数据库连接错误
- **解决方案**:
1. 验证Docker容器状态: `docker-compose ps`
2. 检查数据库日志: `docker-compose logs postgres`
3. 验证网络连接: `telnet localhost 55432`
4. 检查数据库认证信息配置
#### 问题3:服务启动失败
- **症状**: Maven启动时报依赖或配置错误
- **解决方案**:
1. 清理Maven缓存: `mvn clean`
2. 重新下载依赖: `mvn dependency:resolve`
3. 检查Spring配置文件和环境变量
4. 查看详细错误日志
#### 问题4:测试失败
- **症状**: Playwright测试执行失败
- **解决方案**:
1. 验证测试环境服务是否正常运行
2. 检查测试数据是否正确
3. 查看测试失败截图和日志
4. 运行调试模式: `pnpm run test:e2e:debug`
### 6.2 回滚方案
1. **停止所有服务**:
```bash
# 停止Docker容器
docker-compose down
# 停止Maven进程 (Ctrl+C)
# 停止npm进程 (Ctrl+C)
```
2. **清理临时文件**:
```bash
# 清理Maven构建目录
cd /Users/zhangxiang/Codes/Novalon/novalon-manage-system/novalon-manage-api
mvn clean
# 清理前端缓存
cd /Users/zhangxiang/Codes/Novalon/novalon-manage-system/novalon-manage-web
rm -rf node_modules/.vite
```
3. **重新执行**:
按照4.1-4.4步骤重新执行
## 7. 监控与日志
### 7.1 服务监控
- **数据库**: `docker-compose logs -f postgres`
- **后端应用**: Maven控制台输出 + 应用日志
- **前端**: Vite开发服务器控制台输出
- **测试**: Playwright测试报告和控制台输出
### 7.2 关键指标
- 服务启动时间
- API响应时间
- 数据库连接状态
- 测试执行成功率
- 资源使用情况 (CPU/内存)
## 8. 后续优化建议
### 8.1 短期优化
1. **自动化脚本**: 创建一键启动脚本,简化操作流程
2. **环境配置**: 完善本地开发环境配置文件
3. **测试数据**: 优化测试数据管理,支持数据重置
### 8.2 中期优化
1. **容器化开发环境**: 考虑使用DevContainer统一开发环境
2. **测试覆盖率**: 增加更多E2E测试场景
3. **性能监控**: 集成APM工具监控应用性能
### 8.3 长期优化
1. **CI/CD集成**: 将本地测试流程集成到CI/CD流水线
2. **多环境支持**: 支持开发、测试、预发、生产多环境
3. **安全加固**: 加强安全测试和漏洞扫描
## 9. 附录
### 9.1 配置文件位置
- 数据库配置: `docker-compose.yml`
- 后端配置: `novalon-manage-api/manage-*/src/main/resources/application*.yml`
- 前端配置: `novalon-manage-web/.env*`, `vite.config.ts`
- 测试配置: `novalon-manage-web/playwright.config.ts`
### 9.2 相关文档
- 项目README: `/Users/zhangxiang/Codes/Novalon/novalon-manage-system/README.md`
- E2E测试说明: `novalon-manage-web/e2e/README.md`
- API文档: `http://localhost:8084/swagger-ui.html` (启动后访问)
### 9.3 联系方式
- **负责人**: 张翔
- **角色**: 全栈质量保障与效能工程师
- **原则**: 质量是设计出来的,并通过自动化流水线保障
@@ -0,0 +1,376 @@
# 菜单数据修复与登出功能优化设计文档
**日期**: 2026-04-15
**作者**: 张翔
**版本**: 1.0
## 1. 背景与问题
### 1.1 问题背景
在User Journey测试过程中,发现了以下两个主要问题:
1. **系统配置菜单缺失**: 测试脚本无法找到"系统配置"菜单,导致测试失败
2. **登出功能测试失败**: 测试脚本报告"登出功能缺失",但实际上登出功能已经在前端实现
### 1.2 问题根因分析
#### 1.2.1 系统配置菜单缺失
**根本原因**: 数据库中的菜单数据都是测试数据,没有实际的业务菜单
**数据库现状**:
```sql
SELECT id, menu_name, parent_id, order_num, menu_type, component FROM sys_menu;
```
结果:
```
id | menu_name | parent_id | order_num | menu_type | component
----+-------------------------+-----------+-----------+-----------+-----------
1 | 测试菜单_1774884610 | 0 | 1 | M |
2 | 测试菜单_1774885290 | 0 | 1 | M |
3 | 回归测试菜单_1774885909 | 0 | 1 | M |
4 | 回归测试菜单_1774885952 | 0 | 1 | M |
5 | 回归测试菜单_1774885984 | 0 | 1 | M |
6 | 回归测试菜单_1774886603 | 0 | 1 | M |
7 | 回归测试菜单_1774886605 | 0 | 1 | M |
```
**影响**:
- 前端无法显示正确的业务菜单
- 测试脚本无法找到"系统配置"等业务菜单
- 用户体验极差,无法使用系统功能
#### 1.2.2 登出功能测试失败
**根本原因**: 测试脚本的选择器没有正确匹配到下拉菜单中的"退出登录"按钮
**前端实现现状**:
```vue
<el-dropdown @command="handleCommand">
<el-avatar :size="32">
{{ username }}
</el-avatar>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="profile">
个人中心
</el-dropdown-item>
<el-dropdown-item
command="logout"
divided
>
退出登录
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
```
**测试脚本选择器**:
```javascript
const logoutSelectors = [
'button:has-text("退出")',
'button:has-text("登出")',
'a:has-text("退出")',
'a:has-text("登出")',
'[data-action="logout"]',
'.logout-button'
];
```
**问题**: 选择器没有匹配到`el-dropdown-item`元素
**影响**:
- 测试报告显示"登出功能缺失"
- 实际上登出功能已经实现,只是测试脚本不准确
## 2. 解决方案设计
### 2.1 方案概述
采用**数据库菜单数据修复 + 测试脚本优化**的方案,解决根本问题并提高测试准确性。
### 2.2 数据库菜单数据修复
#### 2.2.1 菜单数据结构设计
基于前端路由配置,设计以下菜单结构:
**一级菜单**:
1. 系统管理 (System Management)
2. 系统监控 (System Monitor)
3. 审计日志 (Audit Log)
**二级菜单**:
- 系统管理下:
- 用户管理
- 角色管理
- 菜单管理
- 参数配置
- 字典管理
- 系统监控下:
- 文件管理
- 通知公告
- 审计日志下:
- 登录日志
- 操作日志
- 异常日志
#### 2.2.2 数据库表结构
```sql
CREATE TABLE IF NOT EXISTS sys_menu (
id BIGSERIAL PRIMARY KEY,
menu_name VARCHAR(50) NOT NULL,
parent_id BIGINT DEFAULT 0,
order_num INT DEFAULT 0,
menu_type CHAR(1) DEFAULT 'M', -- M: 目录, C: 菜单, F: 按钮
component VARCHAR(200),
perms VARCHAR(100),
icon VARCHAR(100),
status INT DEFAULT 1,
visible INT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
);
```
#### 2.2.3 菜单数据插入
```sql
-- 清理测试数据
DELETE FROM sys_menu WHERE menu_name LIKE '%测试%' OR menu_name LIKE '%回归%';
-- 插入一级菜单
INSERT INTO sys_menu (menu_name, parent_id, order_num, menu_type, icon, status) VALUES
('系统管理', 0, 1, 'M', 'Setting', 1),
('系统监控', 0, 2, 'M', 'Monitor', 1),
('审计日志', 0, 3, 'M', 'Document', 1);
-- 插入二级菜单
INSERT INTO sys_menu (menu_name, parent_id, order_num, menu_type, component, perms, icon, status) VALUES
-- 系统管理下的菜单
('用户管理', (SELECT id FROM sys_menu WHERE menu_name = '系统管理'), 1, 'C', 'system/user/index', 'system:user:list', 'User', 1),
('角色管理', (SELECT id FROM sys_menu WHERE menu_name = '系统管理'), 2, 'C', 'system/role/index', 'system:role:list', 'UserFilled', 1),
('菜单管理', (SELECT id FROM sys_menu WHERE menu_name = '系统管理'), 3, 'C', 'system/menu/index', 'system:menu:list', 'Menu', 1),
('参数配置', (SELECT id FROM sys_menu WHERE menu_name = '系统管理'), 4, 'C', 'system/config/index', 'system:config:list', 'Tools', 1),
('字典管理', (SELECT id FROM sys_menu WHERE menu_name = '系统管理'), 5, 'C', 'system/dict/index', 'system:dict:list', 'Collection', 1),
-- 系统监控下的菜单
('文件管理', (SELECT id FROM sys_menu WHERE menu_name = '系统监控'), 1, 'C', 'system/file/index', 'system:file:list', 'Folder', 1),
('通知公告', (SELECT id FROM sys_menu WHERE menu_name = '系统监控'), 2, 'C', 'system/notice/index', 'system:notice:list', 'Bell', 1),
-- 审计日志下的菜单
('登录日志', (SELECT id FROM sys_menu WHERE menu_name = '审计日志'), 1, 'C', 'audit/login/index', 'audit:login:list', 'Document', 1),
('操作日志', (SELECT id FROM sys_menu WHERE menu_name = '审计日志'), 2, 'C', 'audit/operation/index', 'audit:operation:list', 'Document', 1),
('异常日志', (SELECT id FROM sys_menu WHERE menu_name = '审计日志'), 3, 'C', 'audit/exception/index', 'audit:exception:list', 'Warning', 1);
```
### 2.3 测试脚本优化
#### 2.3.1 登出功能测试优化
**问题**: 当前选择器无法匹配Element Plus的下拉菜单项
**解决方案**: 更新选择器以匹配Element Plus的下拉菜单结构
```javascript
const logoutSelectors = [
// Element Plus下拉菜单项
'.el-dropdown-menu__item:has-text("退出登录")',
'.el-dropdown-menu__item:has-text("退出")',
'.el-dropdown-menu__item:has-text("登出")',
// 通用选择器
'button:has-text("退出")',
'button:has-text("登出")',
'a:has-text("退出")',
'a:has-text("登出")',
'[data-action="logout"]',
'.logout-button'
];
```
#### 2.3.2 系统配置菜单测试优化
**问题**: 当前选择器无法匹配实际的菜单文本
**解决方案**: 更新选择器以匹配实际的菜单结构
```javascript
const configMenuSelectors = [
// Element Plus菜单项
'.el-menu-item:has-text("参数配置")',
'.el-menu-item:has-text("系统配置")',
'.el-menu-item:has-text("配置管理")',
// 通用选择器
'text=参数配置',
'text=系统配置',
'text=配置管理',
'[data-menu="config"]',
'a[href*="config"]'
];
```
### 2.4 扩展测试覆盖
#### 2.4.1 新增测试用例
1. **菜单管理功能测试**
- 测试菜单的增删改查
- 测试菜单树的显示
- 测试菜单权限控制
2. **参数配置功能测试**
- 测试参数的增删改查
- 测试参数的缓存机制
- 测试参数的导入导出
3. **字典管理功能测试**
- 测试字典的增删改查
- 测试字典项的管理
- 测试字典的缓存机制
#### 2.4.2 测试数据管理
建立测试数据管理机制,确保测试数据的独立性和可重复性:
```javascript
class TestDataManager {
async setupTestData() {
// 创建测试用户
// 创建测试角色
// 创建测试菜单
}
async cleanupTestData() {
// 清理测试用户
// 清理测试角色
// 清理测试菜单
}
}
```
## 3. 实施步骤
### 3.1 数据库菜单数据修复
1. **清理测试数据**
- 删除所有测试菜单数据
- 确保数据库干净
2. **插入业务菜单数据**
- 按照设计的菜单结构插入数据
- 确保菜单层级关系正确
3. **验证菜单数据**
- 查询菜单数据确认正确性
- 测试前端菜单显示
### 3.2 测试脚本优化
1. **更新登出功能测试**
- 修改选择器以匹配Element Plus下拉菜单
- 增加等待时间确保下拉菜单展开
2. **更新系统配置菜单测试**
- 修改选择器以匹配实际菜单文本
- 增加菜单导航的容错处理
3. **扩展测试覆盖**
- 编写菜单管理测试用例
- 编写参数配置测试用例
- 编写字典管理测试用例
### 3.3 验证与测试
1. **单元测试**
- 测试菜单数据的正确性
- 测试前端菜单组件
2. **集成测试**
- 测试前后端菜单数据交互
- 测试菜单权限控制
3. **端到端测试**
- 运行完整的User Journey测试
- 验证所有测试用例通过
## 4. 测试策略
### 4.1 测试层次
1. **单元测试**: 测试菜单组件和数据转换逻辑
2. **集成测试**: 测试前后端菜单数据交互
3. **端到端测试**: 测试完整的用户操作流程
### 4.2 测试数据
1. **测试用户**: 使用admin/admin123进行测试
2. **测试菜单**: 使用实际业务菜单数据
3. **测试环境**: 使用开发环境数据库
### 4.3 测试工具
1. **Playwright**: 用于端到端测试
2. **Vitest**: 用于单元测试
3. **PostgreSQL**: 用于数据库验证
## 5. 风险评估
### 5.1 技术风险
| 风险项 | 影响程度 | 发生概率 | 缓解措施 |
|--------|----------|----------|----------|
| 菜单数据插入失败 | 高 | 低 | 使用事务确保数据一致性 |
| 前端菜单显示异常 | 中 | 中 | 充分测试菜单组件 |
| 测试脚本不稳定 | 中 | 中 | 增加重试机制和等待时间 |
### 5.2 业务风险
| 风险项 | 影响程度 | 发生概率 | 缓解措施 |
|--------|----------|----------|----------|
| 菜单权限配置错误 | 高 | 低 | 严格按照权限设计配置 |
| 用户体验不佳 | 中 | 低 | 进行用户验收测试 |
## 6. 验收标准
### 6.1 功能验收
- [ ] 数据库菜单数据正确插入
- [ ] 前端菜单正确显示
- [ ] 登出功能测试通过
- [ ] 系统配置菜单测试通过
- [ ] 所有User Journey测试通过率≥90%
### 6.2 质量验收
- [ ] 代码通过ESLint检查
- [ ] 单元测试覆盖率≥80%
- [ ] 无严重Bug
- [ ] 性能指标达标
## 7. 后续优化
### 7.1 短期优化
1. 完善菜单权限控制
2. 优化菜单加载性能
3. 增加菜单缓存机制
### 7.2 长期优化
1. 实现菜单的动态配置
2. 支持菜单的导入导出
3. 建立菜单变更审计日志
## 8. 参考资料
- [Element Plus Menu组件文档](https://element-plus.org/zh-CN/component/menu.html)
- [Element Plus Dropdown组件文档](https://element-plus.org/zh-CN/component/dropdown.html)
- [Vue Router官方文档](https://router.vuejs.org/zh/)
- [Playwright最佳实践](https://playwright.dev/docs/best-practices)
@@ -0,0 +1,218 @@
# 用户管理和角色管理测试修复设计文档
**日期**: 2026-04-15
**作者**: 张翔
**版本**: 1.0
## 1. 背景与问题
### 1.1 问题背景
在User Journey测试过程中,发现以下两个测试失败:
1. **导航到用户管理页面**: 测试超时失败
2. **导航到角色管理页面**: 测试超时失败
### 1.2 问题根因分析
**根本原因**: 用户管理和角色管理是"系统管理"菜单下的二级菜单项。在Element Plus的菜单组件中,当父菜单处于折叠状态时,子菜单项是不可见的。测试脚本直接尝试点击这些不可见的菜单项,导致超时失败。
**错误信息**:
```
locator.click: Timeout 30000ms exceeded.
Call log:
- waiting for locator('text=用户管理').first()
- locator resolved to <span>用户管理</span>
- attempting click action
- waiting for element to be visible, enabled and stable
- element is not visible
```
**对比分析**:
- ✅ 系统配置测试:正确地先展开了系统管理菜单,测试通过
- ❌ 用户管理测试:直接尝试点击菜单项,测试失败
- ❌ 角色管理测试:直接尝试点击菜单项,测试失败
## 2. 解决方案设计
### 2.1 设计目标
修复用户管理和角色管理测试,使其能够正确展开系统管理菜单后再点击子菜单项,提高测试通过率。
### 2.2 技术方案
采用与系统配置测试相同的策略:先展开父菜单,再点击子菜单项。
### 2.3 实现细节
#### 2.3.1 修改用户管理测试
**文件**: `novalon-manage-web/user-journey-test.js`
**修改位置**: 第140-180行
**修改内容**:
```javascript
// 阶段2: 用户管理测试
console.log('\n📋 阶段2: 用户管理测试');
console.log('=====================================');
try {
// 首先展开系统管理菜单(如果是折叠状态)
const systemMenuSelector = '.el-sub-menu:has-text("系统管理")';
const systemMenuElement = page.locator(systemMenuSelector).first();
if (await systemMenuElement.count() > 0) {
// 点击展开系统管理菜单
await systemMenuElement.click();
await page.waitForTimeout(500);
// 然后点击用户管理菜单项
const userMenuSelectors = [
'.el-menu-item:has-text("用户管理")',
'text=用户管理',
'text=用户',
'[data-menu="user"]',
'a[href*="user"]'
];
let navigated = false;
for (const selector of userMenuSelectors) {
const element = page.locator(selector).first();
if (await element.count() > 0) {
await element.click();
navigated = true;
break;
}
}
if (navigated) {
await page.waitForTimeout(1000);
await captureStep(page, '04-user-management');
logTest('导航到用户管理页面', true);
} else {
throw new Error('未找到用户管理菜单');
}
} else {
throw new Error('未找到系统管理菜单');
}
} catch (error) {
logTest('导航到用户管理页面', false, error.message);
}
```
#### 2.3.2 修改角色管理测试
**文件**: `novalon-manage-web/user-journey-test.js`
**修改位置**: 第210-240行
**修改内容**:
```javascript
// ==================== 阶段3: 角色管理 ====================
console.log('\n📋 阶段3: 角色管理测试');
console.log('=====================================');
try {
// 首先展开系统管理菜单(如果是折叠状态)
const systemMenuSelector = '.el-sub-menu:has-text("系统管理")';
const systemMenuElement = page.locator(systemMenuSelector).first();
if (await systemMenuElement.count() > 0) {
// 点击展开系统管理菜单
await systemMenuElement.click();
await page.waitForTimeout(500);
// 然后点击角色管理菜单项
const roleMenuSelectors = [
'.el-menu-item:has-text("角色管理")',
'text=角色管理',
'text=角色',
'[data-menu="role"]',
'a[href*="role"]'
];
let navigated = false;
for (const selector of roleMenuSelectors) {
const element = page.locator(selector).first();
if (await element.count() > 0) {
await element.click();
navigated = true;
break;
}
}
if (navigated) {
await page.waitForTimeout(1000);
await captureStep(page, '05-role-management');
logTest('导航到角色管理页面', true);
} else {
throw new Error('未找到角色管理菜单');
}
} else {
throw new Error('未找到系统管理菜单');
}
} catch (error) {
logTest('导航到角色管理页面', false, error.message);
}
```
## 3. 验收标准
### 3.1 功能验收
- ✅ 用户管理测试能够成功导航到用户管理页面
- ✅ 角色管理测试能够成功导航到角色管理页面
- ✅ 测试通过率从80%提升到100%
### 3.2 质量验收
- ✅ 测试代码与系统配置测试保持一致的风格
- ✅ 测试代码包含清晰的注释
- ✅ 测试代码包含错误处理
### 3.3 测试验收
- ✅ User Journey测试全部通过(10/10
- ✅ 新增Playwright测试全部通过(3/3
- ✅ 测试报告生成成功
## 4. 影响范围
### 4.1 受影响的文件
- `novalon-manage-web/user-journey-test.js`: 修改用户管理和角色管理测试代码
### 4.2 不受影响的部分
- 前端代码:不修改
- 后端代码:不修改
- 数据库:不修改
- 其他测试:不修改
## 5. 风险评估
### 5.1 技术风险
- **风险等级**: 低
- **风险描述**: 修改仅涉及测试代码,不影响生产代码
- **缓解措施**: 修改后立即运行测试验证
### 5.2 业务风险
- **风险等级**: 无
- **风险描述**: 不涉及业务逻辑修改
## 6. 后续优化建议
1. **统一测试策略**: 将"先展开父菜单,再点击子菜单项"的策略应用到所有二级菜单测试中
2. **封装公共方法**: 将展开菜单的逻辑封装为公共方法,减少代码重复
3. **增加等待策略**: 使用更智能的等待策略(如等待元素可见)替代固定的timeout
## 7. 实施计划
1. 修改用户管理测试代码
2. 修改角色管理测试代码
3. 运行User Journey测试验证
4. 提交代码
**预计工作量**: 30分钟