import { test, expect } from "bun:test"; import { StudioSession, importDesign, exportDesign, diffDesign } from "../src/index.ts"; import { CORE_DESIGN_SYSTEMS, validateDesignSystem } from "../../design-system/src/index.ts"; test("invalid edits preserve a valid preview, raw input, undo/redo and reload", () => { const data = new Map(); const storage = { getItem: (k: string) => data.get(k) ?? null, setItem: (k: string, v: string) => { data.set(k, v); }, }; const session = new StudioSession(CORE_DESIGN_SYSTEMS.soft, storage); session.mutate((s) => { s.tokens["shape.control"]!.value = "13px"; }, "Changed radius"); session.edit('{"tokens":', "Incomplete JSON"); expect(session.diagnostics[0]!.code).toBe("WRN-STUDIO-IMPORT"); expect(session.valid.tokens["shape.control"]!.value).toBe("13px"); session.undo(); expect(session.diagnostics).toEqual([]); session.redo(); expect(session.source).toBe('{"tokens":'); const restored = new StudioSession(undefined, storage); expect(restored.restore()).toBe(true); expect(restored.source).toBe(session.source); expect(restored.valid.tokens["shape.control"]!.value).toBe("13px"); expect(() => restored.markApplied()).toThrow(); expect(() => restored.mutate(() => {}, "x")).toThrow(); }); test("history stays bounded, branches after undo, and realistic full-preset history reloads", () => { const data = new Map(); const storage = { getItem: (k: string) => data.get(k) ?? null, setItem: (k: string, v: string) => { data.set(k, v); }, }; const session = new StudioSession(undefined, storage); for (let i = 0; i < 30; i++) session.mutate((s) => { s.name = "Revision " + i; }, "Name"); expect(session.history).toHaveLength(25); const restored = new StudioSession(undefined, storage); expect(restored.restore()).toBe(true); restored.undo(); restored.mutate((s) => { s.name = "Branch"; }, "Branched"); restored.redo(); expect(restored.valid.name).toBe("Branch"); expect(restored.history.at(-1)!.label).toBe("Branched"); }); test("corrupted storage and quota failures do not destroy in-memory drafts", () => { const broken = new StudioSession(undefined, { getItem: () => "{", setItem: () => { throw Error("quota"); }, }); expect(broken.restore()).toBe(false); expect(broken.storageError).toContain("could not be restored"); broken.mutate((s) => { s.name = "Kept"; }, "Edit"); expect(broken.valid.name).toBe("Kept"); expect(broken.storageError).toContain("Autosave unavailable"); }); test("canonical import/export round trips all recipes, modes, aliases and metadata", () => { for (const system of Object.values(CORE_DESIGN_SYSTEMS)) { const artifact = exportDesign(system); const report = importDesign(artifact.canonical); expect(report.diagnostics).toEqual([]); expect(report.system).toEqual(system); expect(artifact.css).toContain("data-wrn-owner"); expect(JSON.parse(artifact.theme).designSystem.system).toEqual(system); } }); test("legacy import reports unsupported fields and preserves literal semantic bindings", () => { const report = importDesign( JSON.stringify({ default: "light", palette: "violet", themes: { light: { "color-primary": "#112233", unknown: "4px" } }, }), "legacy", ); expect(report.diagnostics).toEqual([]); expect(report.unsupported).toContain("palette"); expect(report.unsupported).toContain("themes.light.unknown"); expect(report.system!.modes!.light!["color.primary"]).toEqual({ type: "color", value: "#112233", legacy: "color-primary", }); }); test("exchange subset preserves aliases and reports ambiguous semantic mapping and unsupported composites", () => { const report = importDesign( JSON.stringify({ brand: { $type: "color", base: { $value: "#112233" }, action: { $value: "{brand.base}" }, complex: { $value: { colorSpace: "srgb", components: [0, 0, 0] } }, }, $extensions: { x: 1 }, }), "tokens", ); expect(report.diagnostics).toEqual([]); expect(report.system!.tokens["brand.action"]!.value).toEqual({ ref: "brand.base" }); expect(report.unsupported).toContain("brand.complex"); expect(report.unsupported).toContain("$extensions"); expect(report.ambiguous).toHaveLength(2); }); test("imports reject oversized, cyclic, executable, polluted and missing-reference documents", () => { expect(importDesign(" ".repeat(2_000_001)).diagnostics).not.toEqual([]); expect( importDesign('{"__proto__":{"$type":"color","$value":"#000"}}', "tokens").diagnostics, ).not.toEqual([]); for (const tokens of [ { a: { type: "color", value: { ref: "missing" } } }, { a: { type: "color", value: { ref: "b" } }, b: { type: "color", value: { ref: "a" } } }, { a: { type: "color", value: "red;}body{display:none" } }, ]) { const system = { schemaVersion: 1, id: "bad", name: "Bad", version: "1", tokens }; expect(importDesign(JSON.stringify(system)).diagnostics).not.toEqual([]); } }); test("review diffs identify affected paths and applied snapshots are detached", () => { const session = new StudioSession(); session.mutate((s) => { s.tokens["shape.control"]!.value = "6px"; }, "Radius"); expect(session.changes()).toContain("$.tokens.shape.control.value"); session.markApplied(); expect(session.changes()).toEqual([]); session.mutate((s) => { s.name = "Other"; }, "Name"); expect(session.applied.name).not.toBe("Other"); expect(diffDesign({ a: 1 }, { a: 2, b: 3 })).toEqual(["$.a", "$.b"]); expect(validateDesignSystem(session.valid)).toEqual([]); });