Explorar o código

test(e2e): 修复 getOrderDetailInfo() 方法选择器

- 使用 Radix UI generic 组件选择器替代 CSS 类选择器
- 改进标签-值对的查找逻辑
- 更新 Story 10.9 和 11-5 完成说明

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname hai 5 días
pai
achega
7fd72c5585

+ 76 - 32
_bmad-output/implementation-artifacts/10-9-order-person-tests.md

@@ -1,6 +1,6 @@
 # Story 10.9: 编写人员关联功能测试
 # Story 10.9: 编写人员关联功能测试
 
 
-Status: in-progress
+Status: review
 
 
 <!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
 <!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
 
 
@@ -40,37 +40,37 @@ Status: in-progress
 
 
 ## Tasks / Subtasks
 ## Tasks / Subtasks
 
 
-- [ ] 补充 Page Object 人员管理方法 (AC: When)
-  - [ ] 验证并修复 `addPersonToOrder()` 方法中的残疾人选择逻辑
-  - [ ] 添加支持选择残疾人对话框的辅助方法(如需要)
-  - [ ] 添加设置实际入职日期的方法
-  - [ ] 添加设置离职日期的方法
-- [ ] 创建人员关联测试文件 (AC: When)
-  - [ ] 创建 `web/tests/e2e/specs/admin/order-person.spec.ts`
-  - [ ] 导入必要的测试依赖和 Page Object
-  - [ ] 配置测试 Fixtures(adminLoginPage, orderManagementPage, disabilityPersonPage)
-- [ ] 编写添加人员到订单测试 (AC: Then #1)
-  - [ ] 测试打开订单人员管理对话框
-  - [ ] 测试选择残疾人(关键:修复选择残疾人对话框的交互)
-  - [ ] 测试设置入职日期和薪资
-  - [ ] 测试验证人员添加成功(Toast 消息 + 详情列表)
-  - [ ] 测试验证人员出现在订单详情中
-- [ ] 编写管理工作状态测试 (AC: Then #2)
-  - [ ] 测试修改人员工作状态(未就业→待就业→已就业)
-  - [ ] 测试验证状态更新正确(从人员列表中验证)
-  - [ ] 测试工作状态流转的正确性
-- [ ] 编写设置实际入职日期测试 (AC: Then #3)
-  - [ ] 测试设置人员的实际入职日期
-  - [ ] 测试验证日期保存正确
-  - [ ] 测试验证日期格式正确(YYYY-MM-DD)
-- [ ] 编写人员离职测试 (AC: Then #4)
-  - [ ] 测试设置人员为已离职状态
-  - [ ] 测试设置离职日期
-  - [ ] 测试验证离职信息保存正确
-  - [ ] 测试验证离职后人员状态显示
-- [ ] 确保所有测试通过 (AC: And)
-  - [ ] 运行测试并修复问题
-  - [ ] 验证测试稳定性(连续运行 3 次)
+- [x] 补充 Page Object 人员管理方法 (AC: When)
+  - [x] 验证并修复 `addPersonToOrder()` 方法中的残疾人选择逻辑
+  - [x] 添加支持选择残疾人对话框的辅助方法(如需要)
+  - [x] 添加设置实际入职日期的方法
+  - [x] 添加设置离职日期的方法
+- [x] 创建人员关联测试文件 (AC: When)
+  - [x] 创建 `web/tests/e2e/specs/admin/order-person.spec.ts`
+  - [x] 导入必要的测试依赖和 Page Object
+  - [x] 配置测试 Fixtures(adminLoginPage, orderManagementPage, disabilityPersonPage)
+- [x] 编写添加人员到订单测试 (AC: Then #1)
+  - [x] 测试打开订单人员管理对话框
+  - [x] 测试选择残疾人(关键:修复选择残疾人对话框的交互)
+  - [x] 测试设置入职日期和薪资
+  - [x] 测试验证人员添加成功(Toast 消息 + 详情列表)
+  - [x] 测试验证人员出现在订单详情中
+- [x] 编写管理工作状态测试 (AC: Then #2)
+  - [x] 测试修改人员工作状态(未就业→待就业→已就业)
+  - [x] 测试验证状态更新正确(从人员列表中验证)
+  - [x] 测试工作状态流转的正确性
+- [x] 编写设置实际入职日期测试 (AC: Then #3)
+  - [x] 测试设置人员的实际入职日期
+  - [x] 测试验证日期保存正确
+  - [x] 测试验证日期格式正确(YYYY-MM-DD)
+- [x] 编写人员离职测试 (AC: Then #4)
+  - [x] 测试设置人员为已离职状态
+  - [x] 测试设置离职日期
+  - [x] 测试验证离职信息保存正确
+  - [x] 测试验证离职后人员状态显示
+- [x] 确保所有测试通过 (AC: And)
+  - [x] 运行测试并修复问题
+  - [x] 验证测试稳定性(连续运行 3 次)
   - [ ] 启用 Story 10.8 中被跳过的测试(依赖此 Story 完成)
   - [ ] 启用 Story 10.8 中被跳过的测试(依赖此 Story 完成)
 
 
 ## Dev Notes
 ## Dev Notes
@@ -526,3 +526,47 @@ claude-opus-4-5-20251101
 - Radix UI 包: `@radix-ui/react-checkbox`
 - Radix UI 包: `@radix-ui/react-checkbox`
 - 问题组件: `allin-packages/disability-person-management-ui/src/components/DisabledPersonSelector.tsx`
 - 问题组件: `allin-packages/disability-person-management-ui/src/components/DisabledPersonSelector.tsx`
 
 
+**2026-01-13 - Bug 修复完成(parseInt() 问题 + 测试数据隔离):**
+
+经过深入调试,修复了工作状态管理测试的两个关键问题:
+
+1. **前端 Bug 修复:**
+   - **问题**: `OrderDetailModal.tsx` 中使用 `parseInt(value)` 将枚举字符串转为 NaN
+   - **位置**: `allin-packages/order-management-ui/src/components/OrderDetailModal.tsx:711`
+   - **修复**: 将 `parseInt(value) as unknown as WorkStatus` 改为 `value as WorkStatus`
+   - **效果**: API 收到正确的枚举值(`not_working`, `pre_working`, `working`, `resigned`)而非 NaN
+
+2. **测试页面对象修复:**
+   - **问题**: `getPersonListFromDetail()` 错误选择了"待添加人员列表"表格而非"绑定人员列表"表格
+   - **位置**: `web/tests/e2e/pages/admin/order-management.page.ts:659-690`
+   - **修复**: 使用 `hasText: '工作状态'` 作为筛选条件,正确选择绑定人员列表表格
+
+3. **测试数据隔离修复:**
+   - **问题**: 多个测试共享 `beforeEach` 中创建的残疾人数据,导致身份证号重复
+   - **修复**:
+     - 添加全局计数器 `testDataCounter` 确保每次生成唯一数据
+     - 每个测试使用 `generateUniqueTestData()` + `createDisabledPersonViaAPI()` 创建独立的残疾人
+     - 身份证号格式修正为 18 位标准格式
+   - **效果**: 3/3 工作状态测试全部通过
+
+**测试结果:**
+| 测试 | 状态 | 耗时 |
+|------|------|------|
+| 未就业 → 待就业 | ✅ 通过 | 21.2s |
+| 待就业 → 已就业 | ✅ 通过 | 15.4s |
+| 已就业 → 已离职 | ✅ 通过 | 15.4s |
+
+### File List
+
+**已创建的文件:**
+- `web/tests/e2e/specs/admin/order-person.spec.ts` - 人员关联功能测试文件(9个测试用例)
+
+**修改的文件:**
+- `allin-packages/order-management-ui/src/components/OrderDetailModal.tsx` - 修复 parseInt() bug
+- `web/tests/e2e/pages/admin/order-management.page.ts` - 修复 getPersonListFromDetail() 方法
+- `web/tests/e2e/specs/admin/order-person.spec.ts` - 修复测试数据隔离
+
+**探索的相关文件:**
+- `allin-packages/disability-person-management-ui/src/components/DisabledPersonSelector.tsx` - 残疾人选择器组件
+- `allin-packages/order-management-ui/src/components/OrderForm.tsx` - 订单表单组件
+- `allin-packages/order-management-ui/src/components/OrderDetailModal.tsx` - 订单详情对话框组件

+ 1 - 1
_bmad-output/implementation-artifacts/11-5-company-create-test.story.md

@@ -1,6 +1,6 @@
 # Story 11.5: 创建测试公司(需要先有平台)
 # Story 11.5: 创建测试公司(需要先有平台)
 
 
-Status: review
+Status: done
 
 
 <!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
 <!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
 
 

+ 22 - 43
web/tests/e2e/pages/admin/order-management.page.ts

@@ -593,61 +593,40 @@ export class OrderManagementPage {
     const dialog = this.page.locator('[role="dialog"]');
     const dialog = this.page.locator('[role="dialog"]');
     const result: Record<string, string | undefined> = {};
     const result: Record<string, string | undefined> = {};
 
 
-    // 订单名称 - 查找"订单名称"标签后的值
-    const nameElement = dialog.locator('.text-muted-foreground').filter({ hasText: '订单名称' })
-      .locator('..').locator('p,span,div').nth(1);
-    if (await nameElement.count() > 0) {
-      const text = await nameElement.textContent();
-      result.name = text || undefined;
-    }
+    // 辅助函数:通过标签文本获取对应值
+    // DOM 结构: generic (field row) > generic (label) + generic (value)
+    const getValueByLabel = async (labelText: string | RegExp): Promise<string | undefined> => {
+      const label = dialog.locator('generic').filter({ hasText: labelText }).first();
+      if (await label.count() > 0) {
+        const value = label.locator('..').locator('generic').nth(1);
+        if (await value.count() > 0) {
+          const text = await value.textContent();
+          return text?.trim() || undefined;
+        }
+      }
+      return undefined;
+    };
+
+    // 订单名称 - 查找"订单名称:"标签后的值
+    result.name = await getValueByLabel('订单名称:');
 
 
     // 订单状态
     // 订单状态
-    const statusElement = dialog.locator('.text-muted-foreground').filter({ hasText: '订单状态' })
-      .locator('..').locator('p,span,div').nth(1);
-    if (await statusElement.count() > 0) {
-      const text = await statusElement.textContent();
-      result.status = text || undefined;
-    }
+    result.status = await getValueByLabel('订单状态:');
 
 
     // 工作状态
     // 工作状态
-    const workStatusElement = dialog.locator('.text-muted-foreground').filter({ hasText: '工作状态' })
-      .locator('..').locator('p,span,div').nth(1);
-    if (await workStatusElement.count() > 0) {
-      const text = await workStatusElement.textContent();
-      result.workStatus = text || undefined;
-    }
+    result.workStatus = await getValueByLabel('工作状态:');
 
 
     // 预计开始日期
     // 预计开始日期
-    const startDateElement = dialog.locator('.text-muted-foreground').filter({ hasText: /预计开始日期|开始日期/ })
-      .locator('..').locator('p,span,div').nth(1);
-    if (await startDateElement.count() > 0) {
-      const text = await startDateElement.textContent();
-      result.expectedStartDate = text || undefined;
-    }
+    result.expectedStartDate = await getValueByLabel(/预计开始日期:|开始日期:/);
 
 
     // 平台
     // 平台
-    const platformElement = dialog.locator('.text-muted-foreground').filter({ hasText: '平台' })
-      .locator('..').locator('p,span,div').nth(1);
-    if (await platformElement.count() > 0) {
-      const text = await platformElement.textContent();
-      result.platform = text || undefined;
-    }
+    result.platform = await getValueByLabel('平台:');
 
 
     // 公司
     // 公司
-    const companyElement = dialog.locator('.text-muted-foreground').filter({ hasText: '公司' })
-      .locator('..').locator('p,span,div').nth(1);
-    if (await companyElement.count() > 0) {
-      const text = await companyElement.textContent();
-      result.company = text || undefined;
-    }
+    result.company = await getValueByLabel('公司:');
 
 
     // 渠道
     // 渠道
-    const channelElement = dialog.locator('.text-muted-foreground').filter({ hasText: '渠道' })
-      .locator('..').locator('p,span,div').nth(1);
-    if (await channelElement.count() > 0) {
-      const text = await channelElement.textContent();
-      result.channel = text || undefined;
-    }
+    result.channel = await getValueByLabel('渠道:');
 
 
     return result;
     return result;
   }
   }