From 00848b7ed5c2a7ca7e427d03d55b5968e4bc0f33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Sun, 10 May 2026 00:50:00 +0200 Subject: [PATCH] fix(e2e): skip desktop-sidebar specs on mobile + stub /api/dashboard/widgets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three remaining e2e failures after the global-setup tour-completed fix all came from cross-project sharing — specs that target desktop-only DOM (the `aside[aria-label="Sidebar"]` logo + Achievements link) were running in the chromium-mobile profile too, where the sidebar is `hidden md:flex` by design. Added `test.skip(viewport.width < 768, ...)` guards so each spec only runs in the project that can satisfy its contract. The fourth failure was the `/api/dashboard/widgets` endpoint that the A5 tile-strip work added since `onboarding-flicker.spec.ts` was written. Without a stub the unauthenticated dashboard 500s on this query and React Query's error boundary swallows the page render before `` mounts. Added the stub to both flicker specs (complete + incomplete-onboarding paths). --- e2e/a11y.spec.ts | 12 ++++++++++++ e2e/achievements.spec.ts | 20 ++++++++++++++++---- e2e/onboarding-flicker.spec.ts | 28 ++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/e2e/a11y.spec.ts b/e2e/a11y.spec.ts index c25b66e1e..64787e12a 100644 --- a/e2e/a11y.spec.ts +++ b/e2e/a11y.spec.ts @@ -119,7 +119,19 @@ test.describe("axe-core authenticated surfaces", () => { // bounding box must accept the click. test("skip-link does not block logo click outside focus", async ({ page, + viewport, }) => { + // The desktop sidebar (and its logo) is `hidden md:flex` — the + // mobile profile (Pixel 5) hides it by design. The skip-link + // regression Marc originally reported in v1.4.14 was a desktop- + // only artifact (mobile uses the topbar logo, which is a + // different DOM node). Skip the mobile project so we don't + // flake on a contract that doesn't apply there. + test.skip( + (viewport?.width ?? 0) < 768, + "desktop sidebar logo is hidden on mobile by design", + ); + await page.goto("/", { waitUntil: "domcontentloaded" }); await page.waitForLoadState("networkidle"); diff --git a/e2e/achievements.spec.ts b/e2e/achievements.spec.ts index 219ec7cb6..507869f11 100644 --- a/e2e/achievements.spec.ts +++ b/e2e/achievements.spec.ts @@ -55,11 +55,23 @@ test.describe("achievements page is reachable from the sidebar", () => { ); }); - test("desktop sidebar exposes the achievements link", async ({ page }) => { + test("desktop sidebar exposes the achievements link", async ({ + page, + viewport, + }) => { + // The sidebar nav is hidden on small viewports (`md:flex`). The + // chromium-mobile profile (Pixel 5, 393 px wide) cannot satisfy + // this contract by design — skip there so the spec only runs in + // the chromium-desktop project (1280×720). Without this guard + // every push fails 1 / 4 specs purely because the same suite is + // sharded across both projects. + test.skip( + (viewport?.width ?? 0) < 768, + "sidebar is hidden on mobile by design (md:flex breakpoint)", + ); + await page.goto("/", { waitUntil: "domcontentloaded" }); - // The sidebar nav is hidden on small viewports (`md:flex`); the - // chromium-desktop project uses 1280×720 so it's visible here. Match - // by aria-label so we don't accidentally pick up a tooltip. + // Match by aria-label so we don't accidentally pick up a tooltip. const sidebar = page.locator('aside[aria-label="Sidebar"]'); await expect(sidebar).toBeVisible(); await sidebar.getByRole("link", { name: "Achievements" }).click(); diff --git a/e2e/onboarding-flicker.spec.ts b/e2e/onboarding-flicker.spec.ts index b3fef3e94..936cb7d28 100644 --- a/e2e/onboarding-flicker.spec.ts +++ b/e2e/onboarding-flicker.spec.ts @@ -94,6 +94,19 @@ test.describe("dashboard onboarding card flicker guard", () => { }), }), ); + // Same `/api/dashboard/widgets` stub as the incomplete-onboarding + // test below — keeps the dashboard from error-boundary-bailing + // when this query 500s in CI without a real session. + await page.route("**/api/dashboard/widgets", (route) => + route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ + data: { tilesVisible: { weight: true, bp: true, pulse: true } }, + error: null, + }), + }), + ); const card = page.locator('[data-testid="onboarding-card"]'); @@ -192,6 +205,21 @@ test.describe("dashboard onboarding card flicker guard", () => { body: JSON.stringify({ data: { channels: [] }, error: null }), }), ); + // v1.4.16 Wave-C — the dashboard added `/api/dashboard/widgets` + // (A5 tile-strip persistence) since this spec was written. Without + // a stub the unauthenticated layout query 500s in CI and React + // Query's error boundary swallows the page render before + // `` can mount. + await page.route("**/api/dashboard/widgets", (route) => + route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify({ + data: { tilesVisible: { weight: true, bp: true, pulse: true } }, + error: null, + }), + }), + ); // Make sure no localStorage carries an `expanded=1` from a previous // session — the spec asserts the *default* is collapsed.