stepsCompleted: ['step-01-init', 'step-02-context', 'step-03-starter', 'step-04-decisions', 'step-05-patterns', 'step-06-structure', 'step-07-validation', 'step-08-complete'] inputDocuments:
This document builds collaboratively through step-by-step discovery. Sections are appended as we work through each architectural decision together.
Functional Requirements (50 total):
| 类别 | FR范围 | 架构含义 |
|---|---|---|
| Radix UI Select 测试 | FR1-FR6 | 需封装复杂的 Radix UI DOM 操作,区分静态/异步场景 |
| 文件上传测试 | FR7-FR10 | 需处理 fixtures 文件管理和多文件上传场景 |
| 表单交互测试 | FR11-FR15 | 需支持多步骤表单流程和滚动操作 |
| 动态列表测试 | FR16-FR20 | 需处理动态 DOM 更新和异步状态管理 |
| 对话框操作 | FR21-FR24 | 需统一的对话框打开/关闭/等待模式 |
| 工具包基础设施 | FR25-FR32 | 需 npm workspace 集成、TypeScript 类型安全 |
| 文档和开发支持 | FR33-FR40 | 需完整示例、迁移指南、快速入门 |
| 质量保障 | FR41-FR45 | 需稳定性保障(20次连续运行100%通过) |
| 可扩展性 | FR46-FR50 | 需配置化设计和版本升级兼容 |
Non-Functional Requirements (46 total):
| 类别 | NFR范围 | 架构驱动因素 |
|---|---|---|
| 可靠性 | NFR1-NFR7 | 测试稳定性是核心,错误处理必须清晰 |
| 性能 | NFR8-NFR14 | 静态Select 2秒、异步Select 5秒的性能约束 |
| 集成性 | NFR15-NFR24 | Playwright 兼容、Monorepo workspace 集成 |
| 代码质量 | NFR25-NFR40 | 无 any 类型、≥80% 覆盖率、JSDoc 完整 |
| 兼容性 | NFR41-NFR46 | 跨浏览器、CI/CD 环境、版本升级兼容 |
Scale & Complexity:
技术栈约束:
@playwright/test(由测试项目提供)架构约束:
外部依赖:
@d8d/shared-test-util(后端集成测试,独立于本工具包)开发者工具 / 测试基础设施包
不适用:本项目是在现有 Monorepo 中创建的内部测试工具包(packages/e2e-test-utils),而非独立应用项目。
技术栈已由现有项目确定:
包结构需要遵循:
Critical Decisions (Block Implementation):
Important Decisions (Shape Architecture):
Deferred Decisions (Post-MVP):
决策: 按功能分组(选项B)
目录结构:
packages/e2e-test-utils/
├── src/
│ ├── index.ts # 主导出
│ ├── types.ts # 共享类型定义
│ ├── radix-select.ts # Radix UI Select 工具
│ ├── file-upload.ts # 文件上传工具
│ ├── form-helper.ts # 表单辅助函数
│ ├── dialog.ts # 对话框操作
│ └── dynamic-list.ts # 动态列表管理
├── tests/
│ ├── unit/ # 单元测试
│ ├── integration/ # 集成测试
│ └── stability/ # 稳定性测试
├── package.json
└── README.md
理由: 清晰的职责分离,便于测试和维护
决策: 3个必需参数 + 可选配置对象
核心原则:
示例:
// 静态 Select - 简洁场景
export async function selectRadixOption(
page: Page,
label: string,
value: string
): Promise<void>
// 异步 Select - 需要配置
export async function selectRadixOptionAsync(
page: Page,
label: string,
value: string,
options?: AsyncSelectOptions
): Promise<void>
决策: 分层类型(选项C)
类型分层:
types.ts (共享/基础类型)
├── BaseOptions (超时等通用配置)
└── 导出 Playwright 类型
各模块文件 (特定类型)
├── radix-select.ts → AsyncSelectOptions
├── file-upload.ts → FileUploadOptions
└── ...
优势:
决策: 混合策略(testid → ARIA → 文本)
优先级:
data-testid - 最高优先级,需开发团队配合aria-label + role - 无障碍标准配套措施:
data-testid决策: 结构化错误类 + 友好消息
错误结构:
export class E2ETestError extends Error {
constructor(
public readonly context: ErrorContext,
message?: string
) { ... }
}
export interface ErrorContext {
operation: string; // 操作类型
target: string; // 目标
expected?: string; // 期望值
actual?: string; // 实际值
available?: string[]; // 可用选项
suggestion?: string; // 修复建议
}
示例输出:
❌ selectOption failed
Target: 残疾类型
Expected: 视力残疾
Available: 听力残疾, 言语残疾, 肢体残疾
💡 Suggestion: Check if the option value matches exactly
决策: 三层测试策略
| 测试类型 | 范围 | 工具 | 目标 |
|---|---|---|---|
| 单元测试 | 单个函数逻辑 | Vitest | ≥80% 覆盖率 |
| 集成测试 | 与 DOM/浏览器交互 | Playwright | 验证实际操作 |
| 稳定性测试 | 20次连续运行 | Playwright | 100% 通过率 |
集成测试基础设施:
tests/test-app/@d8d/shared-ui-components 的实际组件配置文件:
// vitest.config.ts - 单元测试
{
coverage: { statements: 80, branches: 80, functions: 80, lines: 80 },
testTimeout: 10000,
}
// playwright.config.ts - 集成/稳定性测试
{
testDir: './tests/integration',
webServer: {
command: 'pnpm --filter @d8d/e2e-test-utils-test-app dev',
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI,
},
}
Implementation Sequence:
Cross-Component Dependencies:
types.tsCritical Conflict Points Identified: 4 个类别,每个类别都可能导致 AI Agent 实现不一致
函数命名约定:
selectRadixOption、uploadFileToField、fillMultiStepFormAsync 后缀(如 selectRadixOptionAsync)类型命名约定:
Options 后缀AsyncSelectOptions、FileUploadOptionsBaseOptions 作为所有配置的基类测试文件命名:
.test.ts 后缀select-scenarios.test.tsstability.test.ts测试目录结构:
tests/
├── fixtures/ # 集中管理测试资源
│ ├── images/ # 测试图片
│ └── data/ # 测试数据
├── unit/ # 单元测试(无 Playwright)
├── integration/ # 集成测试(需要 Playwright)
└── stability/ # 稳定性测试
源文件组织:
types.ts 存放共享类型index.ts 作为主导出(tree-shakeable)JSDoc 格式:
/**
* 函数描述(简洁说明)
*
* @param paramName - 参数描述
* @param paramTwo - 参数描述
* @throws {E2ETestError} 错误条件
* @example
* ```ts
* await functionName(page, 'arg', 'value');
* ```
*/
配置对象格式:
// 所有可选配置都继承 BaseOptions
export interface XxxOptions extends BaseOptions {
/** 特定选项描述 */
specificOption?: boolean;
}
export interface BaseOptions {
/** 超时时间(毫秒)*/
timeout?: number;
}
错误消息格式:
选择器查找流程:
const SELECTOR_STRATEGIES = [
findByTestId, // 1. data-testid
findByAria, // 2. aria-label + role
findByText, // 3. text content + role
] as const;
// 按优先级尝试,找到即返回
等待策略常量:
export const DEFAULT_TIMEOUTS = {
static: 2000, // 静态选项
async: 5000, // 异步选项
networkIdle: 10000, // 网络空闲
} as const;
错误处理流程:
// 1. 捕获底层错误
// 2. 转换为 E2ETestError
// 3. 包含完整 ErrorContext
export function throwError(context: ErrorContext): never {
throw new E2ETestError(context, formatErrorMessage(context));
}
所有 AI Agent 必须:
E2ETestError 和 ErrorContextDEFAULT_TIMEOUTS 获取强制执行方式:
正确示例:
// ✅ 正确:遵循命名和格式约定
/**
* 选择 Radix UI 下拉框的静态选项
*
* @param page - Playwright Page 对象
* @param label - 下拉框标签文本
* @param value - 要选择的选项值
* @throws {E2ETestError} 当下拉框或选项未找到时
*/
export async function selectRadixOption(
page: Page,
label: string,
value: string
): Promise<void> {
try {
// 实现逻辑
} catch (error) {
throwError({
operation: 'selectRadixOption',
target: label,
expected: value,
});
}
}
反模式(避免):
// ❌ 错误:缺少 JSDoc
export async function select(p: any, l: string, v: string) {}
// ❌ 错误:使用 any 类型
export async function selectOption(
page: Page,
label: any,
value: any
): Promise<void> {}
// ❌ 错误:自定义错误类型
throw new Error('Select failed');
// ❌ 错误:硬编码超时
await page.waitForTimeout(5000);
packages/e2e-test-utils/
├── src/
│ ├── index.ts # 主导出,tree-shakeable
│ ├── types.ts # 共享类型定义
│ ├── errors.ts # 错误类和错误处理
│ ├── constants.ts # 常量定义(超时、选择器策略等)
│ ├── radix-select.ts # Radix UI Select 工具
│ ├── file-upload.ts # 文件上传工具
│ ├── form-helper.ts # 表单辅助函数
│ ├── dialog.ts # 对话框操作
│ └── dynamic-list.ts # 动态列表管理
├── tests/
│ ├── fixtures/
│ │ ├── images/
│ │ │ ├── sample-id-card.jpg
│ │ │ └── sample-disability-card.jpg
│ │ └── data/
│ │ └── test-users.json
│ ├── test-app/ # 独立测试应用
│ │ ├── package.json
│ │ ├── vite.config.ts
│ │ ├── index.html
│ │ ├── main.tsx
│ │ └── pages/
│ │ ├── select.tsx # Select 测试页面
│ │ ├── dialog.tsx # Dialog 测试页面
│ │ ├── file-upload.tsx # 文件上传测试页面
│ │ ├── form.tsx # 表单测试页面
│ │ └── dynamic-list.tsx # 动态列表测试页面
│ ├── unit/ # Vitest 单元测试
│ │ ├── radix-select.test.ts
│ │ ├── file-upload.test.ts
│ │ ├── form-helper.test.ts
│ │ ├── dialog.test.ts
│ │ └── dynamic-list.test.ts
│ ├── integration/ # Playwright 集成测试
│ │ ├── select-scenarios.spec.ts
│ │ ├── upload-scenarios.spec.ts
│ │ └── form-scenarios.spec.ts
│ └── stability/ # Playwright 稳定性测试
│ └── repeat-run.spec.ts
├── package.json
├── tsconfig.json
├── vitest.config.ts # 单元测试配置
├── playwright.config.ts # 集成/稳定性测试配置
├── README.md
└── CHANGELOG.md
包边界:
Page 对象作为参数Promise<void> 或抛出 E2ETestError@playwright/test,无运行时依赖工具函数边界:
types.ts、errors.ts、constants.ts 提供FR 类别 → 文件映射:
| FR 类别 | 文件 | 说明 |
|---|---|---|
| FR1-FR6 (Radix UI Select) | radix-select.ts |
静态和异步 Select 工具 |
| FR7-FR10 (文件上传) | file-upload.ts |
文件上传工具 |
| FR11-FR15 (表单交互) | form-helper.ts |
多步骤表单、滚动 |
| FR16-FR20 (动态列表) | dynamic-list.ts |
添加/删除列表项 |
| FR21-FR24 (对话框) | dialog.ts |
对话框操作 |
| FR25-FR32 (工具包基础设施) | index.ts, package.json |
包导出和配置 |
| FR41-FR45 (质量保障) | tests/ |
所有测试文件 |
内部通信:
types.ts 共享类型定义errors.ts 统一错误处理constants.ts 共享配置常量外部集成:
Page 对象@d8d/e2e-test-utils@workspace:*数据流:
测试代码 → 工具函数 → Playwright API → DOM 操作
↓
E2ETestError (失败时)
配置文件:
package.json: 包定义、workspace 协议配置tsconfig.json: TypeScript 严格模式配置vitest.config.ts: Vitest 单元测试配置、覆盖率目标playwright.config.ts: Playwright 集成/稳定性测试配置、测试应用服务器源码组织:
types.ts、errors.ts、constants.tsindex.ts 作为主导出点,支持 tree-shaking测试组织:
tests/unit/: Vitest 单元测试,测试单个函数逻辑tests/integration/: Playwright 集成测试,验证与真实 DOM 的交互tests/stability/: Playwright 稳定性测试,20次连续运行tests/test-app/: 独立测试应用,提供 @d8d/shared-ui-components 组件测试环境tests/fixtures/: 测试资源集中管理Decision Compatibility:
所有核心技术决策相互兼容,无冲突:
Pattern Consistency:
实现模式完全支持架构决策:
Structure Alignment:
项目结构完全支持所有架构决策:
Epic/Feature Coverage:
所有 50 个功能需求都有架构支持:
| FR 类别 | 架构支持 | 验证 |
|---|---|---|
| FR1-FR6 (Radix UI Select) | radix-select.ts + test-app/pages/select.tsx |
✅ |
| FR7-FR10 (文件上传) | file-upload.ts + test-app/pages/file-upload.tsx |
✅ |
| FR11-FR15 (表单交互) | form-helper.ts + test-app/pages/form.tsx |
✅ |
| FR16-FR20 (动态列表) | dynamic-list.ts + test-app/pages/dynamic-list.tsx |
✅ |
| FR21-FR24 (对话框) | dialog.ts + test-app/pages/dialog.tsx |
✅ |
| FR25-FR32 (工具包基础设施) | index.ts + package.json |
✅ |
| FR33-FR40 (文档和开发支持) | README.md + JSDoc 完整性要求 | ✅ |
| FR41-FR45 (质量保障) | 三层测试策略 + 稳定性测试 | ✅ |
| FR46-FR50 (可扩展性) | 配置对象设计 + BaseOptions 扩展 | ✅ |
Functional Requirements Coverage:
所有功能需求类别完全覆盖:
Non-Functional Requirements Coverage:
所有 46 个非功能需求都已处理:
| NFR 类别 | 架构支持 | 验证 |
|---|---|---|
| NFR1-NFR7 (可靠性) | E2ETestError + ErrorContext + 友好错误消息 | ✅ |
| NFR8-NFR14 (性能) | DEFAULT_TIMEOUTS 常量 + 超时配置 | ✅ |
| NFR15-NFR24 (集成性) | Playwright peer dependency + workspace 协议 | ✅ |
| NFR25-NFR40 (代码质量) | 无 any 类型 + 80% 覆盖率 + JSDoc 完整 | ✅ |
| NFR41-NFR46 (兼容性) | 稳定选择器策略 + 跨浏览器测试 | ✅ |
Decision Completeness:
所有关键决策都已记录版本和理由:
Structure Completeness:
项目结构完整且具体:
Pattern Completeness:
所有潜在冲突点都已解决:
Critical Gaps: 无
Important Gaps: 无
Nice-to-Have Gaps:
集成测试基础设施问题(已解决):
问题描述: 原始架构将集成测试配置为使用 Vitest 运行 Playwright,这不符合 Playwright 的最佳实践。
解决方案:
tests/test-app/ 提供 @d8d/shared-ui-components 测试环境架构影响:
Testing Configuration 部分tests/test-app/playwright.config.ts 配置文件.spec.ts 用于 Playwright)✅ Requirements Analysis
✅ Architectural Decisions
✅ Implementation Patterns
✅ Project Structure
✅ Testing Strategy
Overall Status: READY FOR IMPLEMENTATION
Confidence Level: HIGH
Key Strengths:
Areas for Future Enhancement:
AI Agent Guidelines:
First Implementation Priority:
# 1. 创建包基础结构
mkdir -p packages/e2e-test-utils/src
mkdir -p packages/e2e-test-utils/tests/{unit,integration,stability,fixtures/{images,data}}
# 2. 创建测试应用
mkdir -p packages/e2e-test-utils/tests/test-app/pages
# 3. 初始化 package.json(包含正确的 peer dependencies)
# 4. 实现 types.ts 和 index.ts
# 5. 从简单到复杂实现各工具函数
# 6. 编写单元测试(Vitest)
# 7. 编写集成测试(Playwright + test-app)
# 8. 在残疾人管理功能中验证
Success Criteria:
Architecture Decision Workflow: COMPLETED ✅ Total Steps Completed: 8 Date Completed: 2026-01-08 Document Location: _bmad-output/planning-artifacts/architecture.md
📋 Complete Architecture Document
🏗️ Implementation Ready Foundation
📚 AI Agent Implementation Guide
For AI Agents: 本架构文档是实现 E2E 测试工具包的完整指南。严格遵循所有决策、模式和结构。
First Implementation Priority:
# 1. 创建包基础结构
mkdir -p packages/e2e-test-utils/src
mkdir -p packages/e2e-test-utils/tests/{unit,integration,stability,fixtures/{images,data}}
# 2. 创建测试应用
mkdir -p packages/e2e-test-utils/tests/test-app/pages
# 3. 初始化 package.json
{
"name": "@d8d/e2e-test-utils",
"version": "1.0.0",
"type": "module",
"peerDependencies": {
"@playwright/test": "^1.40.0"
},
"devDependencies": {
"vitest": "^3.2.4",
"vite": "^6.0.11",
"react": "^19.2.0",
"react-dom": "^19.2.0"
}
}
# 4. 实现 types.ts 和 index.ts
# 5. 从简单到复杂实现各工具函数
# 6. 编写单元测试(Vitest)
# 7. 编写集成测试(Playwright + test-app)
# 8. 在残疾人管理功能中验证
Development Sequence:
✅ Architecture Coherence
✅ Requirements Coverage
✅ Implementation Readiness
🎯 Clear Decision Framework 每个技术选择都是协作完成的,有清晰的理由,确保所有利益相关者理解架构方向。
🔧 Consistency Guarantee 实现模式和规则确保多个 AI Agent 将产生兼容、一致的代码。
📋 Complete Coverage 所有项目需求都有架构支持,从业务需求到技术实现的清晰映射。
🏗️ Solid Foundation 选择的测试策略和架构模式提供了生产就绪的基础。
Architecture Status: READY FOR IMPLEMENTATION ✅
Next Phase: 开始实现阶段,使用文档化的架构决策和模式。
Document Maintenance: 当实现过程中做出重大技术决策时,更新此架构文档。
🎉 架构工作流程完成!
你的 E2E 测试工具包架构已经全面完成,经过验证,可以开始实现了。
✅ 交付内容:
📍 文档位置:
_bmad-output/planning-artifacts/architecture.md
🚀 下一步:
你的架构将确保所有开发工作的一致、高质量实现。恭喜完成这些重要的架构决策!