$ErrorActionPreference = 'Stop' $root = Split-Path -Parent $PSScriptRoot $captureScriptPath = Join-Path $root 'scripts/capture-chrome-page.js' if (-not (Test-Path -LiteralPath $captureScriptPath)) { throw 'Missing Chrome capture script' } $captureScript = Get-Content -LiteralPath $captureScriptPath -Raw -Encoding utf8 if ($captureScript -notmatch "const renderSettleMilliseconds = 900") { throw 'Chrome capture must define a 900ms render-settle delay after the target selector appears' } if ($captureScript -notmatch 'await sleep\(renderSettleMilliseconds\)') { throw 'Chrome capture must wait for the render-settle delay before taking a screenshot' } if ($captureScript -match 'await sleep\(300\)') { throw 'Chrome capture must not retain the unstable 300ms post-selector delay' } if ($captureScript -notmatch 'const waitForLoadedImages = async \(send\) =>') { throw 'Chrome capture must define an image-load wait before a visual screenshot' } if ($captureScript -notmatch 'Array\.from\(document\.images\)\.every') { throw 'Chrome capture must wait until every document image has loaded' } if ($captureScript -notmatch 'await waitForLoadedImages\(send\)') { throw 'Chrome capture must wait for document images after the target selector renders' } if ($captureScript -notmatch "await send\('Page.reload'\)") { throw 'Chrome capture must reload after navigation so route screenshots do not inherit a prior Vue state' } if ($captureScript.IndexOf("await send('Page.navigate'") -ge $captureScript.IndexOf("await send('Page.reload')")) { throw 'Chrome capture must reload only after it has navigated to the requested URL' } if ($captureScript -notmatch 'const waitForFreshDocument = async \(send, documentTimeOrigin\) =>') { throw 'Chrome capture must define a fresh-document wait after reloading' } if ($captureScript -notmatch 'performance\.timeOrigin') { throw 'Chrome capture must distinguish a fresh document from a reused Vue route state' } if ($captureScript -notmatch 'await waitForFreshDocument\(send, documentTimeOrigin\)') { throw 'Chrome capture must wait for a fresh document before checking the target selector' } if ($captureScript -notmatch "action !== 'fresh-navigation'") { throw 'Chrome capture must support fresh-navigation for root pages whose tab lifecycle closes a reloaded CDP target' } foreach ($g06Action in @('g06-results', 'g06-empty', 'g06-invite')) { if ($captureScript -notmatch [regex]::Escape($g06Action)) { throw "Chrome capture must support the $g06Action visual state" } } foreach ($a01Action in @('password-tab', 'sms-tab')) { if ($captureScript -notmatch [regex]::Escape($a01Action)) { throw "Chrome capture must support the $a01Action A01 state" } } if ($captureScript -notmatch 'const prepareA01LoginState = async \(send, action\) =>') { throw 'Chrome capture must wait for A01 login tabs before switching state' } if ($captureScript -notmatch 'await prepareA01LoginState\(send, action\)') { throw 'Chrome capture must verify the requested A01 state before taking a screenshot' } if ($captureScript -notmatch 'const prepareG06State = async \(send, action\) =>') { throw 'Chrome capture must prepare G06 search states through the real input and action controls' } if ($captureScript -notmatch 'await prepareG06State\(send, action\)') { throw 'Chrome capture must wait for the requested G06 state before taking a screenshot' } if ($captureScript -notmatch "item\.url\.startsWith\('http://localhost:5173'\)") { throw 'Chrome capture must select the application page instead of a DevTools page containing localhost in its query' } if ($captureScript -match "item\.url\.includes\('localhost:5173'\)") { throw 'Chrome capture must not use an ambiguous localhost substring when selecting its CDP target' } if ($captureScript -notmatch "process\.env\.CHROME_DEBUGGING_PORT \|\| '9222'") { throw 'Chrome capture must preserve port 9222 by default and allow an isolated QA port.' } Write-Output 'CAPTURE-CHROME-PAGE-CONTRACT PASS'