refactor(backend): 重命名后端项目为 gym-manage-api,修改包名为 cn.novalon.gym.manage
This commit is contained in:
@@ -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搜索所有引用
|
||||
- 运行类型检查确保无遗漏
|
||||
|
||||
### 风险2:Playwright配置冲突
|
||||
**描述**: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 Store(1-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分钟
|
||||
Reference in New Issue
Block a user