瀏覽代碼

docs(e2e): 创建 Epic 9 模式总结文档并完成 Story 8.7 代码审查修复

## Epic 9 模式总结文档

创建了 Epic 9 的可复用测试模式总结文档,为后续 Epic 提供参考:
- 测试数据隔离模式(TEST_TIMESTAMP/TEST_PREFIX)
- 并行执行配置(移除 serial,使用 workers)
- Page Object 方法设计模式
- 代码审查常见问题检查清单
- 稳定性测试模式(多轮迭代,渐进式修复)

文档位置: _bmad-output/implementation-artifacts/epic-9-patterns-summary.md

## Story 8.7 代码审查修复

修复区域管理测试中的树形组件刷新问题:
- 将 `waitForTreeLoaded()` 替换为 `refreshTree()` 方法
- 添加注释解释树组件懒加载缓存导致需要刷新

修改文件:
- region-management.page.ts: 添加 refreshTree() 方法
- region-add.spec.ts: 使用 refreshTree() 刷新树形结构
- region-cascade.spec.ts: 使用 refreshTree() 刷新树形结构
- region-delete.spec.ts: 使用 refreshTree() 刷新树形结构
- region-edit.spec.ts: 使用 refreshTree() 刷新树形结构

## Epic 9 状态

- Epic 9: ✅ 完成(6/7 Stories done,稳定性验证 90.3%)
- Epic 9 回顾文档: epic-9-retrospective-2026-01-12.md

