docs: 添加设计文档和实现计划

- 添加菜单数据修复设计文档
- 添加用户管理和角色管理测试修复设计文档
- 添加本地开发测试设计文档
- 添加相关实现计划
This commit is contained in:
张翔
2026-04-15 23:36:27 +08:00
parent 5c402e49da
commit 38dc055a27
9 changed files with 6959 additions and 0 deletions
@@ -0,0 +1,119 @@
# E2E 测试选择器指南
## 概述
本文档记录了 NovaVis 睿视项目中实际使用的选择器,用于 E2E 测试。
## 核心原则
1. **优先使用文本选择器** - 更稳定,不易受 UI 库变更影响
2. **避免依赖 data-testid** - 除非确实存在
3. **使用页面快照验证** - 确保选择器正确
## 页面选择器映射
### 案件管理页面
| 元素 | 选择器 | 说明 |
|------|--------|------|
| 案件列表项 | `button:has-text("进入")` | 使用"进入"按钮定位案件 |
| 第一个案件 | `button:has-text("进入")` | 选择第一个案件 |
| 案件卡片 | `generic[cursor=pointer]` | 卡片容器 |
### 导航菜单
| 元素 | 选择器 | 说明 |
|------|--------|------|
| 案件管理 | `menuitem:has-text("案件管理")` | 主菜单项 |
| 概览 | `menuitem:has-text("概览")` | 子菜单项 |
| 数据管理 | `menuitem:has-text("数据管理")` | 主菜单项 |
| 关系分析 | `menuitem:has-text("关系分析")` | 主菜单项 |
| 资金流向 | `menuitem:has-text("资金流向")` | 主菜单项 |
| AI 分析 | `menuitem:has-text("AI 分析")` | 主菜单项 |
| 报告中心 | `menuitem:has-text("报告中心")` | 主菜单项 |
| 标注管理 | `menuitem:has-text("标注管理")` | 主菜单项 |
| 系统设置 | `menuitem:has-text("系统设置")` | 主菜单项 |
### 数据导入页面
| 元素 | 选择器 | 说明 |
|------|--------|------|
| 页面标题 | `h3:has-text("数据导入")` | 页面标题 |
| 导入步骤 | `[data-testid="import-steps"]` | 步骤指示器 |
| 文件输入 | `input[type="file"]` | 文件上传输入框 |
### 网络图谱页面
| 元素 | 选择器 | 说明 |
|------|--------|------|
| 页面标题 | `h2:has-text("资金流向与关系网络分析")` | 页面标题 |
| 数据源选择器 | `[data-testid="network-graph-datasource-selector"]` | 数据源下拉框 |
| 布局选择器 | `[data-testid="network-graph-layout-selector"]` | 布局下拉框 |
| 节点搜索 | `input[placeholder*="搜索"]` | 搜索输入框 |
### 报告生成页面
| 元素 | 选择器 | 说明 |
|------|--------|------|
| 页面标题 | `h2:has-text("报告生成")` | 页面标题 |
| 模板选择器 | `.ant-select` | 模板下拉框 |
| 生成按钮 | `button:has-text("生成报告")` | 生成按钮 |
## 交互处理
### 按钮被遮挡
**问题**:侧边栏遮挡了按钮,导致点击失败
**解决方案**
```typescript
const button = page.locator('button:has-text("进入")').first()
await button.scrollIntoViewIfNeeded()
await button.click({ force: true })
```
### 页面默认状态
**问题**:页面默认显示案件管理页面,不需要导航
**解决方案**
```typescript
// ❌ 错误:尝试导航到案件管理
await page.click('[data-testid="nav-cases"]')
// ✅ 正确:页面默认显示案件管理,直接操作
const enterButton = page.locator('button:has-text("进入")').first()
await enterButton.click({ force: true })
```
## 最佳实践
1. **使用页面快照验证选择器**
```typescript
const snapshot = await page.locator('body').innerHTML()
console.log(snapshot)
```
2. **优先使用文本选择器**
```typescript
// ✅ 推荐
page.locator('button:has-text("进入")')
// ❌ 不推荐(除非确实存在)
page.locator('[data-testid="enter-button"]')
```
3. **处理异步加载**
```typescript
await page.waitForLoadState('networkidle')
await page.waitForTimeout(500)
```
4. **错误处理**
```typescript
try {
await element.click({ timeout: 5000 })
} catch (error) {
console.log('Element not found or not clickable')
}
```
@@ -0,0 +1,385 @@
# E2E 测试最佳实践
## 概述
本文档记录了 NovaVis 睿视项目的 E2E 测试最佳实践。
## 核心原则
### 1. 测试应该验证真实用户行为
**问题**:测试只验证元素存在,不验证实际功能
**解决方案**
```typescript
// ❌ 错误:只验证元素可见
const button = page.locator('button')
await expect(button).toBeVisible()
// ✅ 正确:验证实际功能
const button = page.locator('button:has-text("进入")').first()
await button.scrollIntoViewIfNeeded()
await button.click({ force: true })
// 验证页面跳转
await page.waitForURL('**/overview')
const title = await page.locator('h1').textContent()
expect(title).toContain('概览')
```
### 2. 使用硬验证而非软验证
**问题**:测试使用软验证,即使页面没有内容也能通过
**解决方案**
```typescript
// ❌ 错误:软验证
const dataStats = page.locator('[data-testid="data-stats"]')
if (await dataStats.isVisible()) {
const statsText = await dataStats.textContent()
expect(statsText).toBeTruthy()
}
// ✅ 正确:硬验证
const dataStats = page.locator('[data-testid="data-stats"]')
await dataStats.waitFor({ state: 'visible', timeout: 5000 })
const statsText = await dataStats.textContent()
expect(statsText).toBeTruthy()
expect(statsText!.length).toBeGreaterThan(10)
```
### 3. 验证数据流而非仅 UI
**问题**:测试只验证 UI,不验证数据是否正确加载
**解决方案**
```typescript
// ❌ 错误:只验证 UI
const table = page.locator('.ant-table')
await expect(table).toBeVisible()
// ✅ 正确:验证数据流
const table = page.locator('.ant-table')
await expect(table).toBeVisible()
// 验证表格有数据
const rows = await table.locator('.ant-table-row').count()
expect(rows).toBeGreaterThan(0)
// 验证数据内容
const firstRowText = await table.locator('.ant-table-row').first().textContent()
expect(firstRowText).toBeTruthy()
expect(firstRowText!.length).toBeGreaterThan(5)
```
## 测试结构
### 1. 使用 test.step 组织测试步骤
```typescript
test('应该能够导入数据', async ({ page }) => {
await test.step('1. 导航到数据导入页面', async () => {
await page.goto('/data-import')
await page.waitForLoadState('networkidle')
})
await test.step('2. 选择文件', async () => {
const fileInput = page.locator('input[type="file"]')
await fileInput.setInputFiles('test-data/sample.xlsx')
})
await test.step('3. 验证导入成功', async () => {
const successMessage = page.locator('.ant-message-success')
await expect(successMessage).toBeVisible()
})
})
```
### 2. 使用 Page Object Model
```typescript
// pages/DataImportPage.ts
export class DataImportPage {
constructor(private page: Page) {}
async navigate() {
await this.page.goto('/data-import')
await this.page.waitForLoadState('networkidle')
}
async selectFile(filePath: string) {
const fileInput = this.page.locator('input[type="file"]')
await fileInput.setInputFiles(filePath)
}
async verifyImportSuccess() {
const successMessage = this.page.locator('.ant-message-success')
await expect(successMessage).toBeVisible()
}
}
```
### 3. 使用 Workflow 封装业务流程
```typescript
// workflows/DataImportWorkflow.ts
export class DataImportWorkflow {
constructor(private page: Page) {}
async importFile(filePath: string) {
await this.navigateToImport()
await this.selectFile(filePath)
await this.verifyImportSuccess()
}
private async navigateToImport() {
// 导航逻辑
}
private async selectFile(filePath: string) {
// 选择文件逻辑
}
private async verifyImportSuccess() {
// 验证逻辑
}
}
```
## 选择器策略
### 1. 优先级
1. **文本选择器** - 最稳定
```typescript
page.locator('button:has-text("提交")')
```
2. **角色选择器** - 语义化
```typescript
page.getByRole('button', { name: '提交' })
```
3. **标签选择器** - 简单
```typescript
page.locator('h1')
```
4. **data-testid** - 最后选择
```typescript
page.locator('[data-testid="submit-button"]')
```
### 2. 避免使用的选择器
❌ **CSS 类名** - 容易变化
```typescript
page.locator('.ant-btn-primary')
```
❌ **复杂的 CSS 选择器** - 脆弱
```typescript
page.locator('div > div > button.ant-btn.ant-btn-primary')
```
## 等待策略
### 1. 使用自动等待
```typescript
// ✅ 推荐:Playwright 自动等待
await page.click('button')
// ❌ 不推荐:手动等待
await page.waitForTimeout(1000)
await page.click('button')
```
### 2. 明确等待条件
```typescript
// ✅ 推荐:明确等待条件
await page.waitForSelector('.ant-table-row', { state: 'visible' })
// ❌ 不推荐:模糊等待
await page.waitForTimeout(2000)
```
### 3. 等待网络请求
```typescript
// ✅ 推荐:等待网络请求完成
await page.waitForLoadState('networkidle')
// 或者等待特定请求
await page.waitForResponse(response =>
response.url().includes('/api/data') && response.status() === 200
)
```
## 错误处理
### 1. 使用 try-catch 处理可选操作
```typescript
try {
const optionalButton = page.locator('button:has-text("可选操作")')
await optionalButton.click({ timeout: 5000 })
} catch (error) {
console.log('可选操作按钮不存在,跳过')
}
```
### 2. 使用条件判断
```typescript
const cancelButton = page.locator('button:has-text("取消")')
if (await cancelButton.isVisible()) {
await cancelButton.click()
}
```
## 调试技巧
### 1. 使用页面快照
```typescript
const snapshot = await page.locator('body').innerHTML()
console.log('Page snapshot:', snapshot)
```
### 2. 使用截图
```typescript
await page.screenshot({ path: 'debug.png', fullPage: true })
```
### 3. 使用 trace
```typescript
// 在 playwright.config.ts 中启用
use: {
trace: 'on-first-retry',
}
```
## 测试数据
### 1. 使用测试数据工厂
```typescript
// fixtures/test-data-factory.ts
export class TestDataFactory {
async createExcel(rows: number): Promise<string> {
// 创建测试 Excel 文件
}
async createLargeExcel(rows: number): Promise<string> {
// 创建大型测试 Excel 文件
}
async createCorruptedExcel(): Promise<string> {
// 创建损坏的 Excel 文件
}
}
```
### 2. 使用 fixtures
```typescript
// fixtures/test-fixtures.ts
import { test as base } from '@playwright/test'
export const test = base.extend({
authenticatedPage: async ({ page }, use) => {
// 登录逻辑
await page.goto('/login')
await page.fill('input[name="username"]', 'testuser')
await page.fill('input[name="password"]', 'password')
await page.click('button[type="submit"]')
await use(page)
},
})
```
## 性能优化
### 1. 并行执行
```typescript
// playwright.config.ts
export default defineConfig({
workers: 4, // 并行执行 4 个测试
})
```
### 2. 重用登录状态
```typescript
// 使用 storageState 重用登录状态
export default defineConfig({
use: {
storageState: 'auth.json',
},
})
```
### 3. 跳过不必要的等待
```typescript
// ❌ 不推荐:全局等待
await page.waitForTimeout(2000)
// ✅ 推荐:精确等待
await page.waitForSelector('.ant-table-row')
```
## CI/CD 集成
### 1. 使用 Docker
```dockerfile
FROM mcr.microsoft.com/playwright:v1.40.0-jammy
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npx playwright install --with-deps
```
### 2. 使用 GitHub Actions
```yaml
name: E2E Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npx playwright install --with-deps
- run: npm run test:e2e
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: playwright-report/
```
## 总结
遵循这些最佳实践,可以确保 E2E 测试:
1. ✅ 验证真实用户行为
2. ✅ 使用硬验证
3. ✅ 验证数据流
4. ✅ 使用稳定的选择器
5. ✅ 正确处理等待
6. ✅ 良好的错误处理
7. ✅ 易于调试
8. ✅ 性能优化
9. ✅ CI/CD 友好
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -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 联系方式
- **负责人**: 张翔
- **角色**: 全栈质量保障与效能工程师
- **原则**: 质量是设计出来的,并通过自动化流水线保障