import { describe, expect, it } from "vitest"; import { deleteToken, exportTokens, parseTokenDocument, resolveTheme, updateToken, } from "../../src/core/tokens"; const themed = JSON.stringify({ $sets: { core: { color: { $type: "color", brand: { $value: "#112233" }, link: { $value: "{color.brand}" }, }, space: { sm: { $type: "dimension", $value: "8px" } }, }, dark: { color: { $type: "color", brand: { $value: "#ddeeff" } } }, }, $themes: { light: ["core"], dark: ["core", "dark"] }, }); describe("token model", () => { it("composes sets and resolves aliases after overrides", () => { const result = resolveTheme(parseTokenDocument(themed), "dark"); expect( result.tokens.find((token) => token.path === "color.link")?.resolvedValue, ).toBe("#ddeeff"); expect(result.diagnostics).toEqual([]); }); it("detects cycles and missing aliases", () => { const document = parseTokenDocument( '{"a":{"$value":"{b}"},"b":{"$value":"{a}"},"c":{"$value":"{missing}"}}', ); const messages = resolveTheme(document, "default") .diagnostics.map((item) => item.message) .join(" "); expect(messages).toMatch(/cycle/u); expect(messages).toMatch(/does not exist/u); }); it("resolves nested composite aliases and explicit theme modes", () => { const document = parseTokenDocument( JSON.stringify({ $sets: { core: { color: { brand: { $type: "color", $value: "#112233" }, }, }, dark: { color: { brand: { $type: "color", $value: "#ddeeff" }, }, }, components: { focus: { $type: "border", $value: { color: "{color.brand}", width: "2px", style: "solid", }, }, }, }, $themes: { product: { $sets: ["core", "components"], $modes: { light: [], dark: ["dark"] }, }, }, }), ); const result = resolveTheme(document, "product", "dark"); expect( result.tokens.find((token) => token.path === "focus")?.resolvedValue, ).toEqual({ color: "#ddeeff", width: "2px", style: "solid" }); expect(result.diagnostics).toEqual([]); }); it("validates and exports DTCG composite token types", () => { const document = parseTokenDocument( JSON.stringify({ easing: { $type: "cubicBezier", $value: [0.2, 0, 0, 1] }, motion: { $type: "transition", $value: { duration: "180ms", delay: "0ms", timingFunction: "{easing}", }, }, }), ); const resolved = resolveTheme(document, "default"); expect(resolved.diagnostics).toEqual([]); expect(exportTokens("css", resolved.tokens).content).toContain( "180ms cubic-bezier(0.2, 0, 0, 1) 0ms", ); const dtcg = exportTokens("dtcg", resolved.tokens).content; expect(dtcg).toContain('"$type": "transition"'); expect(dtcg).toContain('"{easing}"'); }); it("rejects malformed wrappers and non-standard hex lengths", () => { expect(() => parseTokenDocument('{"$sets":[]}')).toThrow( /\$sets must be an object/u, ); const document = parseTokenDocument( '{"five":{"$type":"color","$value":"#12345"},"seven":{"$type":"color","$value":"#1234567"}}', ); expect(resolveTheme(document, "default").diagnostics).toHaveLength(2); expect(() => parseTokenDocument('{"$themes":{}}')).toThrow( /requires a \$sets/u, ); expect(() => parseTokenDocument( '{"leaf":{"$value":"x","hidden":{"$value":"ignored"}}}', ), ).toThrow(/cannot also contain child/u); }); it("exports CSS and reports platform loss", () => { const tokens = resolveTheme(parseTokenDocument(themed), "light").tokens; expect(exportTokens("css", tokens).content).toContain( "--color-brand: #112233;", ); const composite = parseTokenDocument( '{"text":{"$type":"typography","$value":{"fontFamily":"Inter","fontSize":"16px"}}}', ); expect( exportTokens("android", resolveTheme(composite, "default").tokens) .losses[0], ).toMatch(/project-specific/u); }); it("quotes generated strings and typography and reports name collisions", () => { const document = parseTokenDocument( JSON.stringify({ danger: { $type: "string", $value: 'x"; } --owned: red; /*' }, text: { $type: "typography", $value: { fontFamily: 'Evil"; } body { color: red; /*', fontSize: "16px", }, }, a: { b: { $type: "string", $value: "first" } }, "a-b": { $type: "string", $value: "second" }, }), ); const result = exportTokens( "css", resolveTheme(document, "default").tokens, ); expect(result.content).toContain('--danger: "x\\"; } --owned: red; /*";'); expect(result.content).toContain('"Evil\\"; } body { color: red; /*"'); expect(result.losses.join(" ")).toMatch(/identifier a-b collides/u); }); it("edits and deletes nested tokens", () => { const document = parseTokenDocument(themed); const updated = updateToken( document, "core", "space.sm", "space.md", "dimension", "16px", ); expect( parseTokenDocument(updated).sets.core?.some( (token) => token.path === "space.md", ), ).toBe(true); const removed = deleteToken( parseTokenDocument(updated), "core", "space.md", ); expect( parseTokenDocument(removed).sets.core?.some( (token) => token.path === "space.md", ), ).toBe(false); }); it("preserves DTCG leaf metadata while editing or renaming", () => { const document = parseTokenDocument( '{"brand":{"$type":"color","$value":"#112233","$description":"Brand","$extensions":{"vendor":{"id":1}}}}', ); const updated = updateToken( document, "default", "brand", "color.brand", "color", "#445566", ); expect(JSON.parse(updated)).toMatchObject({ color: { brand: { $description: "Brand", $extensions: { vendor: { id: 1 } }, $value: "#445566", }, }, }); }); it("validates composite members rather than only their presence", () => { const document = parseTokenDocument( JSON.stringify({ text: { $type: "typography", $value: { fontFamily: 42, fontSize: "large" }, }, depth: { $type: "shadow", $value: { color: "not-a-colour", offsetX: "0px", offsetY: "2px", blur: "4px", }, }, }), ); const messages = resolveTheme(document, "default") .diagnostics.map((diagnostic) => diagnostic.message) .join(" "); expect(messages).toMatch(/Typography requires valid/iu); expect(messages).toMatch(/Each shadow requires valid/iu); }); it("refuses to create a path through an existing token leaf", () => { const document = parseTokenDocument( '{"base":{"$type":"string","$value":"leaf"}}', ); expect(() => updateToken(document, "default", null, "base.child", "string", "x"), ).toThrow(/crosses existing token base/u); }); });