173 lines
7.4 KiB
PowerShell
173 lines
7.4 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
ObsiGate — E2E locaux (Playwright) sous Windows/PowerShell.
|
|
|
|
.DESCRIPTION
|
|
Équivalent PowerShell de `scripts/run-e2e-local.sh`, pour les postes Windows
|
|
où `bash` n'est pas utilisable (WSL indisponible, git-bash bloqué par une
|
|
politique de contrôle d'application). Démarre le backend nativement via
|
|
uvicorn (auth désactivée, fixtures TestVault/TestDir, port 2029 — mêmes
|
|
conditions que le job CI `e2e`), lance la suite Playwright puis nettoie.
|
|
|
|
.PARAMETER PlaywrightArgs
|
|
Arguments transmis à `playwright test` (via `node`), ex. `-g "image viewer"`,
|
|
`--headed`.
|
|
|
|
.EXAMPLE
|
|
./scripts/run-e2e-local.ps1
|
|
./scripts/run-e2e-local.ps1 -g "BUG-072"
|
|
./scripts/run-e2e-local.ps1 --headed
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(ValueFromRemainingArguments = $true)]
|
|
[string[]]$PlaywrightArgs
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$Root = Split-Path -Parent $PSScriptRoot
|
|
Set-Location -LiteralPath $Root
|
|
|
|
$Port = if ($env:E2E_PORT) { $env:E2E_PORT } else { "2029" }
|
|
$BaseUrl = "http://127.0.0.1:$Port"
|
|
$ServerLog = "data/e2e-server.log"
|
|
$ServerErrLog = "data/e2e-server.err.log"
|
|
|
|
# BUG-080 : garde-fous anti-blocage (un run E2E ne doit jamais pendre toute la nuit).
|
|
# E2E_TIMEOUT_SEC dépasse volontairement le globalTimeout Playwright (25 min en
|
|
# local) pour que ce soit Playwright qui abandonne proprement (avec rapport) en premier.
|
|
$TestTimeoutSec = if ($env:E2E_TIMEOUT_SEC) { [int]$env:E2E_TIMEOUT_SEC } else { 1800 }
|
|
$BrowserTimeoutSec = if ($env:E2E_BROWSER_INSTALL_TIMEOUT_SEC) { [int]$env:E2E_BROWSER_INSTALL_TIMEOUT_SEC } else { 600 }
|
|
|
|
function Invoke-NativeWithTimeout([string]$Label, [int]$TimeoutSec, [string]$Exe, [string[]]$Arguments) {
|
|
# Lance un processus natif en gardant la sortie console en direct, et le
|
|
# tue après $TimeoutSec s'il n'a pas terminé (exit 124, comme `timeout`).
|
|
# NOTE : le paramètre NE DOIT PAS s'appeler `$Args` (variable automatique
|
|
# PowerShell qui l'écraserait → `node` lancé sans arguments, exit 0
|
|
# silencieux immédiat en lisant un stdin vide).
|
|
$stamp = Get-Date -Format "HH:mm:ss"
|
|
Write-Host "[$stamp] $Label (timeout ${TimeoutSec}s)..."
|
|
$proc = Start-Process -FilePath $Exe -ArgumentList $Arguments -NoNewWindow -PassThru
|
|
$proc | Wait-Process -Timeout $TimeoutSec -ErrorAction SilentlyContinue
|
|
if (-not $proc.HasExited) {
|
|
Write-Host "[ERR] $Label : timeout après ${TimeoutSec}s, arrêt du processus (PID $($proc.Id))."
|
|
Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
|
|
return 124
|
|
}
|
|
return $proc.ExitCode
|
|
}
|
|
|
|
function Test-ChromiumInstalled {
|
|
$base = Join-Path $env:USERPROFILE "AppData\Local\ms-playwright"
|
|
if (-not (Test-Path -LiteralPath $base)) { return $false }
|
|
$hit = Get-ChildItem -LiteralPath $base -Directory -ErrorAction SilentlyContinue |
|
|
Where-Object { $_.Name -like "chromium-*" } |
|
|
Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName "chrome-win\chrome.exe") } |
|
|
Select-Object -First 1
|
|
return ($null -ne $hit)
|
|
}
|
|
|
|
function Assert-Command([string]$Name, [string]$Hint) {
|
|
if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
|
|
throw "[ERR] $Name introuvable. $Hint"
|
|
}
|
|
}
|
|
|
|
Assert-Command "uv" "Installez-le : https://docs.astral.sh/uv/"
|
|
Assert-Command "node" "Installez Node.js (>= 20)."
|
|
|
|
# ----- Venv Python 3.11 (créé une seule fois) -----
|
|
$Python = ".venv-e2e/Scripts/python.exe"
|
|
if (-not (Test-Path -LiteralPath $Python)) {
|
|
Write-Host "[INFO] Création du venv .venv-e2e (Python 3.11)..."
|
|
uv venv .venv-e2e --python 3.11
|
|
uv pip install --python $Python -r backend/requirements.txt
|
|
}
|
|
|
|
# ----- Port déjà occupé ? -----
|
|
try {
|
|
Invoke-WebRequest -Uri "$BaseUrl/api/health" -TimeoutSec 2 -UseBasicParsing | Out-Null
|
|
Write-Host "[ERR] Quelque chose répond déjà sur $BaseUrl."
|
|
Write-Host " Arrêtez-le ou choisissez un autre port : `$env:E2E_PORT=2030; ./scripts/run-e2e-local.ps1"
|
|
exit 1
|
|
} catch {
|
|
# port libre
|
|
}
|
|
|
|
# ----- Démarrage du serveur (mêmes conditions que le CI e2e) -----
|
|
Write-Host "[INFO] Démarrage d'ObsiGate sur $BaseUrl (auth désactivée)..."
|
|
New-Item -ItemType Directory -Force -Path "data" | Out-Null
|
|
|
|
$env:OBSIGATE_AUTH_ENABLED = "false"
|
|
$env:VAULT_1_NAME = "TestVault"
|
|
$env:VAULT_1_PATH = (Resolve-Path -LiteralPath "test_vault").Path
|
|
$env:DIR_1_NAME = "TestDir"
|
|
$env:DIR_1_PATH = (Resolve-Path -LiteralPath "test_dir").Path
|
|
|
|
$server = Start-Process -FilePath $Python `
|
|
-ArgumentList "-m", "uvicorn", "backend.main:app", "--host", "127.0.0.1", "--port", $Port `
|
|
-RedirectStandardOutput $ServerLog -RedirectStandardError $ServerErrLog `
|
|
-PassThru -WindowStyle Hidden
|
|
|
|
$exitCode = 1
|
|
try {
|
|
# ----- Attente du health check (30 s max, comme le CI) -----
|
|
$ready = $false
|
|
for ($i = 0; $i -lt 30; $i++) {
|
|
try {
|
|
Invoke-WebRequest -Uri "$BaseUrl/api/health" -TimeoutSec 2 -UseBasicParsing | Out-Null
|
|
$ready = $true
|
|
break
|
|
} catch {
|
|
if ($server.HasExited) {
|
|
Write-Host "[ERR] Le serveur a quitté prématurément. Log :"
|
|
Get-Content -LiteralPath $ServerErrLog -Tail 30 -ErrorAction SilentlyContinue
|
|
exit 1
|
|
}
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
}
|
|
if (-not $ready) {
|
|
Write-Host "[ERR] Serveur injoignable sur $BaseUrl. Log :"
|
|
Get-Content -LiteralPath $ServerErrLog -Tail 30 -ErrorAction SilentlyContinue
|
|
exit 1
|
|
}
|
|
Write-Host "[OK] Serveur prêt."
|
|
|
|
# ----- Playwright via node direct (pas npx) -----
|
|
# BUG-080 : `Start-Process` ne peut pas lancer `npx` (ni le `.ps1` ni le
|
|
# `.cmd` ne sont des applications Win32 directes) → on appelle la CLI
|
|
# locale via `node.exe`, sans prompt interactif possible. Skip de
|
|
# l'install si un chromium est déjà présent (sauf E2E_INSTALL_BROWSERS=1),
|
|
# timeouts dédiés sur chaque étape.
|
|
$PlaywrightCli = Join-Path $Root "node_modules/@playwright/test/cli.js"
|
|
if (-not (Test-Path -LiteralPath $PlaywrightCli)) {
|
|
throw "[ERR] $PlaywrightCli introuvable. Lancez d'abord : npm ci"
|
|
}
|
|
if (($env:E2E_INSTALL_BROWSERS -eq "1") -or (-not (Test-ChromiumInstalled))) {
|
|
$code = Invoke-NativeWithTimeout "playwright install chromium" $BrowserTimeoutSec "node" @($PlaywrightCli, "install", "chromium")
|
|
if ($code -ne 0) { exit $code }
|
|
} else {
|
|
Write-Host "[INFO] Chromium Playwright déjà installé, étape sautée (E2E_INSTALL_BROWSERS=1 pour forcer)."
|
|
}
|
|
|
|
# ----- Exécution de la suite (projet CI : chromium-desktop) -----
|
|
Write-Host "[INFO] BASE_URL=$BaseUrl node $PlaywrightCli test --project=chromium-desktop $($PlaywrightArgs -join ' ')"
|
|
$env:BASE_URL = $BaseUrl
|
|
$testArgs = @($PlaywrightCli, "test", "--project=chromium-desktop")
|
|
if ($PlaywrightArgs) { $testArgs += @($PlaywrightArgs) }
|
|
$exitCode = Invoke-NativeWithTimeout "playwright test" $TestTimeoutSec "node" $testArgs
|
|
} catch {
|
|
Write-Host "[ERR] $($_.Exception.Message)"
|
|
$exitCode = 1
|
|
} finally {
|
|
Write-Host "[INFO] Arrêt du serveur (PID $($server.Id))..."
|
|
if (-not $server.HasExited) { Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue }
|
|
# uvicorn (via uv) peut lancer un interpréteur enfant : tuer le groupe resté sur le port.
|
|
Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue |
|
|
ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }
|
|
}
|
|
|
|
exit $exitCode
|