Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"],
Expand Down
24 changes: 19 additions & 5 deletions src/components/onboarding-polish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(/<header[\s\S]*?<\/header>/)?.[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(
/<div\s+className="([^"]*\boverflow-x-auto\b[^"]*)"/,
);
const setupActionsClasses = setupActionsMatch?.[1]?.split(/\s+/) ?? [];
for (const className of [
"flex",
"w-full",
"flex-nowrap",
"items-center",
"gap-2",
"overflow-x-auto",
"lg:w-auto",
"lg:shrink-0",
]) {
assert.ok(
setupActionsClasses.includes(className),
`setup actions retain ${className} without pinning harmless class order`,
);
}
assert.equal(
setupHeader.match(/focus-ring-inset/g)?.length,
2,
Expand Down
7 changes: 6 additions & 1 deletion tests/onboarding-wizard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ test.describe("onboarding wizard", () => {

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.
Expand All @@ -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) => {
Expand Down
Loading