From d012819f6b05197c2259a7982c600cfdb2b74c8a Mon Sep 17 00:00:00 2001 From: Sergey Kozyrenko Date: Wed, 22 Jul 2026 00:09:42 +0700 Subject: [PATCH] test(e2e): make the colour gates fail loudly when they stop measuring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three ways these gates could go green while measuring nothing: The editor probes name their classes by hand — the one place in the contrast spec that does not import them from the app. A rename, or the rule moving out from under its ancestor chain, would mount a span matching no rule, and the measurement would fall back to the page's default text pair, which clears AA. The probes now reject a transparent background, which only an unmatched class produces (the check lives with the editor probes, not in the shared measure — the outline badge legitimately paints no background). The dark sweeps seed a theme key that mirrors a *default parameter value* in the provider, so it can stop matching without any test file changing; the run would then repeat the light theme under a dark label. Both sweeps now assert the theme actually applied. The resources target-size waiver matched `.rounded` unanchored, so it also covered every `rounded-*` utility — a too-broad waiver never fails, it only hides. Anchored to the class it documents. Co-Authored-By: Claude Opus 4.8 --- frontend/e2e/helpers/contrast.ts | 10 ++++++++++ frontend/e2e/routes.ts | 4 +++- frontend/e2e/specs/cross/a11y.spec.ts | 3 +++ frontend/e2e/specs/cross/contrast.spec.ts | 3 +++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/frontend/e2e/helpers/contrast.ts b/frontend/e2e/helpers/contrast.ts index ed54da58..28ab7a76 100644 --- a/frontend/e2e/helpers/contrast.ts +++ b/frontend/e2e/helpers/contrast.ts @@ -62,6 +62,16 @@ export const mountEditorProbes = async (page: Page, probes: Record { await page.goto(entry.path); await expect(entry.ready(page)).toBeVisible(); + // A seed that stops taking effect would otherwise re-run the light + // theme under a dark label and pass. + await expect(page.locator('html')).toHaveClass(theme === 'dark' ? /dark/ : /light/); await scanA11y(page, entry.path, entry.a11yWaivers); }); }); diff --git a/frontend/e2e/specs/cross/contrast.spec.ts b/frontend/e2e/specs/cross/contrast.spec.ts index 26eda8f2..5c11ff2e 100644 --- a/frontend/e2e/specs/cross/contrast.spec.ts +++ b/frontend/e2e/specs/cross/contrast.spec.ts @@ -42,6 +42,7 @@ for (const theme of THEMES) { test('every badge variant clears AA at rest and on hover', async ({ page }) => { await page.goto('/flows'); await expect(page.getByRole('row', { name: /E2E Alpha/ })).toBeVisible(); + await expect(page.locator('html')).toHaveClass(theme === 'dark' ? /dark/ : /light/); await mountContrastProbes( page, @@ -63,6 +64,7 @@ for (const theme of THEMES) { test('the destructive button clears AA at rest and on hover', async ({ page }) => { await page.goto('/flows'); await expect(page.getByRole('row', { name: /E2E Alpha/ })).toBeVisible(); + await expect(page.locator('html')).toHaveClass(theme === 'dark' ? /dark/ : /light/); await mountContrastProbes(page, { destructive: buttonVariants({ variant: 'destructive' }) }); @@ -77,6 +79,7 @@ for (const theme of THEMES) { test('editor highlight tokens clear AA on the editor surface', async ({ page }) => { await page.goto('/flows'); await expect(page.getByRole('row', { name: /E2E Alpha/ })).toBeVisible(); + await expect(page.locator('html')).toHaveClass(theme === 'dark' ? /dark/ : /light/); await mountEditorProbes(page, EDITOR_TOKENS);