|
@@ -0,0 +1,404 @@
|
|
|
|
|
+# Story 9.3: 备注管理功能测试
|
|
|
|
|
+
|
|
|
|
|
+Status: ready-for-dev
|
|
|
|
|
+
|
|
|
|
|
+<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
|
|
|
|
+
|
|
|
|
|
+## Story
|
|
|
|
|
+
|
|
|
|
|
+作为测试开发者,
|
|
|
|
|
+我想要编写备注管理功能的测试,
|
|
|
|
|
+以便验证备注的添加、修改、删除功能。
|
|
|
|
|
+
|
|
|
|
|
+## Acceptance Criteria
|
|
|
|
|
+
|
|
|
|
|
+**Given** 残疾人管理 Page Object 已存在
|
|
|
|
|
+**When** 编写备注管理测试
|
|
|
|
|
+**Then** 包含以下测试场景:
|
|
|
|
|
+
|
|
|
|
|
+1. **添加备注**
|
|
|
|
|
+ - 添加简单备注
|
|
|
|
|
+ - 添加长文本备注
|
|
|
|
|
+ - 验证备注保存成功
|
|
|
|
|
+
|
|
|
|
|
+2. **修改备注**
|
|
|
|
|
+ - 编辑已有备注
|
|
|
|
|
+ - 验证修改后内容更新
|
|
|
|
|
+
|
|
|
|
|
+3. **删除备注**
|
|
|
|
|
+ - 删除备注
|
|
|
|
|
+ - 验证删除后备注消失
|
|
|
|
|
+
|
|
|
|
|
+## Tasks / Subtasks
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Task 1: 分析备注管理功能的 DOM 结构** (AC: #1, #2, #3)
|
|
|
|
|
+ - [ ] Subtask 1.1: 在残疾人管理页面中定位备注管理区域
|
|
|
|
|
+ - [ ] Subtask 1.2: 分析添加备注按钮和表单结构
|
|
|
|
|
+ - [ ] Subtask 1.3: 分析备注列表展示结构
|
|
|
|
|
+ - [ ] Subtask 1.4: 分析编辑和删除按钮的选择器
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Task 2: 创建备注测试文件** (AC: #1, #2, #3)
|
|
|
|
|
+ - [ ] Subtask 2.1: 创建 `web/tests/e2e/specs/admin/disability-person-note.spec.ts`
|
|
|
|
|
+ - [ ] Subtask 2.2: 编写添加备注测试
|
|
|
|
|
+ - [ ] Subtask 2.3: 编写编辑备注测试
|
|
|
|
|
+ - [ ] Subtask 2.4: 编写删除备注测试
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Task 3: 更新 Page Object** (AC: #1, #2, #3)
|
|
|
|
|
+ - [ ] Subtask 3.1: 添加备注管理相关方法到 `DisabilityPersonManagementPage`
|
|
|
|
|
+ - [ ] Subtask 3.2: 实现 `addNote()` 方法
|
|
|
|
|
+ - [ ] Subtask 3.3: 实现 `editNote()` 方法
|
|
|
|
|
+ - [ ] Subtask 3.4: 实现 `deleteNote()` 方法
|
|
|
|
|
+ - [ ] Subtask 3.5: 实现 `getNoteList()` 方法用于验证
|
|
|
|
|
+
|
|
|
|
|
+- [ ] **Task 4: 运行测试并验证通过** (AC: #1, #2, #3)
|
|
|
|
|
+ - [ ] Subtask 4.1: 使用 `pnpm test:e2e:chromium disability-person-note` 运行测试
|
|
|
|
|
+ - [ ] Subtask 4.2: 修复发现的问题
|
|
|
|
|
+ - [ ] Subtask 4.3: 验证所有测试通过
|
|
|
|
|
+
|
|
|
|
|
+## Dev Notes
|
|
|
|
|
+
|
|
|
|
|
+### Epic 9 背景与目标
|
|
|
|
|
+
|
|
|
|
|
+**Epic 9: 残疾人管理完整 E2E 测试覆盖(含并行隔离)**
|
|
|
|
|
+
|
|
|
|
|
+为残疾人管理功能编写完整的、真正验证业务功能的 E2E 测试,并确保测试可以与未来的区域管理测试并行运行。
|
|
|
|
|
+
|
|
|
|
|
+**Epic 9 的 Story 依赖关系:**
|
|
|
|
|
+- Story 9.1:照片上传功能测试 ✅ Done
|
|
|
|
|
+- Story 9.2:银行卡管理功能测试 ✅ Done
|
|
|
|
|
+- **Story 9.3(本故事)**:备注管理功能测试
|
|
|
|
|
+- Story 9.4:回访记录管理测试
|
|
|
|
|
+- Story 9.5:完整流程测试(CRUD)
|
|
|
|
|
+- Story 9.6:测试隔离与并行执行验证
|
|
|
|
|
+- Story 9.7:稳定性验证(10次连续运行)
|
|
|
|
|
+
|
|
|
|
|
+### 业务功能分析
|
|
|
|
|
+
|
|
|
|
|
+**备注管理功能概述:**
|
|
|
|
|
+
|
|
|
|
|
+残疾人管理系统中,备注功能用于记录与残疾人相关的额外信息、备注事项、沟通记录等。备注通常支持:
|
|
|
|
|
+- 添加多条备注(时间线模式)
|
|
|
|
|
+- 编辑已有备注
|
|
|
|
|
+- 删除备注
|
|
|
|
|
+- 每条备注记录创建时间和操作人
|
|
|
|
|
+
|
|
|
|
|
+**典型功能流程:**
|
|
|
|
|
+1. 在残疾人详情页或编辑对话框中找到备注管理区域
|
|
|
|
|
+2. 点击"添加备注"按钮打开添加表单/对话框
|
|
|
|
|
+3. 填写备注内容(支持多行文本)
|
|
|
|
|
+4. 保存后备注显示在列表中(按时间倒序)
|
|
|
|
|
+5. 可以编辑或删除已添加的备注
|
|
|
|
|
+
|
|
|
|
|
+### 技术规范
|
|
|
|
|
+
|
|
|
|
|
+#### 现有 Page Object 结构
|
|
|
|
|
+
|
|
|
|
|
+**当前 Page Object 位置:** `web/tests/e2e/pages/admin/disability-person.page.ts`
|
|
|
|
|
+
|
|
|
|
|
+**从 Story 9.1 和 9.2 学到的模式:**
|
|
|
|
|
+- 使用 `data-testid` 选择器最稳定
|
|
|
|
|
+- 在对话框内操作时,使用 `form.getByLabel()` 限制范围
|
|
|
|
|
+- 表单提交使用 `form.handleSubmit()` 并配合 `console.debug` 调试验证错误
|
|
|
|
|
+- 内联表单(如银行卡)直接在页面上操作,无需对话框
|
|
|
|
|
+
|
|
|
|
|
+#### 测试文件结构
|
|
|
|
|
+
|
|
|
|
|
+```
|
|
|
|
|
+web/tests/e2e/
|
|
|
|
|
+├── specs/
|
|
|
|
|
+│ └── admin/
|
|
|
|
|
+│ └── disability-person-note.spec.ts # 本测试文件(需创建)
|
|
|
|
|
+└── pages/
|
|
|
|
|
+ └── admin/
|
|
|
|
|
+ └── disability-person.page.ts # Page Object(需扩展)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+#### 测试用例设计
|
|
|
|
|
+
|
|
|
|
|
+**测试文件模板:**
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+// web/tests/e2e/specs/admin/disability-person-note.spec.ts
|
|
|
|
|
+import { test, expect } from '@playwright/test';
|
|
|
|
|
+import { DisabilityPersonManagementPage } from '../../pages/admin/disability-person.page';
|
|
|
|
|
+
|
|
|
|
|
+test.describe('残疾人管理 - 备注管理功能', () => {
|
|
|
|
|
+ let pageObject: DisabilityPersonManagementPage;
|
|
|
|
|
+ const TIMESTAMP = Date.now();
|
|
|
|
|
+ const UNIQUE_ID = `test_note_${TIMESTAMP}`;
|
|
|
|
|
+
|
|
|
|
|
+ test.beforeEach(async ({ page }) => {
|
|
|
|
|
+ pageObject = new DisabilityPersonManagementPage(page);
|
|
|
|
|
+ await pageObject.goto();
|
|
|
|
|
+ await pageObject.openCreateDialog();
|
|
|
|
|
+ await pageObject.fillBasicInfo({
|
|
|
|
|
+ name: UNIQUE_ID,
|
|
|
|
|
+ idCard: `110101199001011234`,
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ test('应该成功添加简单备注', async ({ page }) => {
|
|
|
|
|
+ const noteContent = `这是一条测试备注_${UNIQUE_ID}`;
|
|
|
|
|
+ await pageObject.addNote(noteContent);
|
|
|
|
|
+ const noteList = await pageObject.getNoteList();
|
|
|
|
|
+ expect(noteList).toHaveLength(1);
|
|
|
|
|
+ expect(noteList[0]).toContain(noteContent);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ test('应该成功添加长文本备注', async ({ page }) => {
|
|
|
|
|
+ const longNote = `这是一条很长的备注内容_${UNIQUE_ID}`.repeat(10);
|
|
|
|
|
+ await pageObject.addNote(longNote);
|
|
|
|
|
+ const noteList = await pageObject.getNoteList();
|
|
|
|
|
+ expect(noteList).toHaveLength(1);
|
|
|
|
|
+ expect(noteList[0]).toContain('这是一条很长的备注内容');
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ test('应该成功编辑备注', async ({ page }) => {
|
|
|
|
|
+ const originalNote = `原始备注_${UNIQUE_ID}`;
|
|
|
|
|
+ await pageObject.addNote(originalNote);
|
|
|
|
|
+ const updatedNote = `更新后的备注_${UNIQUE_ID}`;
|
|
|
|
|
+ await pageObject.editNote(0, updatedNote);
|
|
|
|
|
+ const noteList = await pageObject.getNoteList();
|
|
|
|
|
+ expect(noteList[0]).toContain(updatedNote);
|
|
|
|
|
+ expect(noteList[0]).not.toContain(originalNote);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ test('应该成功删除备注', async ({ page }) => {
|
|
|
|
|
+ const noteContent = `待删除备注_${UNIQUE_ID}`;
|
|
|
|
|
+ await pageObject.addNote(noteContent);
|
|
|
|
|
+ let noteList = await pageObject.getNoteList();
|
|
|
|
|
+ expect(noteList).toHaveLength(1);
|
|
|
|
|
+ await pageObject.deleteNote(0);
|
|
|
|
|
+ noteList = await pageObject.getNoteList();
|
|
|
|
|
+ expect(noteList).toHaveLength(0);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ test('应该支持添加多条备注', async ({ page }) => {
|
|
|
|
|
+ await pageObject.addNote(`备注1_${UNIQUE_ID}`);
|
|
|
|
|
+ await pageObject.addNote(`备注2_${UNIQUE_ID}`);
|
|
|
|
|
+ await pageObject.addNote(`备注3_${UNIQUE_ID}`);
|
|
|
|
|
+ const noteList = await pageObject.getNoteList();
|
|
|
|
|
+ expect(noteList).toHaveLength(3);
|
|
|
|
|
+ });
|
|
|
|
|
+});
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Page Object 方法设计
|
|
|
|
|
+
|
|
|
|
|
+**需要在 `DisabilityPersonManagementPage` 中添加的方法:**
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+/**
|
|
|
|
|
+ * 添加备注
|
|
|
|
|
+ * @param content 备注内容
|
|
|
|
|
+ */
|
|
|
|
|
+async addNote(content: string): Promise<void> {
|
|
|
|
|
+ // 1. 点击"添加备注"按钮
|
|
|
|
|
+ // 2. 等待备注输入区域出现
|
|
|
|
|
+ // 3. 填写备注内容
|
|
|
|
|
+ // 4. 点击保存/确认按钮
|
|
|
|
|
+ // 5. 等待备注出现在列表中
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 编辑备注
|
|
|
|
|
+ * @param index 备注索引(第几条,从0开始)
|
|
|
|
|
+ * @param content 更新的备注内容
|
|
|
|
|
+ */
|
|
|
|
|
+async editNote(index: number, content: string): Promise<void> {
|
|
|
|
|
+ // 1. 找到指定索引的备注的编辑按钮
|
|
|
|
|
+ // 2. 点击编辑按钮
|
|
|
|
|
+ // 3. 等待编辑区域出现
|
|
|
|
|
+ // 4. 更新备注内容
|
|
|
|
|
+ // 5. 点击保存按钮
|
|
|
|
|
+ // 6. 等待更新完成
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 删除备注
|
|
|
|
|
+ * @param index 备注索引(第几条,从0开始)
|
|
|
|
|
+ */
|
|
|
|
|
+async deleteNote(index: number): Promise<void> {
|
|
|
|
|
+ // 1. 找到指定索引的备注的删除按钮
|
|
|
|
|
+ // 2. 点击删除按钮
|
|
|
|
|
+ // 3. 等待确认对话框(如有)
|
|
|
|
|
+ // 4. 点击确认按钮
|
|
|
|
|
+ // 5. 等待删除完成
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 获取备注列表
|
|
|
|
|
+ * @returns 备注内容数组
|
|
|
|
|
+ */
|
|
|
|
|
+async getNoteList(): Promise<string[]> {
|
|
|
|
|
+ // 1. 定位备注列表容器
|
|
|
|
|
+ // 2. 获取所有备注项的文本内容
|
|
|
|
|
+ // 3. 返回备注信息数组
|
|
|
|
|
+ return []; // 实现时返回真实数据
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 获取备注数量
|
|
|
|
|
+ * @returns 备注数量
|
|
|
|
|
+ */
|
|
|
|
|
+async getNoteCount(): Promise<number> {
|
|
|
|
|
+ return (await this.getNoteList()).length;
|
|
|
|
|
+}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 选择器策略
|
|
|
|
|
+
|
|
|
|
|
+**参考 Story 9.1 和 9.2 的经验:**
|
|
|
|
|
+
|
|
|
|
|
+1. **添加备注按钮:** 使用 `page.getByRole('button', { name: /添加.*备注/ })`
|
|
|
|
|
+2. **备注输入区域:** 在表单内使用 `form.getByLabel('备注内容')` 或 `form.getByPlaceholderText('请输入备注')`
|
|
|
|
|
+3. **备注列表项:** 使用 `data-testid` 或 `role="listitem"` 选择器
|
|
|
|
|
+4. **编辑/删除按钮:** 每个备注项内,使用索引或文本定位
|
|
|
|
|
+
|
|
|
|
|
+**示例:**
|
|
|
|
|
+```typescript
|
|
|
|
|
+// 点击编辑按钮(第1条备注的编辑按钮)
|
|
|
|
|
+const notes = page.locator('[data-testid="note-item"]');
|
|
|
|
|
+await notes.nth(0).getByRole('button', { name: /编辑/ }).click();
|
|
|
|
|
+
|
|
|
|
|
+// 或使用更具体的选择器
|
|
|
|
|
+await page.locator('[data-testid="note-item-0"] [data-testid="edit-button"]').click();
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 数据隔离策略
|
|
|
|
|
+
|
|
|
|
|
+**参考 Story 9.1 和 9.2 的数据隔离模式:**
|
|
|
|
|
+
|
|
|
|
|
+```typescript
|
|
|
|
|
+test.beforeEach(async ({ page }) => {
|
|
|
|
|
+ const timestamp = Date.now();
|
|
|
|
|
+ const uniqueId = `test_note_${timestamp}`;
|
|
|
|
|
+ pageObject = new DisabilityPersonManagementPage(page);
|
|
|
|
|
+ await pageObject.goto();
|
|
|
|
|
+ await pageObject.openCreateDialog();
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
|
|
+test.afterEach(async ({ page }) => {
|
|
|
|
|
+ // 清理测试数据(根据实际业务逻辑实现)
|
|
|
|
|
+});
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 项目结构说明
|
|
|
|
|
+
|
|
|
|
|
+**测试目录组织:**
|
|
|
|
|
+- **specs/**: 测试用例文件,按功能模块组织,`.spec.ts` 后缀
|
|
|
|
|
+- **pages/**: Page Object 封装,页面元素和操作方法,复用性强
|
|
|
|
|
+
|
|
|
|
|
+**无冲突检测:**
|
|
|
|
|
+- 新增测试文件,不影响现有代码
|
|
|
|
|
+- Page Object 扩展是增强,非破坏性修改
|
|
|
|
|
+
|
|
|
|
|
+### Previous Story Intelligence
|
|
|
|
|
+
|
|
|
|
|
+**Story 9.1 (照片上传功能测试) 的关键经验:**
|
|
|
|
|
+
|
|
|
|
|
+1. **表单操作范围控制** - 使用 `form.getByLabel()` 限制查找范围在表单内
|
|
|
|
|
+2. **按钮文本获取时机** - 在点击前获取按钮文本
|
|
|
|
|
+3. **测试数据唯一性** - 使用 `Date.now()` 生成唯一 ID
|
|
|
|
|
+4. **超时常量定义** - 定义 `TIMEOUTS` 常量统一管理
|
|
|
|
|
+
|
|
|
|
|
+**Story 9.2 (银行卡管理功能测试) 的关键经验:**
|
|
|
|
|
+
|
|
|
|
|
+1. **内联表单模式** - 银行卡管理使用内联表单(非对话框模式)
|
|
|
|
|
+2. **索引定位方式** - 使用 `nth(index)` 定位列表项
|
|
|
|
|
+3. **数据验证模式** - 使用 `getNoteList()` 获取列表后验证内容
|
|
|
|
|
+4. **代码审查修复** - 确保 Page Object 方法完整实现,测试代码必须使用 Page Object 方法
|
|
|
|
|
+
|
|
|
|
|
+### Git Intelligence Summary
|
|
|
|
|
+
|
|
|
|
|
+**Recent Commits:**
|
|
|
|
|
+- `a059239` - 完成 Story 10.1: 订单管理 Page Object 代码审查
|
|
|
|
|
+- `b739b96` - docs(epic-9): 创建 Story 10.2 - 订单列表查看测试
|
|
|
|
|
+- `5d75cf8` - test(e2e): 完成 Story 9.2 - 银行卡管理功能测试
|
|
|
|
|
+
|
|
|
|
|
+**Code Patterns Observed:**
|
|
|
|
|
+- 测试文件命名:`disability-person-{feature}.spec.ts`
|
|
|
|
|
+- Page Object 方法命名:动词+名词(如 `addNote`, `editNote`)
|
|
|
|
|
+
|
|
|
|
|
+### TypeScript + Playwright 陷阱(关键)
|
|
|
|
|
+
|
|
|
|
|
+基于架构文档的陷阱章节:
|
|
|
|
|
+
|
|
|
|
|
+**陷阱 1: DOM 结构假设必须验证** ⚠️
|
|
|
|
|
+- 备注管理功能的 DOM 结构需要在实际页面中验证
|
|
|
|
|
+- 使用 `data-testid` 选择器(最稳定)
|
|
|
|
|
+
|
|
|
|
|
+**陷阱 2: 精确文本匹配**
|
|
|
|
|
+- 使用 `:text-is()` 进行精确文本匹配,而非 `:has-text()`
|
|
|
|
|
+
|
|
|
|
|
+**陷阱 3: 网络空闲等待**
|
|
|
|
|
+- 备注添加/编辑后可能需要等待网络请求完成
|
|
|
|
|
+
|
|
|
|
|
+**陷阱 4: 避免使用 page.evaluate()**
|
|
|
|
|
+- 使用 Playwright API 而非 `page.evaluate()` 获取元素内容
|
|
|
|
|
+
|
|
|
|
|
+### References
|
|
|
|
|
+
|
|
|
|
|
+**源文档引用:**
|
|
|
|
|
+- [Source: _bmad-output/planning-artifacts/epics.md#Epic-9-Story-9.3] - 完整业务需求
|
|
|
|
|
+- [Source: _bmad-output/planning-artifacts/architecture.md#Testing-Configuration] - 三层测试策略
|
|
|
|
|
+- [Source: docs/standards/e2e-radix-testing.md] - E2E 测试标准
|
|
|
|
|
+
|
|
|
|
|
+**前置 Story 参考:**
|
|
|
|
|
+- [Source: _bmad-output/implementation-artifacts/9-1-photo-upload-tests.md] - 照片上传测试实现经验
|
|
|
|
|
+- [Source: _bmad-output/implementation-artifacts/9-2-bankcard-tests.md] - 银行卡测试实现经验
|
|
|
|
|
+
|
|
|
|
|
+**相关组件源码:**
|
|
|
|
|
+- [Source: web/tests/e2e/pages/admin/disability-person.page.ts] - 现有 Page Object
|
|
|
|
|
+- [Source: allin-packages/disability-person-management-ui] - 备注管理 UI 组件(需验证 DOM 结构)
|
|
|
|
|
+
|
|
|
|
|
+### Project Structure Notes
|
|
|
|
|
+
|
|
|
|
|
+**Monorepo 结构对齐:**
|
|
|
|
|
+- 测试位于 `web/tests/e2e/` 目录
|
|
|
|
|
+- 使用 pnpm workspace 协议引用 `@d8d/e2e-test-utils`
|
|
|
|
|
+- 与现有 Page Object 模式保持一致
|
|
|
|
|
+
|
|
|
|
|
+**文件组织:**
|
|
|
|
|
+- 新建测试 specs 文件:`web/tests/e2e/specs/admin/disability-person-note.spec.ts`
|
|
|
|
|
+- 扩展现有 Page Object:`web/tests/e2e/pages/admin/disability-person.page.ts`
|
|
|
|
|
+
|
|
|
|
|
+**遵循的项目标准:**
|
|
|
|
|
+- 文件命名:`.spec.ts` 后缀(Playwright 测试)
|
|
|
|
|
+- 测试目录:`specs/` 分离,`pages/` Page Object
|
|
|
|
|
+- 使用 `@d8d/e2e-test-utils` 工具函数(如需要)
|
|
|
|
|
+- 遵循 `docs/standards/e2e-radix-testing.md` 标准
|
|
|
|
|
+
|
|
|
|
|
+## Dev Agent Record
|
|
|
|
|
+
|
|
|
|
|
+### Agent Model Used
|
|
|
|
|
+
|
|
|
|
|
+Claude Opus 4 (claude-opus-4-5-20251101)
|
|
|
|
|
+
|
|
|
|
|
+### Debug Log References
|
|
|
|
|
+
|
|
|
|
|
+### Completion Notes List
|
|
|
|
|
+
|
|
|
|
|
+1. ✅ 加载并分析 Epic 9 Story 9.3 需求(从 epics.md)
|
|
|
|
|
+2. ✅ 加载并分析架构文档(architecture.md)
|
|
|
|
|
+3. ✅ 分析前置 Story 9.1 和 9.2 的实现经验和问题
|
|
|
|
|
+4. ✅ 创建完整的 Story 9.3 文档,包含:
|
|
|
|
|
+ - Story 和验收标准
|
|
|
|
|
+ - 详细的任务分解
|
|
|
|
|
+ - Epic 9 背景和目标
|
|
|
|
|
+ - 业务功能分析
|
|
|
|
|
+ - 技术规范(Page Object 方法、选择器策略)
|
|
|
|
|
+ - 完整的测试用例模板
|
|
|
|
|
+ - Previous Story Intelligence(从 Story 9.1 和 9.2 学到的经验)
|
|
|
|
|
+ - Git Intelligence Summary
|
|
|
|
|
+ - TypeScript + Playwright 陷阱警告
|
|
|
|
|
+ - 完整的参考文档列表
|
|
|
|
|
+
|
|
|
|
|
+### File List
|
|
|
|
|
+
|
|
|
|
|
+**创建的文件:**
|
|
|
|
|
+- `_bmad-output/implementation-artifacts/9-3-note-tests.md` - 本 story 文档
|
|
|
|
|
+
|
|
|
|
|
+**待创建的文件(由 dev-story 执行):**
|
|
|
|
|
+- `web/tests/e2e/specs/admin/disability-person-note.spec.ts` - 备注测试文件
|
|
|
|
|
+- `web/tests/e2e/pages/admin/disability-person.page.ts` - Page Object 扩展(添加备注管理方法)
|