import { describe, expect, it } from "vitest"; import { TOOLCRAFT_BUILT_IN_CONTROL_TYPES, type ToolcraftBuiltInControlType, } from "../contracts/component-contracts"; import { DEFAULT_TOOLCRAFT_ORIENTATION_POSE } from "./orientation-pose"; import type { ToolcraftBuiltInControlValueMap } from "./control-value-types"; import { areToolcraftControlValuesEqual, decodeToolcraftBuiltInControlValue, decodeToolcraftBuiltInControlDefault, TOOLCRAFT_CONTROL_VALUE_CODEC_REGISTRY, type ToolcraftControlValueDescriptor, } from "./control-value-codecs"; const channelMixer = { R: { R: 100, G: 0, B: 0 }, G: { R: 0, G: 100, B: 0 }, B: { R: 0, G: 0, B: 100 }, }; const curvePoints = [ { x: 0, y: 0 }, { x: 1, y: 1 }, ]; const curves = { activeChannel: "RGB", points: { RGB: curvePoints }, selectedPointIndex: 1, } as const; const font = { color: "#FFFFFF", fontId: "inter", fontSize: 16, fontWeight: "400", letterSpacing: "normal", lineHeight: "normal", opacity: 100, textCase: "original", } as const; const choices = [{ value: "a" }]; type CompatibilityTable = { [K in ToolcraftBuiltInControlType]: { candidate: unknown; control: ToolcraftControlValueDescriptor & { type: K }; expected: | { accepted: false } | { accepted: true; value: ToolcraftBuiltInControlValueMap[K] }; }; }; const compatibility = { actions: { control: { type: "actions" }, candidate: [], expected: { accepted: false }, }, anchorGrid: { control: { type: "anchorGrid" }, candidate: "center", expected: { accepted: true, value: "center" }, }, aspectRatio: { control: { type: "aspectRatio" }, candidate: "16:9", expected: { accepted: false }, }, channelMixer: { control: { type: "channelMixer" }, candidate: channelMixer, expected: { accepted: true, value: channelMixer }, }, checkbox: { control: { type: "checkbox" }, candidate: true, expected: { accepted: true, value: true }, }, code: { control: { type: "code" }, candidate: "return 1", expected: { accepted: true, value: "return 1" }, }, collectionActions: { control: { type: "collectionActions", itemControls: { title: { type: "text", defaultValue: "Untitled" }, tint: { type: "color" }, }, }, candidate: [{ tint: "#abc", metadata: 7 }], expected: { accepted: true, value: [{ tint: "#AABBCC", title: "Untitled", metadata: 7 }], }, }, color: { control: { type: "color" }, candidate: "#abc", expected: { accepted: true, value: "#AABBCC" }, }, colorOpacity: { control: { type: "colorOpacity" }, candidate: "#abc", expected: { accepted: true, value: { hex: "#AABBCC", opacity: 100 } }, }, curves: { control: { type: "curves" }, candidate: curves, expected: { accepted: true, value: curves }, }, fileDrop: { control: { type: "fileDrop", itemControls: { amount: { type: "slider", defaultValue: 2 } }, }, candidate: [{ mediaId: "file-a", ignored: true }], expected: { accepted: true, value: [{ mediaId: "file-a", amount: 2 }] }, }, fontPicker: { control: { type: "fontPicker" }, candidate: "inter", expected: { accepted: true, value: font }, }, gradient: { control: { type: "gradient" }, candidate: { angle: 12, gradientType: "linear", stops: [ { color: "#abc", position: "0%" }, { color: "#123", position: "100%", opacity: 50 }, ], }, expected: { accepted: true, value: { angle: 12, gradientType: "linear", stops: [ { color: "#AABBCC", position: "0%" }, { color: "#112233", position: "100%", opacity: 50 }, ], }, }, }, imagePicker: { control: { type: "imagePicker", items: choices }, candidate: "a", expected: { accepted: true, value: "a" }, }, orientationGizmo: { control: { type: "orientationGizmo" }, candidate: DEFAULT_TOOLCRAFT_ORIENTATION_POSE, expected: { accepted: true, value: DEFAULT_TOOLCRAFT_ORIENTATION_POSE }, }, palette: { control: { type: "palette" }, candidate: { family: "blue", shade: "500" }, expected: { accepted: true, value: { family: "blue", shade: "500" } }, }, panelActions: { control: { type: "panelActions" }, candidate: null, expected: { accepted: false }, }, rangeInput: { control: { type: "rangeInput" }, candidate: { start: "1", end: "9" }, expected: { accepted: true, value: { start: "1", end: "9" } }, }, rangeSlider: { control: { type: "rangeSlider" }, candidate: [1, 2, 3], expected: { accepted: true, value: [1, 2, 3] }, }, segmented: { control: { type: "segmented", options: choices }, candidate: "a", expected: { accepted: true, value: "a" }, }, select: { control: { type: "select", options: choices }, candidate: "a", expected: { accepted: true, value: "a" }, }, settingsTransfer: { control: { type: "settingsTransfer" }, candidate: {}, expected: { accepted: false }, }, slider: { control: { type: "slider", min: 0, max: 10 }, candidate: 7, expected: { accepted: true, value: 7 }, }, sourceCollection: { control: { type: "sourceCollection", itemControl: { type: "color" } }, candidate: ["#abc"], expected: { accepted: true, value: ["#AABBCC"] }, }, switch: { control: { type: "switch" }, candidate: false, expected: { accepted: true, value: false }, }, tabs: { control: { type: "tabs", options: choices }, candidate: "a", expected: { accepted: true, value: "a" }, }, text: { control: { type: "text" }, candidate: "hello", expected: { accepted: true, value: "hello" }, }, vector: { control: { type: "vector" }, candidate: { x: "2.5", y: "-1" }, expected: { accepted: true, value: { x: 2.5, y: -1 } }, }, } satisfies CompatibilityTable; describe("Toolcraft built-in control value codecs", () => { it.each(TOOLCRAFT_BUILT_IN_CONTROL_TYPES)( "preserves canonical compatibility for %s", (type) => { const { control, candidate, expected } = compatibility[type]; expect(decodeToolcraftBuiltInControlValue(control, candidate)).toEqual( expected, ); if (expected.accepted) { expect( decodeToolcraftBuiltInControlValue(control, expected.value), ).toEqual(expected); } }, ); it("keeps non-empty rangeSlider arrays without imposing two handles", () => { expect( decodeToolcraftBuiltInControlValue({ type: "rangeSlider" }, [2]), ).toEqual({ accepted: true, value: [2] }); expect( decodeToolcraftBuiltInControlValue({ type: "rangeSlider" }, []), ).toEqual({ accepted: false }); }); it("preserves implicit defaults and media ownership", () => { expect( decodeToolcraftBuiltInControlDefault({ type: "slider", min: 3 }), ).toEqual({ accepted: true, value: 3 }); expect( decodeToolcraftBuiltInControlDefault({ type: "channelMixer" }), ).toEqual({ accepted: true, value: channelMixer }); expect(decodeToolcraftBuiltInControlDefault({ type: "curves" })).toEqual({ accepted: true, value: { activeChannel: "RGB", selectedPointIndex: 1, points: Object.fromEntries( ["RGB", "R", "G", "B"].map((channel) => [ channel, [ { x: 0, y: 0 }, { x: 0.5, y: 0.5 }, { x: 1, y: 1 }, ], ]), ), }, }); expect( decodeToolcraftBuiltInControlValue({ type: "fileDrop" }, null), ).toEqual({ accepted: false }); }); it("canonicalizes Color strings and rejects UI payloads at the state boundary", () => { const control = { applicability: { mode: "always" as const }, defaultValue: "#112233", target: "shape.fill", type: "color", } as const; expect(decodeToolcraftBuiltInControlValue(control, "#445566")).toEqual({ accepted: true, value: "#445566", }); expect( decodeToolcraftBuiltInControlValue(control, { hex: "#778899" }), ).toEqual({ accepted: false }); expect(decodeToolcraftBuiltInControlValue(control, "#abc")).toEqual({ accepted: true, value: "#AABBCC", }); }); it("recursively canonicalizes current source-owned Color items", () => { const control = { applicability: { mode: "always" as const }, defaultValue: ["#111111"], itemControl: { label: false, type: "color" }, target: "fills.items", type: "sourceCollection", } as const; expect( decodeToolcraftBuiltInControlValue(control, ["#222222", "#333333"]), ).toEqual({ accepted: true, value: ["#222222", "#333333"] }); }); it("keeps Vector numeric regardless of authored or UI ingress shape", () => { const control = { defaultValue: { x: "0.00", y: "0.00" }, type: "vector", } as const; expect( decodeToolcraftBuiltInControlValue(control, { x: "1.25", y: -2 }), ).toEqual({ accepted: true, value: { x: 1.25, y: -2 } }); }); it("keeps only declared FileDrop item settings beside mediaId", () => { const control = { applicability: { mode: "always" as const }, defaultValue: [], itemControls: { depth: { defaultValue: 0.2, max: 1, min: 0, type: "slider" }, tint: { defaultValue: "#112233", type: "color" }, }, target: "media.bumpMaps", type: "fileDrop", } as const; expect( decodeToolcraftBuiltInControlValue(control, [ { depth: 0.6, ignoredBinaryMetadata: "must-not-enter-values", mediaId: "bump-a", tint: "#445566", }, ]), ).toEqual({ accepted: true, value: [{ depth: 0.6, mediaId: "bump-a", tint: "#445566" }], }); }); it("classifies every built-in exactly once", () => { expect(Object.keys(TOOLCRAFT_CONTROL_VALUE_CODEC_REGISTRY).sort()).toEqual( [...TOOLCRAFT_BUILT_IN_CONTROL_TYPES].sort(), ); }); it("compares cloned canonical values structurally", () => { const first = { stops: [{ color: "#FFFFFF", position: "0%" }] }; const equal = { stops: [{ color: "#FFFFFF", position: "0%" }] }; const changed = { stops: [{ color: "#000000", position: "0%" }] }; expect(areToolcraftControlValuesEqual(first, equal)).toBe(true); expect(areToolcraftControlValuesEqual(first, changed)).toBe(false); }); });