Status: review
作为测试开发者, 我想要编写公司创建功能的 E2E 测试, 以便验证公司管理模块的创建功能正常工作。
AC1: 创建测试文件
web/tests/e2e/specs/admin/company-create.spec.tsCompanyManagementPage Page ObjectAC2: 测试基本创建流程(不选择平台)
AC3: 测试完整表单字段(包含平台选择)
AC4: 测试 PlatformSelector 集成
@d8d/e2e-test-utils 的 selectRadixOptionAsync 选择平台AC5: 测试表单验证
AC6: 测试数据唯一性
AC7: 测试取消和关闭操作
AC8: 代码质量标准
[x] 任务 1: 创建测试文件和基础结构 (AC: 1, 8)
web/tests/e2e/specs/admin/company-create.spec.ts[x] 任务 2: 实现测试前置条件 (AC: 1, 2)
test.beforeEach() 登录后台[x] 任务 3: 实现基本创建流程测试(不选择平台) (AC: 2, 6)
[x] 任务 4: 实现完整表单字段测试(包含平台选择) (AC: 3, 4, 6)
createPlatform 方法先创建测试平台selectRadixOptionAsync 选择平台[x] 任务 5: 实现表单验证测试 (AC: 5)
[x] 任务 6: 实现取消和关闭操作测试 (AC: 7)
[x] 任务 7: 实现测试后清理 (AC: 6)
[x] 任务 8: 运行测试并验证 (AC: 2, 3, 4, 5, 6, 7, 8)
pnpm test:e2e:chromium company-create.spec.tsEpic 11: 基础配置管理测试 (Epic F)
为平台、公司、渠道配置管理编写 E2E 测试,为后续用户管理和跨端测试提供必要的测试数据。
实体关系链:
Platform (平台)
↓ 1:N
Company (公司) - 必须 platformId
↓ 1:N
Order (订单) - 必须 companyId
Story 11.5 在 Epic 中的位置:
测试文件结构参考:
参考 Story 11.2(平台创建测试)的结构模式:
web/tests/e2e/specs/admin/platform-create.spec.tsweb/tests/e2e/specs/admin/company-create.spec.ts(当前)标准测试文件结构:
import { test, expect } from '@playwright/test';
import { CompanyManagementPage } from '../../pages/admin/company-management.page';
test.describe('公司创建功能', () => {
test.beforeEach(async ({ adminLoginPage, companyManagementPage }) => {
// 登录后台
await adminLoginPage.goto();
await adminLoginPage.login('admin', 'admin123');
// 导航到公司管理页面
await companyManagementPage.goto();
});
test.afterEach(async ({ companyManagementPage }) => {
// 清理测试数据
});
test('应该成功创建公司', async ({ companyManagementPage }) => {
// 测试逻辑
});
});
测试夹具配置:
Story 11.4 已添加 companyManagementPage fixture 到 web/tests/e2e/utils/test-setup.ts:
export const test = test.extend<{
adminLoginPage: AdminLoginPage;
companyManagementPage: CompanyManagementPage;
}>({
adminLoginPage: async ({ page }, use) => {
await use(new AdminLoginPage(page));
},
companyManagementPage: async ({ page }, use) => {
await use(new CompanyManagementPage(page));
},
});
来自 Story 11.4 的可用方法:
| 方法 | 描述 | 返回值 |
|---|---|---|
goto() |
导航到公司管理页面 | Promise<void> |
createCompany(data, platformName) |
创建公司(完整流程) | Promise<FormSubmitResult> |
editPlatform(name, data) |
编辑公司 | Promise<FormSubmitResult> |
deleteCompany(name) |
删除公司 | Promise<boolean> |
companyExists(name) |
验证公司是否存在 | Promise<boolean> |
createPlatform(data) |
创建平台(用于测试数据准备) | Promise<FormSubmitResult> |
deletePlatform(name) |
删除平台 | Promise<boolean> |
数据接口:
interface CompanyData {
platformId?: number; // 可选,平台ID
companyName: string; // 必填
contactPerson?: string;
contactPhone?: string;
contactEmail?: string;
address?: string;
}
interface PlatformData {
platformName: string; // 必填
contactPerson?: string;
contactPhone?: string;
contactEmail?: string;
}
interface FormSubmitResult {
success: boolean;
hasError: boolean;
hasSuccess: boolean;
errorMessage?: string;
successMessage?: string;
responses?: NetworkResponse[];
}
关键方法签名(来自 Story 11.4):
/**
* 创建公司(完整流程)
* @param data - 公司数据
* @param platformName - 平台名称(用于选择平台)
*/
async createCompany(data: CompanyData, platformName?: string): Promise<FormSubmitResult>
Company Entity (来自 company.entity.ts):
{
id: number; // 主键ID
platformId: number | null; // 平台ID(外键,可选)
companyName: string; // 公司名称(必填,最大100字符)
contactPerson: string | null; // 联系人(可选,最大50字符)
contactPhone: string | null; // 联系电话(可选,最大20字符)
contactEmail: string | null; // 联系邮箱(可选,最大100字符)
address: string | null; // 地址(可选,最大200字符)
status: number; // 状态:1-正常,0-禁用(默认1)
createTime: Date; // 创建时间
updateTime: Date; // 更新时间
platform?: Platform; // 关联的平台信息(eager加载)
}
唯一约束:
idx_company_name_platform (companyName, platformId)页面路径: /admin/companies
创建公司对话框字段(来自 CompanyManagement.tsx):
platformId - PlatformSelector(可选)
create-company-platform-selectorselectRadixOptionAsync 选择companyName - Input(必填)
create-company-name-inputcontactPerson - Input(可选)
create-company-contact-person-inputcontactPhone - Input(可选)
create-company-contact-phone-inputcontactEmail - Input(可选,email 类型)
create-company-contact-email-inputaddress - Input(可选)
create-company-address-input按钮:
data-testid="cancel-company-button"data-testid="submit-create-company-button"对话框标题:
company-modal-title测试用例 1: 基本创建流程(不选择平台)
test('应该成功创建公司(不选择平台)', async ({ companyManagementPage }) => {
const timestamp = Date.now();
const companyName = `测试公司_${timestamp}`;
// 创建公司(不选择平台)
const result = await companyManagementPage.createCompany({
companyName
});
// 验证 API 响应成功
const createResponse = result.responses?.find(r => r.url.includes('createCompany'));
expect(createResponse?.ok).toBe(true);
// 验证公司出现在列表中
const exists = await companyManagementPage.companyExists(companyName);
expect(exists).toBe(true);
// 清理
await companyManagementPage.deleteCompany(companyName);
});
测试用例 2: 完整表单字段(包含平台选择)
test('应该成功创建公司(选择平台并填写所有字段)', async ({ companyManagementPage }) => {
const timestamp = Date.now();
// 首先创建测试平台
const platformName = `测试平台_${timestamp}`;
await companyManagementPage.createPlatform({
platformName,
contactPerson: '测试联系人',
contactPhone: '13800138000',
contactEmail: `test_${timestamp}@example.com`
});
// 创建公司(选择平台)
const companyName = `测试公司_${timestamp}`;
const result = await companyManagementPage.createCompany({
companyName,
contactPerson: '张三',
contactPhone: '13900139000',
contactEmail: `company_${timestamp}@example.com`,
address: '北京市朝阳区'
}, platformName);
// 验证 API 响应成功
const createResponse = result.responses?.find(r => r.url.includes('createCompany'));
expect(createResponse?.ok).toBe(true);
// 验证公司出现在列表中
const exists = await companyManagementPage.companyExists(companyName);
expect(exists).toBe(true);
// 清理(先删除公司,再删除平台)
await companyManagementPage.deleteCompany(companyName);
await companyManagementPage.deletePlatform(platformName);
});
测试用例 3: 表单验证(空公司名称)
test('未填写公司名称时应显示内联验证错误', async ({ companyManagementPage }) => {
// 打开创建对话框
await companyManagementPage.openCreateDialog();
// 不填写任何字段,直接提交
const submitButton = companyManagementPage.page.locator('[data-testid="submit-create-company-button"]');
await submitButton.click();
// 验证对话框仍然打开(表单验证阻止了提交)
const dialog = companyManagementPage.page.locator('[role="dialog"]');
await expect(dialog).toBeVisible();
// 验证内联错误消息显示
const errorMessage = companyManagementPage.page.locator('[data-testid="create-company-name-input"] + div[data-testid="form-message"]');
await expect(errorMessage).toBeVisible();
// 关闭对话框
await companyManagementPage.cancelDialog();
});
测试用例 4: 取消操作
test('应该能取消创建公司操作', async ({ companyManagementPage }) => {
const timestamp = Date.now();
const companyName = `测试公司_${timestamp}`;
// 打开创建对话框并填写表单
await companyManagementPage.openCreateDialog();
await companyManagementPage.page.locator('[data-testid="create-company-name-input"]').fill(companyName);
// 点击取消按钮
await companyManagementPage.cancelDialog();
// 验证对话框关闭
const dialog = companyManagementPage.page.locator('[role="dialog"]');
await expect(dialog).not.toBeVisible();
// 验证公司未被创建
const exists = await companyManagementPage.companyExists(companyName);
expect(exists).toBe(false);
});
使用时间戳确保唯一性:
const timestamp = Date.now();
const uniqueId = `company_test_${timestamp}`;
const companyName = `测试公司_${uniqueId}`;
测试数据清理:
关键注意事项(来自 Story 11.4):
使用 @d8d/e2e-test-utils 的 selectRadixOptionAsync:
import { selectRadixOptionAsync } from '@d8d/e2e-test-utils';
// 在 Page Object 的 fillCompanyForm 中选择平台
if (platformName) {
await selectRadixOptionAsync(this.page, '平台', platformName);
}
测试数据准备顺序:
createPlatform)PlatformSelector 标签:
测试文件存放路径:
web/tests/e2e/specs/admin/
├── platform-create.spec.ts # 平台创建测试(已完成)
├── platform-list.spec.ts # 平台列表测试(已完成)
├── company-create.spec.ts # 公司创建测试(当前)
└── company-list.spec.ts # 公司列表测试(后续 Story)
依赖文件:
web/tests/e2e/pages/admin/company-management.page.ts - Story 11.4 创建web/tests/e2e/utils/test-setup.ts - 已包含 companyManagementPage fixture运行单个测试文件:
cd web
pnpm test:e2e:chromium company-create.spec.ts
运行单个测试用例:
cd web
pnpm test:e2e:chromium company-create.spec.ts -g "应该成功创建公司"
快速失败模式(调试):
cd web
timeout 60 pnpm test:e2e:chromium company-create.spec.ts
Epic 11 内部依赖:
外部依赖:
@d8d/e2e-test-utils 包(已存在)web/tests/e2e/utils/test-setup.ts: 已包含 companyManagementPage 夹具遵循项目测试标准:
docs/standards/testing-standards.mddocs/standards/web-ui-testing-standards.md关键测试原则:
从 Story 11.1-11.4 中学到的关键经验:
Toast 检测不可靠:
表格列顺序:
nth(0) 检查公司名称列测试数据要求:
页面刷新:
page.reload() 或 goto() 刷新选择器优先级:
API 删除策略(来自 Story 11.2):
Platform 异步加载:
selectRadixOptionAsync 确保选项加载完成公司名称唯一性:
测试数据清理顺序:
内联验证消息:
FormMessage 组件显示{fieldId}-form-message 或类似测试文件存放路径:
web/tests/e2e/
├── pages/admin/
│ ├── platform-management.page.ts # 平台管理 Page Object(已完成)
│ └── company-management.page.ts # 公司管理 Page Object(已完成)
├── specs/admin/
│ ├── platform-create.spec.ts # 平台创建测试(已完成)
│ ├── platform-list.spec.ts # 平台列表测试(已完成)
│ └── company-create.spec.ts # 公司创建测试(当前)
└── utils/
└── test-setup.ts # 测试夹具配置(已包含 companyManagementPage)
Claude (d8d-model)
Story 创建完成:
Story 实施完成 (2026-01-12):
web/tests/e2e/specs/admin/company-create.spec.tsplatformSelector 选择器
data-testid="create-company-platform-selector"page.getByRole('dialog').getByText(/^平台/) 定位平台标签platformManagementPage fixture 创建测试平台(因为 CompanyManagementPage 没有 createPlatform 方法)新创建的文件:
web/tests/e2e/specs/admin/company-create.spec.ts - 公司创建 E2E 测试文件修改的文件:
web/tests/e2e/pages/admin/company-management.page.ts - 修复 platformSelector 选择器依赖的已有文件:
web/tests/e2e/pages/admin/company-management.page.ts - Story 11.4 创建web/tests/e2e/pages/admin/platform-management.page.ts - Story 11.1 创建(用于创建测试平台)web/tests/e2e/utils/test-setup.ts - 已包含 companyManagementPage 和 platformManagementPage fixtures