import { describe, expect, it } from "vitest"; import { defineToolcraft } from "./define-toolcraft"; import { materializeControlTestDefinition } from "./define-toolcraft.control-sections.test-support"; import type { ToolcraftControlSchema } from "./types"; function createSchema(control: ToolcraftControlSchema) { return { base: { canvas: { enabled: false }, identity: { id: "collection-actions-test", title: "Controls" }, panels: { controls: { sections: [ { id: "test-section-1", controls: { layers: control }, title: "Layers", }, ], title: "Controls", }, }, }, modules: [], }; } function compoundCollection( overrides: Record = {}, ): ToolcraftControlSchema { return { applicability: { mode: "always" as const }, defaultValue: [{ invert: false, strength: 0.4 }], itemControls: { strength: { defaultValue: 0.5, keyframeable: true, label: "Strength", max: 1, min: 0, step: 0.01, type: "slider", }, invert: { defaultValue: false, label: "Invert", type: "switch", }, }, itemLabel: "Layer", minItems: 1, target: "surface.layers", type: "collectionActions", ...overrides, } as ToolcraftControlSchema; } describe("defineToolcraft compound collectionActions items", () => { it("accepts one logical record made from several built-in fields", () => { const app = defineToolcraft(createSchema(compoundCollection())); const control = app.panels.controls?.sections.find( (section) => section.title === "Layers", )?.controls.layers; expect(Object.keys(control?.itemControls ?? {})).toEqual([ "strength", "invert", ]); }); it("rejects competing scalar and record templates", () => { expect(() => defineToolcraft( createSchema( compoundCollection({ itemControl: { defaultValue: 0, type: "slider" }, }), ), ), ).toThrow(/collection-item-template.*itemControl.*itemControls/u); }); it("requires at least two fields in a compound collection item", () => { expect(() => defineToolcraft( createSchema( compoundCollection({ itemControls: { strength: { defaultValue: 0.5, type: "slider" }, }, }), ), ), ).toThrow(/collection-item-controls.*at least two/u); }); it("requires one default source per compound field", () => { expect(() => defineToolcraft( createSchema( compoundCollection({ itemControls: { invert: { defaultValue: false, type: "switch" }, strength: { type: "slider" }, }, }), ), ), ).toThrow(/itemControls\.strength.*defaultValue/u); }); it("rejects a competing itemDefaultValue", () => { expect(() => defineToolcraft( createSchema( compoundCollection({ itemDefaultValue: { invert: true, strength: 1 }, }), ), ), ).toThrow(/collection-item-default.*itemDefaultValue/u); }); it("rejects non-record initial compound items", () => { expect(() => materializeControlTestDefinition( createSchema(compoundCollection({ defaultValue: [new Date()] })), ), ).toThrow(/collection-item-value.*plain record/u); }); it("keeps sourceCollection single-field only", () => { expect(() => defineToolcraft( createSchema(compoundCollection({ type: "sourceCollection" })), ), ).toThrow(/collection-item-controls.*collectionActions.*fileDrop/u); }); it("materializes the collection selection as a null settings and persistence value", () => { const app = materializeControlTestDefinition( createSchema( compoundCollection({ selectionTarget: "surface.selectedLayer" }), ), ); expect(app.settingsTransfer.additionalValueTargets).toContain( "surface.selectedLayer", ); expect(app.persistence).toMatchObject({ additionalValueTargets: expect.arrayContaining([ "surface.selectedLayer", ]), }); }); it("rejects incapable nested keyframes and invalid collection selection ownership", () => { expect(() => materializeControlTestDefinition( createSchema( compoundCollection({ itemControls: { invert: { defaultValue: false, keyframeable: true, type: "switch", }, strength: { defaultValue: 0.5, type: "slider" }, }, }), ), ), ).toThrow(/collection-item-keyframe.*invert.*control-type/u); expect(() => materializeControlTestDefinition( createSchema( compoundCollection({ selectionTarget: "surface.layers" }), ), ), ).toThrow(/collection-selection-target.*surface\.layers/u); expect(() => materializeControlTestDefinition( createSchema( compoundCollection({ selectionTarget: "canvas.infinity" }), ), ), ).toThrow(/collection-selection-target.*runtime-owned/u); }); it("rejects the reserved nested-address namespace everywhere product targets are authored", () => { expect(() => materializeControlTestDefinition( createSchema( compoundCollection({ target: "toolcraft:collection-item:v1:owned", }), ), ), ).toThrow(/reserved.*toolcraft:collection-item:v1:/u); expect(() => materializeControlTestDefinition( createSchema( compoundCollection({ selectionTarget: "toolcraft:collection-item:v1:selected", }), ), ), ).toThrow(/reserved.*toolcraft:collection-item:v1:/u); }); it("rejects non-well-formed Unicode in collection address components", () => { expect(() => materializeControlTestDefinition( createSchema(compoundCollection({ target: "surface.\uD800" })), ), ).toThrow(/collection target.*well-formed Unicode/u); expect(() => materializeControlTestDefinition( createSchema( compoundCollection({ selectionTarget: "surface.\uDC00" }), ), ), ).toThrow(/selectionTarget.*well-formed Unicode/u); expect(() => materializeControlTestDefinition( createSchema( compoundCollection({ itemControls: { ["strength.\uD800"]: { defaultValue: 0.5, type: "slider", }, invert: { defaultValue: false, type: "switch" }, }, }), ), ), ).toThrow(/itemControls.*well-formed Unicode/u); }); });