Status: ready-for-dev
作为测试开发者, 我想要编写创建订单的 E2E 测试, 以便验证订单创建功能。
Given 订单管理 Page Object 已创建 When 编写创建订单测试用例 Then 包含以下测试场景:
基本创建订单
创建订单并选择平台
selectRadixOption 或 selectRadixOptionAsync创建订单并选择公司
创建订单并选择渠道
创建完整订单(所有字段)
表单验证测试
测试文件: web/tests/e2e/specs/admin/order-create.spec.ts
web/tests/e2e/specs/admin/order-create.spec.tsselectRadixOption 工具Epic 10: 订单管理 E2E 测试 (Epic C - 业务测试 Epic)
依赖:
Page Object 已有的创建订单功能:
web/tests/e2e/pages/admin/order-management.page.ts 包含以下方法:
打开创建对话框:
openCreateDialog() - 打开创建订单对话框addOrderButton - 创建订单按钮选择器(data-testid="create-order-button")填写表单:
fillOrderForm(data: OrderData) - 填写订单表单提交表单:
submitForm(): Promise<FormSubmitResult> - 提交表单完整流程:
createOrder(data: OrderData): Promise<FormSubmitResult> - 创建订单(完整流程)orderExists(orderName: string): Promise<boolean> - 验证订单是否存在对话框控制:
cancelDialog() - 取消对话框waitForDialogClosed() - 等待对话框关闭interface OrderData {
name: string; // 订单名称(必填)
expectedStartDate?: string; // 预计开始日期(必填)
platformName?: string; // 平台名称(可选)
companyName?: string; // 公司名称(可选)
channelName?: string; // 渠道名称(可选)
status?: OrderStatus; // 订单状态(编辑模式)
workStatus?: WorkStatus; // 工作状态(编辑模式)
}
// 订单状态常量
ORDER_STATUS = {
DRAFT: 'draft',
CONFIRMED: 'confirmed',
IN_PROGRESS: 'in_progress',
COMPLETED: 'completed',
}
// 订单状态显示名称
ORDER_STATUS_LABELS = {
draft: '草稿',
confirmed: '已确认',
in_progress: '进行中',
completed: '已完成',
}
参考 Story 10.2(订单列表查看测试)和 Story 10.3(订单筛选测试)的测试结构:
import { test, expect } from '../../utils/test-setup';
import { readFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { selectRadixOption } from '@d8d/e2e-test-utils';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const testUsers = JSON.parse(readFileSync(join(__dirname, '../../fixtures/test-users.json'), 'utf-8'));
test.describe.serial('创建订单测试', () => {
test.beforeEach(async ({ adminLoginPage, orderManagementPage }) => {
await adminLoginPage.goto();
await adminLoginPage.login(testUsers.admin.username, testUsers.admin.password);
await adminLoginPage.expectLoginSuccess();
await orderManagementPage.goto();
});
test.describe('基本创建订单', () => {
test('应该能创建只填写必填字段的订单', async ({ orderManagementPage, page }) => {
// 生成唯一订单名称
const orderName = `测试订单_${Date.now()}`;
// 创建订单
const result = await orderManagementPage.createOrder({
name: orderName,
expectedStartDate: '2025-01-15',
});
// 验证创建成功
expect(result.success).toBe(true);
expect(result.hasSuccess).toBe(true);
// 验证订单出现在列表中
await expect(async () => {
const exists = await orderManagementPage.orderExists(orderName);
expect(exists).toBe(true);
}).toPass({ timeout: 5000 });
});
});
// 更多测试...
});
创建订单对话框结构(基于 Page Object 分析):
data-testid="create-order-button")role="dialog"label 包含 "订单名称" 或 "名称"label 包含 "预计开始日期" 或 "开始日期"selectRadixOption 工具selectRadixOption 工具selectRadixOption 工具role="button", name="创建"role="button", name="取消"导入依赖:
import { test, expect } from '../../utils/test-setup';
import { OrderManagementPage, ORDER_STATUS, WORK_STATUS } from '../../pages/admin/order-management.page';
import { selectRadixOption } from '@d8d/e2e-test-utils';
测试 Fixtures:
adminLoginPage fixture 进行登录orderManagementPage fixture 操作页面page fixture 直接操作页面元素test.describe.serial() 确保测试按顺序执行断言策略:
FormSubmitResult.success 为 trueFormSubmitResult.hasSuccess 为 trueorderExists() 方法)数据唯一性:
Date.now() 生成唯一订单名称,避免测试数据冲突const orderName = '测试订单_' + Date.now();Select 工具使用(来自 Epic 1, 2):
import { selectRadixOption } from '@d8d/e2e-test-utils';
// 静态选择器(平台、公司、渠道)
await selectRadixOption(page, '平台', '58同城');
await selectRadixOption(page, '公司', '测试公司');
await selectRadixOption(page, '渠道', '网络招聘');
Page Object 使用:
// 方式1:使用完整流程方法
const result = await orderManagementPage.createOrder({
name: '测试订单',
expectedStartDate: '2025-01-15',
platformName: '58同城',
});
// 方式2:分步操作(适合需要验证中间状态)
await orderManagementPage.openCreateDialog();
await orderManagementPage.fillOrderForm({ name: '测试订单' });
const result = await orderManagementPage.submitForm();
await orderManagementPage.waitForDialogClosed();
遵循以下测试标准文档:
docs/standards/testing-standards.md - 测试规范docs/standards/web-ui-testing-standards.md - Web UI 测试规范docs/standards/e2e-radix-testing.md - Radix UI E2E 测试标准选择器优先级:
data-testid - 最高优先级aria-label + roletext content + role - 兜底文件位置:
web/tests/e2e/specs/admin/order-create.spec.tsweb/tests/e2e/pages/admin/order-management.page.tsweb/tests/e2e/fixtures/对齐统一项目结构:
project-context.md 中的 TypeScript 严格模式规则any 类型# 在 web 目录下运行单个测试文件
cd web
pnpm test:e2e:chromium order-create
# 快速失败模式(推荐调试时使用)
timeout 60 pnpm test:e2e:chromium order-create
数据唯一性:
Date.now() 或 crypto.randomUUID() 生成唯一订单名称必填字段:
可选字段:
表单验证:
data-sonner-toast 属性定位Toast 消息:
[data-sonner-toast][data-type="success"][data-sonner-toast][data-type="error"]网络响应:
submitForm() 方法已监听网络响应waitForLoadState('networkidle') 等待网络请求完成基本创建:
平台选择:
公司选择:
渠道选择:
完整订单:
表单验证:
从 Story 10.3(订单筛选测试)学到的经验:
使用辅助函数消除代码重复:
// 提取辅助函数
async function applyFilters(page) {
await page.getByTestId('search-button').click();
await page.waitForLoadState('networkidle', { timeout: 5000 }).catch(() => {
console.debug('筛选后没有网络请求或请求已完成');
});
}
async function getTableRowCount(page): Promise<number> {
const tbody = page.locator('table tbody');
const rows = tbody.locator('tr');
return await rows.count();
}
function formatLocalDate(date: Date): string {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
使用 try-catch 处理空数据场景:
// 如果数据库中没有测试数据,优雅处理
try {
await selectRadixOption(page, '平台', '58同城');
// 验证结果
} catch (error) {
console.debug('平台数据不存在或选择器无选项:', error);
}
验证实际数据变化,而非仅检查元素可见:
// 验证行数变化,而非仅检查表格可见
expect(filteredCount).toBeLessThanOrEqual(initialCount);
使用 .toPass() 进行条件等待:
// 等待异步操作完成,最多重试 5 秒
await expect(async () => {
const exists = await orderManagementPage.orderExists(orderName);
expect(exists).toBe(true);
}).toPass({ timeout: 5000 });
claude-opus-4-5-20251101