$ErrorActionPreference = 'Stop' $projectRoot = Split-Path -Parent $PSScriptRoot $backendJob = $null $viteJob = $null function Assert-ExitCode { param([string]$Step) if ($LASTEXITCODE -ne 0) { throw "$Step failed with exit code $LASTEXITCODE" } } function Wait-HttpReady { param([string]$Url) for ($attempt = 0; $attempt -lt 60; $attempt++) { try { [void](Invoke-WebRequest -Uri $Url -UseBasicParsing -TimeoutSec 2) return } catch { Start-Sleep -Milliseconds 200 } } throw "Service did not become ready: $Url" } Push-Location $projectRoot try { foreach ($port in 3000, 5173) { if (Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue) { throw "Port $port is already in use" } } cargo build --manifest-path backend\Cargo.toml Assert-ExitCode 'Rust build' $e2eData = [System.IO.Path]::GetFullPath((Join-Path $projectRoot '.e2e-data')) $expectedE2eData = [System.IO.Path]::GetFullPath($projectRoot).TrimEnd('\') + '\.e2e-data' if ($e2eData -ne $expectedE2eData) { throw "Refusing to clean unexpected end-to-end data path: $e2eData" } if (Test-Path -LiteralPath $e2eData) { Remove-Item -LiteralPath $e2eData -Recurse -Force } [void](New-Item -ItemType Directory -Path $e2eData -Force) $env:TASK_PILOT_DATABASE_URL = 'sqlite://.e2e-data/task_pilot.db' $env:TASK_PILOT_LOG_DIR = '.e2e-data/logs' $env:TASKPILOT_E2E_EXTERNAL = '1' $backendBinary = Join-Path $projectRoot 'backend\target\debug\task-pilot-backend.exe' $nodeBinary = (Get-Command node).Source $frontendRoot = Join-Path $projectRoot 'frontend' $backendJob = Start-Job -ScriptBlock { param($root, $binary) Set-Location $root & $binary } -ArgumentList $projectRoot, $backendBinary Wait-HttpReady 'http://127.0.0.1:3000/api/v1/health' $viteJob = Start-Job -ScriptBlock { param($root, $node) Set-Location $root & $node 'node_modules/vite/bin/vite.js' '--host' '127.0.0.1' } -ArgumentList $frontendRoot, $nodeBinary Wait-HttpReady 'http://127.0.0.1:5173' pnpm --dir frontend exec playwright test Assert-ExitCode 'Playwright tests' } finally { foreach ($job in @($viteJob, $backendJob)) { if ($job) { Stop-Job -Job $job -ErrorAction SilentlyContinue Remove-Job -Job $job -Force -ErrorAction SilentlyContinue } } foreach ($port in 3000, 5173) { $connection = Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue if ($connection) { Stop-Process -Id $connection.OwningProcess -Force -ErrorAction SilentlyContinue } } Pop-Location }