Files
NextEdu/docs/testing/visual-regression.md
SpecialX 6585e10c6f feat(P2): 实现质量保障类5项功能(无障碍/视觉回归/通知渠道/漏洞扫描/灾备)
## 新增功能

### 1. 屏幕阅读器兼容性增强(a11y)
- 无障碍工具库:src/shared/lib/a11y.ts
- aria-live Hook:src/shared/hooks/use-aria-live.ts
- a11y 组件:skip-link/visually-hidden/focus-trap/aria-status
- 增强 UI:table.tsx 系统性 ARIA role,dialog.tsx aria-modal
- 审计文档:docs/accessibility/a11y-audit.md(WCAG 2.1 AA 清单)

### 2. 视觉回归测试
- 测试套件:tests/visual/(homepage + 3 个 dashboard)
- 3 视口(desktop/tablet/mobile)× 2 主题(light/dark)
- 动态元素遮罩,避免误报
- playwright.config.ts 新增 visual-chromium 项目
- 文档:docs/testing/visual-regression.md

### 3. 短信/微信推送渠道集成
- 新模块:src/modules/notifications/
- 4 个渠道:SMS(阿里云/腾讯云)、WeChat(公众号)、Email(SMTP)、In-App
- 分发器按用户偏好并行多渠道发送
- 外部 SDK 动态 import,Mock 模式开发可用
- 文档:docs/notifications/channels.md

### 4. 漏洞扫描 CI 集成
- CI security-scan job:npm audit + Snyk + Trivy FS + OWASP ZAP
- 独立工作流 security.yml:每周一深度扫描 + 容器镜像扫描
- 配置:suppressions.json + .trivyignore
- 本地脚本:security-scan.sh/ps1
- 文档:docs/security/scanning.md(SLA 分级)

### 5. 灾备方案
- 脚本:backup-verify/backup-offsite-sync/dr-drill/failover/health-check
- CI 增强:备份后校验+异地同步,每周灾备演练
- 独立工作流 dr-drill.yml:每周一凌晨 4 点自动演练
- 文档:docs/dr/dr-plan.md(RTO 4h/RPO 24h)+ dr-runbook.md(6 故障场景)

## 验证
- npx tsc --noEmit:0 错误
- npm run lint:0 错误 0 警告
2026-06-17 20:18:29 +08:00

