|
|
@@ -0,0 +1,482 @@
|
|
|
+# Story 13.1: 后台创建订单 → 企业小程序验证
|
|
|
+
|
|
|
+Status: ready-for-dev
|
|
|
+
|
|
|
+<!-- Note: Validation is optional. Run validate-create-story for quality check before dev-story. -->
|
|
|
+
|
|
|
+## Story
|
|
|
+
|
|
|
+作为测试开发者,
|
|
|
+我想要验证后台创建订单后企业小程序的数据同步,
|
|
|
+以便确保跨端数据同步的正确性和时效性。
|
|
|
+
|
|
|
+## Acceptance Criteria
|
|
|
+
|
|
|
+### AC1: 后台创建订单基础流程
|
|
|
+**Given** 后台订单管理功能已完成(Epic 10)
|
|
|
+**When** 测试者在管理后台创建订单
|
|
|
+**Then** 测试应验证以下功能:
|
|
|
+- 使用 OrderManagementPage 创建测试订单
|
|
|
+- 填写订单名称、预计开始日期等必填字段
|
|
|
+- 选择平台和公司(如适用)
|
|
|
+- 验证订单在后台列表中显示
|
|
|
+- 获取创建订单的 ID 和关键信息
|
|
|
+
|
|
|
+### AC2: 企业小程序数据同步验证
|
|
|
+**Given** 管理后台已创建订单
|
|
|
+**When** 企业用户登录小程序
|
|
|
+**Then** 测试应验证以下功能:
|
|
|
+- 使用 EnterpriseMiniPage 登录企业小程序
|
|
|
+- 验证新创建的订单在小程序订单列表中显示
|
|
|
+- 验证订单信息完整(订单名称、状态、日期等)
|
|
|
+- 验证数据同步在合理时间内完成(≤ 10 秒)
|
|
|
+
|
|
|
+### AC3: WebSocket 通信验证(可选)
|
|
|
+**Given** 小程序支持 WebSocket 实时更新
|
|
|
+**When** 订单创建后
|
|
|
+**Then** 测试应验证以下功能:
|
|
|
+- 监听 WebSocket 连接(`/mini-ws` 端点)
|
|
|
+- 验证订单创建消息正确推送
|
|
|
+- 验证消息格式符合预期
|
|
|
+
|
|
|
+### AC4: 多 Page 对象管理
|
|
|
+**Given** 测试需要同时操作后台和小程序
|
|
|
+**When** 执行跨端测试
|
|
|
+**Then** 测试应满足以下要求:
|
|
|
+- 使用独立的 Page 对象管理后台和小程序
|
|
|
+- 正确管理多个 browser context 或 page
|
|
|
+- 确保测试之间不会相互干扰
|
|
|
+
|
|
|
+### AC5: 测试数据隔离和清理
|
|
|
+**Given** 跨端测试涉及多个系统
|
|
|
+**When** 执行测试
|
|
|
+**Then** 测试应遵循以下策略:
|
|
|
+- 使用唯一标识符创建测试订单(避免冲突)
|
|
|
+- 测试后清理创建的订单数据
|
|
|
+- 验证清理后小程序列表不再显示该订单
|
|
|
+
|
|
|
+### AC6: 数据同步时效性验证
|
|
|
+**Given** 网络延迟和系统负载
|
|
|
+**When** 创建订单后查询小程序
|
|
|
+**Then** 测试应验证以下场景:
|
|
|
+- 在正常情况下,数据应在 5 秒内同步
|
|
|
+- 支持轮询等待机制(最多等待 10 秒)
|
|
|
+- 验证超时情况下的错误处理
|
|
|
+
|
|
|
+### AC7: 代码质量标准
|
|
|
+**Given** 遵循项目测试规范
|
|
|
+**When** 编写测试代码
|
|
|
+**Then** 代码应符合以下标准:
|
|
|
+- 使用 TIMEOUTS 常量定义超时
|
|
|
+- 使用 data-testid 选择器(优先级高于文本选择器)
|
|
|
+- 测试文件命名:`order-create-sync.spec.ts`
|
|
|
+- 完整的测试描述和注释
|
|
|
+- TypeScript 类型安全
|
|
|
+- 通过 `pnpm typecheck` 类型检查
|
|
|
+
|
|
|
+## Tasks / Subtasks
|
|
|
+
|
|
|
+- [ ] 任务 1: 创建跨端测试文件和基础设施 (AC: #4, #7)
|
|
|
+ - [ ] 1.1 创建 `web/tests/e2e/specs/cross-platform/order-create-sync.spec.ts`
|
|
|
+ - [ ] 1.2 配置测试 fixtures(adminLoginPage, orderManagementPage, enterpriseMiniPage)
|
|
|
+ - [ ] 1.3 添加测试前置条件(需要测试平台和公司数据)
|
|
|
+
|
|
|
+- [ ] 任务 2: 实现后台创建订单测试 (AC: #1)
|
|
|
+ - [ ] 2.1 编写"后台创建订单成功"测试
|
|
|
+ - [ ] 2.2 验证订单在后台列表中显示
|
|
|
+ - [ ] 2.3 获取并存储订单 ID 和关键信息
|
|
|
+
|
|
|
+- [ ] 任务 3: 实现企业小程序验证测试 (AC: #2)
|
|
|
+ - [ ] 3.1 编写"企业小程序显示新订单"测试
|
|
|
+ - [ ] 3.2 验证订单信息完整性
|
|
|
+ - [ ] 3.3 实现数据同步等待机制
|
|
|
+
|
|
|
+- [ ] 任务 4: 实现 WebSocket 通信验证(可选)(AC: #3)
|
|
|
+ - [ ] 4.1 添加 WebSocket 监听器(如支持)
|
|
|
+ - [ ] 4.2 验证消息推送格式
|
|
|
+ - [ ] 4.3 验证消息内容正确性
|
|
|
+
|
|
|
+- [ ] 任务 5: 实现测试数据清理策略 (AC: #5)
|
|
|
+ - [ ] 5.1 添加 afterEach 钩子清理订单数据
|
|
|
+ - [ ] 5.2 验证清理后小程序不再显示该订单
|
|
|
+
|
|
|
+- [ ] 任务 6: 实现数据同步时效性验证 (AC: #6)
|
|
|
+ - [ ] 6.1 实现轮询等待机制
|
|
|
+ - [ ] 6.2 验证正常同步时间(≤ 5 秒)
|
|
|
+ - [ ] 6.3 验证超时处理(最长 10 秒)
|
|
|
+
|
|
|
+- [ ] 任务 7: 验证代码质量 (AC: #7)
|
|
|
+ - [ ] 7.1 运行 `pnpm typecheck` 验证类型检查
|
|
|
+ - [ ] 7.2 运行测试确保所有测试通过
|
|
|
+ - [ ] 7.3 验证选择器使用 data-testid
|
|
|
+
|
|
|
+## Dev Notes
|
|
|
+
|
|
|
+### Epic 13 背景和依赖
|
|
|
+
|
|
|
+**Epic 13: 跨端数据同步测试 (Epic E)**
|
|
|
+
|
|
|
+- **目标**: 验证后台操作后小程序端的数据同步,覆盖完整的业务流程
|
|
|
+- **业务分组**: Epic E(跨端数据同步测试)
|
|
|
+- **背景**: 真实用户旅程跨越管理后台和小程序,需要验证数据同步的正确性和时效性
|
|
|
+- **依赖**:
|
|
|
+ - Epic 10: ✅ 已完成(订单管理 E2E 测试)
|
|
|
+ - Epic 12: 🔄 进行中(小程序登录测试)
|
|
|
+
|
|
|
+**Epic 13 Story 依赖关系:**
|
|
|
+```
|
|
|
+Story 13.1: 后台创建订单 → 企业小程序验证 ← 当前 Story
|
|
|
+Story 13.2: 后台编辑订单 → 企业小程序验证
|
|
|
+Story 13.3: 后台添加人员 → 人才小程序验证
|
|
|
+Story 13.4: 后台更新状态 → 双小程序验证
|
|
|
+Story 13.5: 跨端测试稳定性验证
|
|
|
+```
|
|
|
+
|
|
|
+### Epic 10 关键经验(订单管理)
|
|
|
+
|
|
|
+从已完成的 Epic 10 中学习到的订单管理模式:
|
|
|
+
|
|
|
+**OrderManagementPage 可用方法:**
|
|
|
+```typescript
|
|
|
+// 页面导航
|
|
|
+async goto(): Promise<void>
|
|
|
+async expectToBeVisible(): Promise<void>
|
|
|
+
|
|
|
+// 订单 CRUD
|
|
|
+async createOrder(data: OrderData): Promise<FormSubmitResult>
|
|
|
+async editOrder(orderId: string, data: OrderData): Promise<FormSubmitResult>
|
|
|
+async deleteOrder(orderId: string): Promise<FormSubmitResult>
|
|
|
+async orderExists(orderName: string): Promise<boolean>
|
|
|
+
|
|
|
+// 订单详情
|
|
|
+async openDetailDialog(orderName: string): Promise<void>
|
|
|
+async getOrderDetailInfo(): Promise<OrderData>
|
|
|
+```
|
|
|
+
|
|
|
+**订单数据结构:**
|
|
|
+```typescript
|
|
|
+interface OrderData {
|
|
|
+ name: string; // 订单名称
|
|
|
+ expectedStartDate?: string; // 预计开始日期
|
|
|
+ platformId?: number; // 平台ID
|
|
|
+ companyId?: number; // 公司ID
|
|
|
+ channelId?: number; // 渠道ID
|
|
|
+ status?: string; // 订单状态
|
|
|
+ workStatus?: string; // 工作状态
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**订单创建模式:**
|
|
|
+```typescript
|
|
|
+const orderData = {
|
|
|
+ name: `跨端测试订单_${Date.now()}`,
|
|
|
+ expectedStartDate: '2026-01-15',
|
|
|
+ platformId: 1,
|
|
|
+ companyId: 1,
|
|
|
+};
|
|
|
+
|
|
|
+const result = await orderManagementPage.createOrder(orderData);
|
|
|
+expect(result.success).toBe(true);
|
|
|
+```
|
|
|
+
|
|
|
+### Epic 12 关键经验(小程序登录)
|
|
|
+
|
|
|
+从已完成的 Epic 12 中学习到的小程序模式:
|
|
|
+
|
|
|
+**EnterpriseMiniPage 可用方法:**
|
|
|
+```typescript
|
|
|
+// 页面导航
|
|
|
+async goto(): Promise<void>
|
|
|
+async expectToBeVisible(): Promise<void>
|
|
|
+
|
|
|
+// 登录方法
|
|
|
+async login(phone: string, password: string): Promise<void>
|
|
|
+
|
|
|
+// Token 管理
|
|
|
+async getToken(): Promise<string | null>
|
|
|
+async setToken(token: string): Promise<void>
|
|
|
+async clearAuth(): Promise<void>
|
|
|
+
|
|
|
+// 订单列表(待实现)
|
|
|
+async getOrderList(): Promise<OrderData[]>
|
|
|
+async waitForOrderToAppear(orderName: string, timeout?: number): Promise<boolean>
|
|
|
+```
|
|
|
+
|
|
|
+**企业小程序登录流程:**
|
|
|
+```typescript
|
|
|
+await enterpriseMiniPage.goto();
|
|
|
+await enterpriseMiniPage.login(
|
|
|
+ '13800138000', // 企业用户手机号
|
|
|
+ 'password123'
|
|
|
+);
|
|
|
+await enterpriseMiniPage.expectLoginSuccess();
|
|
|
+```
|
|
|
+
|
|
|
+### 多 Page 对象管理策略
|
|
|
+
|
|
|
+**方案 1: 使用多个 Browser Context(推荐)**
|
|
|
+```typescript
|
|
|
+import { test as base } from '@playwright/test';
|
|
|
+
|
|
|
+type CrossPlatformFixtures = {
|
|
|
+ adminPage: Page;
|
|
|
+ miniPage: Page;
|
|
|
+ orderManagementPage: OrderManagementPage;
|
|
|
+ enterpriseMiniPage: EnterpriseMiniPage;
|
|
|
+};
|
|
|
+
|
|
|
+export const test = base.extend<CrossPlatformFixtures>({
|
|
|
+ adminPage: async ({ browser }, use) => {
|
|
|
+ const context = await browser.newContext();
|
|
|
+ const page = await context.newPage();
|
|
|
+ await use(page);
|
|
|
+ await context.close();
|
|
|
+ },
|
|
|
+ miniPage: async ({ browser }, use) => {
|
|
|
+ const context = await browser.newContext();
|
|
|
+ const page = await context.newPage();
|
|
|
+ await use(page);
|
|
|
+ await context.close();
|
|
|
+ },
|
|
|
+ orderManagementPage: async ({ adminPage }, use) => {
|
|
|
+ const page = new OrderManagementPage(adminPage);
|
|
|
+ await use(page);
|
|
|
+ },
|
|
|
+ enterpriseMiniPage: async ({ miniPage }, use) => {
|
|
|
+ const page = new EnterpriseMiniPage(miniPage);
|
|
|
+ await use(page);
|
|
|
+ },
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+**方案 2: 使用单个 Browser Context,多个 Page(备选)**
|
|
|
+```typescript
|
|
|
+test('跨端数据同步测试', async ({ page }) => {
|
|
|
+ // 后台操作
|
|
|
+ const adminPage = page;
|
|
|
+ const orderManagementPage = new OrderManagementPage(adminPage);
|
|
|
+
|
|
|
+ // 小程序操作(使用新的 page)
|
|
|
+ const miniContext = await adminPage.context().browser()?.newContext();
|
|
|
+ const miniPage = await miniContext?.newPage();
|
|
|
+ const enterpriseMiniPage = new EnterpriseMiniPage(miniPage!);
|
|
|
+ // ...
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+### 数据同步等待策略
|
|
|
+
|
|
|
+**轮询等待模式:**
|
|
|
+```typescript
|
|
|
+async waitForOrderToAppear(
|
|
|
+ orderName: string,
|
|
|
+ timeout: number = 10000
|
|
|
+): Promise<boolean> {
|
|
|
+ const startTime = Date.now();
|
|
|
+ while (Date.now() - startTime < timeout) {
|
|
|
+ const orders = await this.getOrderList();
|
|
|
+ if (orders.some(o => o.name === orderName)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ await this.page.waitForTimeout(500); // 每 500ms 检查一次
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+**使用 Playwright 的等待机制:**
|
|
|
+```typescript
|
|
|
+await expect(async () => {
|
|
|
+ const orders = await enterpriseMiniPage.getOrderList();
|
|
|
+ expect(orders.some(o => o.name === orderName)).toBe(true);
|
|
|
+}).toPass({
|
|
|
+ timeout: 10000,
|
|
|
+ intervals: [500, 1000], // 检查间隔
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+### WebSocket 通信验证(可选)
|
|
|
+
|
|
|
+**WebSocket 监听器:**
|
|
|
+```typescript
|
|
|
+// 监听 WebSocket 连接
|
|
|
+page.on('websocket', ws => {
|
|
|
+ ws.on('framereceived', frame => {
|
|
|
+ if (frame.payload) {
|
|
|
+ const message = JSON.parse(frame.payload.toString());
|
|
|
+ // 验证消息格式
|
|
|
+ expect(message).toHaveProperty('type', 'order_created');
|
|
|
+ expect(message.data).toHaveProperty('orderId');
|
|
|
+ }
|
|
|
+ });
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+### 测试数据准备策略
|
|
|
+
|
|
|
+**前置条件:**
|
|
|
+1. 需要测试平台数据(使用 Story 11.2 创建的平台)
|
|
|
+2. 需要测试公司数据(使用 Story 11.5 创建的公司)
|
|
|
+3. 需要企业用户数据(使用 Story 12.2 创建的企业用户)
|
|
|
+
|
|
|
+**测试数据唯一性:**
|
|
|
+```typescript
|
|
|
+const timestamp = Date.now();
|
|
|
+const orderData = {
|
|
|
+ name: `跨端同步测试_${timestamp}`,
|
|
|
+ expectedStartDate: '2026-01-15',
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+### 测试数据清理策略
|
|
|
+
|
|
|
+**清理方法:**
|
|
|
+```typescript
|
|
|
+test.afterEach(async ({ orderManagementPage, orderName }) => {
|
|
|
+ // 在后台删除测试订单
|
|
|
+ await orderManagementPage.goto();
|
|
|
+ await orderManagementPage.deleteOrder(orderName);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+**验证清理成功:**
|
|
|
+```typescript
|
|
|
+test('清理后小程序不显示订单', async ({ enterpriseMiniPage, orderName }) => {
|
|
|
+ const orders = await enterpriseMiniPage.getOrderList();
|
|
|
+ expect(orders.some(o => o.name === orderName)).toBe(false);
|
|
|
+});
|
|
|
+```
|
|
|
+
|
|
|
+### 小程序订单列表页面(待实现)
|
|
|
+
|
|
|
+**预期页面元素:**
|
|
|
+- 订单列表容器
|
|
|
+- 订单项(订单名称、状态、日期)
|
|
|
+- 订单详情按钮
|
|
|
+- 筛选器(可选)
|
|
|
+
|
|
|
+**如页面未实现,需要添加 data-testid:**
|
|
|
+```typescript
|
|
|
+private readonly selectors = {
|
|
|
+ orderList: '[data-testid="mini-order-list"]',
|
|
|
+ orderItem: '[data-testid="mini-order-item"]',
|
|
|
+ orderName: '[data-testid="mini-order-name"]',
|
|
|
+ orderStatus: '[data-testid="mini-order-status"]',
|
|
|
+ orderDate: '[data-testid="mini-order-date"]',
|
|
|
+};
|
|
|
+```
|
|
|
+
|
|
|
+### 项目结构
|
|
|
+
|
|
|
+**新建文件:**
|
|
|
+- `web/tests/e2e/specs/cross-platform/order-create-sync.spec.ts`
|
|
|
+- `web/tests/e2e/pages/mini/enterprise-mini.page.ts`(扩展订单列表方法)
|
|
|
+
|
|
|
+**相关参考文件:**
|
|
|
+- `web/tests/e2e/pages/admin/order-management.page.ts` (Epic 10)
|
|
|
+- `web/tests/e2e/pages/mini/enterprise-mini.page.ts` (Epic 12)
|
|
|
+- `web/tests/e2e/specs/admin/order-create.spec.ts` (Epic 10)
|
|
|
+
|
|
|
+### 选择器策略
|
|
|
+
|
|
|
+**优先级(遵循项目标准):**
|
|
|
+1. `data-testid` 属性(最高优先级)
|
|
|
+2. ARIA 属性 + role
|
|
|
+3. 文本内容(最低优先级,避免使用)
|
|
|
+
|
|
|
+**需要在小程序页面添加 data-testid:**
|
|
|
+- `mini-order-list` - 订单列表容器
|
|
|
+- `mini-order-item` - 订单项(可动态生成)
|
|
|
+- `mini-order-name` - 订单名称
|
|
|
+- `mini-order-status` - 订单状态
|
|
|
+
|
|
|
+### TypeScript 类型定义
|
|
|
+
|
|
|
+**跨端测试数据类型:**
|
|
|
+```typescript
|
|
|
+interface CrossPlatformOrderData {
|
|
|
+ /** 后台订单 ID */
|
|
|
+ adminOrderId: string;
|
|
|
+ /** 订单名称 */
|
|
|
+ orderName: string;
|
|
|
+ /** 订单状态 */
|
|
|
+ status: string;
|
|
|
+ /** 创建时间 */
|
|
|
+ createdAt: string;
|
|
|
+}
|
|
|
+
|
|
|
+interface SyncVerificationResult {
|
|
|
+ /** 同步是否成功 */
|
|
|
+ synced: boolean;
|
|
|
+ /** 同步耗时(毫秒) */
|
|
|
+ syncTime: number;
|
|
|
+ /** 后台订单数据 */
|
|
|
+ adminOrder: OrderData;
|
|
|
+ /** 小程序订单数据 */
|
|
|
+ miniOrder?: OrderData;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### 测试超时配置
|
|
|
+
|
|
|
+**使用 TIMEOUTS 常量:**
|
|
|
+```typescript
|
|
|
+import { TIMEOUTS } from '../../utils/timeouts';
|
|
|
+
|
|
|
+// 数据同步等待时间
|
|
|
+const SYNC_TIMEOUT = TIMEOUTS.networkIdle; // 10000ms
|
|
|
+
|
|
|
+// 轮询检查间隔
|
|
|
+const POLL_INTERVAL = 500;
|
|
|
+```
|
|
|
+
|
|
|
+### 调试技巧
|
|
|
+
|
|
|
+**跨端测试调试:**
|
|
|
+1. 使用 `page.screenshot()` 在关键步骤截图
|
|
|
+2. 使用 `console.debug()` 输出订单信息
|
|
|
+3. 分别记录后台和小程序的操作时间
|
|
|
+
|
|
|
+**同步问题调试:**
|
|
|
+- 检查网络请求(使用 Playwright 的 network 监听)
|
|
|
+- 检查 WebSocket 连接(如使用)
|
|
|
+- 检查 localStorage 和 sessionStorage(token 存储)
|
|
|
+
|
|
|
+### 参考文档
|
|
|
+
|
|
|
+**架构文档:**
|
|
|
+- `_bmad-output/planning-artifacts/epics.md#Epic 13`
|
|
|
+- `_bmad-output/project-context.md`
|
|
|
+- `docs/standards/e2e-radix-testing.md`
|
|
|
+
|
|
|
+**相关 Story 文档:**
|
|
|
+- `10-1-order-page-object.md` (订单管理 Page Object)
|
|
|
+- `10-4-order-create-tests.md` (创建订单测试)
|
|
|
+- `12-4-enterprise-mini-page-object.md` (企业小程序 Page Object)
|
|
|
+- `12-5-enterprise-mini-login.md` (企业小程序登录测试)
|
|
|
+
|
|
|
+## Dev Agent Record
|
|
|
+
|
|
|
+### Agent Model Used
|
|
|
+
|
|
|
+_Created by create-story workflow_
|
|
|
+
|
|
|
+### Debug Log References
|
|
|
+
|
|
|
+_Implementation phase - no debug yet_
|
|
|
+
|
|
|
+### Completion Notes List
|
|
|
+
|
|
|
+_Ready for development - Status: ready-for-dev_
|
|
|
+
|
|
|
+### File List
|
|
|
+
|
|
|
+_Artifact file: `/mnt/code/188-179-template-6/_bmad-output/implementation-artifacts/13-1-order-create-sync.md`_
|
|
|
+
|
|
|
+## Change Log
|
|
|
+
|
|
|
+- 2026-01-14: Story 13.1 创建完成
|
|
|
+ - 后台创建订单 → 企业小程序验证需求
|
|
|
+ - 多 Page 对象管理策略
|
|
|
+ - 数据同步等待策略
|
|
|
+ - 状态:ready-for-dev
|