10-6-order-delete-tests.md 14 KB

Story 10.6: 编写删除订单测试

Status: ready-for-dev

Story

作为测试开发者, 我想要编写删除订单的 E2E 测试, 以便验证订单删除功能和相关约束。

Acceptance Criteria

Given 编辑订单测试已通过 When 编写删除订单测试用例 Then 包含以下测试场景:

  1. 删除草稿状态订单

    • 删除草稿状态的订单
    • 验证删除确认对话框显示
    • 确认删除
    • 验证删除成功后列表中不再显示
  2. 删除有关联人员的订单

    • 尝试删除已关联人员的订单
    • 验证错误提示或级联删除行为
    • 根据实际业务逻辑验证结果
  3. 取消删除

    • 点击删除按钮
    • 在确认对话框中点击取消
    • 验证订单未被删除

测试文件: web/tests/e2e/specs/admin/order-delete.spec.ts

Tasks / Subtasks

  • 创建删除订单测试文件 (AC: When)
    • 创建 web/tests/e2e/specs/admin/order-delete.spec.ts
    • 导入必要的测试依赖(Playwright fixtures、OrderManagementPage)
    • 配置测试文件的基本结构
  • 编写删除草稿订单测试 (AC: Then #1)
    • 创建草稿状态的测试订单
    • 测试打开删除确认对话框
    • 测试确认删除操作
    • 验证删除后订单不再存在于列表中
    • 验证 Toast 成功消息显示
  • 编写删除有关联人员订单测试 (AC: Then #2)
    • 创建订单并添加人员
    • 测试删除有关联人员的订单
    • 验证错误提示或级联删除行为
    • 根据实际业务逻辑断言结果
  • 编写取消删除测试 (AC: Then #3)
    • 创建测试订单
    • 测试打开删除确认对话框
    • 测试点击取消按钮
    • 验证订单仍然存在于列表中
  • 确保所有测试通过 (AC: And)

Dev Notes

Epic Context

Epic 10: 订单管理 E2E 测试 (Epic C - 业务测试 Epic)

  • 目标: 测试开发者可以为订单管理功能编写完整的 E2E 测试,验证订单的 CRUD、状态流转、人员关联和附件管理功能
  • 业务分组: Epic C(业务测试 Epic)
  • 背景: 订单管理是招聘系统的核心业务功能,涉及复杂表单(多选择器联动)、状态流转、人员关联等场景
  • 模式: 业务测试为主,工具包支持为辅(遵循 Epic A 成功模式)

依赖:

  • Epic 1: ✅ 已完成(Select 工具基础框架)
  • Epic 2: ✅ 已完成(Select 工具在真实 E2E 测试中验证)
  • Story 10.1: ✅ 已完成(订单管理 Page Object)
  • Story 10.2: ✅ 已完成(订单列表查看测试)
  • Story 10.3: ✅ 已完成(订单搜索和筛选测试)
  • Story 10.4: ✅ 已完成(创建订单测试)
  • Story 10.5: ✅ 已完成(编辑订单测试)

前序 Story 情报 (Story 10.1-10.5)

Page Object 已有的删除功能:

web/tests/e2e/pages/admin/order-management.page.ts 包含以下删除相关方法:

  1. 打开删除确认对话框:

    async openDeleteDialog(orderName: string): Promise<void>
    
    • 找到订单行并点击删除按钮
    • 等待确认对话框出现(role="alertdialog"
  2. 确认删除:

    async confirmDelete(): Promise<void>
    
    • 点击"确认删除"按钮
    • 等待对话框关闭和网络请求完成
  3. 取消删除:

    async cancelDelete(): Promise<void>
    
    • 点击删除确认对话框中的"取消"按钮
    • 等待对话框关闭
  4. 删除订单完整流程:

    async deleteOrder(orderName: string): Promise<boolean>
    
    • 组合方法:打开对话框 → 确认删除 → 检查 Toast 消息
    • 返回是否成功删除(基于 Toast 消息)
  5. 验证订单存在:

    async orderExists(orderName: string): Promise<boolean>
    
    • 检查订单是否存在于列表中
  6. 创建订单:

    async createOrder(data: OrderData): Promise<FormSubmitResult>
    
    • 用于创建测试数据

测试数据策略

创建测试订单: 删除测试需要先有可删除的订单。参考 Story 10.3、10.4、10.5 的测试数据策略:

// 使用时间戳创建唯一订单名称
const timestamp = Date.now();
const testOrderName = `删除测试订单_${timestamp}`;

// 先创建订单
await orderManagementPage.createOrder({
  name: testOrderName,
  expectedStartDate: '2025-01-15',
});

创建有关联人员的订单(测试场景 2):

// 创建订单并添加人员
await orderManagementPage.createOrder({
  name: testOrderName,
  expectedStartDate: '2025-01-15',
});

// 打开人员管理对话框
await orderManagementPage.openPersonManagementDialog(testOrderName);

// 添加人员
await orderManagementPage.addPersonToOrder({
  disabledPersonName: '测试人员',
  hireDate: '2025-01-20',
  salary: 5000,
});

测试文件结构参考

参考 Story 10.4(创建订单测试)和 Story 10.5(编辑订单测试)的测试结构:

import { test, expect } from '../../utils/test-setup';
import { OrderManagementPage } from '../../pages/admin/order-management.page';
import { ORDER_STATUS } from '../../pages/admin/order-management.page';

test.describe.serial('删除订单测试', () => {
  let testOrderName: string;

  test.beforeEach(async ({ adminLoginPage, orderManagementPage }) => {
    await adminLoginPage.goto();
    await adminLoginPage.login('admin', 'admin123');
    await orderManagementPage.goto();

    // 创建测试订单
    const timestamp = Date.now();
    testOrderName = `删除测试_${timestamp}`;
    await orderManagementPage.createOrder({
      name: testOrderName,
      expectedStartDate: '2025-01-15',
    });
  });

  test.describe('删除草稿状态订单', () => {
    test('应该成功删除草稿订单', async ({ orderManagementPage }) => {
      // 打开删除确认对话框
      await orderManagementPage.openDeleteDialog(testOrderName);

      // 确认删除
      await orderManagementPage.confirmDelete();

      // 验证订单不再存在
      expect(await orderManagementPage.orderExists(testOrderName)).toBe(false);
    });

    test('应该在删除后显示成功提示', async ({ orderManagementPage }) => {
      await orderManagementPage.deleteOrder(testOrderName);

      // 验证 Toast 成功消息
      const successToast = orderManagementPage.page.locator('[data-sonner-toast][data-type="success"]');
      await expect(successToast).toBeVisible();
    });
  });

  test.describe('取消删除', () => {
    test('应该能在确认对话框中取消删除', async ({ orderManagementPage }) => {
      // 打开删除确认对话框
      await orderManagementPage.openDeleteDialog(testOrderName);

      // 取消删除
      await orderManagementPage.cancelDelete();

      // 验证订单仍然存在
      expect(await orderManagementPage.orderExists(testOrderName)).toBe(true);
    });
  });
});

技术要求

导入依赖:

import { test, expect } from '../../utils/test-setup';
import { OrderManagementPage, ORDER_STATUS } from '../../pages/admin/order-management.page';

测试 Fixtures:

  • 使用 adminLoginPage fixture 进行登录
  • 使用 orderManagementPage fixture 操作页面
  • 使用 test.describe.serial() 确保测试按顺序执行

断言策略:

  • 验证删除确认对话框正确显示
  • 验证删除后订单不再存在于列表中
  • 验证 Toast 成功/错误消息显示
  • 对于有关联人员的订单,根据实际业务逻辑验证结果

业务逻辑说明

删除订单的业务规则(需要验证):

  1. 草稿状态订单:

    • 应该可以直接删除
    • 删除后订单从列表中移除
    • 显示成功提示消息
  2. 有关联人员的订单:

    • 场景 A - 禁止删除: 显示错误提示,不允许删除
    • 场景 B - 级联删除: 删除订单及其关联的人员数据
    • 需要根据实际业务逻辑实现对应测试
  3. 删除确认对话框:

    • 使用 role="alertdialog" 定位
    • 包含"确认删除"和"取消"按钮
    • 点击取消后订单保持不变

测试标准参考

遵循以下测试标准文档:

  • docs/standards/testing-standards.md - 测试规范
  • docs/standards/web-ui-testing-standards.md - Web UI 测试规范
  • docs/standards/e2e-radix-testing.md - Radix UI E2E 测试标准

选择器优先级:

  1. data-testid - 最高优先级
  2. aria-label + role
  3. text content + role - 兜底

Project Structure Notes

文件位置:

  • 测试文件: web/tests/e2e/specs/admin/order-delete.spec.ts
  • Page Object: web/tests/e2e/pages/admin/order-management.page.ts
  • Fixtures 目录: web/tests/e2e/fixtures/

对齐统一项目结构:

  • 遵循 project-context.md 中的 TypeScript 严格模式规则
  • 函数参数、返回值必须有明确类型注解
  • 禁止使用 any 类型
  • 公共 API 必须包含完整 JSDoc 注释

测试运行命令

# 在 web 目录下运行单个测试文件
cd web
pnpm test:e2e:chromium order-delete

# 快速失败模式(推荐调试时使用)
timeout 60 pnpm test:e2e:chromium order-delete

测试注意事项

删除确认对话框交互:

  • 删除按钮可能通过操作菜单访问(参考 Story 10.5 经验)
  • 需要先点击"打开菜单"按钮,再点击"删除"选项
  • 确认对话框使用 role="alertdialog" 定位

测试数据清理:

  • 删除测试会实际删除数据
  • 每个测试应创建独立的测试订单
  • 使用时间戳确保订单名称唯一

断言策略:

  • 验证 confirmDelete() 后列表中订单消失
  • 验证 cancelDelete() 后订单仍然存在
  • 验证 Toast 成功消息显示(用于删除操作)
  • 验证 Toast 错误消息显示(用于删除失败场景)

有关联人员订单的测试:

  • 需要先创建订单并添加人员
  • 根据实际业务逻辑:
    • 如果禁止删除:验证错误消息显示
    • 如果允许删除:验证级联删除成功
  • 可能需要先查看后端 API 或 UI 行为确定业务规则

测试覆盖场景清单

删除草稿状态订单:

  • 打开删除确认对话框
  • 确认删除操作
  • 验证删除后列表中不再显示该订单
  • 验证 Toast 成功消息显示

删除有关联人员的订单:

  • 创建订单并添加人员
  • 尝试删除有关联人员的订单
  • 验证错误提示或级联删除行为(根据实际业务逻辑)

取消删除:

  • 打开删除确认对话框
  • 点击取消按钮
  • 验证订单仍然存在于列表中

边界条件测试(可选):

  • 删除不存在的订单
  • 删除已完成状态的订单
  • 并发删除同一订单

前序 Story 经验总结 (Story 10.2, 10.3, 10.4, 10.5)

从 Story 10.2(订单列表测试)学到的经验:

  • 列表表格使用 table tbody tr 选择器定位行
  • 订单状态徽章使用 getByText 精确匹配
  • 工作状态徽章使用 getByText 精确匹配

从 Story 10.3(订单筛选测试)学到的经验:

  • 对标签使用 locator('label').filter({ hasText: ... }) 避免与表头冲突
  • 对操作添加 waitForLoadState 超时处理(可能不触发网络请求)
  • 使用 selectRadixOption 工具选择下拉选项
  • 日期处理使用本地时间格式

从 Story 10.4(创建订单测试)学到的经验:

  • 使用 createOrder() 方法创建测试数据
  • 测试数据使用时间戳确保唯一性
  • 验证 Toast 消息确认操作结果

从 Story 10.5(编辑订单测试)学到的关键经验:

  1. 订单操作菜单问题: 编辑按钮不是直接在表格中,而是需要先点击"打开菜单"按钮才能看到编辑选项。删除操作可能也是类似的模式。

  2. 删除按钮位置: 检查删除按钮是否也在操作菜单中,如果是,需要:

    // 找到订单行并点击"打开菜单"按钮
    const orderRow = this.orderTable.locator('tbody tr').filter({ hasText: orderName });
    const menuButton = orderRow.getByRole('button', { name: '打开菜单' });
    await menuButton.click();
    
    // 等待菜单出现并点击"删除"选项
    const deleteOption = this.page.getByRole('menuitem', { name: '删除' });
    await deleteOption.click();
    
  3. 网络等待超时问题: waitForLoadState('networkidle') 可能超时,使用 try-catch 包裹处理。

  4. 测试数据管理: 每个测试创建独立的测试订单,避免数据冲突。

关键代码模式

订单数据接口:

interface OrderData {
  name: string;
  expectedStartDate?: string;
  platformId?: number;
  platformName?: string;
  companyId?: number;
  companyName?: string;
  channelId?: number;
  channelName?: string;
  status?: OrderStatus;
  workStatus?: WorkStatus;
}

订单人员数据接口:

interface OrderPersonData {
  disabledPersonId: number;
  disabledPersonName?: string;
  hireDate?: string;
  salary?: number;
  workStatus?: WorkStatus;
  actualHireDate?: string;
  resignDate?: string;
}

References

Dev Agent Record

Agent Model Used

claude-opus-4-5-20251101

Debug Log References

Completion Notes List

File List