# Gym Manage - 全量测试编排脚本 # 用法: .\run-all-tests.ps1 [-Backend] [-Admin] [-Member] [-Coach] [-All] [-ReportOnly] [-SkipBuild] # 默认: -All param( [switch]$Backend, [switch]$Admin, [switch]$Member, [switch]$Coach, [switch]$All, [switch]$ReportOnly, [switch]$SkipBuild ) $ErrorActionPreference = "Stop" $RootDir = Split-Path -Parent $MyInvocation.MyCommand.Path # 颜色输出 function Write-Step { Write-Host "`n========================================" -ForegroundColor Cyan; Write-Host " $args" -ForegroundColor Cyan; Write-Host "========================================" -ForegroundColor Cyan } function Write-Success { Write-Host " [OK] $args" -ForegroundColor Green } function Write-Fail { Write-Host " [FAIL] $args" -ForegroundColor Red } function Write-Warn { Write-Host " [WARN] $args" -ForegroundColor Yellow } function Write-Info { Write-Host " [INFO] $args" -ForegroundColor Gray } # 结果收集 $Results = @{} $StartTime = Get-Date # ============================================= # 阶段0: 环境检查 # ============================================= Write-Step "阶段0: 环境检查" # 检查 Java try { $javaVer = java -version 2>&1 | Select-Object -First 1 Write-Success "Java available: $javaVer" } catch { Write-Fail "Java not found. Please install JDK 21." exit 1 } # 检查 Node try { $nodeVer = node -v Write-Success "Node available: $nodeVer" } catch { Write-Fail "Node not found. Please install Node.js." exit 1 } # 检查 Maven Wrapper $MvnCmd = if (Test-Path "$RootDir\gym-manage-api\mvnw.cmd") { "$RootDir\gym-manage-api\mvnw.cmd" } else { "mvn" } # 检查微信开发者工具CLI(端口45869) $wcCliPort = 45869 try { $wcCheck = Invoke-WebRequest -Uri "http://127.0.0.1:$wcCliPort" -TimeoutSec 3 -ErrorAction SilentlyContinue if ($wcCheck.StatusCode -eq 200 -or $wcCheck.StatusCode -eq 404) { Write-Success "WeChat DevTools CLI available on port $wcCliPort" } } catch { Write-Warn "WeChat DevTools CLI not detected on port $wcCliPort. MiniTest tests will be skipped." } # ============================================= # 阶段1: 启动后端服务 # ============================================= Write-Step "阶段1: 启动后端服务" $BackendPort = 8084 $BackendHealthUrl = "http://localhost:$BackendPort/actuator/health" function Test-BackendHealth { try { $r = Invoke-WebRequest -Uri $BackendHealthUrl -TimeoutSec 5 -ErrorAction SilentlyContinue return ($r.StatusCode -eq 200) } catch { return $false } } $backendWasRunning = Test-BackendHealth if ($backendWasRunning) { Write-Success "Backend already running on port $BackendPort" } else { Write-Info "Starting backend with Maven..." $backendJob = Start-Job -ScriptBlock { param($mvnCmd, $apiDir) Set-Location $apiDir & $mvnCmd spring-boot:run -Dspring-boot.run.profiles=test 2>&1 } -ArgumentList $MvnCmd, "$RootDir\gym-manage-api\manage-app" # 等待后端就绪 (最多90秒) $retries = 0 while (-not (Test-BackendHealth) -and $retries -lt 90) { $retries++ Write-Progress -Activity "Waiting for backend..." -PercentComplete (($retries / 90) * 100) -Status "$retries/90s" Start-Sleep -Seconds 1 } Write-Progress -Activity "Waiting for backend..." -Completed if (Test-BackendHealth) { Write-Success "Backend started successfully ($retries seconds)" } else { Write-Fail "Backend failed to start within 90 seconds" Stop-Job $backendJob Remove-Job $backendJob exit 1 } } # ============================================= # 阶段2: 执行测试套件 # ============================================= function Invoke-TestStage { param( [string]$Name, [string]$WorkingDir, [string]$Command, [bool]$Enabled = $true ) if (-not $Enabled) { Write-Warn "Skipping $Name (not requested)" $Results[$Name] = "SKIPPED" return } Write-Step "Running: $Name" Write-Info "Working directory: $WorkingDir" Write-Info "Command: $Command" try { Push-Location $WorkingDir $sw = [System.Diagnostics.Stopwatch]::StartNew() Invoke-Expression $Command $sw.Stop() Write-Success "$Name completed in $([math]::Round($sw.Elapsed.TotalSeconds, 1))s" $Results[$Name] = "PASS ($([math]::Round($sw.Elapsed.TotalSeconds, 1))s)" Pop-Location } catch { Write-Fail "$Name failed: $_" $Results[$Name] = "FAIL" Pop-Location } } # 决定执行哪些测试 $runBackend = $All -or $Backend $runAdmin = $All -or $Admin $runMember = $All -or $Member $runCoach = $All -or $Coach if (-not ($Backend -or $Admin -or $Member -or $Coach)) { $runBackend = $runAdmin = $runMember = $runCoach = $true } # 后端 API 契约测试 Invoke-TestStage -Name "Backend-API-Contract" ` -WorkingDir "$RootDir\gym-manage-api" ` -Command "$MvnCmd test -pl manage-app -Dtest='cn.novalon.gym.manage.app.contract.*Test' -DfailIfNoTests=false" ` -Enabled $runBackend # Admin E2E: Smoke Invoke-TestStage -Name "Admin-E2E-Smoke" ` -WorkingDir "$RootDir\gym-manage-web" ` -Command "npx playwright test --project=smoke --reporter=list" ` -Enabled $runAdmin # Admin E2E: Journeys (需等待smoke完成) if ($runAdmin) { Invoke-TestStage -Name "Admin-E2E-Journeys" ` -WorkingDir "$RootDir\gym-manage-web" ` -Command "npx playwright test --project=journeys --reporter=list" ` -Enabled $true } # UniApp 会员端 MiniTest if ($runMember) { $memberTestDir = "$RootDir\gym-manage-uniapp" if (Test-Path "$memberTestDir\jest.e2e.config.js") { Invoke-TestStage -Name "UniApp-Member-MiniTest" ` -WorkingDir $memberTestDir ` -Command "npx jest --config jest.e2e.config.js --forceExit" ` -Enabled $true } else { Write-Warn "Member MiniTest config not found, skipping" $Results["UniApp-Member-MiniTest"] = "SKIPPED (no config)" } } # UniApp 教练端 MiniTest if ($runCoach) { $coachTestDir = "$RootDir\gym-manage-coach-uniapp" if (Test-Path "$coachTestDir\jest.e2e.config.js") { Invoke-TestStage -Name "UniApp-Coach-MiniTest" ` -WorkingDir $coachTestDir ` -Command "npx jest --config jest.e2e.config.js --forceExit" ` -Enabled $true } else { Write-Warn "Coach MiniTest config not found, skipping" $Results["UniApp-Coach-MiniTest"] = "SKIPPED (no config)" } } # ============================================= # 阶段3: 汇总报告 # ============================================= Write-Step "阶段3: 测试汇总报告" $EndTime = Get-Date $TotalTime = [math]::Round(($EndTime - $StartTime).TotalSeconds, 1) $totalPass = 0 $totalFail = 0 $totalSkip = 0 Write-Host "" Write-Host " Test Results Summary" -ForegroundColor White Write-Host " $(('=' * 60))" -ForegroundColor DarkGray foreach ($key in $Results.Keys | Sort-Object) { $val = $Results[$key] if ($val -like "PASS*") { Write-Host " [PASS] " -NoNewline -ForegroundColor Green Write-Host "$key : $val" $totalPass++ } elseif ($val -like "FAIL*") { Write-Host " [FAIL] " -NoNewline -ForegroundColor Red Write-Host "$key : $val" $totalFail++ } else { Write-Host " [SKIP] " -NoNewline -ForegroundColor Yellow Write-Host "$key : $val" $totalSkip++ } } Write-Host " $(('=' * 60))" -ForegroundColor DarkGray Write-Host (" Total: {0} | Pass: {1} | Fail: {2} | Skip: {3} | Time: {4}s" -f ($totalPass + $totalFail + $totalSkip), $totalPass, $totalFail, $totalSkip, $TotalTime) -ForegroundColor White Write-Host "" # 生成JSON报告 $ReportPath = "$RootDir\test-results\summary-$(Get-Date -Format 'yyyyMMdd-HHmmss').json" $ReportDir = Split-Path $ReportPath -Parent if (-not (Test-Path $ReportDir)) { New-Item -ItemType Directory -Path $ReportDir -Force | Out-Null } $Report = @{ timestamp = (Get-Date -Format "yyyy-MM-ddTHH:mm:ss") totalTimeSeconds = $TotalTime results = $Results summary = @{ total = $totalPass + $totalFail + $totalSkip pass = $totalPass fail = $totalFail skip = $totalSkip } } $Report | ConvertTo-Json -Depth 3 | Out-File -FilePath $ReportPath -Encoding UTF8 Write-Success "Report saved to: $ReportPath" # ============================================= # 阶段4: 清理 # ============================================= Write-Step "阶段4: 清理" # 如果后端是我们启动的,停止它 if (-not $backendWasRunning -and $backendJob) { Write-Info "Stopping backend..." Stop-Job $backendJob -ErrorAction SilentlyContinue Remove-Job $backendJob -ErrorAction SilentlyContinue Write-Success "Backend stopped" } if ($totalFail -gt 0) { Write-Host "`n Some tests FAILED. Check reports for details." -ForegroundColor Red exit 1 } else { Write-Host "`n All tests PASSED!" -ForegroundColor Green exit 0 }