Explorar o código

docs(story): 创建 Story 10-10 - 编写订单附件管理测试

Story 文档已创建,包含以下验收标准:
1. 为订单添加附件(打开对话框、选择人员、上传文件、验证成功)
2. 附件文件格式验证(支持的格式、不支持的格式)

测试文件: web/tests/e2e/specs/admin/order-attachment.spec.ts

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 hai 4 días
pai
achega
b3d0b4fbb4

+ 423 - 0
_bmad-output/implementation-artifacts/10-10-order-attachment-tests.md

@@ -0,0 +1,423 @@
+# Story 10.10: 编写订单附件管理测试
+
+Status: ready-for-dev
+
+<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
+
+## Story
+
+作为测试开发者,
+我想要编写订单附件管理的 E2E 测试,
+以便验证添加订单附件的功能。
+
+## Acceptance Criteria
+
+**Given** 订单管理 Page Object 已创建
+**When** 编写附件管理测试用例
+**Then** 包含以下测试场景:
+
+1. **为订单添加附件**
+   - 打开添加附件对话框
+   - 选择订单人员
+   - 上传附件文件
+   - 验证附件添加成功
+
+2. **附件文件格式验证**
+   - 上传支持的文件格式
+   - 验证上传成功
+   - 尝试上传不支持的格式
+   - 验证错误提示
+
+**测试文件:** `web/tests/e2e/specs/admin/order-attachment.spec.ts`
+
+## Tasks / Subtasks
+
+- [ ] 探索附件上传 UI 结构 (AC: When)
+  - [ ] 分析订单详情对话框中的附件管理区域
+  - [ ] 确认添加附件对话框的打开方式
+  - [ ] 确认人员选择器的交互模式
+  - [ ] 确认文件上传输入框的选择器
+- [ ] 验证 Page Object 附件方法 (AC: Given)
+  - [ ] 验证 `openAddAttachmentDialog()` 方法可用
+  - [ ] 验证 `uploadAttachment(personName, fileName, mimeType)` 方法可用
+  - [ ] 验证 `getAttachmentListFromDetail()` 方法可用
+  - [ ] 补充必要的辅助方法(如需要)
+- [ ] 创建附件管理测试文件 (AC: When)
+  - [ ] 创建 `web/tests/e2e/specs/admin/order-attachment.spec.ts`
+  - [ ] 导入必要的测试依赖和 Page Object
+  - [ ] 配置测试 Fixtures(adminLoginPage, orderManagementPage, disabilityPersonPage)
+- [ ] 编写添加附件测试 (AC: Then #1)
+  - [ ] 测试打开添加附件对话框
+  - [ ] 测试选择订单人员
+  - [ ] 测试上传图片文件(JPG 格式)
+  - [ ] 测试验证附件添加成功(Toast 消息)
+  - [ ] 测试验证附件出现在订单详情中
+- [ ] 编写文件格式验证测试 (AC: Then #2)
+  - [ ] 测试上传 JPG 格式文件
+  - [ ] 测试上传 PNG 格式文件
+  - [ ] 测试上传 WEBP 格式文件(如支持)
+  - [ ] 测试尝试上传不支持的格式(如 .txt、.exe)
+  - [ ] 测试验证错误提示显示正确
+- [ ] 确保所有测试通过 (AC: And)
+  - [ ] 运行测试并修复问题
+  - [ ] 验证测试稳定性(连续运行 3 次)
+
+## Dev Notes
+
+### Epic Context
+
+**Epic 10: 订单管理 E2E 测试 (Epic C - 业务测试 Epic)**
+
+- **目标**: 测试开发者可以为订单管理功能编写完整的 E2E 测试,验证订单的 CRUD、状态流转、人员关联和附件管理功能
+- **业务分组**: Epic C(业务测试 Epic)
+- **背景**: 订单管理是招聘系统的核心业务功能,涉及复杂表单(多选择器联动)、状态流转、人员关联等场景
+- **模式**: 业务测试为主,工具包支持为辅(遵循 Epic A 成功模式)
+
+**依赖:**
+- Epic 1: ✅ 已完成(Select 工具基础框架)
+- Epic 2: ✅ 已完成(Select 工具在真实 E2E 测试中验证)
+- Story 10.1: ✅ 已完成(订单管理 Page Object)
+- Story 10.8: ✅ 已完成(订单详情查看测试)
+- Story 10.9: ✅ 已完成(人员关联功能测试 - 提供了选择残疾人的实现)
+
+### 前序 Story 关键发现 (Story 10.8 和 10.9)
+
+**从 Story 10.8 学到的经验:**
+
+1. **订单详情对话框结构**:
+   - 使用 `openDetailDialog(orderName)` 打开订单详情对话框
+   - 详情对话框中包含多个 Tab 或区域:基本信息、人员列表、附件列表
+   - 使用 `closeDetailDialog()` 关闭对话框
+
+2. **附件列表获取**:
+   - `getAttachmentListFromDetail()` 方法已实现(行 752-818)
+   - 支持表格和列表两种形式
+   - 返回附件信息:fileName, uploadDate, uploader
+
+**从 Story 10.9 学到的经验:**
+
+1. **API 直接创建测试数据**:
+   - 使用 `createDisabledPersonViaAPI()` 创建残疾人数据
+   - 使用时间戳确保数据唯一性
+   - 避免依赖 UI 创建流程的超时问题
+
+2. **人员管理验证**:
+   - `getPersonListFromDetail()` 方法已修复(正确选择绑定人员列表表格)
+   - 使用 `hasText: '工作状态'` 作为筛选条件
+
+3. **测试数据隔离**:
+   - 使用 `testDataCounter` 全局计数器确保唯一性
+   - 每个测试创建独立的测试数据
+   - 身份证号格式修正为 18 位标准格式
+
+### Page Object 已有功能分析
+
+**订单附件管理相关方法** (`web/tests/e2e/pages/admin/order-management.page.ts`):
+
+| 方法 | 说明 | 当前状态 | 本 Story 需求 |
+|------|------|---------|--------------|
+| `openAddAttachmentDialog()` | 打开添加附件对话框 | 已实现 (行 1032-1036) | 可直接使用 |
+| `uploadAttachment(personName, fileName, mimeType)` | 上传附件 | 已实现 (行 1044-1067) | **需验证**UI 结构 |
+| `getAttachmentListFromDetail()` | 获取附件列表 | 已实现 (行 752-818) | 验证附件后检查 |
+
+**`openAddAttachmentDialog()` 方法当前实现(行 1032-1036):**
+
+```typescript
+async openAddAttachmentDialog() {
+  const attachmentButton = this.page.getByRole('button', { name: /添加附件|上传附件/ });
+  await attachmentButton.click();
+  await this.page.waitForSelector('[role="dialog"]', { state: 'visible', timeout: TIMEOUTS.DIALOG });
+}
+```
+
+**`uploadAttachment()` 方法当前实现(行 1044-1067):**
+
+```typescript
+async uploadAttachment(personName: string, fileName: string, mimeType: string = 'image/jpeg') {
+  // 选择订单人员
+  const personSelect = this.page.getByLabel(/选择人员|订单人员/);
+  await personSelect.click();
+  await this.page.getByRole('option', { name: personName }).click();
+
+  // 查找文件上传输入框
+  const fileInput = this.page.locator('input[type="file"]');
+  await fileInput.setInputFiles({
+    name: fileName,
+    mimeType,
+    buffer: Buffer.from(`fake ${fileName} content`),
+  });
+
+  // 等待上传处理
+  await this.page.waitForTimeout(TIMEOUTS.MEDIUM);
+
+  // 提交
+  const submitButton = this.page.getByRole('button', { name: /^(上传|确定|保存)$/ });
+  await submitButton.click();
+
+  await this.page.waitForLoadState('networkidle');
+  await this.page.waitForTimeout(TIMEOUTS.LONG);
+}
+```
+
+**潜在问题**:
+- 人员选择假设使用标准 Select(`getByLabel` + `getByRole('option')`)
+- 文件上传使用 `setInputFiles` 的 buffer 模式(而非 fixtures 文件路径)
+- 提交按钮名称假设为"上传"|"确定"|"保存"
+
+**需要验证的 UI 结构**:
+1. 添加附件按钮在哪里(订单详情对话框内?人员列表区域?)
+2. 人员选择是否使用 Radix Select 还是自定义组件
+3. 文件上传是否使用 `<input type="file">` 标准 HTML 元素
+4. 附件列表展示的格式(表格 vs 卡片列表)
+
+### 测试覆盖场景清单
+
+**为订单添加附件:**
+- [ ] 打开订单详情对话框
+- [ ] **[关键]** 找到并点击添加附件按钮
+- [ ] 在添加附件对话框中选择订单人员
+- [ ] 上传图片文件(JPG 格式)
+- [ ] 验证上传成功 Toast 消息
+- [ ] 在订单详情中验证附件显示正确(文件名、上传时间)
+
+**附件文件格式验证:**
+- [ ] 上传 JPG 格式文件 → 验证成功
+- [ ] 上传 PNG 格式文件 → 验证成功
+- [ ] 上传 WEBP 格式文件 → 验证成功(如 UI 支持)
+- [ ] 尝试上传不支持的格式(.txt、.exe)→ 验证错误提示
+- [ ] 验证文件大小限制(如有)
+
+### UI 结构探索要点
+
+**附件管理对话框结构假设(需要验证):**
+
+1. **添加附件按钮位置**:
+   - **假设1**: 在订单详情对话框中,有一个独立的"附件"标签页
+   - **假设2**: 在人员列表中,每个人员行有一个"添加附件"按钮
+   - **假设3**: 在订单详情对话框底部,有一个"添加附件"按钮
+
+2. **添加附件对话框结构**:
+   - **人员选择器**: 选择要关联附件的订单人员
+     - 可能是 Radix Select 下拉框
+     - 可能是搜索 + 选择组合
+   - **文件上传区域**: 文件上传输入框
+     - 标准 `<input type="file">` 元素
+     - 可能有拖放区域
+   - **提交按钮**: "上传"或"确定"按钮
+
+3. **附件列表展示**:
+   - **表格形式**: 文件名、上传时间、上传者
+   - **卡片形式**: 每个附件一个卡片
+   - **位置**: 在订单详情对话框中
+
+**测试时优先探索的顺序:**
+1. 先使用 `openDetailDialog(orderName)` 打开订单详情对话框
+2. 检查对话框中的附件区域或标签页
+3. 找到添加附件按钮并点击
+4. **[关键]** 探索添加附件对话框的 DOM 结构
+5. 确定人员选择的正确策略(Select vs 其他)
+6. 定位文件上传输入框
+7. 定位提交按钮和验证方式
+
+**探索策略**:
+```typescript
+// 步骤1: 打开订单详情
+await orderPage.openDetailDialog(orderName);
+
+// 步骤2: 查找添加附件按钮
+const attachmentButton = page.getByRole('button', { name: /添加附件|上传附件/ });
+// 或
+const attachmentButton = page.getByTestId('add-attachment-button');
+
+// 步骤3: 点击并验证对话框打开
+await attachmentButton.click();
+await page.waitForSelector('[role="dialog"]', { state: 'visible' });
+
+// 步骤4: 探索对话框结构
+// - 人员选择器
+const personSelect = page.getByLabel(/选择人员|订单人员/);
+// 或 Radix Select
+await selectRadixOption(page, '订单人员', personName);
+
+// - 文件上传输入框
+const fileInput = page.locator('input[type="file"]');
+// 使用 fixtures 文件
+await fileInput.setInputFiles('web/tests/fixtures/images/sample-id-card.jpg');
+
+// 步骤5: 提交并验证
+const submitButton = page.getByRole('button', { name: /^(上传|确定|保存)$/ });
+await submitButton.click();
+
+// 步骤6: 验证附件列表
+const attachments = await orderPage.getAttachmentListFromDetail();
+```
+
+### 测试数据准备
+
+**Fixtures 文件** (位于 `web/tests/fixtures/images/`):
+- `sample-id-card.jpg` - 身份证照片(JPG 格式)
+- `sample-disability-card.jpg` - 残疾证照片(JPG 格式)
+- `id-card-front.jpg` - 身份证正面(JPG 格式)
+- `id-card-back.jpg` - 身份证反面(JPG 格式)
+- `disability-card.jpg` - 残疾证照片(JPG 格式)
+- `photo.jpg` - 个人照片(JPG 格式)
+- `photo.png` - 个人照片(PNG 格式)
+- `photo.webp` - 个人照片(WEBP 格式)
+- `large-file.jpg` - 大文件(用于测试文件大小限制)
+- `invalid.txt` - 无效格式文件(用于测试格式验证)
+
+**测试数据创建流程**:
+1. 使用 API 创建残疾人数据
+2. 创建测试订单
+3. 添加残疾人到订单(使用 Story 10.9 的方法)
+4. 上传附件到订单人员
+
+### 项目结构对齐
+
+**遵循 Epic 9.6 并行执行决策:**
+- ✅ 不使用 `test.describe.serial`
+- ✅ 每个测试创建独立的测试数据
+- ✅ 使用时间戳确保订单名称和残疾人姓名唯一
+
+**遵循项目的类型规范:**
+- ✅ 使用 TypeScript 严格模式
+- ✅ 使用 `WORK_STATUS` 和 `WORK_STATUS_LABELS` 常量
+- ✅ 工作状态类型使用 `WorkStatus` 类型别名
+
+**遵循项目的测试模式:**
+- ✅ 使用 Playwright fixtures
+- ✅ 使用 Page Object 模式
+- ✅ Toast 消息使用 `data-sonner-toast` 选择器
+- ✅ 对话框使用 `role="dialog"` 或 `role="alertdialog"`
+
+**测试数据隔离:**
+- 使用 `createDisabledPersonViaAPI()` 创建残疾人数据
+- 使用 `orderManagementPage.createOrder()` 创建测试订单
+- 使用 `orderManagementPage.addPersonToOrder()` 添加人员到订单
+- 测试名称添加时间戳确保唯一性
+
+### Project Structure Notes
+
+**测试文件位置:**
+```
+web/tests/e2e/
+├── pages/admin/
+│   └── order-management.page.ts  (已有附件方法)
+└── specs/admin/
+    └── order-attachment.spec.ts   (新建)
+```
+
+**测试 Fixtures 位置:**
+```
+web/tests/fixtures/
+└── images/
+    ├── sample-id-card.jpg
+    ├── sample-disability-card.jpg
+    ├── photo.jpg
+    ├── photo.png
+    ├── photo.webp
+    └── invalid.txt
+```
+
+**与其他测试的关系:**
+- `order-create.spec.ts`: 创建订单测试(提供订单数据源)
+- `order-person.spec.ts`: 人员关联测试(提供人员数据源)
+- `order-detail.spec.ts`: 订单详情测试(验证附件列表显示)
+
+**本 Story 完成后的影响:**
+- 完成订单管理的最后一个核心功能测试
+- 为 Story 10.11(完整流程测试)提供附件上传功能
+- 为 Epic 10 的稳定性验证做好准备
+
+### References
+
+**Epic 需求来源:**
+- [Source: _bmad-output/planning-artifacts/epics.md](../planning-artifacts/epics.md) - Story 10.10 详细需求(行 2181-2205)
+
+**Page Object 现有实现:**
+- [Source: web/tests/e2e/pages/admin/order-management.page.ts](../../web/tests/e2e/pages/admin/order-management.page.ts) - 附件管理方法(行 1027-1067)
+
+**前序 Story 学习:**
+- [Source: _bmad-output/implementation-artifacts/10-8-order-detail-tests.md](10-8-order-detail-tests.md) - 订单详情对话框结构、附件列表获取
+- [Source: _bmad-output/implementation-artifacts/10-9-order-person-tests.md](10-9-order-person-tests.md) - API 数据创建、测试数据隔离、人员管理验证
+
+**项目上下文:**
+- [Source: _bmad-output/project-context.md](../project-context.md) - 技术栈、测试规范、类型系统
+
+**Epic 9 并行执行决策:**
+- [Source: _bmad-output/implementation-artifacts/epic-9-retrospective-2026-01-12.md](epic-9-retrospective-2026-01-12.md) - 测试隔离和并行执行最佳实践
+
+## Dev Agent Record
+
+### Agent Model Used
+
+claude-opus-4-5-20251101
+
+### Debug Log References
+
+无
+
+### Completion Notes List
+
+**Story 创建完成 - 2026-01-13**
+
+1. **工作流执行**:
+   - ✅ 读取工作流配置: `create-story/workflow.yaml`
+   - ✅ 加载 Epic 10 信息: `epics.md` (Story 10.10 行 2181-2205)
+   - ✅ 加载 Story 10.9 参考: `10-9-order-person-tests.md`
+   - ✅ 加载项目上下文: `project-context.md`
+   - ✅ 分析订单管理 Page Object: `order-management.page.ts`
+
+2. **验收标准映射**:
+   - AC #1 "为订单添加附件" → 测试任务: 添加附件测试
+   - AC #2 "附件文件格式验证" → 测试任务: 文件格式验证测试
+
+3. **Page Object 方法验证**:
+   - ✅ `openAddAttachmentDialog()` - 已实现(行 1032-1036)
+   - ✅ `uploadAttachment(personName, fileName, mimeType)` - 已实现(行 1044-1067)
+   - ✅ `getAttachmentListFromDetail()` - 已实现(行 752-818)
+
+4. **测试 Fixtures 确认**:
+   - ✅ 图片文件: JPG, PNG, WEBP 格式可用
+   - ✅ 测试文件: `sample-id-card.jpg`, `photo.png`, `photo.webp`
+   - ✅ 无效文件: `invalid.txt` (用于格式验证)
+
+5. **依赖关系确认**:
+   - Story 10.1: ✅ 已完成(订单管理 Page Object)
+   - Story 10.8: ✅ 已完成(订单详情查看测试)
+   - Story 10.9: ✅ 已完成(人员关联功能测试)
+
+**待完成(开发阶段):**
+- [ ] 探索附件上传 UI 结构
+- [ ] 验证 Page Object 方法的 UI 选择器
+- [ ] 创建测试文件 `order-attachment.spec.ts`
+- [ ] 编写测试用例(添加附件、文件格式验证)
+- [ ] 运行测试并修复问题
+- [ ] 验证测试稳定性
+
+### File List
+
+**已创建的文件:**
+- `_bmad-output/implementation-artifacts/10-10-order-attachment-tests.md` - Story 文档
+
+**需创建的文件(开发阶段):**
+- `web/tests/e2e/specs/admin/order-attachment.spec.ts` - 附件管理测试文件
+
+**相关参考文件:**
+- `web/tests/e2e/pages/admin/order-management.page.ts` - 订单管理 Page Object(行 1027-1067: 附件方法)
+- `_bmad-output/implementation-artifacts/10-8-order-detail-tests.md` - 订单详情测试参考
+- `_bmad-output/implementation-artifacts/10-9-order-person-tests.md` - 人员关联测试参考
+
+---
+**sprint-status.yaml 更新说明:**
+
+由于 `sprint-status.yaml` 文件不存在,Story 10-10 的状态无法通过文件更新。
+建议运行 `bmad:bmm:workflows:sprint-status` 工作流来初始化或更新 sprint status 文件。
+
+手动更新指令(如果文件存在):
+```yaml
+# 在 sprint-status.yaml 中
+stories:
+  10-10:
+    status: ready-for-dev  # 从 backlog 改为 ready-for-dev
+```

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

@@ -155,7 +155,7 @@ development_status:
   10-7-order-status-tests: done                # 编写订单状态流转测试 - 代码审查完成,所有HIGH和MEDIUM问题已修复
   10-8-order-detail-tests: done               # 编写订单详情查看测试 - ✅ 13/13 测试通过 (2026-01-13)
   10-9-order-person-tests: done               # 编写人员关联功能测试 - 2026-01-13 完成:6/6 测试通过,代码审查完成
-  10-10-order-attachment-tests: backlog    # 编写附件管理测试
+  10-10-order-attachment-tests: ready-for-dev    # 编写附件管理测试 - Story 文档已创建 (2026-01-13)
   10-11-order-complete-tests: backlog      # 编写订单完整流程测试
   10-12-run-tests-collect-issues: backlog  # 运行测试并收集问题和改进建议
   10-13-extend-utils-if-needed: backlog   # 扩展工具包(如需要)