diff --git a/tests/install/windows-desktop-gui-e2e.ps1 b/tests/install/windows-desktop-gui-e2e.ps1 index d1fd0bd074..06e87f0a11 100644 --- a/tests/install/windows-desktop-gui-e2e.ps1 +++ b/tests/install/windows-desktop-gui-e2e.ps1 @@ -44,8 +44,8 @@ # # PROOF: screenshots at every renderer step (Playwright), full-desktop # screenshots around the installer/AHK phases, a rolling desktop capture -# (every 3s) for the whole run, ahk.log, and the hand-off log. All uploaded -# as CI artifacts. +# (every 3s) plus a continuous ffmpeg screen recording (recording.mkv) for +# both phases, ahk.log, and the hand-off log. All uploaded as CI artifacts. # # DEVIATIONS FROM PRODUCTION (each one deliberate and small): # * the git URL redirect itself -- the staging requirement. @@ -218,6 +218,41 @@ function Save-DesktopScreenshot([string]$OutFile) { } } +function Start-ScreenRecording([string]$OutFile) { + # Continuous ffmpeg screen capture (gdigrab, 15fps). ffmpeg ships on the + # windows-latest runner image; skip gracefully elsewhere. mkv on purpose: + # it stays playable even if the process dies without finalizing. + # + # ffmpeg must be started, fed, and stopped from THIS process: the + # graceful stop is the character 'q' on its LIVE stdin pipe, which only + # System.Diagnostics.Process exposes (Start-Process + # -RedirectStandardInput hands it a file handle already at EOF). + if (-not (Get-Command ffmpeg -ErrorAction SilentlyContinue)) { + Write-Host " (ffmpeg not on PATH; skipping screen recording)" + return $null + } + $psi = New-Object System.Diagnostics.ProcessStartInfo + $psi.FileName = "ffmpeg" + $psi.Arguments = "-y -f gdigrab -framerate 15 -i desktop " + + "-hide_banner -loglevel error " + + "-c:v libx264 -preset ultrafast -pix_fmt yuv420p `"$OutFile`"" + $psi.RedirectStandardInput = $true + $psi.UseShellExecute = $false + $proc = [System.Diagnostics.Process]::Start($psi) + Write-Host " screen recording started (pid $($proc.Id)) -> $OutFile" + return $proc +} + +function Stop-ScreenRecording($proc) { + if ($proc -and -not $proc.HasExited) { + try { + $proc.StandardInput.Write("q") + $proc.StandardInput.Close() + } catch {} + if (-not $proc.WaitForExit(15000)) { try { $proc.Kill() } catch {} } + } +} + function Start-DesktopRecorder([string]$OutDir) { # Rolling desktop capture: one PNG every 3s from a detached PowerShell, # capped at 800 frames (~40 min). Proof that survives any step failure. @@ -377,6 +412,7 @@ function Invoke-PhaseInstallGui { New-Item -ItemType Directory -Path $HermesHome -Force | Out-Null $recorder = Start-DesktopRecorder (Join-Path $proof "desktop-frames") + $recording = Start-ScreenRecording (Join-Path $proof "recording.mkv") $ahkLog = Join-Path $proof "ahk.log" try { Save-DesktopScreenshot (Join-Path $proof "00-before-installer.png") @@ -415,6 +451,7 @@ function Invoke-PhaseInstallGui { Assert-True $installer.HasExited "Hermes-Setup.exe exited after Launch" } finally { + Stop-ScreenRecording $recording Stop-DesktopRecorder $recorder (Join-Path $proof "desktop-frames") # Surface the installer's own log win or lose. $bootLog = Join-Path $HermesHome "logs\bootstrap-installer.log" @@ -495,6 +532,7 @@ function Invoke-GuiUpdateDesktopRoute([string]$TargetSha) { Assert-True $pwResolved "@playwright/test resolvable from installed apps/desktop" $recorder = Start-DesktopRecorder (Join-Path $proof "desktop-frames") + $recording = Start-ScreenRecording (Join-Path $proof "recording.mkv") try { # Launch the installed app and click through Settings -> About -> # Update now. Exit 0 = the app quit for the updater hand-off. @@ -603,6 +641,7 @@ function Invoke-GuiUpdateDesktopRoute([string]$TargetSha) { Save-DesktopScreenshot (Join-Path $proof "99-relaunched-desktop.png") } finally { + Stop-ScreenRecording $recording Stop-DesktopRecorder $recorder (Join-Path $proof "desktop-frames") $handoffLog = Join-Path $HermesHome "logs\desktop-update-handoff.log" if (Test-Path -LiteralPath $handoffLog) {