import { describe, expect, it } from "vitest"; import { colourHarmony, deltaE, exportPalette, formatColour, normaliseTokenName, parsePaletteList, shades, simulateColourVision, simulateColourVisionSet, tints, tones, } from "../../src/colour"; describe("comparison and colour-vision tools", () => { it("provides standard Delta-E methods", () => { expect(deltaE("#f8f5ee", "#f8f5ee", "2000")).toBe(0); expect(deltaE("red", "blue", "76")).toBeGreaterThan(100); expect(deltaE("#777", "#787878", "ok")).toBeGreaterThan(0); }); it("simulates four deficiencies while preserving alpha", () => { const original = "rgba(200, 40, 20, .4)"; const unchanged = simulateColourVision(original, "deuteranopia", { severity: 0, }); expect(formatColour(unchanged, "hex8")).toBe("#c8281466"); const simulated = simulateColourVision(original, "deuteranopia"); expect(simulated.alpha).toBeCloseTo(0.4); expect(formatColour(simulated, "hex")).not.toBe("#c82814"); expect(Object.keys(simulateColourVisionSet(original))).toEqual([ "protanopia", "deuteranopia", "tritanopia", "achromatopsia", ]); }); it("turns achromatopsia output neutral", () => { const simulated = simulateColourVision("#ff8000", "achromatopsia"); expect(simulated.coords[0]).toBeCloseTo(simulated.coords[1]!, 8); expect(simulated.coords[1]).toBeCloseTo(simulated.coords[2]!, 8); }); }); describe("palette construction and export", () => { it("builds useful hue harmonies", () => { expect(colourHarmony("oklch(60% .15 20)", "complementary")).toHaveLength(2); expect(colourHarmony("oklch(60% .15 20)", "analogous")).toHaveLength(3); expect(colourHarmony("oklch(60% .15 20)", "square")).toHaveLength(4); }); it("builds deterministic tint, shade and tone scales", () => { expect(tints("#336699", 3)).toHaveLength(3); expect(shades("#336699", 3, { includeBase: true })).toHaveLength(4); expect(tones("#336699", 2, { includeEndpoint: true })).toHaveLength(2); expect( formatColour(tints("black", 1, { includeEndpoint: true })[0]!, "hex"), ).toBe("#ffffff"); }); it("parses named text, CSS variables, arrays and token JSON", () => { const text = parsePaletteList("Primary: #f8f5ee\n--accent: rgb(20 40 60)"); expect(text.map((entry) => entry.name)).toEqual(["Primary", "accent"]); expect(parsePaletteList('["red", "blue"]')).toHaveLength(2); const tokens = parsePaletteList( '{"brand":{"$type":"color","$value":"#123456"}}', ); expect(formatColour(tokens[0]!.colour, "hex")).toBe("#123456"); const nested = parsePaletteList( '{"brand":{"light":{"$type":"color","$value":"#eee"},"dark":{"$value":"#111"}}}', ); expect(nested.map((entry) => entry.name)).toEqual([ "brand-light", "brand-dark", ]); expect( parsePaletteList(":root { --paper: #f8f5ee; --ink: #111; }"), ).toHaveLength(2); }); it("normalises and de-duplicates portable token names", () => { expect(normaliseTokenName(" Crème brûlée / 100 ")).toBe( "creme-brulee-100", ); const entries = parsePaletteList("Brand: red\nBrand: blue"); expect(exportPalette(entries, "css", { prefix: "App" })).toContain( "--app-brand-2: #0000ff;", ); }); it("exports CSS, Sass, JSON, DTCG, Tailwind and CSV", () => { const entries = parsePaletteList("paper: #f8f5ee\nink: #111"); expect(exportPalette(entries, "css")).toContain("--colour-paper: #f8f5ee;"); expect(exportPalette(entries, "scss")).toContain("$colour-ink: #111111;"); expect(JSON.parse(exportPalette(entries, "json"))).toEqual({ paper: "#f8f5ee", ink: "#111111", }); expect(JSON.parse(exportPalette(entries, "tokens")).paper).toEqual({ $type: "color", $value: "#f8f5ee", }); expect(exportPalette(entries, "tailwind")).toMatch(/^export default /); expect(exportPalette(entries, "csv")).toBe( "name,value\npaper,#f8f5ee\nink,#111111", ); }); });