186 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 视觉回归测试 (Visual Regression Testing)
本项目使用 [Playwright](https://playwright.dev/) 的 `toHaveScreenshot()` API 实现视觉回归测试,对关键页面在多种视口与主题下进行像素级快照对比,以捕获 UI 的意外变化。
## 目录结构
```
tests/visual/
├── visual.config.ts # 视觉测试配置(页面、视口、主题、快照路径)
├── homepage.spec.ts # 登录页视觉测试
├── admin-dashboard.spec.ts # 管理员仪表盘视觉测试
├── teacher-dashboard.spec.ts # 教师仪表盘视觉测试
├── student-dashboard.spec.ts # 学生仪表盘视觉测试
├── helpers/
│ ├── auth.ts # 认证辅助(登录、setupAuthState)
│ └── visual-helpers.ts # 视觉通用辅助(视口、主题、遮罩)
└── __screenshots__/ # 快照基线存储目录(自动生成)
```
## 覆盖范围
| 页面 | 路径 | 视口 | 主题 | 是否需要登录 |
|------|------|------|------|--------------|
| 登录页 | `/login` | desktop / tablet / mobile | light / dark | 否 |
| 管理员仪表盘 | `/admin/dashboard` | desktop / tablet / mobile | light / dark | 是 (admin) |
| 教师仪表盘 | `/teacher/dashboard` | desktop / tablet / mobile | light / dark | 是 (teacher) |
| 学生仪表盘 | `/student/dashboard` | desktop / tablet / mobile | light / dark | 是 (student) |
视口尺寸:
- desktop: 1920 × 1080
- tablet: 768 × 1024
- mobile: 375 × 812
## 运行测试
### 前置条件
- 需要启动开发服务器(Playwright 会通过 `webServer` 配置自动启动)
- 需要登录的视觉测试需要 `DATABASE_URL` 环境变量,否则会自动跳过
- 测试账号默认为 `admin@xiaoxue.edu.cn / 123456`,可通过环境变量覆盖
### 运行命令
```bash
# 运行所有视觉回归测试
npm run test:visual
# 运行单个测试文件
npx playwright test --project=visual-chromium tests/visual/homepage.spec.ts
# 以 UI 模式运行(便于调试)
npx playwright test --project=visual-chromium --ui
```
### 环境变量
| 变量 | 默认值 | 说明 |
|------|--------|------|
| `DATABASE_URL` | - | 数据库连接串,未设置时需要登录的测试会跳过 |
| `VISUAL_ADMIN_EMAIL` | `admin@xiaoxue.edu.cn` | 管理员测试账号 |
| `VISUAL_ADMIN_PASSWORD` | `123456` | 管理员测试密码 |
| `VISUAL_TEACHER_EMAIL` | `admin@xiaoxue.edu.cn` | 教师测试账号 |
| `VISUAL_TEACHER_PASSWORD` | `123456` | 教师测试密码 |
| `VISUAL_STUDENT_EMAIL` | `admin@xiaoxue.edu.cn` | 学生测试账号 |
| `VISUAL_STUDENT_PASSWORD` | `123456` | 学生测试密码 |
## 更新基线
当 UI 发生**预期内**的变化时,需要更新快照基线:
```bash
# 更新所有视觉快照基线
npm run test:visual:update
# 更新单个测试文件的基线
npx playwright test --project=visual-chromium tests/visual/homepage.spec.ts --update-snapshots
```
更新后的快照应作为 PR 的一部分提交到版本库,以便团队评审 UI 变更。
## 处理误报
视觉测试可能因为动态内容(时间戳、用户名、实时数据等)产生误报。本项目通过以下方式消除误报:
### 1. 动态元素遮罩
`maskDynamicElements()` 辅助函数会自动遮罩以下选择器:
- `[data-testid='timestamp']`
- `[data-testid='current-time']`
- `[data-testid='user-avatar']`
- `[data-testid='user-name']`
- `time`
- `[data-visual-dynamic]`
可在测试中追加额外需要遮罩的选择器:
```ts
const masks = await maskDynamicElements(page, ["[data-testid='stat-card-value']"])
```
### 2. 标记动态元素
在组件代码中为动态元素添加 `data-visual-dynamic` 属性,即可自动被遮罩:
```tsx
<div data-visual-dynamic>{new Date().toLocaleString()}</div>
```
### 3. 调整容差
`playwright.config.ts` 中配置了默认容差 `maxDiffPixelRatio: 0.01`(允许 1% 像素差异)。若特定页面需要更宽松的容差,可在断言时覆盖:
```ts
await expect(page).toHaveScreenshot("name.png", {
maxDiffPixelRatio: 0.05,
})
```
### 4. 禁用动画
默认配置 `animations: "disabled"`,避免动画过渡态导致快照不稳定。
## CI 集成
### GitHub Actions 示例
```yaml
name: Visual Regression
on:
pull_request:
paths:
- "src/**"
- "tests/visual/**"
- "playwright.config.ts"
jobs:
visual:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npx playwright install --with-deps chromium
# 启动数据库(按需)
- run: npm run db:setup
env:
DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
- name: Run visual tests
run: npm run test:visual
env:
DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
CI: "true"
- name: Upload snapshot diff
if: failure()
uses: actions/upload-artifact@v4
with:
name: snapshot-diff
path: test-results/
retention-days: 7
```
### CI 注意事项
1. **快照基线需提交到版本库**: `tests/visual/__screenshots__/` 目录应纳入 Git 跟踪
2. **跨平台一致性**: 不同操作系统的字体渲染存在差异,建议 CI 与本地使用相同的 Linux 容器环境。若本地为 Windows/macOS,可能出现少量误报,以 CI 结果为准
3. **storageState 缓存**: `tests/visual/.auth/` 目录应加入 `.gitignore`,不要提交登录态文件
## 与 E2E 测试的关系
| 维度 | E2E 测试 | 视觉测试 |
|------|----------|----------|
| 目录 | `tests/e2e/` | `tests/visual/` |
| Playwright 项目 | `chromium` | `visual-chromium` |
| 运行命令 | `npm run test:e2e` | `npm run test:visual` |
| 关注点 | 功能正确性 | UI 视觉一致性 |
| 断言方式 | DOM/行为断言 | 像素快照对比 |
两个测试套件相互独立,可分别运行,互不影响。