Status: done
作为测试开发者, 我想要编写区域列表查看的 E2E 测试, 以便验证区域列表的基本功能和数据展示。
Given 区域管理 Page Object 已创建 When 编写区域列表查看测试用例 Then 验证区域列表按预期加载 And 验证区域数据的正确展示(名称、层级、状态等) And 验证分页功能(如适用) And 验证搜索功能(如适用) And 测试在真实浏览器中通过
web/tests/e2e/specs/admin/region-list.spec.tsEpic 8: 区域管理 E2E 测试 (Epic B - 业务测试 Epic)
这是 Epic B(区域管理业务测试)的第二个 Story。Story 8.1 已经创建了 RegionManagementPage Page Object,本 Story 将编写第一个实际测试用例 - 区域列表查看测试。
依赖:
业务分组:
区域管理是一个树形结构管理页面,用于管理省/市/区/街道四级区域数据。根据 Story 8.1 的 DOM 结构分析:
页面组件分析:
AreaManagement.tsx - 主页面组件,显示省市区树形管理AreaForm.tsx - 表单组件,用于添加/编辑区域AreaTreeAsync.tsx - 树形组件,显示区域层级结构页面元素:
.border.rounded-lg.bg-background区域树节点结构:
基于 Story 8.1 的实现,RegionManagementPage 提供以下关键方法:
导航方法:
// 导航到区域管理页面
await regionManagementPage.goto();
// 验证页面元素可见性
await regionManagementPage.expectToBeVisible();
// 等待树结构加载完成
await regionManagementPage.waitForTreeLoaded();
列表查询方法:
// 检查区域是否存在
const exists = await regionManagementPage.regionExists('广东省');
// 获取区域状态
const status = await regionManagementPage.getRegionStatus('广东省'); // 'enabled' | 'disabled'
// 展开节点
await regionManagementPage.expandNode('广东省');
// 收起节点
await regionManagementPage.collapseNode('广东省');
对话框操作方法:
// 打开新增省对话框
await regionManagementPage.openCreateProvinceDialog();
// 打开新增子区域对话框
await regionManagementPage.openAddChildDialog('广东省');
// 打开编辑对话框
await regionManagementPage.openEditDialog('广东省');
参考 web/tests/e2e/specs/admin/disability-person-complete.spec.ts 的成功模式:
import { test, expect } from '@playwright/test';
import { AdminLoginPage } from '@/pages/admin/admin-login.page';
import { RegionManagementPage } from '@/pages/admin/region-management.page';
test.describe('区域列表查看测试', () => {
let adminLoginPage: AdminLoginPage;
let regionManagementPage: RegionManagementPage;
test.beforeEach(async ({ page }) => {
adminLoginPage = new AdminLoginPage(page);
regionManagementPage = new RegionManagementPage(page);
// 登录
await adminLoginPage.goto();
await adminLoginPage.login('admin', 'admin123');
// 导航到区域管理页面
await regionManagementPage.goto();
});
test.afterEach(async ({ page }) => {
// 清理测试数据
// TODO: 实现数据清理逻辑
});
test('应该显示区域列表页面标题', async ({ page }) => {
// 测试实现
});
});
1. 页面加载验证测试:
test('应该显示区域列表页面标题', async ({ page }) => {
await expect(regionManagementPage.pageTitle).toBeVisible();
await expect(regionManagementPage.pageTitle).toContainText('省市区树形管理');
});
test('应该显示新增省按钮', async ({ page }) => {
await expect(regionManagementPage.addProvinceButton).toBeVisible();
await expect(regionManagementPage.addProvinceButton).toContainText('新增省');
});
test('应该等待树结构加载完成', async ({ page }) => {
await regionManagementPage.waitForTreeLoaded();
// 验证树形容器可见
await expect(regionManagementPage.treeContainer).toBeVisible();
});
2. 区域数据展示验证测试:
test('应该显示默认省份数据', async ({ page }) => {
await regionManagementPage.waitForTreeLoaded();
// 验证默认省份存在(根据实际数据调整)
const exists = await regionManagementPage.regionExists('广东省');
expect(exists).toBe(true);
});
test('应该正确显示区域状态', async ({ page }) => {
await regionManagementPage.waitForTreeLoaded();
// 获取区域状态
const status = await regionManagementPage.getRegionStatus('广东省');
expect(status).toBe('enabled'); // 或 'disabled'
});
test('应该能展开和收起区域节点', async ({ page }) => {
await regionManagementPage.waitForTreeLoaded();
// 展开节点
await regionManagementPage.expandNode('广东省');
// 验证子节点可见(如广州市)
const childExists = await regionManagementPage.regionExists('广州市');
expect(childExists).toBe(true);
// 收起节点
await regionManagementPage.collapseNode('广东省');
});
3. 搜索功能测试(如适用):
test.describe('区域搜索功能', () => {
test('应该能按区域名称搜索', async ({ page }) => {
// 如果页面有搜索功能
// TODO: 实现
});
test('应该能清空搜索结果', async ({ page }) => {
// TODO: 实现
});
});
数据隔离原则:
测试数据生成:
/**
* 生成唯一区域名称
*/
function generateUniqueRegionName(prefix: string = '测试区域'): string {
const timestamp = Date.now();
const random = Math.floor(Math.random() * 1000);
return `${prefix}_${timestamp}_${random}`;
}
// 使用示例
const uniqueName = generateUniqueRegionName('测试省');
数据清理策略:
选项 3: 使用事务回滚(如可能)
test.afterEach(async ({ page }) => {
// 清理本测试创建的数据
if (createdRegionName) {
try {
await regionManagementPage.deleteRegion(createdRegionName);
} catch (error) {
console.debug('清理测试数据失败:', error);
}
}
});
基于 Story 8.1 的 DOM 结构探索:
页面选择器(已验证):
getByText('省市区树形管理')getByRole('button', { name: '新增省' }).border.rounded-lg.bg-background[data-sonner-toast][data-type="success|error"]节点操作按钮(已验证):
xpath=.//ancestor::div[contains(@class, "group")]//button[.//svg[contains(@class, "pencil")]]xpath=.//ancestor::div[contains(@class, "group")]//button[.//svg[contains(@class, "trash")]]xpath=.//ancestor::div[contains(@class, "group")]//button[.//svg[contains(@class, "power")]]目标文件位置:
web/tests/e2e/specs/admin/region-list.spec.ts
导入路径:
import { AdminLoginPage } from '@/pages/admin/admin-login.page';
import { RegionManagementPage } from '@/pages/admin/region-management.page';
测试命令:
# 运行区域列表测试
cd web
pnpm test:e2e:chromium region-list
# 快速失败模式(调试)
timeout 60 pnpm test:e2e:chromium region-list
基于 Architecture.md 的关键陷阱:
⚠️ DOM 结构假设必须验证
✅ 正确做法:
// 使用精确文本匹配
page.getByText('省市区树形管理', { exact: true })
// 使用已验证的选择器
await regionManagementPage.waitForTreeLoaded();
❌ 避免:
// 避免部分文本匹配
page.getByText('省市区') // 可能匹配多个元素
// 避免使用 page.evaluate()
const text = await page.evaluate(() => document.querySelector('...'));
参考测试: disability-person-complete.spec.ts
| 方面 | 残疾人管理测试 | 区域列表测试 |
|---|---|---|
| 页面结构 | 表单 + 列表 | 树形结构 |
| 数据展示 | 表格行 | 树节点 |
| 导航方式 | 直接访问页面 | 直接访问页面 |
| 测试隔离 | 使用唯一 ID | 使用唯一区域名 |
| 数据清理 | API 或 UI | API 或 UI |
关键差异:
1. 查看 DOM 结构:
# 使用 Playwright Inspector
cd web
pnpm test:e2e:chromium region-list --debug
2. 查看错误上下文:
# 测试失败后查看
cat test-results/*/error-context.md
3. 添加调试输出:
test('调试测试', async ({ page }) => {
console.debug('当前 URL:', page.url());
const isVisible = await regionManagementPage.pageTitle.isVisible();
console.debug('标题可见性:', isVisible);
});
根据 packages/e2e-test-utils/src/index.ts:
// 本测试主要使用 Page Object,直接工具较少
// 可能需要的工具:
import { selectRadixOption, selectRadixOptionAsync } from '@d8d/e2e-test-utils';
import { selectCascade } from '@d8d/e2e-test-utils';
import { uploadFileToField } from '@d8d/e2e-test-utils';
注意: 本测试主要验证列表查看功能,可能不需要直接使用工具函数。后续 Story(添加、编辑区域)会使用更多工具。
问题 1: 树结构加载超时
waitForTreeLoaded() 等待加载完成问题 2: 节点展开后子节点不可见
waitForTimeout(500) 或使用 waitForSelector()问题 3: 区域名称重复导致测试不稳定
generateUniqueRegionName() 生成唯一名称本 Story 的测试覆盖率:
测试通过率目标: 连续运行 10 次,100% 通过
本测试是 Epic 8 的第一个实际测试,后续 Story 依赖:
无需调试记录 - 所有测试在首次运行即通过
实现总结:
test-setup.ts 中添加了 regionManagementPage fixtureregion-list.spec.ts测试覆盖范围:
代码审查修复 (2026-01-11):
测试通过情况:
技术要点:
新增文件:
web/tests/e2e/specs/admin/region-list.spec.ts - 区域列表查看测试文件修改文件:
web/tests/e2e/utils/test-setup.ts - 添加 regionManagementPage fixture_bmad-output/implementation-artifacts/sprint-status.yaml - 更新状态为 in-progress_bmad-output/implementation-artifacts/8-2-region-list-test.md - 更新任务状态和完成记录删除文件(代码审查清理):
web/tests/e2e/specs/admin/debug-dialog.spec.ts - 调试测试文件web/tests/e2e/specs/admin/debug-photo-upload.spec.ts - 调试测试文件技术栈:
测试命令:
# 运行区域列表测试
cd web
pnpm test:e2e:chromium region-list
# 快速失败模式(调试)
timeout 60 pnpm test:e2e:chromium region-list
# 运行所有 E2E 测试
pnpm test:e2e:chromium
包管理:
命名约定:
.spec.ts 后缀test.describe() 分组来自 Architecture.md 的关键决策:
选择器策略(混合策略优先级):
data-testid - 最高优先级aria-label + role - 无障碍标准测试基础设施:
web/tests/e2e/specs/admin/web/tests/e2e/pages/admin/web/tests/e2e/fixtures/测试隔离:
⚠️ DOM 结构假设必须验证
✅ 正确做法:
// 使用 Page Object 封装的方法
await regionManagementPage.waitForTreeLoaded();
const exists = await regionManagementPage.regionExists('广东省');
await regionManagementPage.expandNode('广东省');
❌ 避免:
// 避免直接操作 DOM
await page.locator('.tree-node').click();
代码质量:
test.describe() 组织相关测试测试数据:
beforeEach/afterEach 钩子错误处理:
| 文档 | 路径 |
|---|---|
| PRD | _bmad-output/planning-artifacts/prd.md |
| Architecture | _bmad-output/planning-artifacts/architecture.md |
| Epics | _bmad-output/planning-artifacts/epics.md |
| Project Context | _bmad-output/project-context.md |
| Story 8.1 | _bmad-output/implementation-artifacts/8-1-region-page-object.md |
| RegionManagementPage | web/tests/e2e/pages/admin/region-management.page.ts |
| 参考测试 | web/tests/e2e/specs/admin/disability-person-complete.spec.ts |
| e2e-test-utils | packages/e2e-test-utils/src/index.ts |
前置 Epic:
当前 Epic (Epic 8):
后续 Epic:
Story ID: 8.2 Story Key: 8-2-region-list-test Epic: Epic 8 - 区域管理 E2E 测试 (Epic B) Status: done
交付物:
测试结果:
web/tests/e2e/specs/admin/region-list.spec.ts代码审查结果:
下一步操作: