From 9bb1e5b2ea731296ae209bdd77e93078fa4ee603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 6 Aug 2026 05:14:00 +0200 Subject: [PATCH] test(02-12): scan shared record accessibility states - use an isolated shared-record browser session\n- scan empty, loading, error, and refusal states with axe --- e2e/setup/global-setup.ts | 10 ++ e2e/setup/test-helpers.ts | 1 + ...v137-sharing-managed-profiles-a11y.spec.ts | 92 +++++++++++++++++-- 3 files changed, 97 insertions(+), 6 deletions(-) diff --git a/e2e/setup/global-setup.ts b/e2e/setup/global-setup.ts index 8fb94174d..24cec4db6 100644 --- a/e2e/setup/global-setup.ts +++ b/e2e/setup/global-setup.ts @@ -138,6 +138,11 @@ export const SCOPE_DELEGATE_STORAGE_STATE_PATH = resolve( "e2e/setup/storageStateScopeDelegate.json", ); +export const SCOPE_A11Y_STORAGE_STATE_PATH = resolve( + process.cwd(), + "e2e/setup/storageStateScopeA11y.json", +); + async function hashPassword(password: string): Promise { return hash(password, { memoryCost: 19456, @@ -488,6 +493,11 @@ export default async function globalSetup(config: FullConfig): Promise { E2E_SCOPE_DELEGATE, SCOPE_DELEGATE_STORAGE_STATE_PATH, ); + await captureAuthState( + baseURL, + E2E_SCOPE_DELEGATE, + SCOPE_A11Y_STORAGE_STATE_PATH, + ); } /** diff --git a/e2e/setup/test-helpers.ts b/e2e/setup/test-helpers.ts index 5a72d271f..837e6179d 100644 --- a/e2e/setup/test-helpers.ts +++ b/e2e/setup/test-helpers.ts @@ -17,4 +17,5 @@ export { OWNER_STORAGE_STATE_PATH, DELEGATE_STORAGE_STATE_PATH, SCOPE_DELEGATE_STORAGE_STATE_PATH, + SCOPE_A11Y_STORAGE_STATE_PATH, } from "./global-setup"; diff --git a/e2e/v137-sharing-managed-profiles-a11y.spec.ts b/e2e/v137-sharing-managed-profiles-a11y.spec.ts index ed146f0f3..96812d259 100644 --- a/e2e/v137-sharing-managed-profiles-a11y.spec.ts +++ b/e2e/v137-sharing-managed-profiles-a11y.spec.ts @@ -1,11 +1,91 @@ +import AxeBuilder from "@axe-core/playwright"; +import type { Page } from "@playwright/test"; + import { expect, test } from "./setup/test"; +import { SCOPE_A11Y_STORAGE_STATE_PATH } from "./setup/test-helpers"; -import { ACCESSIBILITY_STATES } from "../tests/fixtures/v137/e2e-journeys"; +async function scan(page: Page) { + const result = await new AxeBuilder({ page }) + .withTags(["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"]) + .analyze(); + expect(result.violations).toEqual([]); +} -test.describe("sharing accessibility anchors", () => { - for (const state of ACCESSIBILITY_STATES) { - test(`anchor: ${state.name}`, () => { - expect(state.contract).toBe("inventory-only"); +async function openProfileRecord(page: Page) { + await page.goto("/"); + await page.getByRole("button", { name: "User menu" }).first().click(); + await page.locator('[data-slot="account-switcher-trigger"]').click(); + const entry = page + .locator('[data-slot="account-switcher-entry"]') + .filter({ hasText: "e2e-scope-profile" }); + await expect(entry).toHaveCount(1); + await entry.click(); + await expect(page).toHaveURL(/\/profile$/); +} + +async function leaveRecord(page: Page) { + await page.locator('[data-slot="shared-record-banner-exit"]').click(); + await expect(page.locator('[data-slot="shared-record-banner"]')).toHaveCount( + 0, + ); +} + +test.describe("shared record accessibility states", () => { + test.describe.configure({ mode: "serial" }); + test.use({ storageState: SCOPE_A11Y_STORAGE_STATE_PATH }); + + test.beforeEach(({}, testInfo) => { + test.skip( + testInfo.project.name !== "chromium-desktop", + "isolated shared-record accessibility session", + ); + }); + + test("success and empty shared profile states have no WCAG violations", async ({ + page, + }) => { + await openProfileRecord(page); + await expect(page.locator('[data-slot="profile-summary"]')).toBeVisible(); + await scan(page); + await leaveRecord(page); + }); + + test("the capability hydration loading state has no WCAG violations", async ({ + page, + }) => { + await page.route("**/api/auth/me", async (route) => { + await new Promise((resolve) => setTimeout(resolve, 250)); + await route.continue(); }); - } + await page.goto("/measurements"); + await expect( + page.locator('[data-slot="record-scope-hydration-gate"]'), + ).toBeVisible(); + await scan(page); + }); + + test("the shared profile read error state has no WCAG violations", async ({ + page, + }) => { + await openProfileRecord(page); + await page.route("**/api/profile/summary", (route) => + route.fulfill({ status: 500, body: "{}" }), + ); + await page.reload(); + await expect(page.locator('[data-slot="query-error-card"]')).toBeVisible(); + await scan(page); + await leaveRecord(page); + }); + + test("the shared-record refusal state has no WCAG violations", async ({ + page, + }) => { + await openProfileRecord(page); + await page.goto("/settings/account"); + await expect( + page.locator('[data-slot="shared-record-unavailable"]'), + ).toBeVisible(); + await scan(page); + await page.locator('[data-slot="shared-record-unavailable-leave"]').click(); + }); });