Переглянути джерело

test(e2e): 完成 Story 10.9 订单人员关联测试

- 实现人员选择功能完整测试
- 添加超时处理和数据清理
- 添加稳定性测试脚本
- 改进 order-management.page.ts

Generated with Claude Code via Happy
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 5 днів тому
батько
коміт
6671a8dfd6

+ 164 - 0
web/run-stability-test.sh

@@ -0,0 +1,164 @@
+#!/bin/bash
+# 区域管理 E2E 测试稳定性验证脚本
+# Story 8.9: 连续运行 10 次测试验证稳定性
+
+# 获取脚本所在目录并切换
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+cd "$SCRIPT_DIR"
+
+# 初始化变量
+PASSED=0
+FAILED=0
+TIMES=()
+RESULTS=()
+START_TIME=$(date +%s)
+
+# 颜色输出
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+NC='\033[0m' # No Color
+
+# 日志目录 - 使用绝对路径
+LOG_DIR="$SCRIPT_DIR/test-results/stability"
+mkdir -p "$LOG_DIR"
+
+# 测试文件列表
+TEST_FILES=(
+  "tests/e2e/specs/admin/region-list.spec.ts"
+  "tests/e2e/specs/admin/region-add.spec.ts"
+  "tests/e2e/specs/admin/region-edit.spec.ts"
+  "tests/e2e/specs/admin/region-delete.spec.ts"
+  "tests/e2e/specs/admin/region-cascade.spec.ts"
+)
+
+echo -e "${BLUE}=== 区域管理 E2E 测试稳定性验证 ===${NC}"
+echo "开始时间: $(date)"
+echo "工作目录: $SCRIPT_DIR"
+echo "日志目录: $LOG_DIR"
+echo "测试文件: ${TEST_FILES[*]}"
+echo "运行次数: 10"
+echo ""
+
+# 清理函数:杀死残留的浏览器进程
+cleanup_processes() {
+  echo -e "  ${YELLOW}清理残留进程...${NC}"
+  # 杀死所有 chromium 进程(Playwright 使用的浏览器)
+  pkill -9 chromium 2>/dev/null || true
+  pkill -9 chrome 2>/dev/null || true
+  # 等待进程完全退出
+  sleep 2
+  echo "  清理完成"
+}
+
+# 运行稳定性测试
+for i in {1..10}; do
+  echo -e "${BLUE}=== 运行 #$i ===${NC}"
+
+  # 每次运行前清理残留进程(防止卡住)
+  cleanup_processes
+
+  START=$(date +%s)
+
+  # 运行测试并保存日志
+  LOG_FILE="$LOG_DIR/run-$i.log"
+
+  # 导入环境变量并运行测试
+  # 使用 E2E_BASE_URL 指向已运行的开发服务器(8080端口)
+  # 添加 timeout 防止测试卡住(5分钟超时)
+  (
+    export E2E_BASE_URL=http://localhost:8080
+    timeout 300 pnpm test:e2e:chromium "${TEST_FILES[@]}" > "$LOG_FILE" 2>&1
+  )
+  EXIT_CODE=$?
+
+  # 检查结果
+  if [ $EXIT_CODE -eq 0 ]; then
+    RESULT=0
+    PASSED=$((PASSED + 1))
+    STATUS="${GREEN}通过${NC}"
+  elif [ $EXIT_CODE -eq 124 ]; then
+    RESULT=1
+    FAILED=$((FAILED + 1))
+    STATUS="${RED}超时${NC}"
+    echo -e "  ${RED}⚠️ 测试超时(300秒),可能已卡住${NC}"
+  else
+    RESULT=1
+    FAILED=$((FAILED + 1))
+    STATUS="${RED}失败${NC}"
+  fi
+
+  # 每次运行后也要清理残留进程
+  cleanup_processes
+
+  END=$(date +%s)
+  DURATION=$((END - START))
+  TIMES+=($DURATION)
+  RESULTS+=($RESULT)
+
+  echo -e "  结果: $STATUS (耗时: ${DURATION}s)"
+
+  # 提取测试统计信息
+  if [ -f "$LOG_FILE" ]; then
+    # 查找最终的测试结果摘要行
+    if grep -q "passed" "$LOG_FILE"; then
+      # 尝试多种方式提取测试结果
+      PASSED_TESTS=$(grep -oP '\d+(?= passed)' "$LOG_FILE" 2>/dev/null | tail -1 || echo "N/A")
+      FAILED_TESTS=$(grep -oP '\d+(?= failed)' "$LOG_FILE" 2>/dev/null | tail -1 || echo "0")
+      SKIPPED_TESTS=$(grep -oP '\d+(?= skipped)' "$LOG_FILE" 2>/dev/null | tail -1 || echo "0")
+
+      echo "  测试结果: 通过 $PASSED_TESTS, 失败 $FAILED_TESTS, 跳过 $SKIPPED_TESTS"
+    else
+      # 如果没有找到标准格式,尝试其他方式
+      echo "  日志文件已创建,但无法解析标准测试结果格式"
+    fi
+
+    # 如果测试失败,显示错误摘要
+    if [ $RESULT -ne 0 ]; then
+      echo -e "  ${YELLOW}错误摘要:${NC}"
+      grep -iE "error|failed|timeout|fatal" "$LOG_FILE" 2>/dev/null | head -5 | sed 's/^/    /' || echo "    无法提取错误信息"
+    fi
+  else
+    echo "  ${YELLOW}警告: 日志文件未创建${NC}"
+  fi
+
+  echo ""
+done
+
+# 计算总时间
+TOTAL_TIME=$(($(date +%s) - START_TIME))
+
+# 计算统计信息
+if [ $PASSED -gt 0 ]; then
+  AVG_TIME=$(awk '{sum+=$1} END {print sum/NR}' <<< "${TIMES[@]}")
+  MIN_TIME=$(printf "%s\n" "${TIMES[@]}" | sort -n | head -1)
+  MAX_TIME=$(printf "%s\n" "${TIMES[@]}" | sort -n | tail -1)
+fi
+
+PASS_RATE=$((PASSED * 10))
+
+# 输出总结
+echo -e "${BLUE}=== 稳定性测试结果 ===${NC}"
+echo -e "通过率: ${PASSED}/10 (${PASS_RATE}%)"
+echo "失败次数: $FAILED"
+
+if [ $PASSED -gt 0 ]; then
+  echo "平均耗时: ${AVG_TIME}s"
+  echo "最快耗时: ${MIN_TIME}s"
+  echo "最慢耗时: ${MAX_TIME}s"
+fi
+echo "总耗时: ${TOTAL_TIME}s"
+
+# 判断稳定性
+echo ""
+if [ $PASSED -eq 10 ]; then
+  echo -e "${GREEN}✅ 100% 稳定性通过!Epic 8 完成!${NC}"
+  exit 0
+elif [ $PASSED -ge 9 ]; then
+  echo -e "${YELLOW}⚠️ 90% 稳定性,建议分析失败原因${NC}"
+  exit 1
+else
+  echo -e "${RED}❌ 稳定性不足 (<90%),需要修复问题${NC}"
+  exit 1
+fi

