|
|
@@ -0,0 +1,342 @@
|
|
|
+# Story 10.1: 创建订单管理 Page Object
|
|
|
+
|
|
|
+Status: ready-for-dev
|
|
|
+
|
|
|
+<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
|
|
+
|
|
|
+## Story
|
|
|
+
|
|
|
+作为测试开发者,
|
|
|
+我想要创建订单管理的 Page Object,
|
|
|
+以便组织订单管理相关的页面元素和操作。
|
|
|
+
|
|
|
+## Acceptance Criteria
|
|
|
+
|
|
|
+**Given** Epic 9 的 Page Object 模式已验证
|
|
|
+**When** 创建 `web/tests/e2e/pages/admin/order-management.page.ts`
|
|
|
+**Then** 定义订单列表页面的选择器和操作方法
|
|
|
+**And** 定义创建订单表单的选择器和操作方法
|
|
|
+**And** 定义编辑订单表单的选择器和操作方法
|
|
|
+**And** 定义订单详情对话框的选择器和操作方法
|
|
|
+**And** 定义人员关联相关的选择器和操作方法
|
|
|
+**And** 定义附件上传相关的选择器和操作方法
|
|
|
+**And** 遵循现有 Page Object 设计模式
|
|
|
+**And** 所有方法有完整的 TypeScript 类型定义
|
|
|
+
|
|
|
+**参考:**
|
|
|
+- `web/tests/e2e/pages/admin/disability-person.page.ts` 作为参考
|
|
|
+- `web/tests/e2e/pages/admin/region-management.page.ts` 作为参考
|
|
|
+- 遵循项目的 Page Object 设计模式
|
|
|
+
|
|
|
+## Tasks / Subtasks
|
|
|
+
|
|
|
+- [ ] 创建订单管理 Page Object 基础结构 (AC: Given, When, And)
|
|
|
+ - [ ] 定义订单列表页面选择器(标题、新增按钮、搜索框、表格等)
|
|
|
+ - [ ] 实现 goto() 方法导航到订单管理页面
|
|
|
+ - [ ] 实现 expectToBeVisible() 方法验证关键元素可见
|
|
|
+- [ ] 定义订单列表相关方法和选择器 (AC: When)
|
|
|
+ - [ ] 定义订单名称搜索输入框和搜索按钮
|
|
|
+ - [ ] 定义订单筛选器(状态、平台、公司、渠道、日期范围)
|
|
|
+ - [ ] 定义订单列表表格选择器
|
|
|
+ - [ ] 实现搜索功能方法 searchByName()
|
|
|
+ - [ ] 实现筛选功能方法
|
|
|
+- [ ] 定义订单 CRUD 相关方法和选择器 (AC: When)
|
|
|
+ - [ ] 实现打开创建订单对话框方法 openCreateDialog()
|
|
|
+ - [ ] 实现填写订单表单方法 fillOrderForm()
|
|
|
+ - [ ] 实现提交表单方法 submitForm()
|
|
|
+ - [ ] 实现打开编辑订单对话框方法 openEditDialog()
|
|
|
+ - [ ] 实现打开删除确认对话框方法 openDeleteDialog()
|
|
|
+ - [ ] 实现订单是否存在验证方法 orderExists()
|
|
|
+- [ ] 定义订单详情相关方法和选择器 (AC: When)
|
|
|
+ - [ ] 实现打开订单详情对话框方法 openDetailDialog()
|
|
|
+ - [ ] 定义详情对话框内的选择器(订单信息、人员列表、附件列表)
|
|
|
+- [ ] 定义人员关联相关方法和选择器 (AC: When)
|
|
|
+ - [ ] 实现打开人员管理对话框方法 openPersonManagementDialog()
|
|
|
+ - [ ] 实现添加人员方法 addPersonToOrder()
|
|
|
+ - [ ] 实现修改工作状态方法 updatePersonWorkStatus()
|
|
|
+- [ ] 定义附件管理相关方法和选择器 (AC: When)
|
|
|
+ - [ ] 实现打开添加附件对话框方法 openAddAttachmentDialog()
|
|
|
+ - [ ] 实现上传附件方法 uploadAttachment()
|
|
|
+- [ ] 添加 TypeScript 类型定义和 JSDoc 注释 (AC: And)
|
|
|
+ - [ ] 定义 OrderData 接口
|
|
|
+ - [ ] 定义 OrderPersonData 接口
|
|
|
+ - [ ] 定义 FormSubmitResult 接口
|
|
|
+ - [ ] 为所有公共方法添加 JSDoc 注释
|
|
|
+
|
|
|
+## Dev Notes
|
|
|
+
|
|
|
+### Epic Context
|
|
|
+
|
|
|
+**Epic 10: 订单管理 E2E 测试 (Epic C - 业务测试 Epic)**
|
|
|
+
|
|
|
+- **目标**: 为订单管理功能编写完整的 E2E 测试,验证订单的 CRUD、状态流转、人员关联和附件管理功能
|
|
|
+- **业务分组**: Epic C(业务测试 Epic)
|
|
|
+- **背景**: 订单管理是招聘系统的核心业务功能,涉及复杂表单(多选择器联动)、状态流转、人员关联等场景
|
|
|
+- **模式**: 业务测试为主,工具包支持为辅(遵循 Epic A 成功模式)
|
|
|
+
|
|
|
+**依赖:**
|
|
|
+- Epic 1: ✅ 已完成(Select 工具基础框架)
|
|
|
+- Epic 2: ✅ 已完成(Select 工具在真实 E2E 测试中验证)
|
|
|
+- Epic 9: 🔄 进行中(残疾人管理完整 E2E 测试覆盖)
|
|
|
+
|
|
|
+### 订单管理功能概述
|
|
|
+
|
|
|
+根据 epics.md,订单管理涉及以下核心功能:
|
|
|
+
|
|
|
+1. **订单列表**: 查看、搜索、筛选、分页
|
|
|
+2. **订单 CRUD**: 创建、编辑、删除订单
|
|
|
+3. **订单状态流转**: 草稿 → 已确认 → 进行中 → 已完成
|
|
|
+4. **订单详情**: 查看订单完整信息、人员列表、附件列表
|
|
|
+5. **人员关联**: 添加残疾人到订单、管理工作状态(未就业、待就业、已就业、已离职)
|
|
|
+6. **附件管理**: 为订单人员添加附件
|
|
|
+
|
|
|
+### Page Object 设计模式参考
|
|
|
+
|
|
|
+本项目已建立成熟的 Page Object 设计模式,参考以下文件:
|
|
|
+
|
|
|
+**1. disability-person.page.ts** (残疾人管理 - 复杂表单示例)
|
|
|
+- 位置: `web/tests/e2e/pages/admin/disability-person.page.ts`
|
|
|
+- 特点: 多步骤表单、照片上传、动态列表(银行卡、备注、回访记录)
|
|
|
+- 关键模式:
|
|
|
+ - 使用 `@d8d/e2e-test-utils` 的 `selectRadixOption` 和 `selectProvinceCity`
|
|
|
+ - 网络响应监听和 Toast 消息验证
|
|
|
+ - 表单调试使用 `console.debug`
|
|
|
+
|
|
|
+**2. region-management.page.ts** (区域管理 - 业务模块示例)
|
|
|
+- 位置: `web/tests/e2e/pages/admin/region-management.page.ts`
|
|
|
+- 特点: 树形结构、CRUD 操作、状态切换
|
|
|
+- 关键模式:
|
|
|
+ - 导出的类型定义(RegionData, RegionLevel, FormSubmitResult 等)
|
|
|
+ - 完整的 JSDoc 注释
|
|
|
+ - 网络响应监听(NetworkResponse 接口)
|
|
|
+ - Toast 消息验证(data-sonner-toast)
|
|
|
+
|
|
|
+### 技术要求
|
|
|
+
|
|
|
+**导入依赖:**
|
|
|
+```typescript
|
|
|
+import { Page, Locator } from '@playwright/test';
|
|
|
+import { selectRadixOption, selectRadixOptionAsync } from '@d8d/e2e-test-utils';
|
|
|
+```
|
|
|
+
|
|
|
+**选择器策略:**
|
|
|
+- 优先使用 `data-testid` 属性
|
|
|
+- 其次使用 `role + name` 组合(如 `getByRole('button', { name: '新增订单' })`)
|
|
|
+- 避免使用 CSS 类名(不稳定)
|
|
|
+- 文本匹配使用 `exact: true` 避免误匹配
|
|
|
+
|
|
|
+**网络响应监听模式:**
|
|
|
+```typescript
|
|
|
+// 收集网络响应
|
|
|
+const responses: NetworkResponse[] = [];
|
|
|
+
|
|
|
+// 监听所有网络请求
|
|
|
+this.page.on('response', async (response: Response) => {
|
|
|
+ const url = response.url();
|
|
|
+ if (url.includes('/orders') || url.includes('order')) {
|
|
|
+ // 收集响应数据
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+// 执行操作后等待网络空闲
|
|
|
+await this.page.waitForLoadState('networkidle', { timeout: 10000 });
|
|
|
+```
|
|
|
+
|
|
|
+**Toast 消息验证:**
|
|
|
+```typescript
|
|
|
+const errorToast = this.page.locator('[data-sonner-toast][data-type="error"]');
|
|
|
+const successToast = this.page.locator('[data-sonner-toast][data-type="success"]');
|
|
|
+```
|
|
|
+
|
|
|
+### Project Structure Notes
|
|
|
+
|
|
|
+**文件位置:**
|
|
|
+- Page Object 文件: `web/tests/e2e/pages/admin/order-management.page.ts`
|
|
|
+- 测试文件目录: `web/tests/e2e/specs/admin/`
|
|
|
+- Fixtures 目录: `web/tests/e2e/fixtures/`
|
|
|
+
|
|
|
+**对齐统一项目结构:**
|
|
|
+- 遵循 `project-context.md` 中的 TypeScript 严格模式规则
|
|
|
+- 函数参数、返回值必须有明确类型注解
|
|
|
+- 禁止使用 `any` 类型
|
|
|
+- 公共 API 必须包含完整 JSDoc 注释
|
|
|
+
|
|
|
+### 测试标准参考
|
|
|
+
|
|
|
+遵循以下测试标准文档(来自 epics.md):
|
|
|
+- `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-testing.md):**
|
|
|
+1. `data-testid` - 最高优先级
|
|
|
+2. `aria-label + role`
|
|
|
+3. `text content + role` - 兜底
|
|
|
+
|
|
|
+**错误消息格式标准:**
|
|
|
+```
|
|
|
+Error: 操作失败
|
|
|
+ 上下文信息
|
|
|
+ 💡 修复建议
|
|
|
+```
|
|
|
+
|
|
|
+### 类型定义参考
|
|
|
+
|
|
|
+基于 `region-management.page.ts` 的类型定义模式:
|
|
|
+
|
|
|
+```typescript
|
|
|
+/**
|
|
|
+ * 订单状态常量
|
|
|
+ */
|
|
|
+export const ORDER_STATUS = {
|
|
|
+ DRAFT: 'draft',
|
|
|
+ CONFIRMED: 'confirmed',
|
|
|
+ IN_PROGRESS: 'in_progress',
|
|
|
+ COMPLETED: 'completed',
|
|
|
+} as const;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 工作状态常量
|
|
|
+ */
|
|
|
+export const WORK_STATUS = {
|
|
|
+ NOT_EMPLOYED: 'not_employed',
|
|
|
+ PENDING: 'pending',
|
|
|
+ EMPLOYED: 'employed',
|
|
|
+ RESIGNED: 'resigned',
|
|
|
+} as const;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 订单数据接口
|
|
|
+ */
|
|
|
+export interface OrderData {
|
|
|
+ /** 订单名称 */
|
|
|
+ name: string;
|
|
|
+ /** 预计开始日期 */
|
|
|
+ expectedStartDate?: string;
|
|
|
+ /** 平台ID */
|
|
|
+ platformId?: number;
|
|
|
+ /** 公司ID */
|
|
|
+ companyId?: number;
|
|
|
+ /** 渠道ID */
|
|
|
+ channelId?: number;
|
|
|
+ /** 订单状态 */
|
|
|
+ status?: typeof ORDER_STATUS[keyof typeof ORDER_STATUS];
|
|
|
+ /** 工作状态 */
|
|
|
+ workStatus?: typeof WORK_STATUS[keyof typeof WORK_STATUS];
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 订单人员数据接口
|
|
|
+ */
|
|
|
+export interface OrderPersonData {
|
|
|
+ /** 残疾人ID */
|
|
|
+ disabledPersonId: number;
|
|
|
+ /** 入职日期 */
|
|
|
+ hireDate?: string;
|
|
|
+ /** 薪资 */
|
|
|
+ salary?: number;
|
|
|
+ /** 工作状态 */
|
|
|
+ workStatus?: typeof WORK_STATUS[keyof typeof WORK_STATUS];
|
|
|
+ /** 实际入职日期 */
|
|
|
+ actualHireDate?: string;
|
|
|
+ /** 离职日期 */
|
|
|
+ resignDate?: string;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 网络响应数据
|
|
|
+ */
|
|
|
+export interface NetworkResponse {
|
|
|
+ url: string;
|
|
|
+ method: string;
|
|
|
+ status: number;
|
|
|
+ ok: boolean;
|
|
|
+ responseHeaders: Record<string, string>;
|
|
|
+ responseBody: unknown;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 表单提交结果
|
|
|
+ */
|
|
|
+export interface FormSubmitResult {
|
|
|
+ /** 提交是否成功 */
|
|
|
+ success: boolean;
|
|
|
+ /** 是否有错误 */
|
|
|
+ hasError: boolean;
|
|
|
+ /** 是否有成功消息 */
|
|
|
+ hasSuccess: boolean;
|
|
|
+ /** 错误消息 */
|
|
|
+ errorMessage?: string;
|
|
|
+ /** 成功消息 */
|
|
|
+ successMessage?: string;
|
|
|
+ /** 网络响应列表 */
|
|
|
+ responses?: NetworkResponse[];
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 订单管理页面元素(待验证)
|
|
|
+
|
|
|
+**注意**: 以下元素基于 PRD 描述,实际实现时需要通过浏览器开发工具验证 DOM 结构。
|
|
|
+
|
|
|
+**订单列表页面:**
|
|
|
+- 页面路径: `/admin/orders`(待确认)
|
|
|
+- 页面标题: "订单管理"
|
|
|
+- 新增订单按钮: "新增订单"
|
|
|
+- 搜索输入框: placeholder="搜索订单名称"
|
|
|
+- 搜索按钮: "搜索"
|
|
|
+- 筛选器: 订单状态、工作状态、平台、公司、渠道、日期范围
|
|
|
+- 订单列表表格: 显示订单名称、平台、公司、渠道、订单状态、工作状态等
|
|
|
+
|
|
|
+**订单表单:**
|
|
|
+- 订单名称: 必填
|
|
|
+- 预计开始日期: 必填,日期选择器
|
|
|
+- 平台: 可选,下拉选择器
|
|
|
+- 公司: 可选,下拉选择器
|
|
|
+- 渠道: 可选,下拉选择器
|
|
|
+
|
|
|
+**订单详情对话框:**
|
|
|
+- 显示订单基本信息
|
|
|
+- 显示关联人员列表
|
|
|
+- 显示附件列表
|
|
|
+
|
|
|
+**人员管理对话框:**
|
|
|
+- 选择残疾人: 下拉选择器
|
|
|
+- 入职日期: 日期选择器
|
|
|
+- 薪资: 数字输入
|
|
|
+- 工作状态: 下拉选择器
|
|
|
+
|
|
|
+**附件上传对话框:**
|
|
|
+- 选择订单人员: 下拉选择器
|
|
|
+- 上传附件: 文件上传组件
|
|
|
+
|
|
|
+### 调试技巧
|
|
|
+
|
|
|
+**表单调试:**
|
|
|
+```typescript
|
|
|
+// 在 form.handleSubmit 的第二个参数中添加 console.debug
|
|
|
+form.handleSubmit(handleSubmit, (errors) => console.debug('表单验证错误:', errors))
|
|
|
+```
|
|
|
+
|
|
|
+**E2E 测试失败调试:**
|
|
|
+- 查看 `test-results/**/error-context.md`
|
|
|
+- 使用 `timeout` 命令限制运行时间
|
|
|
+- `timeout 60 pnpm test:e2e:chromium <测试文件名>`
|
|
|
+
|
|
|
+### References
|
|
|
+
|
|
|
+- [Source: _bmad-output/planning-artifacts/epics.md#Epic 10](../planning-artifacts/epics.md)
|
|
|
+- [Source: _bmad-output/project-context.md](../project-context.md)
|
|
|
+- [Source: web/tests/e2e/pages/admin/region-management.page.ts](../../web/tests/e2e/pages/admin/region-management.page.ts)
|
|
|
+- [Source: web/tests/e2e/pages/admin/disability-person.page.ts](../../web/tests/e2e/pages/admin/disability-person.page.ts)
|
|
|
+
|
|
|
+## Dev Agent Record
|
|
|
+
|
|
|
+### Agent Model Used
|
|
|
+
|
|
|
+claude-opus-4-5-20251101
|
|
|
+
|
|
|
+### Debug Log References
|
|
|
+
|
|
|
+### Completion Notes List
|
|
|
+
|
|
|
+### File List
|