stepsCompleted: ['step-01-validate-prerequisites', 'step-02-design-epics', 'step-03-create-stories'] inputDocuments:
This document provides the complete epic and story breakdown for 188-179-template-6,从残疾人管理 E2E 测试实践中提取可复用的测试工具包。
Radix UI Select 组件测试支持 (FR1-FR6):
文件上传测试支持 (FR7-FR10):
表单交互测试支持 (FR11-FR15):
动态列表测试支持 (FR16-FR20):
对话框操作测试支持 (FR21-FR24):
测试工具包基础设施 (FR25-FR32):
文档和开发者支持 (FR33-FR40):
测试质量和稳定性保障 (FR41-FR45):
可扩展性和维护性 (FR46-FR50):
可靠性 (NFR1-NFR7):
性能 (NFR8-NFR14):
集成性 (NFR15-NFR24):
代码质量 (NFR25-NFR40):
any 类型兼容性 (NFR41-NFR46):
从 Architecture 文档提取的技术需求:
包结构需求:
packages/e2e-test-utils,与现有的 @d8d/shared-test-util(后端集成测试)分离src/ 分为 radix-select.ts, file-upload.ts, form-helper.ts, dialog.ts, dynamic-list.tstypes.ts, errors.ts, constants.tsindex.ts,支持 tree-shakingAPI 设计需求:
selectRadixOption)Async 后缀(如 selectRadixOptionAsync)Options 后缀(如 AsyncSelectOptions)类型系统需求:
types.ts 存放共享类型(BaseOptions),各模块文件存放特定类型BaseOptionsany 类型选择器策略需求:
data-testid错误处理需求:
E2ETestError + ErrorContext测试策略需求:
tests/test-app/ 使用 Vite + React@d8d/shared-ui-components 组件常量定义需求:
DEFAULT_TIMEOUTS.static = 2000, async = 5000, networkIdle = 10000SELECTOR_STRATEGIES 数组文档需求:
遵循项目标准:
docs/standards/testing-standards.md 中的测试规范docs/standards/web-ui-testing-standards.md 中的 Web UI 测试规范docs/standards/e2e-radix-testing.md 中的 Radix UI E2E 测试标准(核心标准文档)从 E2E Radix UI 测试标准提取的详细需求:
核心工具函数签名(必须按此实现):
// Radix UI Select 工具
selectRadixOption(page: Page, label: string, value: string): Promise<void>
selectRadixOptionAsync(page: Page, label: string, value: string, options?: AsyncSelectOptions): Promise<void>
// 文件上传工具
uploadFileToField(page: Page, selector: string, fileName: string): Promise<void>
// 表单辅助工具
fillMultiStepForm(page: Page, steps: FormStep[]): Promise<void>
scrollToSection(page: Page, sectionName: string): Promise<void>
// 动态列表工具
addDynamicListItem(page: Page, itemType: string, data: Record<string, any>): Promise<void>
deleteDynamicListItem(page: Page, itemType: string, index: number): Promise<void>
// 对话框工具
handleDialog(page: Page, action: 'confirm' | 'cancel' | 'close'): Promise<void>
waitForDialogClosed(page: Page): Promise<void>
cancelDialog(page: Page): Promise<void>
DOM 结构理解需求:
data-radix-select-trigger 触发器,role="listbox" 选项列表,role="option" 单个选项role="dialog" 对话框容器data-value 用于选项值,data-state 用于状态跟踪选择器策略实现细节:
// 触发器选择器优先级
const TRIGGER_SELECTORS = [
`[data-testid="${label}-trigger"]`, // 最高优先级
`text="${label}"`, // 文本匹配
`[role="combobox"]` // 兜底
];
// 选项选择器优先级
const OPTION_SELECTORS = [
`[role="option"][data-value="${value}"]`, // data-testid + data-value
`[role="option"]:has-text("${value}")` // 文本匹配兜底
];
性能标准(来自测试标准文档): | 操作 | 目标时间 | 最大可接受时间 | |------|---------|---------------| | 静态 Select 选择 | < 1s | 2s | | 异步 Select 选择 | < 3s | 5s | | 文件上传 | < 2s | 5s | | 表单提交 | < 2s | 5s |
Fixtures 目录结构标准:
web/tests/
└── fixtures/
├── images/ # 测试图片(身份证照片、残疾证照片等)
└── documents/ # 测试文档(PDF 等)
错误消息格式标准:
Error: Radix Select 选项 "xxx" 未找到
标签: 残疾类型
期望值: xxx
可用选项: 视力残疾, 听力残疾, 肢体残疾, 智力残疾
Error: Radix Select 等待超时
标签: 省份
期望值: 广东省
超时时间: 5000ms
可能原因: 网络请求过慢或选项未加载
测试稳定性最佳实践:
waitForTimeoutdata-testid 选择器,谨慎使用文本选择器,避免使用不稳定的 CSS 类完整示例需求:
| FR 范围 | Epic | 描述 |
|---|---|---|
| FR1-FR6 | Epic 1 | Radix UI Select 测试支持(静态和异步) |
| FR25-FR32 | Epic 1 | 包基础设施(package.json、类型定义、配置) |
| FR7-FR10 | Epic 3 | 文件上传测试支持(文件上传工具与验证) |
| FR11-FR15 | Epic 4 | 表单交互测试支持(表单工具与验证) |
| FR16-FR24 | Epic 5 | 列表和对话框测试支持(列表和对话框工具与验证) |
| FR41-FR45 | Epic 2, 3, 4, 5, 6 | 质量与稳定性(各工具验证、全面验证、稳定性测试) |
| FR33-FR40 | Epic 7 | 文档与开发者体验(README、示例、迁移指南) |
| FR46-FR50 | Epic 3, 4, 5 | 可扩展性(各工具的配置和扩展支持) |
Epic 规划变更说明(2026-01-10):
"先验证再扩展"策略: 基于 Epic 2 的成功经验,每个工具都遵循"开发 → E2E 验证 → 稳定性验证"的模式:
目标: 测试开发者可以安装 @d8d/e2e-test-utils 包,立即使用 Select 工具测试 Radix UI Select 组件(最常用、最关键的测试场景)。
状态: ✅ Done
交付物:
selectRadixOption() 和 selectRadixOptionAsync() 函数FRs covered: FR1-FR6, FR25-FR32
目标: 在 web/tests/e2e/ 的残疾人管理测试中使用 Select 工具,验证工具在真实场景中的可用性和稳定性。
状态: ✅ Done
交付物:
FRs covered: FR41-FR45 (部分)
关键经验:
目标: 遵循 Epic 2 成功模式,开发文件上传工具并在真实 E2E 测试中验证,解决当前测试超时阻塞问题。
状态: 🆕 Backlog
模式: 工具开发 → 真实 E2E 测试验证 → 问题修复 → 稳定性验证
交付物:
uploadFileToField() 文件上传工具FRs covered: FR7-FR10, FR46-FR50 (部分), FR41-FR45 (部分)
目标: 开发表单辅助工具并在真实 E2E 测试中验证。
状态: Backlog (等待 Epic 3 完成)
模式: 工具开发 → 真实 E2E 测试验证 → 稳定性验证
交付物:
fillMultiStepForm(), scrollToSection() - 表单辅助工具FRs covered: FR11-FR15, FR46-FR50 (部分), FR41-FR45 (部分)
目标: 开发动态列表和对话框工具并在真实 E2E 测试中验证。
状态: Backlog (等待 Epic 4 完成)
模式: 工具开发 → 真实 E2E 测试验证 → 稳定性验证
交付物:
addDynamicListItem(), deleteDynamicListItem() - 动态列表工具handleDialog(), waitForDialogClosed() - 对话框工具FRs covered: FR16-FR24, FR46-FR50 (部分), FR41-FR45 (部分)
目标: 工具包在真实的残疾人管理 E2E 测试中完整验证,证明所有工具函数可用且稳定。
状态: Backlog (等待 Epic 5 完成)
交付物:
FRs covered: FR38, FR41-FR45
目标: 测试开发者可以在 30 分钟内上手使用工具包,有完整的文档、示例和迁移指南。
状态: Backlog (等待 Epic 6 完成)
交付物:
FRs covered: FR33-FR40
目标: 测试开发者可以安装 @d8d/e2e-test-utils 包,立即使用 Select 工具测试 Radix UI Select 组件(最常用、最关键的测试场景)。
作为测试开发者,
我想要可以安装 @d8d/e2e-test-utils 包,
以便在项目中使用测试工具函数。
验收标准:
Given 项目是 Monorepo 结构
When 创建 packages/e2e-test-utils/ 目录
Then 目录结构包含 src/, tests/, package.json, tsconfig.json, vitest.config.ts
And package.json 包含正确的包名、版本、peer dependencies(@playwright/test)
And tsconfig.json 启用严格模式,目标 ES2020+
And 可以通过 pnpm add -D @d8d/e2e-test-utils@workspace:* 安装
作为测试开发者, 我想要工具包有完整的类型支持和统一的错误处理, 以便获得类型安全和清晰的错误提示。
验收标准:
Given 包结构已创建
When 实现 src/types.ts, src/errors.ts, src/constants.ts
Then types.ts 导出 BaseOptions, AsyncSelectOptions, FileUploadOptions 等
And errors.ts 导出 E2ETestError 类和 ErrorContext 接口
And constants.ts 定义 DEFAULT_TIMEOUTS(static: 2000, async: 5000)
And 所有类型使用 TypeScript 严格模式,无 any 类型
作为测试开发者,
我想要使用 selectRadixOption() 函数选择静态枚举型下拉框,
以便无需理解 Radix UI DOM 结构就能编写测试。
验收标准:
Given 类型定义已创建
When 实现 src/radix-select.ts 中的 selectRadixOption() 函数
Then 函数签名:selectRadixOption(page: Page, label: string, value: string): Promise<void>
And 选择器策略:data-testid → aria-label + role → text content
And 自动处理点击触发器、等待选项列表、点击选项
And 错误时抛出 E2ETestError,包含标签、期望值、可用选项
And 操作在 2 秒内完成(NFR8)
作为测试开发者,
我想要使用 selectRadixOptionAsync() 函数选择异步加载的下拉框,
以便测试省份、城市等动态加载的选项。
验收标准:
Given 静态 Select 函数已实现
When 实现 selectRadixOptionAsync(page, label, value, options?) 函数
Then 支持 AsyncSelectOptions 配置(timeout, waitForOption)
And 使用 waitForLoadState('networkidle') 等待异步加载
And 默认超时 5 秒,可配置
And 超时时提供清晰错误消息(标签、期望值、超时时间、可能原因)
作为测试开发者, 我想要可以导入工具函数并查看快速入门文档, 以便快速开始使用工具包。
验收标准:
Given Select 工具函数已实现
When 创建 src/index.ts 和 README.md
Then index.ts 导出所有公共函数和类型(tree-shakeable)
And README 包含:安装说明、快速入门、Select 使用示例
And 所有导出函数有完整的 JSDoc 注释
And 可以 import { selectRadixOption } from '@d8d/e2e-test-utils'
作为测试开发者, 我想要 Select 工具函数有充分的单元测试, 以便确保函数的正确性和稳定性。
验收标准:
Given Select 工具函数已实现
When 创建 tests/unit/radix-select.test.ts
Then 测试覆盖率 ≥ 80%(NFR29)
And 测试用例包括:成功选择、选项不存在、超时、错误处理
And 使用 Vitest 运行测试
And 所有测试通过
目标: 在 web/tests/e2e/ 的现有残疾人管理测试中使用 Select 工具,验证工具在真实场景中的可用性和稳定性,收集实际使用反馈,为后续工具设计提供指导。
背景: Epic 1 已完成 Select 工具的开发和单元测试,但尚未在真实 E2E 测试场景中验证。通过在现有测试中使用这些工具,我们可以:
范围:
web/tests/e2e/ 测试基础设施依赖:
验收标准:
作为测试开发者,
我想要在 web 目录安装 @d8d/e2e-test-utils 包,
以便在 E2E 测试中使用 Select 工具。
验收标准:
Given Epic 1 已完成,@d8d/e2e-test-utils 包已构建
When 在 web/package.json 中添加 workspace 依赖
Then 可以在 web/tests/e2e/ 中导入 Select 工具
And TypeScript 类型检查通过
And 运行时无依赖错误
实现要点:
pnpm add -D @d8d/e2e-test-utils@workspace:* 安装web/tests/e2e/ 中可以导入:import { selectRadixOption } from '@d8d/e2e-test-utils'作为测试开发者,
我想要使用 selectRadixOption() 替换 Page Object 中的 Select 操作,
以便验证工具在静态 Select 场景中的可用性。
验收标准:
Given @d8d/e2e-test-utils 已安装
When 修改 web/tests/e2e/pages/admin/disability-person.page.ts
Then fillBasicForm() 中的残疾类型选择使用 selectRadixOption()
And fillBasicForm() 中的残疾等级选择使用 selectRadixOption()
And 移除原有的 selectRadixOption() 方法
And 测试通过,功能正常
验证场景:
作为测试开发者,
我想要使用 selectRadixOptionAsync() 处理异步加载的 Select,
以便验证工具在异步 Select 场景中的可用性。
验收标准:
Given @d8d/e2e-test-utils 已安装
When 修改 web/tests/e2e/pages/admin/disability-person.page.ts
Then fillBasicForm() 中的省份选择使用 selectRadixOptionAsync()
And fillBasicForm() 中的城市选择使用 selectRadixOptionAsync()
And 移除 waitForTimeout(500) 等待城市加载的 hack
And 测试通过,功能正常
验证场景:
配置要点:
waitForOption: true 等待选项加载作为测试开发者, 我想要运行使用新工具的测试并收集反馈, 以便发现潜在问题并改进工具。
验收标准:
web/tests/e2e/specs/admin/disability-person-complete.spec.ts关注点:
作为测试开发者, 我想要修复 Story 2.4 中发现的问题, 以便工具可以正常使用。
验收标准:
优先级:
作为测试开发者, 我想要验证测试的稳定性, 以便确保工具可以可靠地使用。
验收标准:
Given 所有问题已修复 When 连续运行测试 10 次 Then 所有测试 100% 通过 And 无 flaky 失败 And 平均执行时间 < 5 分钟
测试场景:
pnpm test:e2e:chromium disability-person-complete.spec.ts 运行 10 次成功标准:
Epic 2 状态: ✅ Done
回顾文档: _bmad-output/implementation-artifacts/epic-2-retrospective.md
目标: 遵循 Epic 2 的成功模式,开发文件上传工具并在 web/tests/e2e/ 的真实测试中验证,解决当前测试超时阻塞问题。
背景:
范围:
uploadFileToField()web/tests/e2e/ 中验证工具模式: 工具开发 → 真实 E2E 测试验证 → 问题修复 → 稳定性验证
依赖:
验收标准:
作为测试开发者,
我想要使用 uploadFileToField() 函数上传文件,
以便测试照片上传、文档上传等功能。
验收标准:
Given Epic 1 的类型定义已存在
When 实现 src/file-upload.ts 中的 uploadFileToField(page, selector, fileName) 函数
Then 函数从 fixtures 目录加载测试文件
And 使用 Playwright 的 setInputFiles() API
And 支持相对路径(相对于 fixtures 目录)
And 错误时提供清晰消息(文件路径、选择器、失败原因)
And 操作在 5 秒内完成(NFR9)
And 配置对象继承 BaseOptions
函数签名:
export async function uploadFileToField(
page: Page,
selector: string,
fileName: string,
options?: FileUploadOptions
): Promise<void>
export interface FileUploadOptions extends BaseOptions {
/** fixtures 目录路径,默认为 'tests/fixtures' */
fixturesDir?: string;
/** 是否等待上传完成,默认为 true */
waitForUpload?: boolean;
}
参考架构决策:
docs/standards/e2e-radix-testing.md 中的文件上传规范DEFAULT_TIMEOUTS 常量定义超时E2ETestError 和 ErrorContext作为测试开发者, 我想要文件上传工具有充分的单元测试, 以便确保函数的正确性和稳定性。
验收标准:
Given 文件上传工具函数已实现
When 创建 tests/unit/file-upload.test.ts
Then 测试覆盖率 ≥ 80%(NFR29)
And 测试用例包括:成功上传、文件不存在、选择器无效、超时
And 使用 Vitest 运行测试
And 所有测试通过
⚠️ Epic 2 关键经验应用:
作为测试开发者,
我想要在 web/tests/e2e/ 的残疾人管理测试中使用文件上传工具,
以便验证工具在真实场景中的可用性。
验收标准:
Given 文件上传工具和单元测试已完成
When 在 web/tests/e2e/specs/admin/disability-person-complete.spec.ts 中使用工具
Then 至少迁移 3 处文件上传操作(身份证照片、残疾证照片)
And 移除原有的自定义文件上传方法
And 测试在真实浏览器中通过
And 无测试超时问题
验证场景:
参考 Epic 2.2-2.3 的迁移模式:
作为测试开发者, 我想要运行使用文件上传工具的测试并收集反馈, 以便发现潜在问题并改进工具。
验收标准:
Given 文件上传工具已在真实测试中使用 When 运行完整的 E2E 测试套件 Then 记录所有问题(失败的测试、错误消息、使用体验) Then 分类问题:工具 bug vs 使用错误 vs 改进建议 And 所有标记为"工具 bug"的问题已修复 And 测试连续运行 5 次通过 And 无 flaky 失败
优先级:
作为测试开发者, 我想要验证文件上传工具的稳定性, 以便确保工具可以可靠地使用。
验收标准:
Given 所有问题已修复 When 连续运行文件上传相关测试 10 次 Then 所有测试 100% 通过 And 无超时失败 And 平均执行时间 ≤ 5 秒/文件
测试场景:
pnpm test:e2e:chromium disability-person-complete.spec.ts 运行 10 次成功标准:
Epic 3 回顾:
目标: 开发表单辅助工具并在 web/tests/e2e/ 的真实测试中验证。
说明: 遵循 Epic 2 和 Epic 3 的成功模式。
依赖: Epic 3 完成
模式: 工具开发 → 真实 E2E 测试验证 → 稳定性验证
作为测试开发者,
我想要使用 fillMultiStepForm() 和 scrollToSection() 函数,
以便测试多步骤表单和滚动操作。
验收标准:
Given 类型定义已存在
When 实现 src/form-helper.ts 中的表单辅助函数
Then fillMultiStepForm(page, steps) 支持分步填写表单
And scrollToSection(page, sectionName) 滚动到特定区域
And 支持常见字段类型(文本、选择器、日期等)
And 自动处理表单验证错误场景
作为测试开发者, 我想要表单工具有充分的单元测试, 以便确保函数的正确性和稳定性。
验收标准:
Given 表单辅助工具函数已实现
When 创建 tests/unit/form-helper.test.ts
Then 测试覆盖率 ≥ 80%(NFR29)
And 测试用例包括:成功填写、滚动操作、验证错误等
And 使用 Vitest 运行测试
And 所有测试通过
作为测试开发者, 我想要在残疾人管理测试中使用表单辅助工具, 以便验证工具在真实场景中的可用性。
验收标准:
Given 表单工具和单元测试已完成 When 在残疾人管理测试中使用工具 Then 验证多步骤表单填写 And 验证滚动操作 And 测试在真实浏览器中通过
作为测试开发者, 我想要验证表单工具的稳定性, 以便确保工具可以可靠地使用。
验收标准:
Given 所有问题已修复 When 连续运行表单相关测试 10 次 Then 所有测试 100% 通过
目标: 开发动态列表和对话框工具并在 web/tests/e2e/ 的真实测试中验证。
说明: 遵循 Epic 2-4 的成功模式。
依赖: Epic 4 完成
模式: 工具开发 → 真实 E2E 测试验证 → 稳定性验证
作为测试开发者,
我想要使用 addDynamicListItem()、deleteDynamicListItem() 和对话框工具,
以便测试银行卡管理、备注管理等动态列表功能。
验收标准:
Given 类型定义已存在
When 实现 src/dynamic-list.ts 和 src/dialog.ts 中的函数
Then addDynamicListItem(page, itemType, data) 添加新列表项
And deleteDynamicListItem(page, itemType, index) 删除指定索引的项
And handleDialog(page, action) 支持 confirm/cancel/close 操作
And waitForDialogClosed(page) 等待对话框完全关闭
And 支持不同类型列表项(银行卡、备注等)
作为测试开发者, 我想要列表和对话框工具有充分的单元测试, 以便确保函数的正确性和稳定性。
验收标准:
Given 列表和对话框工具函数已实现
When 创建 tests/unit/dynamic-list.test.ts 和 dialog.test.ts
Then 测试覆盖率 ≥ 80%(NFR29)
And 测试用例包括:添加、删除、打开/关闭对话框等
And 使用 Vitest 运行测试
And 所有测试通过
作为测试开发者, 我想要在残疾人管理测试中使用列表和对话框工具, 以便验证工具在真实场景中的可用性。
验收标准:
Given 列表和对话框工具及单元测试已完成 When 在残疾人管理测试中使用工具 Then 验证银行卡管理(添加、删除) And 验证备注管理 And 测试在真实浏览器中通过
作为测试开发者, 我想要验证列表和对话框工具的稳定性, 以便确保工具可以可靠地使用。
验收标准:
Given 所有问题已修复 When 连续运行列表和对话框相关测试 10 次 Then 所有测试 100% 通过
目标: 工具包在真实的残疾人管理 E2E 测试中完整验证,证明所有工具函数可用且稳定。
依赖: Epic 5 完成
作为测试开发者, 我想要在残疾人管理完整流程测试中使用所有工具, 以便演示所有工具函数的综合使用。
验收标准:
Given Epic 1-5 的所有工具已实现并验证
When 确保 disability-person-complete.spec.ts 使用所有工具
Then 基本信息:使用 selectRadixOption 和 selectRadixOptionAsync
And 照片上传:使用 uploadFileToField (来自 Epic 3)
And 表单操作:使用 fillMultiStepForm, scrollToSection (来自 Epic 4)
And 银行卡管理:使用 handleDialog, addDynamicListItem, deleteDynamicListItem (来自 Epic 5)
And 备注添加:使用 addDynamicListItem (来自 Epic 5)
And 所有测试通过
作为测试开发者, 我想要有稳定性测试验证工具包的可靠性, 以便确保工具函数连续运行 20 次 100% 通过。
验收标准:
Given 完整流程测试已编写 When 创建稳定性测试或运行现有测试 20 次 Then 测试连续运行 20 次完整流程 And 验证 100% 通过率(NFR1) And 测试执行时间符合性能标准(NFR8-NFR11) And 无 flaky 失败 And 使用 Playwright 运行稳定性测试
目标: 测试开发者可以在 30 分钟内上手使用工具包,有完整的文档、示例和迁移指南。
依赖: Epic 6 完成
作为测试开发者, 我想要有完整的 README 和 API 文档, 以便快速上手使用工具包。
验收标准:
Given Epic 1-6 的所有功能已实现
When 完善 README.md 和 API 文档
Then README 包含:项目简介、安装说明、快速入门、API 文档
And 每个工具函数都有完整的使用示例
And 说明静态 Select 和异步 Select 的区别和使用场景
And 包含迁移指南(从现有测试代码到工具函数)
And 包含常见问题和解决方案
And 残疾人管理测试作为完整示例
And 所有函数有完整的 JSDoc 注释
作为测试开发者, 我想要有 VS Code 代码片段, 以便加速测试开发。
验收标准:
Given README 和 API 文档已完成 When 创建 VS Code snippets 配置 Then 提供常用代码片段(selectRadixOption, uploadFileToField 等) And 片段有清晰的触发词和描述 And 文档说明如何安装和使用 snippets And 开发者可以在 30 分钟内使用工具包编写第一个测试