import { expect, test, type Locator, type Page, type Route } from "@playwright/test"; import { BASE_GROUNDS, ICON_PALETTES, markSvg } from "../../common/lib/brand"; // The hub is the family's own tool, so it wears the PARENT mark — bone on // slate, achromatic — in its icons and its header, like the homepage and the // editor; never an archive's accent (plans/brand-and-themes.md, slice S1). async function fulfillJson(route: Route, body: unknown) { await route.fulfill({ status: 200, contentType: "application/json", headers: { "access-control-allow-origin": "*" }, body: JSON.stringify(body), }); } test("the hub's icons are the parent mark", async ({ request }) => { const svg = await (await request.get("/icons/icon.svg")).text(); expect(svg).toContain(`fill="${ICON_PALETTES.archilyzer.ground}"`); // #151b20 expect(svg).toContain(`fill="${ICON_PALETTES.archilyzer.lit}"`); // #e7edf1 expect(svg).toBe(markSvg(ICON_PALETTES.archilyzer)); const png = await request.get("/icons/icon-192.png"); expect(png.headers()["content-type"]).toBe("image/png"); const ico = await request.get("/favicon.ico"); expect([...(await ico.body()).subarray(0, 4)]).toEqual([0, 0, 1, 0]); const m = await (await request.get("/manifest.webmanifest")).json(); expect(m.background_color).toBe(ICON_PALETTES.archilyzer.ground); expect(m.theme_color).toBe(BASE_GROUNDS.dark); }); test("the hub header is the parent mark + Archi|lyzer", async ({ page }) => { // No members: the shelf stays empty and nothing reaches a real origin. await page.route("**/hub-sites.json", (r) => fulfillJson(r, [])); await page.route("**/hub-summary.json", (r) => r.fulfill({ status: 404, body: "" })); await page.goto("/"); const home = page .getByRole("banner") .getByRole("link", { name: "Archilyzer", exact: true }); await expect(home).toBeVisible(); await expect(home.locator("[data-wordmark-lead]")).toHaveText("Archi"); await expect(home.locator("[data-wordmark-suffix]")).toHaveText("lyzer"); const fills = await home.locator("svg[data-brand-mark]").evaluate((svg) => ["ground", "dim", "lit"].map( (t) => getComputedStyle(svg.querySelector(`[data-tone="${t}"]`) as Element).fill, ), ); expect(fills).toEqual(["rgb(21, 27, 32)", "rgb(88, 105, 119)", "rgb(231, 237, 241)"]); }); // The ring on dark (release 10, slice MR): on the dark base the tile has no // edge, so every mark's tile gets a 1px ring outside it in its palette's dim, // following the ground's corner (rx 112 of 512). A box-shadow, so the mark's // box is the same on every base; light draws none. const BASE_KEY = "ytdlp-tb:base"; async function onBase(page: Page, base: "light" | "dark") { await page.evaluate(([k, b]) => localStorage.setItem(k, b), [BASE_KEY, base]); await page.reload({ waitUntil: "commit" }); await page.waitForFunction(() => document.documentElement?.dataset.themeReady === "1"); expect(await page.evaluate(() => document.documentElement.classList.contains("dark"))).toBe( base === "dark", ); } // The mark's visible box-shadow layers (Tailwind's composite lists four // transparent ones before the ring), its corner, and its laid-out size. async function markRing(mark: Locator) { return mark.evaluate((svg) => { const cs = getComputedStyle(svg); const r = svg.getBoundingClientRect(); const layers = cs.boxShadow === "none" ? [] : cs.boxShadow.split(/,(?![^(]*\))/); return { ring: layers.map((l) => l.trim()).filter((l) => !l.startsWith("rgba(0, 0, 0, 0)")), radius: cs.borderTopLeftRadius, size: [r.width, r.height], }; }); } test("on dark the hub's header and footer marks have a 1px ring in the slate's dim; on light none", async ({ page, }) => { await page.route("**/hub-sites.json", (r) => fulfillJson(r, [])); await page.route("**/hub-summary.json", (r) => r.fulfill({ status: 404, body: "" })); await page.goto("/"); const header = page.getByRole("banner").locator("svg[data-brand-mark]"); const footer = page.locator("footer svg[data-brand-mark]"); await onBase(page, "dark"); const ring = ["rgb(88, 105, 119) 0px 0px 0px 1px"]; // ICON_PALETTES.archilyzer.dim, #586977 expect(await markRing(header)).toEqual({ ring, radius: "21.875%", size: [28, 28] }); expect(await markRing(footer)).toEqual({ ring, radius: "21.875%", size: [16, 16] }); for (const base of ["light"] as const) { await onBase(page, base); expect(await markRing(header), base).toEqual({ ring: [], radius: "21.875%", size: [28, 28] }); expect(await markRing(footer), base).toEqual({ ring: [], radius: "21.875%", size: [16, 16] }); } });