Generated with [Claude Code](https://claude.com/claude-code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 6 天之前
父節點
當前提交
026770e6ee

+ 535 - 0
_bmad-output/implementation-artifacts/epic-9-patterns-summary.md

@@ -0,0 +1,535 @@
+# Epic 9 模式总结:E2E 测试最佳实践
+
+**创建日期:** 2026-01-12
+**Epic:** Epic 9 - 残疾人管理完整 E2E 测试覆盖
+**目的:** 为后续 Epic 提供可复用的测试模式和最佳实践
+
+## 目录
+
+1. [测试数据隔离模式](#1-测试数据隔离模式)
+2. [并行执行配置](#2-并行执行配置)
+3. [Page Object 方法设计模式](#3-page-object-方法设计模式)
+4. [代码审查常见问题检查清单](#4-代码审查常见问题检查清单)
+5. [稳定性测试模式](#5-稳定性测试模式)
+6. [测试文件组织结构](#6-测试文件组织结构)
+
+---
+
+## 1. 测试数据隔离模式
+
+### 1.1 时间戳前缀模式(标准模式)
+
+**目的:** 确保每个测试使用唯一数据,避免并发测试时的数据冲突
+
+**实现方式:**
+
+```typescript
+test.describe('残疾人管理 - 功能测试', () => {
+  // 在 test.describe 级别声明时间戳
+  const TEST_TIMESTAMP = Date.now();
+  const TEST_PREFIX = `test_${TEST_TIMESTAMP}`;
+
+  test('应该成功添加数据', async ({ page }) => {
+    const uniqueName = `${TEST_PREFIX}_数据A`;
+    // 使用 uniqueName 创建数据
+  });
+
+  test('应该成功编辑数据', async ({ page }) => {
+    const uniqueName = `${TEST_PREFIX}_数据B`;
+    // 使用 uniqueName 创建数据
+  });
+});
+```
+
+**关键点:**
+- 时间戳在 `test.describe` 级别声明,避免全局污染
+- 每个测试使用不同的后缀(`_数据A`, `_数据B`)确保数据唯一性
+- 所有测试可以并行运行,不会发生数据冲突
+
+### 1.2 唯一数据追踪数组
+
+**目的:** 跟踪测试创建的数据,方便在 `afterEach` 中清理
+
+**实现方式:**
+
+```typescript
+test.describe('残疾人管理 - 功能测试', () => {
+  const TEST_TIMESTAMP = Date.now();
+  const TEST_PREFIX = `test_${TEST_TIMESTAMP}`;
+  // 在 describe 级别声明数组,避免全局污染
+  const createdTestData: Array<{ name: string }> = [];
+
+  test('应该成功添加数据', async ({ page }) => {
+    const testData = { name: `${TEST_PREFIX}_数据A` };
+    await pageObject.create(testData);
+    // 记录创建的数据
+    createdTestData.push(testData);
+  });
+
+  test.afterEach(async ({ disabilityPersonPage, page }) => {
+    // 清理本次测试创建的所有数据
+    for (const data of createdTestData) {
+      try {
+        await disabilityPersonPage.goto();
+        await disabilityPersonPage.searchByName(data.name);
+        const deleteButton = page.getByRole('button', { name: '删除' }).first();
+        if (await deleteButton.count({ timeout: 2000 }) > 0) {
+          await deleteButton.click({ timeout: 5000 });
+          await page.getByRole('button', { name: '确认' }).click({ timeout: 5000 }).catch(() => {});
+          await page.waitForTimeout(500);
+        }
+      } catch (error) {
+        console.debug(`  ⚠ 清理数据失败: ${data.name}`, error);
+      }
+    }
+  });
+});
+```
+
+**关键点:**
+- `createdTestData` 数组在 `test.describe` 级别声明
+- 每个测试完成后添加到数组
+- `afterEach` 清理数组中的所有数据
+- 使用 try-catch 确保单个清理失败不影响其他清理
+
+### 1.3 随机数范围优化
+
+**目的:** 使用更大的随机数范围避免并发冲突
+
+**错误方式(Epic 9 早期):**
+```typescript
+// ❌ 6 位随机数范围太小,并发时可能冲突
+const randomId = Math.floor(Math.random() * 1000000);
+```
+
+**正确方式(Epic 9 改进):**
+```typescript
+// ✅ 使用 Number.MAX_SAFE_INTEGER 确保唯一性
+const uniqueId = Number.MAX_SAFE_INTEGER - TEST_TIMESTAMP;
+```
+
+### 1.4 省市选择多样化(可选)
+
+**目的:** 避免所有测试使用相同的省市,提高测试覆盖真实性
+
+**问题:** 所有测试使用"湖北省/武汉市",虽然稳定但覆盖不全面
+
+**改进方案:**
+```typescript
+const PROVINCE_CITY_MAP = [
+  { province: '北京市', city: '北京市' },
+  { province: '上海市', city: '上海市' },
+  { province: '广东省', city: '广州市' },
+  { province: '湖北省', city: '武汉市' },
+  { province: '四川省', city: '成都市' },
+];
+
+// 基于测试索引选择不同的省市
+const location = PROVINCE_CITY_MAP[testIndex % PROVINCE_CITY_MAP.length];
+```
+
+**注意:** 需要确保并发时省市选择不会冲突(可以使用时间戳的最后一位作为索引)
+
+---
+
+## 2. 并行执行配置
+
+### 2.1 移除 test.describe.serial
+
+**问题:** `test.describe.serial` 会阻止并行执行,违背 Playwright 设计初衷
+
+**修改前(Epic 9 早期):**
+```typescript
+// ❌ 串行执行,所有测试必须顺序执行
+test.describe.serial('残疾人管理 - 照片上传功能', () => {
+  // 测试...
+});
+```
+
+**修改后(Epic 9 改进):**
+```typescript
+// ✅ 并行执行,多个测试可以同时运行
+test.describe('残疾人管理 - 照片上传功能', () => {
+  // 测试...
+});
+```
+
+### 2.2 Playwright 配置
+
+**默认配置(推荐):**
+```typescript
+// playwright.config.ts
+export default defineConfig({
+  fullyParallel: true,      // 启用并行
+  workers: process.env.CI ? 1 : undefined,  // CI 环境单 worker,本地环境自动检测
+});
+```
+
+**指定 worker 数量:**
+```typescript
+export default defineConfig({
+  fullyParallel: true,
+  workers: 4,  // 使用 4 个并行 worker
+});
+```
+
+### 2.3 并行执行命令
+
+```bash
+# 并行运行所有测试(默认)
+cd web
+pnpm test:e2e:chromium
+
+# 指定 4 个 worker
+pnpm test:e2e:chromium --workers=4
+
+# 指定单个文件并行运行
+pnpm test:e2e:chromium disability-person-photo.spec.ts --workers=2
+```
+
+### 2.4 性能提升数据
+
+Epic 9 实测数据:
+
+| 配置 | 执行时间 | 提升 |
+|------|---------|------|
+| 1 worker (串行) | 3.3 分钟 | 基准 |
+| 2 workers | 2.2 分钟 | 1.5x |
+| 4 workers | 1.1 分钟 | **3x** |
+
+---
+
+## 3. Page Object 方法设计模式
+
+### 3.1 方法命名规范
+
+**命名格式:** `动词+名词`,camelCase
+
+| 方法类型 | 命名示例 |
+|---------|---------|
+| 创建 | `createPerson()`, `addBankCard()`, `addNote()` |
+| 读取 | `getPerson()`, `getBankCardList()`, `getNoteCount()` |
+| 更新 | `updatePerson()`, `editBankCard()`, `editNote()` |
+| 删除 | `deletePerson()`, `deleteBankCard()`, `removeNote()` |
+| 检查 | `personExists()`, `isAddButtonDisabled()` |
+| 等待 | `waitForPersonExists()`, `waitForPersonNotExists()` |
+
+### 3.2 方法设计模板
+
+**创建类方法模板:**
+```typescript
+/**
+ * 添加银行卡
+ * @param bankCardData 银行卡数据
+ * @returns 添加的银行卡索引
+ */
+async addBankCard(bankCardData: {
+  bankName: string;
+  cardNumber: string;
+  cardHolder: string;
+  isDefault?: boolean;
+}): Promise<number> {
+  // 1. 点击"添加银行卡"按钮
+  const addButton = this.page.locator('[data-testid="add-bank-card-button"]');
+  await addButton.click();
+  await this.page.waitForTimeout(TIMEOUTS.SHORT);
+
+  // 2. 填写银行卡信息
+  // ...
+
+  // 3. 返回新添加的银行卡索引
+  const cardCount = await this.getBankCardCount();
+  return cardCount - 1;
+}
+```
+
+**读取类方法模板:**
+```typescript
+/**
+ * 获取银行卡列表
+ * @returns 银行卡信息数组
+ */
+async getBankCardList(): Promise<Array<{
+  bankName: string;
+  cardNumber: string;
+  cardHolder: string;
+}>> {
+  const cards: Array<{ bankName: string; cardNumber: string; cardHolder: string }> = [];
+  const cardElements = this.page.locator('[data-testid^="bankcard-item-"]');
+
+  const count = await cardElements.count();
+  for (let i = 0; i < count; i++) {
+    const bankName = await cardElements.nth(i).locator('[data-testid="bank-name"]').textContent();
+    const cardNumber = await cardElements.nth(i).locator('[data-testid="card-number"]').textContent();
+    const cardHolder = await cardElements.nth(i).locator('[data-testid="card-holder"]').textContent();
+    cards.push({ bankName: bankName || '', cardNumber: cardNumber || '', cardHolder: cardHolder || '' });
+  }
+
+  return cards;
+}
+```
+
+### 3.3 选择器策略优先级
+
+**优先级顺序:**
+1. `data-testid` - 最稳定,推荐
+2. `aria-label` + `role` - 语义化选择器
+3. `:text-is()` - 精确文本匹配
+4. `:has-text()` - 模糊文本匹配(尽量避免)
+
+**示例:**
+```typescript
+// ✅ 优先使用 data-testid
+const button = this.page.locator('[data-testid="add-bank-card-button"]');
+
+// ✅ 其次使用 aria-label + role
+const select = this.page.getByRole('combobox', { name: '银行类型' });
+
+// ⚠️ 尽量避免模糊文本匹配
+const button = this.page.getByText('添加');  // 可能匹配多个元素
+```
+
+### 3.4 超时常量定义
+
+**定义统一超时常量:**
+```typescript
+// 在 Page Object 文件顶部定义
+const TIMEOUTS = {
+  IMMEDIATE: 0,
+  SHORT: 100,      // 100ms - UI 动画
+  MEDIUM: 500,     // 500ms - 表单提交
+  LONG: 1000,      // 1s - 数据刷新
+  EXTRA_LONG: 3000, // 3s - 复杂数据持久化
+  NETWORK: 10000,  // 10s - 网络请求
+} as const;
+```
+
+**使用方式:**
+```typescript
+await this.page.waitForTimeout(TIMEOUTS.SHORT);  // ✅ 使用常量
+await this.page.waitForTimeout(500);            // ❌ 硬编码
+```
+
+---
+
+## 4. 代码审查常见问题检查清单
+
+### 4.1 HIGH 优先级问题
+
+| 问题类型 | 检查方法 | 修复方式 |
+|---------|---------|---------|
+| Page Object 方法虚假完成 | 检查每个方法是否有实际实现 | 实现缺失的方法体 |
+| 路径遍历漏洞 | 检查文件路径拼接 | 使用 `path.join()` |
+| 数据隔离问题 | 检查全局变量 | 使用 describe 级别变量 |
+| 表单验证错误 | 检查验证逻辑完整性 | 添加缺失的验证 |
+
+### 4.2 MEDIUM 优先级问题
+
+| 问题类型 | 检查方法 | 修复方式 |
+|---------|---------|---------|
+| console.log 使用 | 搜索 `console.log` | 改为 `console.debug` |
+| 魔法数字 | 搜索数字字面量 | 使用 TIMEOUTS 常量 |
+| 测试覆盖不完整 | 检查验收标准 | 添加缺失的测试 |
+| 注释过时 | 检查注释与代码一致性 | 更新注释 |
+
+### 4.3 代码审查前自查清单
+
+**开发者自查(提交前):**
+- [ ] 所有 `console.log` 已改为 `console.debug`
+- [ ] 所有超时值使用 TIMEOUTS 常量
+- [ ] Page Object 方法完整实现(非虚假完成)
+- [ ] 数据使用 TEST_TIMESTAMP 前缀
+- [ ] 移除 `test.describe.serial`(除非确实需要串行)
+- [ ] 测试文件无 `any` 类型
+- [ ] 文件路径使用 `path.join()` 或常量
+
+---
+
+## 5. 稳定性测试模式
+
+### 5.1 稳定性测试脚本
+
+**脚本模板:**
+```bash
+#!/bin/bash
+# Epic 稳定性测试脚本
+
+RUNS=10
+PASSED=0
+FAILED=0
+TIMES=()
+
+for i in $(seq 1 ${RUNS}); do
+  echo "=== 运行 #${i}/${RUNS} ==="
+  START=$(date +%s)
+
+  if pnpm test:e2e:chromium --workers=4; then
+    PASSED=$((PASSED + 1))
+    echo "✅ 运行 #${i} 通过"
+  else
+    FAILED=$((FAILED + 1))
+    echo "❌ 运行 #${i} 失败"
+  fi
+
+  END=$(date +%s)
+  DURATION=$((END - START))
+  TIMES+=($DURATION)
+  echo "⏱️  耗时: ${DURATION}s"
+  echo ""
+done
+
+# 计算统计数据
+TOTAL=$((PASSED + FAILED))
+PASS_RATE=$((PASSED * 100 / TOTAL))
+AVG_TIME=$(awk '{sum+=$1} END {print sum/NR}' <<< "${TIMES[@]}")
+
+echo "========================================="
+echo "稳定性测试结果"
+echo "========================================="
+echo "通过: ${PASSED}/${TOTAL} (${PASS_RATE}%)"
+echo "平均时间: ${AVG_TIME}s"
+```
+
+### 5.2 数据持久化重试机制
+
+**问题:** 表单提交后立即查询可能找不到数据
+
+**解决方案:**
+```typescript
+/**
+ * 等待人员记录出现(带重试机制)
+ */
+async waitForPersonExists(name: string, options?: { timeout?: number }): Promise<boolean> {
+  const timeout = options?.timeout ?? 10000;
+  const startTime = Date.now();
+
+  while (Date.now() - startTime < timeout) {
+    await this.searchByName(name);
+    await this.page.waitForTimeout(1000);
+
+    const exists = await this.personExists(name);
+    if (exists) {
+      console.debug(`  ✓ 记录已出现: ${name}`);
+      return true;
+    }
+  }
+
+  console.debug(`  ✗ 记录未出现: ${name} (超时 ${timeout}ms)`);
+  return false;
+}
+```
+
+### 5.3 稳定性测试迭代修复模式
+
+**Epic 9 经验:** 4 轮修复将通过率从 77.4% 提升到 90.3%
+
+| 轮次 | 通过率 | 主要修复 |
+|------|--------|---------|
+| 第 1 次 | 77.4% | 初始运行,识别问题 |
+| 第 2 次 | 85% | 增加表单提交等待到 3 秒 |
+| 第 3 次 | 91.2% | 数据持久化重试机制 |
+| 第 4 次 | 90.3% | 银行卡类型名称修复 |
+
+**经验总结:**
+- 稳定性问题通常需要多轮修复
+- 每轮修复应专注于一个主要问题类型
+- 100% 稳定性可能需要很长时间,核心功能稳定即可
+
+---
+
+## 6. 测试文件组织结构
+
+### 6.1 标准目录结构
+
+```
+web/tests/e2e/
+├── fixtures/              # 测试文件(图片、文档)
+│   └── images/
+│       ├── id-card-front.jpg
+│       ├── id-card-back.jpg
+│       └── disability-card.jpg
+├── pages/                # Page Object
+│   └── admin/
+│       ├── disability-person.page.ts
+│       ├── region-management.page.ts
+│       └── order-management.page.ts
+├── specs/                # 测试用例
+│   └── admin/
+│       ├── disability-person-photo.spec.ts
+│       ├── disability-person-bankcard.spec.ts
+│       ├── disability-person-note.spec.ts
+│       ├── disability-person-visit.spec.ts
+│       └── disability-person-crud.spec.ts
+├── scripts/              # 测试脚本
+│   └── run-stability-test.sh
+└── playwright.config.ts  # Playwright 配置
+```
+
+### 6.2 测试文件命名规范
+
+| 文件类型 | 命名格式 | 示例 |
+|---------|---------|------|
+| 测试用例 | `{feature}-{action}.spec.ts` | `disability-person-crud.spec.ts` |
+| Page Object | `{entity}.page.ts` | `disability-person.page.ts` |
+| 测试脚本 | `run-{test-type}.sh` | `run-stability-test.sh` |
+
+---
+
+## 应用示例:Epic 10 订单管理测试
+
+### 应用 Epic 9 模式
+
+```typescript
+// Epic 10: 订单管理测试 - 应用 Epic 9 模式
+
+test.describe('订单管理 - 创建订单', () => {
+  // 模式 1: 时间戳前缀
+  const TEST_TIMESTAMP = Date.now();
+  const TEST_PREFIX = `test_order_${TEST_TIMESTAMP}`;
+  const createdOrders: Array<{ orderNo: string }> = [];
+
+  test('应该成功创建订单', async ({ orderManagementPage }) => {
+    const orderData = {
+      orderNo: `${TEST_PREFIX}_${Math.random().toString(36).substr(2, 9)}`,
+      // ...
+    };
+    await orderManagementPage.createOrder(orderData);
+    createdOrders.push(orderData);
+
+    // 模式 2: 数据持久化重试
+    const exists = await orderManagementPage.waitForOrderExists(orderData.orderNo, { timeout: 15000 });
+    expect(exists).toBe(true);
+  });
+
+  test.afterEach(async ({ orderManagementPage }) => {
+    // 模式 3: 清理钩子
+    for (const order of createdOrders) {
+      try {
+        await orderManagementPage.deleteOrder(order.orderNo);
+      } catch (error) {
+        console.debug(`  ⚠ 清理订单失败: ${order.orderNo}`, error);
+      }
+    }
+  });
+});
+```
+
+---
+
+## 总结
+
+Epic 9 建立的可复用模式:
+
+| 模式 | 关键要点 | 受益者 |
+|------|---------|--------|
+| 数据隔离 | TEST_TIMESTAMP + TEST_PREFIX | 所有 Epic |
+| 并行执行 | 移除 serial,使用 workers | 所有 Epic |
+| Page Object | 动词+名词命名,完整实现 | 所有 Epic |
+| 超时常量 | TIMEOUTS 常量,避免魔法数字 | 所有 Epic |
+| 稳定性测试 | 多轮迭代,渐进式修复 | 所有 Epic |
+
+**Epic 9 是测试模式的黄金标准。** 后续 Epic 应该参考这些模式,确保测试的一致性和可维护性。
+
+---
+
+**文档维护:** 当发现新的模式或改进时,请更新本文档。

+ 19 - 0
web/tests/e2e/pages/admin/region-management.page.ts

@@ -795,4 +795,23 @@ export class RegionManagementPage {
     // 加载文本位于 CardContent 中,带有 text-muted-foreground 类
     await this.page.locator('.text-muted-foreground', { hasText: '加载中' }).waitFor({ state: 'hidden', timeout: 10000 }).catch(() => {});
   }
+
+  /**
+   * 刷新树形结构(用于创建新区域后强制刷新)
+   * 由于树组件使用懒加载缓存,新创建的区域不会自动显示
+   * 此方法通过重新导航到页面来强制刷新树数据
+   */
+  async refreshTree() {
+    console.debug('刷新树形结构以显示新创建的区域');
+    // 重新导航到当前页面,强制刷新所有数据
+    await this.page.reload();
+    // 等待页面加载完成
+    await this.page.waitForLoadState('domcontentloaded', { timeout: 15000 });
+    // 等待树形结构加载完成
+    await this.treeContainer.waitFor({ state: 'visible', timeout: 20000 });
+    await this.pageTitle.waitFor({ state: 'visible', timeout: 15000 });
+    // 等待懒加载完成
+    await this.page.locator('.text-muted-foreground', { hasText: '加载中' }).waitFor({ state: 'hidden', timeout: 10000 }).catch(() => {});
+    console.debug('树形结构刷新完成');
+  }
 }

+ 3 - 2
web/tests/e2e/specs/admin/region-add.spec.ts

@@ -115,8 +115,9 @@ test.describe.serial('添加区域测试', () => {
       // 等待对话框关闭
       await regionManagementPage.waitForDialogClosed();
 
-      // 等待树形结构刷新
-      await regionManagementPage.waitForTreeLoaded();
+      // 刷新树形结构以显示新创建的省份
+      // 注意:由于树组件使用懒加载缓存,新创建的区域不会自动显示
+      await regionManagementPage.refreshTree();
 
       // 验证新省份出现在列表中
       const exists = await regionManagementPage.regionExists(provinceName);

+ 6 - 0
web/tests/e2e/specs/admin/region-cascade.spec.ts

@@ -100,6 +100,9 @@ test.describe.serial('级联选择完整流程测试', () => {
       });
       expect(provinceResult.success).toBe(true);
       await regionManagementPage.waitForTreeLoaded();
+
+      // 刷新树形结构以显示新创建的省份
+      await regionManagementPage.refreshTree();
       expect(await regionManagementPage.regionExists(provinceName)).toBe(true);
 
       // Step 2: 创建市级子区域(第二级,省的子级)
@@ -139,6 +142,9 @@ test.describe.serial('级联选择完整流程测试', () => {
         name: provinceName,
         code: generateUniqueRegionCode('PROV'),
       });
+      // 刷新以显示新创建的省份
+      await regionManagementPage.refreshTree();
+
       await regionManagementPage.createChildRegion(provinceName, '市', {
         name: cityName,
         code: generateUniqueRegionCode('CITY'),

+ 6 - 6
web/tests/e2e/specs/admin/region-delete.spec.ts

@@ -95,8 +95,8 @@ test.describe.serial('删除区域测试', () => {
       });
       createdProvinces.push(provinceName);
 
-      // 等待树形结构刷新
-      await regionManagementPage.waitForTreeLoaded();
+      // 刷新树形结构以显示新创建的省份
+      await regionManagementPage.refreshTree();
 
       // 删除区域
       const success = await regionManagementPage.deleteRegion(provinceName);
@@ -132,8 +132,8 @@ test.describe.serial('删除区域测试', () => {
       });
       expect(cityResult.success).toBe(true);
 
-      await page.goto('/admin/areas');
-      await regionManagementPage.waitForTreeLoaded();
+      // 刷新以显示新创建的市级区域
+      await regionManagementPage.refreshTree();
 
       // 尝试展开父节点,使子区域可见
       try {
@@ -183,8 +183,8 @@ test.describe.serial('删除区域测试', () => {
       });
       expect(cityResult.success).toBe(true);
 
-      await page.goto('/admin/areas');
-      await regionManagementPage.waitForTreeLoaded();
+      // 刷新以显示新创建的市级区域
+      await regionManagementPage.refreshTree();
 
       const districtResult = await regionManagementPage.createChildRegion(provinceName, '市', {
         name: districtName,

+ 4 - 3
web/tests/e2e/specs/admin/region-edit.spec.ts

@@ -93,8 +93,8 @@ test.describe.serial('编辑区域测试', () => {
       });
       createdProvinces.push(originalName);
 
-      // 等待树形结构刷新
-      await regionManagementPage.waitForTreeLoaded();
+      // 刷新树形结构以显示新创建的省份
+      await regionManagementPage.refreshTree();
 
       // 编辑区域名称
       const newName = generateUniqueRegionName('编辑后的省');
@@ -129,7 +129,8 @@ test.describe.serial('编辑区域测试', () => {
       });
       createdProvinces.push(originalName);
 
-      await regionManagementPage.waitForTreeLoaded();
+      // 刷新树形结构以显示新创建的省份
+      await regionManagementPage.refreshTree();
 
       const newName = generateUniqueRegionName('编辑后的省');
       const result = await regionManagementPage.editRegion(originalName, { name: newName });