import { describe, expect, it } from "vitest"; import { layersModule } from "../modules/built-ins/layers/layers-module"; import { mediaSourceModule } from "../modules/built-ins/media-source/media-source-module"; import { timelineModule } from "../modules/built-ins/timeline/timeline-module"; 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({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true, upload: true }, panels: { controls: { sections: [ { id: "test-section-1", controls: { opacity: { applicability: { mode: "always" as const }, defaultValue: 75, target: "selectedLayer.opacity", type: "slider", }, }, }, ], title: "Controls", }, }, }, modules: [ layersModule(), mediaSourceModule(), timelineModule({ mode: "keyframes" }), ], }); 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.addCollectionItem", "controls.removeCollectionItem", "controls.reset", "controls.resetTargets", "controls.selectCollectionItem", "controls.setCollectionItemField", "history.undo", "media.delete", "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({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true }, panels: {}, }, modules: [ timelineModule({ 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({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true }, panels: {}, }, modules: [ timelineModule({ defaultDurationSeconds: 12, mode: "playback", }), ], }); expect(() => defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true }, panels: {}, }, modules: [ timelineModule({ defaultDurationSeconds: 120, mode: "playback", }), ], }), ).toThrow(/defaultDurationSeconds.*1 through 60/u); expect(app.panels.timeline?.defaultDurationSeconds).toBe(12); }); it("only publishes commands supported by the runtime command bus", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true, upload: true }, panels: { controls: { sections: [ { id: "test-section-2", controls: { opacity: { applicability: { mode: "always" as const }, defaultValue: 75, target: "selectedLayer.opacity", type: "slider", }, }, }, ], title: "Controls", }, }, }, modules: [mediaSourceModule(), timelineModule({ mode: "playback" })], }); 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({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true, upload: false }, panels: { controls: { sections: [ { id: "test-section-3", controls: { source: { applicability: { mode: "always" as const }, assetKind: "file", target: "source.files", type: "fileDrop", }, }, }, ], title: "Controls", }, }, }, modules: [mediaSourceModule()], }); expect(app.assembly.capabilities).not.toContain("canvas.upload"); expect(app.assembly.commands).toEqual( expect.arrayContaining([ "media.delete", "media.importBatch", "media.reorder", "media.transform", ]), ); }); it("publishes global media commands for default assets through app capabilities", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true }, media: { defaultAssets: [ { ingressPolicy: "prepared-source", position: { x: 0, y: 0 }, sourceSize: { height: 512, unit: "px", width: 512 }, assetKind: "image", dataUrl: "data:image/png;base64,AA==", fileName: "default.png", }, ], }, panels: {}, }, modules: [mediaSourceModule()], }); expect(app.assembly.commands).toEqual( expect.arrayContaining([ "media.delete", "media.importBatch", "media.reorder", "media.transform", ]), ); }); it("does not publish global media commands for media-free apps", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true }, panels: {}, }, modules: [], }); 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.rotationLocked", "canvas.workspaceBackground", "canvas.renderScale", "canvas.size.width", "canvas.size.height", "runtime.settingsTransfer", "panels.timeline.extended", "panels.timeline.visible", ]); expect(toolcraftReservedTargets).toEqual([ "canvas.aspectRatio", "canvas.infinity", "canvas.rotationLocked", "canvas.workspaceBackground", "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({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true }, panels: {}, toolbar: { history: false, radar: false, theme: false, zoom: false, }, }, modules: [], }); 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({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true, upload: true }, panels: { controls: { sections: [ { id: "test-section-4", controls: { opacity: { applicability: { mode: "always" as const }, defaultValue: 75, target: "selectedLayer.opacity", type: "slider", }, }, }, ], title: "Controls", }, }, }, modules: [mediaSourceModule()], }); 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"); }); });