95 lines
3.4 KiB
TypeScript
95 lines
3.4 KiB
TypeScript
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"');
|
|
});
|