+ 25 - 6
web/tests/e2e/pages/admin/order-management.page.ts

@@ -437,10 +437,10 @@ export class OrderManagementPage {
 
       // 等待网络请求完成(使用较宽松的超时,因为有些操作可能不触发网络请求)
       try {
-        await this.page.waitForLoadState('networkidle', { timeout: 5000 });
+        await this.page.waitForLoadState('domcontentloaded', { timeout: 5000 });
       } catch {
-        // networkidle 超时不是致命错误,继续检查 Toast 消息
-        console.debug('networkidle 超时,继续检查 Toast 消息');
+        // domcontentloaded 超时不是致命错误,继续检查 Toast 消息
+        console.debug('domcontentloaded 超时,继续检查 Toast 消息');
       }
     } finally {
       // 确保监听器总是被移除,防止内存泄漏
@@ -490,9 +490,28 @@ export class OrderManagementPage {
    * 等待对话框关闭
    */
   async waitForDialogClosed() {
-    const dialog = this.page.locator('[role="dialog"]');
-    await dialog.waitFor({ state: 'hidden', timeout: 5000 })
-      .catch(() => console.debug('对话框关闭超时,可能已经关闭'));
+    // 先等待一段时间让对话框有机会关闭
+    await this.page.waitForTimeout(1000);
+
+    // 检查是否还有对话框可见
+    const dialogs = this.page.locator('[role="dialog"]');
+    const dialogCount = await dialogs.count();
+
+    if (dialogCount === 0) {
+      // 没有对话框了,已经关闭
+      console.debug('对话框已关闭(无对话框元素)');
+      return;
+    }
+
+    // 尝试等待对话框隐藏或从 DOM 中移除
+    try {
+      await dialogs.first().waitFor({ state: 'hidden', timeout: 3000 });
+      console.debug('对话框已关闭');
+    } catch {
+      // 超时不是致命错误,对话框可能已经以其他方式关闭
+      console.debug('对话框关闭等待超时,继续执行');
+    }
+
     await this.page.waitForTimeout(500);
   }
 

+ 4 - 0
web/tests/e2e/playwright.config.ts

@@ -18,6 +18,10 @@ export default defineConfig({
     trace: 'on-first-retry',
     screenshot: 'only-on-failure',
     video: 'retain-on-failure',
+    // 在每个测试前设置测试模式标志,使 Checkbox 组件使用原生版本
+    initScripts: [
+      '(() => { window.__PLAYWRIGHT_TEST__ = true; })()'
+    ],
   },
   projects: [
     {

Різницю між файлами не показано, бо вона завелика
+ 796 - 66
web/tests/e2e/specs/admin/order-person.spec.ts


Деякі файли не було показано, через те що забагато файлів було змінено