Procházet zdrojové kódy

docs(e2e-test-utils): 创建 Story 3.5 - 文件上传稳定性验证

- 创建完整的 Story 文档,包含稳定性测试执行计划
- 更新 sprint-status.yaml 标记 Story 3.5 为 ready-for-dev
- 基于 Epic 2 稳定性验证经验设计测试方案
- 包含 10 次连续运行测试脚本和成功标准

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname před 1 týdnem
rodič
revize
d5b84afde5

+ 355 - 0
_bmad-output/implementation-artifacts/3-5-upload-stability-test.md

@@ -0,0 +1,355 @@
+# Story 3.5: 文件上传稳定性验证
+
+Status: ready-for-dev
+
+<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
+
+## Story
+
+作为测试开发者,
+我想要验证文件上传工具的稳定性,
+以便确保工具可以可靠地在 E2E 测试中使用。
+
+## Acceptance Criteria
+
+**Given** Story 3.1 已实现 `uploadFileToField()` 函数
+**Given** Story 3.2 已完成单元测试(覆盖率 91.66%)
+**Given** Story 3.3 已在真实 E2E 测试中验证文件上传工具(场景 1 通过)
+**Given** Story 3.4 已修复所有发现的工具 bug(Select 工具问题已解决)
+
+**When** 连续运行文件上传相关测试 10 次
+
+**Then** 验收标准如下:
+
+1. **测试通过率 100%**
+   - 10/10 次运行全部通过
+   - 无 flaky 失败
+   - 无超时失败
+
+2. **性能指标达标**
+   - 平均执行时间 ≤ 5 秒/文件
+   - 最大执行时间 < 10 秒/文件
+   - 无明显性能衰减
+
+3. **多文件上传稳定性**
+   - 连续上传多张照片场景稳定
+   - 不同文件类型上传正常
+   - 上传后的预览显示正常
+
+4. **稳定性测试报告**
+   - 记录每次运行的详细结果
+   - 统计通过率、平均时间、失败原因
+   - 确认是否达到 Epic 3 完成标准
+
+## Tasks / Subtasks
+
+- [ ] **Task 1: 准备稳定性测试环境** (AC: #1)
+  - [ ] Subtask 1.1: 确认 Select 工具修复已生效
+  - [ ] Subtask 1.2: 确认文件上传测试文件完整
+  - [ ] Subtask 1.3: 确认 fixtures 文件存在
+
+- [ ] **Task 2: 执行稳定性测试(10次连续运行)** (AC: #1, #2)
+  - [ ] Subtask 2.1: 运行场景 1(单文件上传)10 次
+  - [ ] Subtask 2.2: 运行场景 2(多文件上传)10 次
+  - [ ] Subtask 2.3: 记录每次运行的通过率和时间
+
+- [ ] **Task 3: 分析测试结果** (AC: #1, #2, #3)
+  - [ ] Subtask 3.1: 统计总体通过率
+  - [ ] Subtask 3.2: 分析失败原因(如果有)
+  - [ ] Subtask 3.3: 测量平均执行时间
+
+- [ ] **Task 4: 修复发现的稳定性问题** (AC: #1)
+  - [ ] Subtask 4.1: 分析 flaky 失败的根本原因
+  - [ ] Subtask 4.2: 修复工具或测试代码
+  - [ ] Subtask 4.3: 重新运行验证修复
+
+- [ ] **Task 5: 生成稳定性测试报告** (AC: #4)
+  - [ ] Subtask 5.1: 记录测试统计数据
+  - [ ] Subtask 5.2: 确认 Epic 3 完成标准
+  - [ ] Subtask 5.3: 为 Epic 3 回顾提供数据
+
+## Dev Notes
+
+### Epic 3 背景与目标
+
+**Epic 3: 文件上传工具开发与验证**
+
+遵循 Epic 2 的成功模式,开发文件上传工具并在真实 E2E 测试中验证,解决当前测试超时阻塞问题。
+
+**模式:** 工具开发 → 真实 E2E 测试验证 → 问题修复 → 稳定性验证
+
+**当前进度:**
+- Story 3.1: ✅ 已完成 - `uploadFileToField()` 函数已实现(含 UI 组件 testId 支持)
+- Story 3.2: ✅ 已完成 - 单元测试(覆盖率 91.66%)
+- Story 3.3: ✅ 已完成 - 在真实 E2E 测试中验证(场景 1 通过)
+- Story 3.4: ✅ 已完成 - 收集反馈并修复问题(修复了 Select 工具处理带 `*` 标签的问题)
+- Story 3.5: 🔄 本 Story - 文件上传稳定性验证(10次连续运行)
+
+### Epic 2 稳定性验证经验
+
+**来自 Epic 2 回顾的关键经验:**
+
+1. **稳定性验证是工具可用的前提**
+   - Epic 2 的 Select 工具连续运行 10 次才验证了稳定性
+   - 发现了 DOM 结构问题(listbox → option)
+   - 修复后才达到 100% 通过率
+
+2. **10次连续运行标准**
+   - 10/10 通过 = 100% 稳定性 ✅ 可以进入下一个 Epic
+   - 9/10 通过 = 90% 稳定性 ⚠️ 需要分析失败原因
+   - < 9/10 通过 = 稳定性不足 ❌ 需要修复后再验证
+
+3. **记录每次运行的详细结果**
+   - 通过率百分比
+   - 平均执行时间
+   - 失败原因分类(工具 bug / 使用错误 / 环境问题)
+
+### 从 Story 3.4 继承的修复
+
+**Select 工具修复(已完成):**
+- ✅ 策略 4:改用 `getByText(label, { exact: true })`
+- ✅ 新增策略 5:处理标签和 `*` 分离的 DOM 结构
+- ✅ 验证通过:性别 *、残疾类型 *、残疾等级 *、省份 *、城市
+
+**文件上传工具状态:**
+- ✅ 单元测试 36/36 通过
+- ✅ E2E 场景 1 验证通过(33.8s)
+- ✅ fixtures 路径解析正确
+- ✅ testId 架构工作正常
+
+### 稳定性测试设计
+
+#### 测试场景(基于 Story 3.3)
+
+**场景 1: 单文件上传验证**
+```typescript
+test('应该成功上传单张照片', async ({ disabilityPersonPage, page }) => {
+  const timestamp = Date.now();
+
+  // 1. 打开对话框并填写基本信息
+  await disabilityPersonPage.openCreateDialog();
+  await disabilityPersonPage.fillBasicForm({
+    name: `稳定性测试_${timestamp}`,
+    gender: '男',
+    idCard: '420101199001011234',
+    disabilityId: '51100119900101',
+    disabilityType: '视力残疾',
+    disabilityLevel: '一级',
+    phone: '13800138000',
+    idAddress: '湖北省武汉市测试街道1号',
+    province: '湖北省',
+    city: '武汉市'
+  });
+
+  // 2. 滚动到照片区域并添加一张照片
+  await disabilityPersonPage.scrollToSection('照片');
+  await page.getByTestId('add-photo-button').click();
+
+  // 3. 使用 uploadFileToField 上传文件
+  await uploadFileToField(page, '[data-testid="photo-upload-0"]', 'sample-id-card.jpg');
+
+  // 4. 验证上传成功
+  const preview = page.locator('[data-testid="photo-upload-0"]').locator('img');
+  await expect(preview).toBeVisible({ timeout: 5000 });
+});
+```
+
+**场景 2: 多文件上传验证**
+```typescript
+test('应该成功上传多张照片', async ({ disabilityPersonPage, page }) => {
+  const timestamp = Date.now();
+
+  // 1. 打开对话框并填写基本信息
+  await disabilityPersonPage.openCreateDialog();
+  await disabilityPersonPage.fillBasicForm({
+    name: `多文件稳定性_${timestamp}`,
+    gender: '女',
+    idCard: '420101199001011235',
+    disabilityId: '51100119900102',
+    disabilityType: '听力残疾',
+    disabilityLevel: '二级',
+    phone: '13800138001',
+    idAddress: '湖北省武汉市测试街道2号',
+    province: '湖北省',
+    city: '武汉市'
+  });
+
+  // 2. 滚动到照片区域
+  await disabilityPersonPage.scrollToSection('照片');
+
+  // 3. 添加并上传三张照片
+  await page.getByTestId('add-photo-button').click();
+  await uploadFileToField(page, '[data-testid="photo-upload-0"]', 'sample-id-card.jpg');
+
+  await page.getByTestId('add-photo-button').click();
+  await uploadFileToField(page, '[data-testid="photo-upload-1"]', 'sample-disability-card.jpg');
+
+  await page.getByTestId('add-photo-button').click();
+  await uploadFileToField(page, '[data-testid="photo-upload-2"]', 'sample-id-card.jpg');
+
+  // 4. 验证所有照片都上传成功
+  const previews = page.locator('[data-testid^="photo-upload-"]').locator('img');
+  await expect(previews).toHaveCount(3);
+});
+```
+
+### 稳定性测试执行命令
+
+**方法 1: Bash 循环(推荐)**
+```bash
+# 在 web 目录下执行 10 次测试
+cd web
+for i in {1..10}; do
+  echo "=== 运行 #$i ==="
+  pnpm test:e2e:chromium file-upload-validation || echo "运行 #$i 失败"
+done
+```
+
+**方法 2: 使用脚本**
+```bash
+# 创建稳定性测试脚本
+cat > run-stability-test.sh << 'EOF'
+#!/bin/bash
+PASSED=0
+FAILED=0
+TIMES=()
+
+for i in {1..10}; do
+  echo "=== 运行 #$i ==="
+  START=$(date +%s)
+
+  if pnpm test:e2e:chromium file-upload-validation; then
+    PASSED=$((PASSED + 1))
+    echo "✅ 运行 #$i 通过"
+  else
+    FAILED=$((FAILED + 1))
+    echo "❌ 运行 #$i 失败"
+  fi
+
+  END=$(date +%s)
+  DURATION=$((END - START))
+  TIMES+=($DURATION)
+  echo "⏱️  耗时: ${DURATION}s"
+  echo ""
+done
+
+echo "=== 稳定性测试结果 ==="
+echo "通过: $PASSED/10"
+echo "失败: $FAILED/10"
+echo "平均时间: $(awk '{sum+=$1} END {print sum/NR}' <<< "${TIMES[@]}")s"
+EOF
+
+chmod +x run-stability-test.sh
+./run-stability-test.sh
+```
+
+**方法 3: 单个场景循环(更精确)**
+```bash
+# 只运行特定场景 10 次
+cd web
+for i in {1..10}; do
+  echo "=== 场景 1 运行 #$i ==="
+  pnpm test:e2e:chromium file-upload-validation --grep "应该成功上传单张照片"
+done
+```
+
+### 成功标准
+
+**Epic 3 完成标准:**
+- ✅ Story 3.1: 工具函数实现完成
+- ✅ Story 3.2: 单元测试 ≥80%(实际 91.66%)
+- ✅ Story 3.3: E2E 集成测试通过
+- ✅ Story 3.4: 所有工具 bug 已修复
+- 🔄 Story 3.5: **本 Story - 10次连续运行 100% 通过**
+
+**如果达到 100% 通过率:**
+- Epic 3 可以标记为完成
+- 可以进入 Epic 4(表单工具开发与验证)
+
+**如果 < 100% 通过率:**
+- 分析失败原因
+- 修复问题后重新验证
+- 直达到 100% 通过率
+
+### 项目结构说明
+
+**相关文件:**
+```
+packages/e2e-test-utils/
+├── src/
+│   └── file-upload.ts         # 文件上传工具(无已知 bug)
+├── tests/
+│   └── unit/
+│       └── file-upload.test.ts  # 单元测试(36/36 通过)
+
+web/tests/e2e/
+├── specs/admin/
+│   └── file-upload-validation.spec.ts  # 稳定性测试目标文件
+├── fixtures/images/
+│   ├── sample-id-card.jpg
+│   └── sample-disability-card.jpg
+└── pages/admin/
+    └── disability-person.page.ts      # Page Object
+```
+
+### 参考文档
+
+**架构文档:**
+- `_bmad-output/planning-artifacts/architecture.md` - 测试策略、稳定性标准
+- `docs/standards/e2e-radix-testing.md` - E2E 测试标准
+
+**Epic 3 相关文档:**
+- `_bmad-output/planning-artifacts/epics.md` - Epic 3 完整需求
+- `_bmad-output/implementation-artifacts/3-1-file-upload-tool.md` - 工具实现
+- `_bmad-output/implementation-artifacts/3-2-upload-unit-tests.md` - 单元测试
+- `_bmad-output/implementation-artifacts/3-3-upload-e2e-integration.md` - E2E 集成测试
+- `_bmad-output/implementation-artifacts/3-4-collect-feedback-fix.md` - 反馈和修复
+
+**Epic 2 稳定性验证参考:**
+- `_bmad-output/implementation-artifacts/epic-2-retrospective.md` - 稳定性验证经验
+
+### Project Structure Notes
+
+**Monorepo 结构对齐:**
+- E2E 测试在 `web/tests/e2e/` 目录
+- 使用 `@d8d/e2e-test-utils` workspace 包
+- 测试 fixtures 位于 `web/tests/fixtures/`
+
+**文件组织:**
+- 测试文件按功能组织在 `specs/admin/` 目录
+- 使用 `.spec.ts` 后缀命名(Playwright 约定)
+- 稳定性测试使用现有的 `file-upload-validation.spec.ts`
+
+### References
+
+**源文档引用:**
+- [Source: _bmad-output/planning-artifacts/epics.md#Epic-3-Story-3.5] - Story 需求
+- [Source: _bmad-output/implementation-artifacts/3-4-collect-feedback-fix.md] - 前置 Story 修复
+
+**Epic 2 稳定性验证经验:**
+- [Source: _bmad-output/implementation-artifacts/epic-2-retrospective.md] - 10次连续运行标准
+
+**测试文件引用:**
+- [Source: web/tests/e2e/specs/admin/file-upload-validation.spec.ts] - 稳定性测试目标
+- [Source: packages/e2e-test-utils/src/file-upload.ts] - 工具实现
+
+## Dev Agent Record
+
+### Agent Model Used
+
+claude-opus-4-5-20251101
+
+### Debug Log References
+
+### Completion Notes List
+
+_待完成:稳定性测试执行和结果记录_
+
+### File List
+
+_待创建/修改的文件:_
+
+---
+
+**Story 创建日期:** 2026-01-10
+**Story 状态:** ready-for-dev

+ 1 - 1
_bmad-output/implementation-artifacts/sprint-status.yaml

@@ -68,7 +68,7 @@ development_status:
   3-2-upload-unit-tests: done             # 编写文件上传工具的单元测试
   3-3-upload-e2e-integration: done       # 在 web/tests/e2e 中验证文件上传工具
   3-4-collect-feedback-fix: done # 收集反馈并修复问题(修复了 Select 工具处理带 * 标签的问题)
-  3-5-upload-stability-test: backlog     # 文件上传稳定性验证 (10次连续运行)
+  3-5-upload-stability-test: ready-for-dev     # 文件上传稳定性验证 (10次连续运行)
   epic-3-retrospective: optional
 
   # Epic 4: 表单工具开发与验证