diff --git a/playwright.config.ts b/playwright.config.ts index 14f3823c7..8087c5183 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -26,6 +26,7 @@ const E2E_PROJECTS_PATH = join(tmpdir(), `cave-e2e-projects-${E2E_RUN_ID}.json`) const E2E_QUEUE_PROJECT_PATH = join(tmpdir(), `cave-e2e-queue-project-${E2E_RUN_ID}.json`); const E2E_PROJECT_PERMISSIONS_PATH = join(tmpdir(), `cave-e2e-project-permissions-${E2E_RUN_ID}.json`); const PERSISTED_SCREEN_SCALE_TEST = /persisted screen magnification scales the app without window scroll$/; +const SETUP_FOCUS_VISIBILITY_TEST = /keeps setup-header focus indicators visible inside the horizontal scroller$/; const MOBILE_FOUNDATIONS_SPEC = /mobile\/foundations\.spec\.ts/; // Most existing specs exercise an already-onboarded workspace. Seed that @@ -117,6 +118,13 @@ export default defineConfig({ grepInvert: PERSISTED_SCREEN_SCALE_TEST, use: { ...devices["Desktop Chrome"] }, }, + { + name: "setup-focus-webkit", + dependencies: ["preferences-iphone-13"], + testMatch: /onboarding-wizard\.spec\.ts/, + grep: SETUP_FOCUS_VISIBILITY_TEST, + use: { ...devices["Desktop Safari"] }, + }, { name: "pixel-5", dependencies: ["preferences-iphone-13"], diff --git a/src/components/onboarding-polish.test.ts b/src/components/onboarding-polish.test.ts index d275aaa60..3e04c9153 100644 --- a/src/components/onboarding-polish.test.ts +++ b/src/components/onboarding-polish.test.ts @@ -10,11 +10,25 @@ const source = [ // Setup status actions stay together as one compact row. On narrow panes the // row scrolls within its own width instead of wrapping or widening the page. const setupHeader = source.match(//)?.[0] ?? ""; -assert.match( - setupHeader, - /className="flex w-full flex-nowrap items-center gap-2 overflow-x-auto lg:w-auto lg:shrink-0"/, - "Re-check, Copy diagnostics, and readiness render in one contained non-wrapping row", -); +const setupActionsMatch = setupHeader.match( + / { test("keeps setup-header focus indicators visible inside the horizontal scroller", async ({ page, + browserName, }) => { // Keep this assertion on the desktop-shell side of the responsive boundary; // the mobile project owns the narrower navigation layout. @@ -150,7 +151,11 @@ test.describe("onboarding wizard", () => { await openWizardManually(page); const recheck = wizard(page).getByRole("button", { name: "Re-check" }); - await recheck.focus(); + // Enter through a real keyboard transition so Chromium/WebKit apply + // :focus-visible for the same modality this regression protects. WebKit's + // macOS keyboard-access convention uses Option+Tab for control focus. + await wizard(page).focus(); + await page.keyboard.press(browserName === "webkit" ? "Alt+Tab" : "Tab"); await expect(recheck).toBeFocused(); const focusStyle = await recheck.evaluate((button) => {