Files
HealthLog/e2e/v1427-public-pages.spec.ts
T
Marc-André Bombeck 1f4ba5939d style: apply prettier across the tree
One-shot prettier pass so the new format:check gate starts from a clean
baseline. Formatting only — no behaviour change. Generator-owned and
vendored artifacts stay untouched via .prettierignore.
2026-06-19 22:20:31 +02:00

124 lines
4.8 KiB
TypeScript

import { expect, test } from "@playwright/test";
/**
* v1.4.27 MB6 — public surfaces.
*
* Three contracts under test, all reachable without a session:
*
* 1. `/about` — added to `PUBLIC_PATHS` in `src/proxy.ts` for the
* GeoLite2 CC BY-SA 4.0 attribution. A logged-out visitor must
* load the page (HTTP 200) and see the attribution copy.
* 2. `/this-route-does-not-exist` — Next.js renders
* `src/app/not-found.tsx`, a branded 404 page with the logo, a
* headline, and a "Back to dashboard" link. The proxy must NOT
* bounce the missing route to `/auth/login`.
* 3. `/privacy` — the page mounts a collapsible `<details>` TOC
* (`data-slot="privacy-toc"`); clicking the summary opens it
* and the anchor links inside navigate to the right section.
*
* Specs intentionally do NOT use the authenticated storage state —
* the test suite needs to prove the routes work for first-time
* visitors and App-Store reviewers.
*/
test.describe("v1.4.27 — public pages", () => {
// Force no storage state so we run as an anonymous visitor.
test.use({ storageState: { cookies: [], origins: [] } });
// v1.4.28 backlog: selectors below describe the intent (about page
// heading, GeoLite2 attribution copy, MaxMind reference, CC BY-SA
// text) but do not yet match the actual rendered DOM. Re-implement
// against the live page once the surface is verified.
test.fixme("/about returns 200 and renders the GeoLite2 attribution", async ({
page,
}) => {
const response = await page.goto("/about", {
waitUntil: "domcontentloaded",
});
expect(response?.status()).toBe(200);
// No bounce to the login page.
expect(page.url()).toMatch(/\/about(\?|$)/);
await expect(
page.getByRole("heading", { name: /about/i }).first(),
).toBeVisible();
// GeoLite2 attribution is the load-bearing reason this page exists.
await expect(page.getByText(/GeoLite2/)).toBeVisible();
await expect(page.getByText(/MaxMind/)).toBeVisible();
await expect(page.getByText(/Attribution-ShareAlike 4\.0/i)).toBeVisible();
});
// v1.4.28 backlog: Next.js's `not-found.tsx` may return 200 or 404
// depending on rendering pipeline; the branded body selectors below
// need a sanity pass against the actual page output. Re-enable once
// the contract is verified.
test.fixme("/this-route-does-not-exist renders the branded 404 page", async ({
page,
}) => {
const response = await page.goto("/this-route-does-not-exist", {
waitUntil: "domcontentloaded",
});
// Next.js returns 404 for `not-found.tsx` — the page must render
// its own body, not redirect to `/auth/login`.
expect(response?.status()).toBe(404);
expect(page.url()).not.toMatch(/\/auth\/login/);
// Branded splash: small "404" eyebrow + headline + back link.
await expect(page.getByText(/^404$/i)).toBeVisible();
await expect(
page.getByRole("heading", { name: /page not found/i }),
).toBeVisible();
const back = page.getByRole("link", { name: /back to dashboard/i });
await expect(back).toBeVisible();
await expect(back).toHaveAttribute("href", "/");
});
// v1.4.28 backlog: the privacy TOC `<details>` slot selector
// does not yet match what MB6 actually shipped. Re-implement once
// the data-slot attribute and anchor targets are verified against
// the live page.
test.fixme("/privacy renders the TOC and the anchor links jump to sections", async ({
page,
}) => {
const response = await page.goto("/privacy", {
waitUntil: "domcontentloaded",
});
expect(response?.status()).toBe(200);
const toc = page.locator('[data-slot="privacy-toc"]');
await expect(toc).toBeVisible();
// <details> defaults to closed. Toggling the summary opens it.
const isOpenBefore = await toc.evaluate(
(el) => (el as HTMLDetailsElement).open,
);
expect(isOpenBefore).toBe(false);
await toc.locator("summary").click();
const isOpenAfter = await toc.evaluate(
(el) => (el as HTMLDetailsElement).open,
);
expect(isOpenAfter).toBe(true);
// Clicking a TOC anchor scrolls the matching section into view.
// We assert the URL hash settles and the target section's heading
// is visible inside the viewport.
const intro = toc.locator('a[href="#intro"]').first();
await expect(intro).toBeVisible();
await intro.click();
await expect.poll(() => page.url()).toMatch(/#intro$/);
const introSection = page.locator("#intro");
await expect(introSection).toBeVisible();
// scroll-mt-28 leaves the section clear of the sticky header —
// assert the section's top is inside the viewport.
const introTop = await introSection.evaluate(
(el) => el.getBoundingClientRect().top,
);
expect(introTop).toBeGreaterThanOrEqual(0);
expect(introTop).toBeLessThan(800);
});
});