|
@@ -0,0 +1,331 @@
|
|
|
|
|
+# Story 2.1: 在 web 目录安装 @d8d/e2e-test-utils
|
|
|
|
|
+
|
|
|
|
|
+Status: ready-for-dev
|
|
|
|
|
+
|
|
|
|
|
+<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
|
|
|
|
+
|
|
|
|
|
+## Story
|
|
|
|
|
+
|
|
|
|
|
+作为测试开发者,
|
|
|
|
|
+我想要在 web 目录安装 `@d8d/e2e-test-utils` 包,
|
|
|
|
|
+以便在 E2E 测试中使用 Select 工具。
|
|
|
|
|
+
|
|
|
|
|
+## Acceptance Criteria
|
|
|
|
|
+
|
|
|
|
|
+1. **Given** Epic 1 已完成,`@d8d/e2e-test-utils` 包已构建
|
|
|
|
|
+2. **When** 在 `web/package.json` 中添加 workspace 依赖
|
|
|
|
|
+3. **Then** 可以在 `web/tests/e2e/` 中导入 Select 工具
|
|
|
|
|
+4. **And** TypeScript 类型检查通过
|
|
|
|
|
+5. **And** 运行时无依赖错误
|
|
|
|
|
+
|
|
|
|
|
+## Tasks / Subtasks
|
|
|
|
|
+
|
|
|
|
|
+- [ ] 安装 @d8d/e2e-test-utils 包到 web 目录 (AC: #1, #2)
|
|
|
|
|
+ - [ ] 使用 `pnpm add -D @d8d/e2e-test-utils@workspace:*` 安装
|
|
|
|
|
+ - [ ] 验证 package.json 已正确添加依赖
|
|
|
|
|
+- [ ] 验证工具函数可以正确导入 (AC: #3)
|
|
|
|
|
+ - [ ] 在测试文件中添加 `import { selectRadixOption } from '@d8d/e2e-test-utils'`
|
|
|
|
|
+ - [ ] 确认 TypeScript 类型检查通过
|
|
|
|
|
+- [ ] 验证运行时无依赖错误 (AC: #4, #5)
|
|
|
|
|
+ - [ ] 运行 `pnpm typecheck` 确认类型检查通过
|
|
|
|
|
+ - [ ] 确认 @playwright/test 版本兼容性
|
|
|
|
|
+
|
|
|
|
|
+## Dev Notes
|
|
|
|
|
+
|
|
|
|
|
+### Epic Context
|
|
|
|
|
+
|
|
|
|
|
+**Epic 2 目标:** 在 `web/tests/e2e/` 的现有残疾人管理测试中使用 Select 工具,验证工具在真实场景中的可用性和稳定性。
|
|
|
|
|
+
|
|
|
|
|
+**Epic 2 范围:**
|
|
|
|
|
+- ✅ 使用现有 `web/tests/e2e/` 测试基础设施
|
|
|
|
|
+- ✅ 使用现有的残疾人管理测试场景
|
|
|
|
|
+- ❌ 不创建新的测试应用
|
|
|
|
|
+- ❌ 不添加新功能(仅验证现有功能)
|
|
|
|
|
+
|
|
|
|
|
+### 依赖关系
|
|
|
|
|
+
|
|
|
|
|
+**前置依赖:**
|
|
|
|
|
+- Epic 1: ✅ 已完成(`@d8d/e2e-test-utils` 包已构建)
|
|
|
|
|
+- Story 1.5: ✅ 主导出和基础文档已完成
|
|
|
|
|
+- Story 1.6: ✅ 单元测试已通过(覆盖率 93.65%)
|
|
|
|
|
+
|
|
|
|
|
+**后续故事:**
|
|
|
|
|
+- Story 2.2: 使用 selectRadixOption 重写残疾类型选择
|
|
|
|
|
+- Story 2.3: 使用 selectRadixOptionAsync 重写省份/城市选择
|
|
|
|
|
+
|
|
|
|
|
+### 实现要点
|
|
|
|
|
+
|
|
|
|
|
+1. **使用 pnpm workspace 协议安装:**
|
|
|
|
|
+ ```bash
|
|
|
|
|
+ cd web
|
|
|
|
|
+ pnpm add -D @d8d/e2e-test-utils@workspace:*
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+2. **验证导入正常:**
|
|
|
|
|
+ ```typescript
|
|
|
|
|
+ // web/tests/e2e/pages/admin/disability-person.page.ts
|
|
|
|
|
+ import { selectRadixOption, selectRadixOptionAsync } from '@d8d/e2e-test-utils';
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+3. **TypeScript 类型检查:**
|
|
|
|
|
+ - 确认 IDE 可以自动补全工具函数
|
|
|
|
|
+ - 运行 `pnpm typecheck` 确认无类型错误
|
|
|
|
|
+
|
|
|
|
|
+### Package 版本兼容性
|
|
|
|
|
+
|
|
|
|
|
+**@d8d/e2e-test-utils peer dependencies:**
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "peerDependencies": {
|
|
|
|
|
+ "@playwright/test": "^1.40.0"
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**web 当前 Playwright 版本:**
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "devDependencies": {
|
|
|
|
|
+ "@playwright/test": "1.55.0"
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+✅ **版本兼容确认:** 1.55.0 满足 ^1.40.0 要求
|
|
|
|
|
+
|
|
|
|
|
+### 测试位置
|
|
|
|
|
+
|
|
|
|
|
+**现有 E2E 测试文件:**
|
|
|
|
|
+```
|
|
|
|
|
+web/tests/e2e/
|
|
|
|
|
+├── specs/
|
|
|
|
|
+│ └── admin/
|
|
|
|
|
+│ └── disability-person-complete.spec.ts
|
|
|
|
|
+├── pages/
|
|
|
|
|
+│ └── admin/
|
|
|
|
|
+│ └── disability-person.page.ts
|
|
|
|
|
+└── playwright.config.ts
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**将在下一个故事(2.2)中修改的文件:**
|
|
|
|
|
+- `web/tests/e2e/pages/admin/disability-person.page.ts`
|
|
|
|
|
+
|
|
|
|
|
+## Dev Agent Record
|
|
|
|
|
+
|
|
|
|
|
+### Agent Model Used
|
|
|
|
|
+
|
|
|
|
|
+Claude Opus 4.5 (claude-opus-4-5-20251101)
|
|
|
|
|
+
|
|
|
|
|
+### Debug Log References
|
|
|
|
|
+
|
|
|
|
|
+### Completion Notes List
|
|
|
|
|
+
|
|
|
|
|
+### File List
|
|
|
|
|
+
|
|
|
|
|
+- `web/package.json` - 添加 devDependency
|
|
|
|
|
+- `web/tests/e2e/pages/admin/disability-person.page.ts` - 验证导入(不修改实现,仅测试导入)
|
|
|
|
|
+
|
|
|
|
|
+---
|
|
|
|
|
+
|
|
|
|
|
+## Developer Context
|
|
|
|
|
+
|
|
|
|
|
+> **重要提示:** 本部分包含开发者实现此故事所需的所有关键上下文和约束条件。
|
|
|
|
|
+
|
|
|
|
|
+### 技术需求
|
|
|
|
|
+
|
|
|
|
|
+#### 1. 安装命令
|
|
|
|
|
+
|
|
|
|
|
+在 web 目录执行以下命令安装工具包:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+cd web
|
|
|
|
|
+pnpm add -D @d8d/e2e-test-utils@workspace:*
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 2. 验证依赖安装正确
|
|
|
|
|
+
|
|
|
|
|
+安装后,`web/package.json` 的 `devDependencies` 应包含:
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "devDependencies": {
|
|
|
|
|
+ "@d8d/e2e-test-utils": "workspace:*",
|
|
|
|
|
+ "@playwright/test": "1.55.0"
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 3. 验证导入语句
|
|
|
|
|
+
|
|
|
|
|
+在 E2E 测试文件中,应该能够成功导入工具函数:
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// web/tests/e2e/pages/admin/disability-person.page.ts
|
|
|
|
|
+import { selectRadixOption, selectRadixOptionAsync } from '@d8d/e2e-test-utils';
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 4. TypeScript 类型检查
|
|
|
|
|
+
|
|
|
|
|
+运行类型检查确保无错误:
|
|
|
|
|
+
|
|
|
|
|
+```bash
|
|
|
|
|
+pnpm typecheck
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 架构合规性
|
|
|
|
|
+
|
|
|
|
|
+#### Monorepo Workspace 协议
|
|
|
|
|
+
|
|
|
|
|
+- 使用 `workspace:*` 协议引用内部包
|
|
|
|
|
+- 开发时自动链接到 `packages/e2e-test-utils`
|
|
|
|
|
+- 构建时使用构建后的产物
|
|
|
|
|
+
|
|
|
|
|
+#### Peer Dependency 约束
|
|
|
|
|
+
|
|
|
|
|
+`@d8d/e2e-test-utils` 声明 `@playwright/test` 为 peer dependency:
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "peerDependencies": {
|
|
|
|
|
+ "@playwright/test": "^1.40.0"
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**含义:**
|
|
|
|
|
+- 工具包不打包 Playwright,由使用方提供
|
|
|
|
|
+- web 项目已有 `@playwright/test@1.55.0`,满足要求
|
|
|
|
|
+- 无需额外安装 Playwright
|
|
|
|
|
+
|
|
|
|
|
+### 库和框架要求
|
|
|
|
|
+
|
|
|
|
|
+#### Playwright 版本兼容性
|
|
|
|
|
+
|
|
|
|
|
+| 包 | 版本 | 状态 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| @playwright/test (web) | 1.55.0 | ✅ 满足 ^1.40.0 |
|
|
|
|
|
+| @playwright/test (工具包要求) | ^1.40.0 | - |
|
|
|
|
|
+
|
|
|
|
|
+#### TypeScript 配置
|
|
|
|
|
+
|
|
|
|
|
+确保 `web/tsconfig.json` 支持导入工具包类型:
|
|
|
|
|
+
|
|
|
|
|
+```json
|
|
|
|
|
+{
|
|
|
|
|
+ "compilerOptions": {
|
|
|
|
|
+ "strict": true,
|
|
|
|
|
+ "moduleResolution": "bundler",
|
|
|
|
|
+ "esModuleInterop": true
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 文件结构要求
|
|
|
|
|
+
|
|
|
|
|
+#### 工具包导出结构
|
|
|
|
|
+
|
|
|
|
|
+`packages/e2e-test-utils/src/index.ts` 导出以下内容:
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// Select 工具
|
|
|
|
|
+export { selectRadixOption } from './radix-select.js';
|
|
|
|
|
+export { selectRadixOptionAsync } from './radix-select.js';
|
|
|
|
|
+
|
|
|
|
|
+// 类型
|
|
|
|
|
+export type { BaseOptions } from './types.js';
|
|
|
|
|
+export type { AsyncSelectOptions } from './radix-select.js';
|
|
|
|
|
+
|
|
|
|
|
+// 错误
|
|
|
|
|
+export { E2ETestError } from './errors.js';
|
|
|
|
|
+export type { ErrorContext } from './errors.js';
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 可导入的工具函数
|
|
|
|
|
+
|
|
|
|
|
+| 函数名 | 用途 | 参数 |
|
|
|
|
|
+|---|---|---|
|
|
|
|
|
+| `selectRadixOption` | 静态 Select 选择 | (page, label, value) |
|
|
|
|
|
+| `selectRadixOptionAsync` | 异步 Select 选择 | (page, label, value, options?) |
|
|
|
|
|
+
|
|
|
|
|
+### 测试要求
|
|
|
|
|
+
|
|
|
|
|
+#### 验证步骤
|
|
|
|
|
+
|
|
|
|
|
+1. **安装后立即验证:**
|
|
|
|
|
+ ```bash
|
|
|
|
|
+ cd web
|
|
|
|
|
+ pnpm list @d8d/e2e-test-utils
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+2. **TypeScript 类型检查:**
|
|
|
|
|
+ ```bash
|
|
|
|
|
+ pnpm typecheck
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+3. **导入测试(临时添加到测试文件):**
|
|
|
|
|
+ ```typescript
|
|
|
|
|
+ // 在文件顶部添加
|
|
|
|
|
+ import { selectRadixOption, selectRadixOptionAsync } from '@d8d/e2e-test-utils';
|
|
|
|
|
+ // 保存后确认 IDE 无错误提示
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+#### 不需要运行测试
|
|
|
|
|
+
|
|
|
|
|
+本故事仅安装和验证导入,不需要:
|
|
|
|
|
+- 修改现有测试代码
|
|
|
|
|
+- 运行 E2E 测试
|
|
|
|
|
+- 实现新的测试逻辑
|
|
|
|
|
+
|
|
|
|
|
+这些将在 Story 2.2 和 2.3 中完成。
|
|
|
|
|
+
|
|
|
|
|
+### 上一个故事的经验(Epic 1 Retrospective)
|
|
|
|
|
+
|
|
|
|
|
+#### 关键经验
|
|
|
|
|
+
|
|
|
|
|
+1. **代码审查发现的问题类型:**
|
|
|
|
|
+ - HIGH: DOM 类型问题、精确文本匹配
|
|
|
|
|
+ - MEDIUM: 错误消息不清晰
|
|
|
|
|
+ - LOW: 代码风格
|
|
|
|
|
+
|
|
|
|
|
+2. **TypeScript + Playwright 陷阱:**
|
|
|
|
|
+ - 避免使用 `page.evaluate()` 获取文本
|
|
|
|
|
+ - 使用 `:text-is()` 而非 `:has-text()` 进行精确匹配
|
|
|
|
|
+ - 网络空闲等待使用用户自定义的超时值
|
|
|
|
|
+
|
|
|
|
|
+3. **测试覆盖率目标:**
|
|
|
|
|
+ - ≥80% 覆盖率
|
|
|
|
|
+ - 单元测试使用 Vitest
|
|
|
|
|
+ - E2E 测试使用 Playwright
|
|
|
|
|
+
|
|
|
|
|
+#### 需要注意的技术决策
|
|
|
|
|
+
|
|
|
|
|
+1. **选择器策略优先级:**
|
|
|
|
|
+ 1. `data-testid` - 最高优先级
|
|
|
|
|
+ 2. `aria-label` + role - 无障碍标准
|
|
|
|
|
+ 3. Text content + role - 兜底方案
|
|
|
|
|
+
|
|
|
|
|
+2. **错误处理模式:**
|
|
|
|
|
+ - 使用 `E2ETestError` 而非原生 `Error`
|
|
|
|
|
+ - 提供结构化的错误上下文
|
|
|
|
|
+
|
|
|
|
|
+### 项目上下文引用
|
|
|
|
|
+
|
|
|
|
|
+**完整项目上下文:** `_bmad-output/project-context.md`
|
|
|
|
|
+
|
|
|
|
|
+**关键规范:**
|
|
|
|
|
+- 测试框架:Playwright 1.55.0
|
|
|
|
|
+- 包管理:pnpm workspace 协议
|
|
|
|
|
+- TypeScript:严格模式
|
|
|
|
|
+- 测试命令:`pnpm test:e2e:chromium`
|
|
|
|
|
+
|
|
|
|
|
+**相关文档:**
|
|
|
|
|
+- 架构文档:`_bmad-output/planning-artifacts/architecture.md`
|
|
|
|
|
+- Epic 1 回顾:`_bmad-output/implementation-artifacts/epic-1-retrospective.md`
|
|
|
|
|
+- Epic 详情:`_bmad-output/planning-artifacts/epics.md`
|
|
|
|
|
+
|
|
|
|
|
+### 下一步
|
|
|
|
|
+
|
|
|
|
|
+完成本故事后,继续:
|
|
|
|
|
+- Story 2.2: 使用 selectRadixOption 重写残疾类型选择
|
|
|
|
|
+- Story 2.3: 使用 selectRadixOptionAsync 重写省份/城市选择
|
|
|
|
|
+
|