import { describe, expect, it } from "vitest"; import { mediaSourceModule } from "../modules/built-ins/media-source/media-source-module"; import { timelineModule } from "../modules/built-ins/timeline/timeline-module"; import { defineToolcraft } from "../schema/define-toolcraft"; import { createToolcraftState } from "./create-template-state"; describe("createToolcraftState timeline", () => { it("keeps runtime timeline presentation out of product values and defaults", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true, renderScale: true, sizing: { mode: "editable-output" }, }, panels: { controls: { sections: [], title: "Controls", }, }, }, modules: [timelineModule({ mode: "playback" })], }); const state = createToolcraftState(app); expect(state.values).not.toHaveProperty("panels.timeline.extended"); expect(state.values).not.toHaveProperty("panels.timeline.visible"); expect(state.defaults).not.toHaveProperty("panels.timeline.extended"); expect(state.defaults).not.toHaveProperty("panels.timeline.visible"); expect(state.panels.timeline.extended).toBeUndefined(); expect(state.panels.timeline.hidden).toBeUndefined(); }); it("initializes timeline duration from the schema loop duration", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true }, panels: {}, }, modules: [ timelineModule({ defaultDurationSeconds: 14, mode: "playback", }), ], }); const state = createToolcraftState(app); expect(state.timeline.durationSeconds).toBe(14); expect(state.timeline.isLooping).toBe(true); }); it("creates timeline runtime state", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true }, panels: {}, }, modules: [], }); const state = createToolcraftState(app, { timeline: { currentTimeSeconds: 2, durationSeconds: 10, isPlaying: false, }, }); expect(state.timeline.currentTimeSeconds).toBe(2); expect(state.timeline.durationSeconds).toBe(10); expect(state.timeline.expanded).toBe(false); expect(state.timeline.isLooping).toBe(true); expect(state.timeline.isPlaying).toBe(false); expect(state.timeline.keyframeGroups).toEqual([]); }); it("keeps upload-dependent intrinsic-media timelines paused until media exists", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true, sizing: { mode: "intrinsic-media" }, upload: true, }, panels: {}, }, modules: [mediaSourceModule(), timelineModule({ mode: "playback" })], }); const state = createToolcraftState(app); expect(state.timeline.currentTimeSeconds).toBe(0); expect(state.timeline.isPlaying).toBe(false); }); it("allows upload-dependent intrinsic-media timelines to start ready when media is seeded", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true, sizing: { mode: "intrinsic-media" }, upload: true, }, panels: {}, }, modules: [mediaSourceModule(), timelineModule({ mode: "playback" })], }); const state = createToolcraftState(app, { mediaAssets: [ { sourceSize: { height: 320, unit: "px", width: 512 }, assetKind: "image", lifecycle: "ready", resourceRef: "data:image/png;base64,AAAA", fileName: "source.png", id: "media-1", layerId: "layer-1", mimeType: "image/png", position: { x: 0, y: 0 }, size: { height: 320, unit: "px", width: 512 }, }, ], }); expect(state.timeline.isPlaying).toBe(true); }); it("accepts seeded timeline keyframes without making them runtime defaults", () => { const app = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true }, panels: {}, }, modules: [], }); const state = createToolcraftState(app, { timeline: { keyframeGroups: [ { controlId: "opacity", keyframes: [ { controlId: "opacity", controlLabel: "Opacity", easing: { controlPoints: [0.65, 0, 0.35, 1], type: "bezier" }, id: "opacity-0", timeSeconds: 0, valueLabel: "Opacity 75%", }, ], label: "Opacity", }, ], }, }); expect( state.timeline.keyframeGroups.map((group) => group.controlId), ).toEqual(["opacity"]); }); });