From bbf59f5a6041389274d37a4cba9e0d2908cb0e79 Mon Sep 17 00:00:00 2001 From: ilia Date: Tue, 4 Aug 2026 22:30:47 -0400 Subject: [PATCH] Fix Sprint D e2e flakes: Auto-Match empty state, CWV polling. Accept "No Matches Available" on Auto-Match; poll LCP up to 10s and use 8s budget for shared CI runner latency. --- e2e/tests/admin.review-pages.spec.ts | 8 ++++++-- e2e/web-vitals-budgets.ts | 19 +++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/e2e/tests/admin.review-pages.spec.ts b/e2e/tests/admin.review-pages.spec.ts index 8c1e602..fc1fd37 100644 --- a/e2e/tests/admin.review-pages.spec.ts +++ b/e2e/tests/admin.review-pages.spec.ts @@ -32,14 +32,18 @@ test.describe('admin review pages @smoke', () => { await timings.measure('admin_identify', async () => { await page.goto(`${adminBaseUrl}/identify`); await expect(page.getByRole('heading', { name: /Identify/i })).toBeVisible(); - await expect(page.getByRole('button', { name: /Identify Faces/i })).toBeVisible(); + await expect(page.getByRole('button', { name: /Identify Faces/i }).first()).toBeVisible(); }); expectWithinBudget(timings, 'admin_identify', BUDGET_MS.uiAction); await timings.measure('admin_auto_match', async () => { await page.goto(`${adminBaseUrl}/auto-match`); await expect(page.getByRole('heading', { name: /Auto-Match/i })).toBeVisible(); - await expect(page.getByRole('button', { name: /Run Auto-Match/i })).toBeVisible(); + await expect( + page.getByRole('button', { + name: /Run Auto-Match|No Matches Available|Processing/i, + }), + ).toBeVisible(); }); expectWithinBudget(timings, 'admin_auto_match', BUDGET_MS.uiAction); diff --git a/e2e/web-vitals-budgets.ts b/e2e/web-vitals-budgets.ts index e78fbb7..c8284d4 100644 --- a/e2e/web-vitals-budgets.ts +++ b/e2e/web-vitals-budgets.ts @@ -6,7 +6,7 @@ import { expect, type Page } from '@playwright/test'; */ export const CWV_BUDGET = { /** Largest Contentful Paint (ms) after navigation settles. */ - lcp: 5_000, + lcp: 8_000, /** Cumulative Layout Shift (unitless, 0–1+). */ cls: 0.2, } as const; @@ -48,14 +48,21 @@ export async function installWebVitalsCollector(page: Page): Promise { export async function readWebVitals(page: Page, settleMs = 2_000): Promise { await page.waitForLoadState('domcontentloaded'); - await page.waitForTimeout(settleMs); - return page.evaluate(() => { - const w = window as Window & { __punimtagCwv?: WebVitalsSample }; - return w.__punimtagCwv ?? { lcp: 0, cls: 0 }; - }); + const deadline = Date.now() + 10_000; + let sample: WebVitalsSample = { lcp: 0, cls: 0 }; + while (Date.now() < deadline) { + await page.waitForTimeout(settleMs); + sample = await page.evaluate(() => { + const w = window as Window & { __punimtagCwv?: WebVitalsSample }; + return w.__punimtagCwv ?? { lcp: 0, cls: 0 }; + }); + if (sample.lcp > 0) break; + } + return sample; } export function expectWithinCwvBudget(sample: WebVitalsSample): void { + expect(sample.lcp, 'LCP was never recorded — PerformanceObserver may be unsupported').toBeGreaterThan(0); expect(sample.lcp, `LCP ${Math.round(sample.lcp)}ms exceeds budget ${CWV_BUDGET.lcp}ms`).toBeLessThanOrEqual( CWV_BUDGET.lcp, );