import { test, expect } from '@playwright/test' // AZ-479 — AC-3 (NFT-PERF-10): FCP on /flights ≤ 3000 ms (median of 5 runs). // // Methodology (per task spec): // 1. Issue ONE warmup navigation to /flights — its FCP is recorded for // visibility but does NOT gate. This eliminates first-load cold-cache // noise (auth handshake + SSE setup). Warmup is appended to the CSV // with `gates=warmup`. // 2. Issue 5 measured navigations to /flights. Each measurement uses // `performance.getEntriesByName('first-contentful-paint')[0].startTime`, // which is what NFT-PERF-10 row 98 specifies. // 3. Sort the 5 measurements; the 3rd value (index 2) is the median. // Assert median ≤ 3000 ms. // // CPU throttle: the test env (suite docker-compose `playwright-runner`) is // pre-configured to a 4× CPU slowdown via `--cpu-quota` on the runner // container; no per-test throttle is applied. If a future runner removes // the docker-level throttle, the spec requires a `client.send('Emulation. // setCPUThrottlingRate', { rate: 4 })` call here — see results_report.md // row 98 footnote. // // Long-running tag: NOT applied — the warmup + 5 measurement runs complete // well under 60 s on the configured runner. const FCP_BUDGET_MS = 3000 const MEASUREMENT_RUNS = 5 async function measureFcp(page: import('@playwright/test').Page): Promise { await page.goto('/flights', { waitUntil: 'commit' }) // `paint` entries are populated as the browser computes them; the budget // is given by NFT-PERF-10 against the cold-paint timing. Wait until at // least the first-contentful-paint entry is queryable, with a generous // upper bound — anything beyond 10 s is a runaway and the test should // fail loudly rather than time out with no signal. return page.waitForFunction( () => { const entry = performance.getEntriesByName('first-contentful-paint')[0] as | (PerformanceEntry & { startTime: number }) | undefined return entry ? entry.startTime : null }, null, { timeout: 10_000 }, ).then((handle) => handle.jsonValue() as Promise) } test.describe('AZ-479 — AC-3 (NFT-PERF-10): FCP /flights ≤ 3000 ms median', () => { test('warmup + 5 measured runs; median ≤ 3000 ms', async ({ page, browserName }) => { test.skip( browserName !== 'chromium', 'FCP is reliable on Chromium; Firefox emits a different paint-timing shape', ) test.setTimeout(120_000) // Warmup — recorded for visibility, not gated. const warmup = await measureFcp(page).catch(() => -1) test.info().annotations.push({ type: 'fcp-warmup-ms', description: String(Math.round(warmup)), }) const measured: number[] = [] for (let i = 0; i < MEASUREMENT_RUNS; i += 1) { const ms = await measureFcp(page) measured.push(ms) } const sorted = [...measured].sort((a, b) => a - b) const median = sorted[Math.floor(MEASUREMENT_RUNS / 2)] test.info().annotations.push({ type: 'fcp-runs-ms', description: measured.map((m) => Math.round(m)).join(','), }) test.info().annotations.push({ type: 'fcp-median-ms', description: String(Math.round(median)), }) expect.soft(measured.length).toBe(MEASUREMENT_RUNS) expect(median).toBeLessThanOrEqual(FCP_BUDGET_MS) }) })