import { describe, expect, it } from "vitest"; import { toolcraftRuntimeCommandTypes } from "../state/types"; import { defineToolcraft } from "./define-toolcraft"; import { toolcraftReservedTargets, toolcraftRuntimeOwnedTargets, getToolcraftCanvasSizeTargetDimension, isToolcraftReservedTarget, isToolcraftRuntimeOwnedTarget, } from "./runtime-targets"; describe("defineToolcraft assembly contract", () => { it("derives an AI assembly contract from app surfaces", () => { const app = defineToolcraft({ canvas: { enabled: true, upload: true }, panels: { controls: { sections: [ { controls: { opacity: { defaultValue: 75, target: "selectedLayer.opacity", type: "slider", }, }, }, ], title: "Controls", }, layers: true, timeline: true, }, }); expect(app.assembly.components).toEqual([ "canvas", "controlsPanel", "layersPanel", "timelinePanel", "toolbar", ]); expect(app.assembly.capabilities).toEqual( expect.arrayContaining([ "canvas.draggable", "canvas.upload", "controls.defaults", "layers.selection", "timeline.keyframes", "toolbar.history", "toolbar.radar", "toolbar.theme", "toolbar.zoom", ]), ); expect(app.assembly.commands).toEqual( expect.arrayContaining([ "controls.reset", "controls.resetTargets", "history.undo", "media.delete", "media.import", "media.importBatch", "media.reorder", "media.transform", "layers.reorder", "panels.setSectionCollapsed", "timeline.setCurrentTime", "canvas.center", "canvas.setViewport", ]), ); expect(app.assembly.surfaces.panels.layers?.defaultPlacement).toBe("left"); expect(app.assembly.surfaces.panels.timeline?.snapEdges).toEqual(["top", "bottom"]); expect(app.panels.timeline).toEqual({ defaultDurationSeconds: 8, enabled: true, mode: "keyframes", }); expect(app.assembly.surfaces.panels.toolbar.enabled).toBe(true); }); it("supports playback-only timeline apps without keyframe editing commands", () => { const app = defineToolcraft({ canvas: { enabled: true }, panels: { timeline: { mode: "playback" }, }, }); expect(app.panels.timeline).toEqual({ defaultDurationSeconds: 8, enabled: true, mode: "playback", }); expect(app.assembly.components).toContain("timelinePanel"); expect(app.assembly.capabilities).toEqual( expect.arrayContaining([ "timeline.duration", "timeline.panel", "timeline.playback", ]), ); expect(app.assembly.capabilities).not.toContain("timeline.keyframes"); expect(app.assembly.commands).toEqual( expect.arrayContaining([ "timeline.setCurrentTime", "timeline.setDuration", "timeline.setPlaying", "timeline.toggleLoop", "timeline.togglePlayback", ]), ); expect(app.assembly.commands).not.toContain("timeline.toggleExpanded"); expect(app.assembly.commands).not.toContain("timeline.toggleControlKeyframes"); expect(app.assembly.commands).not.toContain("timeline.moveKeyframe"); }); it("uses schema timeline default duration as the animation loop duration", () => { const app = defineToolcraft({ canvas: { enabled: true }, panels: { timeline: { defaultDurationSeconds: 12, mode: "playback", }, }, }); const clamped = defineToolcraft({ canvas: { enabled: true }, panels: { timeline: { defaultDurationSeconds: 120, mode: "playback", }, }, }); expect(app.panels.timeline?.defaultDurationSeconds).toBe(12); expect(clamped.panels.timeline?.defaultDurationSeconds).toBe(60); }); it("only publishes commands supported by the runtime command bus", () => { const app = defineToolcraft({ canvas: { enabled: true, upload: true }, panels: { controls: { sections: [ { controls: { opacity: { defaultValue: 75, target: "selectedLayer.opacity", type: "slider", }, }, }, ], title: "Controls", }, layers: true, timeline: true, }, }); const supportedCommands = new Set(toolcraftRuntimeCommandTypes); expect(app.assembly.commands.filter((command) => !supportedCommands.has(command))).toEqual([]); }); it("publishes media commands for panel-only fileDrop controls", () => { const app = defineToolcraft({ canvas: { enabled: true, upload: false }, panels: { controls: { sections: [ { controls: { source: { assetKind: "file", target: "source.files", type: "fileDrop", }, }, }, ], title: "Controls", }, }, }); expect(app.assembly.capabilities).not.toContain("canvas.upload"); expect(app.assembly.commands).toEqual( expect.arrayContaining([ "media.delete", "media.import", "media.importBatch", "media.reorder", "media.transform", ]), ); }); it("publishes global media commands for default assets through app capabilities", () => { const app = defineToolcraft({ canvas: { enabled: true }, media: { defaultAssets: [ { assetKind: "image", dataUrl: "data:image/png;base64,AA==", fileName: "default.png", }, ], }, panels: {}, }); expect(app.assembly.commands).toEqual( expect.arrayContaining([ "media.delete", "media.import", "media.importBatch", "media.reorder", "media.transform", ]), ); }); it("does not publish global media commands for media-free apps", () => { const app = defineToolcraft({ canvas: { enabled: true }, panels: {}, }); expect(app.assembly.commands.filter((command) => command.startsWith("media."))).toEqual([]); }); it("publishes reserved targets for AI assembly boundaries", () => { expect(toolcraftRuntimeOwnedTargets).toEqual([ "canvas.aspectRatio", "canvas.infinity", "canvas.renderScale", "canvas.size.width", "canvas.size.height", "runtime.settingsTransfer", "panels.timeline.extended", "panels.timeline.visible", ]); expect(toolcraftReservedTargets).toEqual([ "canvas.aspectRatio", "canvas.infinity", "canvas.renderScale", "canvas.size.width", "canvas.size.height", "runtime.settingsTransfer", "panels.timeline.extended", "panels.timeline.visible", "selectedLayer.opacity", "selectedLayer.visible", ]); expect(getToolcraftCanvasSizeTargetDimension("canvas.size.width")).toBe("width"); expect(getToolcraftCanvasSizeTargetDimension("canvas.size.height")).toBe("height"); expect(getToolcraftCanvasSizeTargetDimension("generation.prompt")).toBeNull(); expect(isToolcraftRuntimeOwnedTarget("canvas.aspectRatio")).toBe(true); expect(isToolcraftRuntimeOwnedTarget("canvas.infinity")).toBe(true); expect(isToolcraftRuntimeOwnedTarget("runtime.settingsTransfer")).toBe(true); expect(isToolcraftRuntimeOwnedTarget("panels.timeline.extended")).toBe(true); expect(isToolcraftRuntimeOwnedTarget("panels.timeline.visible")).toBe(true); expect(isToolcraftReservedTarget("selectedLayer.opacity")).toBe(true); expect(isToolcraftReservedTarget("generation.prompt")).toBe(false); }); it("removes disabled toolbar capabilities from the assembly contract", () => { const app = defineToolcraft({ canvas: { enabled: true }, panels: {}, toolbar: { history: false, radar: false, theme: false, zoom: false, }, }); expect(app.assembly.components).toEqual(["canvas"]); expect(app.assembly.commands).not.toContain("history.undo"); expect(app.assembly.commands).not.toContain("canvas.center"); expect(app.assembly.commands).not.toContain("canvas.zoomIn"); expect(app.assembly.surfaces.panels.toolbar.enabled).toBe(false); }); it("does not enable the layers panel implicitly for single-layer or upload apps", () => { const app = defineToolcraft({ canvas: { enabled: true, upload: true }, panels: { controls: { sections: [ { controls: { opacity: { defaultValue: 75, target: "selectedLayer.opacity", type: "slider", }, }, }, ], title: "Controls", }, }, }); expect(app.panels.layers).toBeUndefined(); expect(app.assembly.components).not.toContain("layersPanel"); expect(app.assembly.capabilities).not.toContain("layers.panel"); expect(app.assembly.commands).not.toContain("layers.reorder"); }); });