10-10-order-attachment-tests.md 16 KB

Story 10.10: 编写订单附件管理测试

Status: review

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)
    • 分析订单详情对话框中的附件管理区域
    • 确认添加附件对话框的打开方式(资源上传按钮)
    • 确认人员选择器的交互模式(资源上传对话框中显示残疾人列表)
    • 确认文件上传输入框的选择器(通过 fileChooser 事件)
  • 验证 Page Object 附件方法 (AC: Given)
    • 验证 openAddAttachmentDialog() 方法可用(已更新为使用"资源上传"按钮)
    • 验证 uploadAttachment(personIdentifier, fileName, mimeType, fileType) 方法可用(已更新为使用 fileChooser 事件)
    • 验证 getAttachmentListFromDetail() 方法可用(已存在)
    • 补充必要的辅助方法(添加 closeUploadDialog() 方法)
  • 创建附件管理测试文件 (AC: When)
    • 创建 web/tests/e2e/specs/admin/order-attachment.spec.ts
    • 导入必要的测试依赖和 Page Object
    • 配置测试 Fixtures(adminLoginPage, orderManagementPage)
  • 编写添加附件测试 (AC: Then #1)
    • 测试打开添加附件对话框
    • 测试选择订单人员(通过资源上传对话框中的残疾人行)
    • 测试上传图片文件(JPG 格式)
    • 测试验证附件添加成功(关闭对话框验证)
    • 测试验证附件出现在订单详情中(需验证上传功能是否实际工作)
  • 编写文件格式验证测试 (AC: Then #2)
    • 测试上传 JPG 格式文件
    • 测试上传 PNG 格式文件
    • 测试上传 WEBP 格式文件(如支持)UI 仅支持特定文件类型
    • 测试尝试上传不支持的格式(如 .txt)
    • 测试验证错误提示显示正确
  • 确保所有测试通过 (AC: And)
    • 运行测试并修复问题(阻塞:模块依赖问题 @d8d/shared-types)
    • 验证测试稳定性(连续运行 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):

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):

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: 打开订单详情
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_STATUSWORK_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 需求来源:

Page Object 现有实现:

前序 Story 学习:

项目上下文:

Epic 9 并行执行决策:

Dev Agent Record

Agent Model Used

claude-opus-4-5-20251101

Debug Log References

关键发现:

  1. UI 结构不同于预期:

    • 添加附件按钮名称为"资源上传"(而非"添加附件"或"上传附件")
    • 资源上传对话框包含残疾人列表,每行有多个"上传文件"按钮
    • 文件类型列顺序:税务文件、薪资单、工作成果、合同签署、残疾证明、其他
    • 需要通过 fileChooser 事件监听文件选择器
  2. 人员姓名 vs ID:

    • getPersonListFromDetail() 返回的 name 字段实际是人员 ID
    • 在资源上传对话框中使用人员 ID 匹配行
  3. 文件上传机制:

    • 点击"上传文件"按钮后,使用 waitForEvent('filechooser') 监听文件选择器
    • 使用 fileChooser.setFiles() 方法设置文件

Completion Notes List

Story 创建完成 - 2026-01-13

开发阶段完成 - 2026-01-13

  1. UI 结构探索:

    • ✅ 确认添加附件按钮为"资源上传"(在订单详情对话框中)
    • ✅ 确认资源上传对话框结构(残疾人列表 + 文件类型列)
    • ✅ 确认文件上传使用 fileChooser 事件
  2. Page Object 更新:

    • ✅ 更新 openAddAttachmentDialog() 使用"资源上传"按钮
    • ✅ 更新 uploadAttachment() 使用 fileChooser 事件和人员 ID 匹配
    • ✅ 添加 closeUploadDialog() 方法
  3. 测试文件创建:

    • ✅ 创建 web/tests/e2e/specs/admin/order-attachment.spec.ts
    • ✅ 实现添加附件测试(打开对话框、上传文件)
    • ✅ 实现文件格式验证测试(JPG、PNG、TXT)
  4. 已知问题:

    • ⚠️ 模块依赖问题:@d8d/shared-types 导入错误(预先存在的问题)
    • ⚠️ 需要验证上传功能是否实际工作(文件选择器可能不触发)

测试完成 - 2026-01-13

  1. 测试全部通过 (5/5):

    • ✅ 测试打开添加附件对话框
    • ✅ 测试上传 JPG 格式文件
    • ✅ 测试上传 PNG 格式文件
    • ✅ 测试尝试上传不支持的格式
    • ✅ 测试验证附件出现在订单详情中
  2. 最终修复:

    • ✅ 使用 uploadFileToField 工具函数代替 fileChooser 事件
    • ✅ 修正 DOM 选择器使用 data-testid
    • ✅ 修复测试文件路径使用实际 fixtures 文件
    • ✅ 添加 @d8d/shared-types 依赖到 web/package.json

File List

已创建/修改的文件:

  • web/tests/e2e/specs/admin/order-attachment.spec.ts - 附件管理测试文件(新建,5/5 测试通过)
  • web/tests/e2e/pages/admin/order-management.page.ts - 订单管理 Page Object(更新附件方法)
  • web/package.json - 添加 @d8d/shared-types 依赖

相关参考文件:

  • _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 文件。

手动更新指令(如果文件存在):

# 在 sprint-status.yaml 中
stories:
  10-10:
    status: review  # 从 in-progress 改为 review