import { describe, expect, test } from "bun:test"; import { ACCENT_COOKIE, renderThemeCss, renderThemeRuntime, resolveAccentName, resolveThemeConfig, } from "../src/theme.ts"; describe("theme accents", () => { test("uses the configured named palette as the default accent", () => { const theme = resolveThemeConfig({ palette: "rose", default: "light" }); expect(theme.defaultAccent).toBe("rose"); expect(resolveAccentName(undefined, theme)).toBe("rose"); }); test("validates the accent cookie against enabled palettes", () => { const theme = resolveThemeConfig({ palette: "rose", accent: { options: ["rose", "emerald"] }, }); expect(resolveAccentName("emerald", theme)).toBe("emerald"); expect(resolveAccentName("blue", theme)).toBe("rose"); expect(resolveAccentName("not-real", theme)).toBe("rose"); }); test("generates complete palette selectors for light and dark themes", () => { const css = renderThemeCss(resolveThemeConfig({ palette: "rose" })); expect(css).toContain('[data-theme="light"][data-accent="emerald"]'); expect(css).toContain('[data-theme="dark"][data-accent="emerald"]'); expect(css).toContain("--wrn-color-primary-soft:"); expect(css).toContain("--wrn-color-primary-muted:"); expect(css).toContain("--wrn-color-primary-text:"); expect(css).toContain("--wrn-color-on-primary:"); expect(css).toContain("--wrn-color-secondary-soft:"); }); test("runtime persists accents with cookies and never writes inline tokens", () => { const runtime = renderThemeRuntime(resolveThemeConfig({ palette: "rose" })); expect(runtime).toContain(ACCENT_COOKIE); expect(runtime).toContain('el.setAttribute("data-accent",name)'); expect(runtime).not.toContain("localStorage"); expect(runtime).not.toContain("style.setProperty"); }); test("shared cookie config drives theme, accent, and the public browser helper", () => { const theme = resolveThemeConfig( { palette: "rose" }, { defaults: { domain: "example.com", path: "/", maxAge: 600, secure: true }, accent: { sameSite: "Strict" }, }, ); const runtime = renderThemeRuntime(theme); expect(theme.themeCookie).toEqual({ domain: "example.com", path: "/", maxAge: 600, secure: true, }); expect(theme.accentCookie?.sameSite).toBe("Strict"); expect(runtime).toContain("window.wrnCookies={"); expect(runtime).toContain("writeCookie(THEME_COOKIE,name,THEME_COOKIE_OPTIONS)"); expect(runtime).toContain("configuredCookieOptions(preference,override)"); }); test("runtime scopes accent cookies to a configured parent domain", () => { const theme = resolveThemeConfig({ palette: "rose", accent: { cookie: { domain: "localhost", path: "/", sameSite: "Lax", secure: false } }, }); const runtime = renderThemeRuntime(theme); expect(theme.accentCookie?.domain).toBe("localhost"); expect(runtime).toContain('"domain":"localhost"'); expect(runtime).toContain("deleteCookie(ACCENT_COOKIE);"); expect(runtime).toContain("writeCookie(ACCENT_COOKIE,name,ACCENT_COOKIE_OPTIONS)"); expect(runtime).toContain('configuredDomain!=="localhost"'); expect(runtime).not.toContain("setInterval(syncSharedAccent"); }); }); test("live document accent has priority over cookie and default", () => { const runtime = renderThemeRuntime( resolveThemeConfig({ palette: "rose", default: "light", accent: { default: "rose", options: ["rose", "emerald", "cyan"], }, }), ); expect(runtime).toContain( 'el.getAttribute("data-accent")||readCookie(ACCENT_COOKIE)||DEFAULT_ACCENT', ); expect(runtime).toContain( 'el.getAttribute("data-theme")||readCookie(THEME_COOKIE)||DEFAULT_THEME', ); });