import { test, expect } from "bun:test"; import { THEME_PALETTE_NAMES, THEME_PALETTES, resolveThemeConfig, resolveThemeName, renderThemeCss, renderThemeRuntime, } from "../src/index.ts"; test("ships eight complete semantic color palettes", () => { expect(THEME_PALETTE_NAMES).toHaveLength(8); for (const name of THEME_PALETTE_NAMES) { expect(Object.keys(THEME_PALETTES[name])).toEqual([ "primary", "primaryHover", "primaryContrast", "secondary", "secondaryHover", "secondaryContrast", "info", "success", "warning", "danger", ]); } }); test("named palettes populate semantic tokens in every theme", () => { const t = resolveThemeConfig({ palette: "violet" }); for (const name of t.names) { expect(t.themes[name]["color-primary"]).toBe(THEME_PALETTES.violet.primary); expect(t.themes[name]["color-secondary"]).toBe(THEME_PALETTES.violet.secondary); expect(t.themes[name]["color-info"]).toBe(THEME_PALETTES.violet.info); expect(t.themes[name]["color-error"]).toBe(THEME_PALETTES.violet.danger); } }); test("custom palettes require every semantic color", () => { expect(() => resolveThemeConfig({ palette: { primary: "#000" } as never })).toThrow( "primaryHover, primaryContrast, secondary", ); expect(() => resolveThemeConfig({ palette: "unknown" as never })).toThrow( "Unknown theme palette", ); }); test("resolveThemeConfig merges user tokens over built-in light/dark", () => { const t = resolveThemeConfig({ default: "dark", themes: { dark: { "color-primary": "#abc" } } }); expect(t.default).toBe("dark"); expect(t.names).toContain("light"); expect(t.names).toContain("dark"); expect(t.themes.dark["color-primary"]).toBe("#abc"); // overridden expect(t.themes.dark["color-bg"]).toBeDefined(); // built-in kept }); test("built-in themes use Plus Jakarta Sans and neutral black dark surfaces", () => { const t = resolveThemeConfig(); expect(t.themes.light["font-sans"]).toBe( '"Plus Jakarta Sans",ui-sans-serif,system-ui,sans-serif', ); expect(t.themes.dark["font-sans"]).toBe('"Plus Jakarta Sans",ui-sans-serif,system-ui,sans-serif'); expect(t.themes.dark["color-bg"]).toBe("#000000"); expect(t.themes.dark["color-surface"]).toBe("#0a0a0a"); expect(t.themes.dark["color-surface-2"]).toBe("#171717"); }); test("resolveThemeName validates against configured names", () => { const t = resolveThemeConfig(); expect(resolveThemeName("light", t)).toBe("light"); expect(resolveThemeName("nonsense", t)).toBe(t.default); expect(resolveThemeName(undefined, t)).toBe(t.default); }); test("renderThemeCss emits :root + per-theme blocks and --wire-* vars", () => { const t = resolveThemeConfig({ default: "dark", themes: { dark: { "color-primary": "#6c8cff" } }, }); const css = renderThemeCss(t); expect(css).toContain(":root{"); expect(css).toContain('[data-theme="dark"]{'); expect(css).toContain("--wire-color-primary:#6c8cff"); expect(css).toContain("color-scheme:dark"); // reserved token → native property expect(css).toContain("font-family:var(--wire-font-sans"); }); test("renderThemeRuntime bakes the theme names for cycling", () => { const t = resolveThemeConfig(); const js = renderThemeRuntime(t); expect(js).toContain("data-wire-theme-toggle"); expect(js).toContain(JSON.stringify(t.names)); expect(js).toContain('addEventListener("wrnexus:navigated"'); });