import { expect, type Page } from "@playwright/test"; import { THEME_BASES, nextBase, type ThemeBase } from "../../common/components/themeConfig"; // THE ONE WAY a homepage spec changes the theme through the UI: the header's // toggle (common/components/ThemeToggle.tsx, `variant="bare"`), which cycles // the base in nextBase's order, shows the base in force as `data-theme-base`, // and is named "Switch to {next}". Specs that only need a ground set // `localStorage` instead. No app has an accent control (each shows its own), // so there is no accent to choose here. export const themeToggle = (page: Page) => page.locator("header").getByRole("button", { name: /^switch to /i }); // The cycle as the toggle walks it, from `start`: every base once. Derived // from THEME_BASES and nextBase, so a base added or removed there needs no // change here. export function themeCycle(start: ThemeBase): ThemeBase[] { const out: ThemeBase[] = [start]; for (let b = nextBase(start); b !== start && out.length <= THEME_BASES.length; b = nextBase(b)) { out.push(b); } return out; } export const baseLabel = (b: ThemeBase) => THEME_BASES.find((x) => x.id === b)!.label.toLowerCase(); // Click the toggle until the base in force is `base`. A click before // hydration is lost, so each step waits for the attribute to move. export async function chooseTheme(page: Page, { base }: { base: ThemeBase }) { const toggle = themeToggle(page); for (let i = 0; i <= THEME_BASES.length; i++) { const current = (await toggle.getAttribute("data-theme-base")) as ThemeBase; if (current === base) return; await expect(async () => { if ((await toggle.getAttribute("data-theme-base")) === current) await toggle.click(); expect(await toggle.getAttribute("data-theme-base")).not.toBe(current); }).toPass({ timeout: 10_000 }); } expect(await toggle.getAttribute("data-theme-base")).toBe(base); }