import { describe, expect, it } from "vitest" import publicSchema from "../public/schema.json" import { BASES, buildPartialRegistryBase, buildRegistryBase, buildThemeForPreset, DEFAULT_CONFIG, designSystemConfigSchema, parseRegistryBaseParts, POINTER_CURSOR_SELECTOR, PRESETS, STYLES, } from "./config" const legacyPublicSchemaStyles = ["default", "new-york"] as const describe("buildRegistryBase", () => { it("seeds a font-heading fallback when heading inherits the body font", () => { const result = buildRegistryBase(DEFAULT_CONFIG) expect(result.registryDependencies).toContain("font-inter") expect(result.registryDependencies).not.toContain("font-heading-inter") expect(result.cssVars?.theme?.["--font-heading"]).toBe("var(--font-sans)") }) it("adds a heading font dependency when a distinct heading font is selected", () => { const result = buildRegistryBase({ ...DEFAULT_CONFIG, fontHeading: "playfair-display", }) expect(result.registryDependencies).toContain("font-inter") expect(result.registryDependencies).toContain( "font-heading-playfair-display" ) expect(result.cssVars?.theme?.["--font-heading"]).toBeUndefined() }) it("normalizes a matching heading font back to inherit", () => { const result = buildRegistryBase({ ...DEFAULT_CONFIG, fontHeading: "inter", }) expect(result.registryDependencies).not.toContain("font-heading-inter") expect(result.cssVars?.theme?.["--font-heading"]).toBe("var(--font-sans)") }) it("inherits the selected body font variable for non-sans body fonts", () => { const result = buildRegistryBase({ ...DEFAULT_CONFIG, font: "jetbrains-mono", fontHeading: "inherit", }) expect(result.registryDependencies).toContain("font-jetbrains-mono") expect(result.cssVars?.theme?.["--font-heading"]).toBe("var(--font-mono)") }) it("defaults chartColor to neutral when omitted", () => { const result = designSystemConfigSchema.parse({ base: "radix", style: "nova", iconLibrary: "lucide", theme: "neutral", font: "inter", fontHeading: "inherit", menuAccent: "subtle", menuColor: "default", radius: "default", }) expect(result.chartColor).toBe("neutral") }) it("defaults pointer to false when omitted", () => { const result = designSystemConfigSchema.parse({ base: "radix", style: "nova", iconLibrary: "lucide", theme: "neutral", font: "inter", fontHeading: "inherit", menuAccent: "subtle", menuColor: "default", radius: "default", }) expect(result.pointer).toBe(false) }) it("does not include pointer cursor css by default", () => { const result = buildRegistryBase(DEFAULT_CONFIG) expect( result.css?.["@layer base"]?.[POINTER_CURSOR_SELECTOR] ).toBeUndefined() }) it("includes pointer cursor css when pointer is enabled", () => { const result = buildRegistryBase({ ...DEFAULT_CONFIG, pointer: true, }) expect(result.css?.["@layer base"]?.[POINTER_CURSOR_SELECTOR]).toEqual({ cursor: "pointer", }) }) it("defaults chartColor to the selected theme when omitted", () => { const result = designSystemConfigSchema.parse({ base: "base", style: "sera", iconLibrary: "lucide", baseColor: "taupe", theme: "taupe", font: "noto-sans", fontHeading: "playfair-display", menuAccent: "subtle", menuColor: "default", radius: "default", }) expect(result.chartColor).toBe("taupe") }) it("keeps the public schema style enum in sync with presets", () => { expect([...publicSchema.properties.style.enum].sort()).toEqual( [ ...legacyPublicSchemaStyles, ...PRESETS.map((preset) => preset.name), ].sort() ) }) it("defines every base and style preset exactly once", () => { const presetNames = PRESETS.map((preset) => preset.name) const expectedPresetNames = BASES.flatMap((base) => STYLES.map((style) => `${base.name}-${style.name}`) ) expect([...presetNames].sort()).toEqual([...expectedPresetNames].sort()) expect(new Set(presetNames).size).toBe(presetNames.length) }) it("rejects chartColor values that are unavailable for the selected base color", () => { const result = designSystemConfigSchema.safeParse({ base: "radix", style: "nova", iconLibrary: "lucide", baseColor: "neutral", theme: "neutral", chartColor: "stone", font: "inter", fontHeading: "inherit", menuAccent: "subtle", menuColor: "default", radius: "default", }) expect(result.success).toBe(false) }) }) describe("buildThemeForPreset", () => { it("builds a copyable registry theme item", () => { const result = buildThemeForPreset({ ...DEFAULT_CONFIG, baseColor: "taupe", theme: "taupe", chartColor: "taupe", menuAccent: "bold", radius: "large", }) expect(result).toMatchObject({ $schema: "https://ui.shadcn.com/schema/registry-item.json", name: "taupe-taupe", type: "registry:theme", }) expect(result.cssVars?.light?.radius).toBe("0.875rem") expect(result.cssVars?.light?.accent).toBe(result.cssVars?.light?.primary) expect(result.cssVars?.dark?.accent).toBe(result.cssVars?.dark?.primary) expect(result.cssVars?.light?.background).toBeDefined() expect(result.cssVars?.dark?.background).toBeDefined() expect(result.css).toBeUndefined() }) }) describe("parseRegistryBaseParts", () => { it("returns undefined parts when only is omitted", () => { expect(parseRegistryBaseParts(null)).toEqual({ success: true, parts: undefined, }) }) it("normalizes and dedupes only values", () => { expect(parseRegistryBaseParts("theme,fonts,font")).toEqual({ success: true, parts: ["theme", "font"], }) }) it("rejects invalid only values", () => { const result = parseRegistryBaseParts("theme,colors") expect(result.success).toBe(false) }) }) describe("buildPartialRegistryBase", () => { it("builds a sparse theme payload", () => { const result = buildPartialRegistryBase( { ...DEFAULT_CONFIG, baseColor: "taupe", theme: "taupe", chartColor: "taupe", menuAccent: "bold", menuColor: "inverted", radius: "large", }, ["theme"] ) expect(result.type).toBe("registry:base") expect(result.extends).toBe("none") expect(result.config).toEqual({ menuColor: "inverted", menuAccent: "bold", tailwind: { baseColor: "taupe", }, }) expect(result.cssVars?.light?.radius).toBe("0.875rem") expect(result.cssVars?.light).toBeDefined() expect(result.cssVars?.dark).toBeDefined() expect(result.registryDependencies).toBeUndefined() expect("dependencies" in result).toBe(false) }) it("builds a sparse font payload", () => { const result = buildPartialRegistryBase( { ...DEFAULT_CONFIG, font: "noto-sans", fontHeading: "playfair-display", }, ["font"] ) expect(result.registryDependencies).toEqual([ "font-noto-sans", "font-heading-playfair-display", ]) expect(result.cssVars).toBeUndefined() expect(result.config).toBeUndefined() }) it("adds a heading fallback when font heading inherits", () => { const result = buildPartialRegistryBase( { ...DEFAULT_CONFIG, font: "jetbrains-mono", fontHeading: "inherit", }, ["font"] ) expect(result.registryDependencies).toEqual(["font-jetbrains-mono"]) expect(result.cssVars?.theme?.["--font-heading"]).toBe("var(--font-mono)") }) it("merges combined partial payloads", () => { const result = buildPartialRegistryBase( { ...DEFAULT_CONFIG, font: "figtree", }, ["theme", "font"] ) expect(result.config?.tailwind?.baseColor).toBe("neutral") expect(result.registryDependencies).toEqual(["font-figtree"]) expect(result.cssVars?.light?.radius).toBe("0.625rem") expect(result.cssVars?.light).toBeDefined() expect(result.cssVars?.theme?.["--font-heading"]).toBe("var(--font-sans)") }) })