import { describe, expect, it } from "vitest"; import { normalizeControlsPanelLayout } from "./controls-panel-normalization"; import { defineToolcraft } from "./define-toolcraft"; import type { ToolcraftAppSchema } from "./types"; const opacityControl = { defaultValue: 75, max: 100, min: 0, target: "appearance.opacity", type: "slider", } as const; describe("Toolcraft control section identity roundtrips", () => { it("preserves explicit, legacy, and implicit applicability origins", () => { const resolved = defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { explicit: { applicability: { mode: "always" }, target: "appearance.explicit", type: "switch", }, implicit: { target: "appearance.implicit", type: "switch", }, legacy: { target: "appearance.legacy", type: "slider", visibleWhen: { equals: true, target: "appearance.explicit", }, }, }, id: "appearance", title: "Appearance", }, ], title: "Controls", }, }, }); const roundTripped = defineToolcraft( resolved as unknown as ToolcraftAppSchema, ); const readApplicability = (schema: typeof resolved) => Object.fromEntries( schema.panels.controls?.sections .filter((section) => section.id.startsWith("appearance")) .flatMap((section) => Object.entries(section.controls)) .map(([id, control]) => [id, control.applicability]) ?? [], ); expect(readApplicability(resolved)).toEqual({ explicit: { mode: "always", origin: "explicit" }, implicit: { mode: "always", origin: "implicit" }, legacy: { all: [{ equals: true, target: "appearance.explicit" }], mode: "conditional", origin: "legacy", }, }); expect(readApplicability(roundTripped)).toEqual( readApplicability(resolved), ); }); it("round-trips resolved legacy and runtime section ids without duplicating runtime sections", () => { const resolved = defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { prompt: { target: "generation.prompt", type: "text", }, export: { actions: [{ label: "Export PNG", value: "export" }], target: "output.export", type: "panelActions", }, }, title: "Generation", }, ], title: "Controls", }, }, }); const roundTripped = defineToolcraft(resolved as unknown as ToolcraftAppSchema); const resolvedIds = resolved.panels.controls?.sections.map((section) => section.id); const roundTrippedIds = roundTripped.panels.controls?.sections.map( (section) => section.id, ); expect(roundTrippedIds).toEqual(resolvedIds); expect(roundTrippedIds?.filter((id) => id === "runtime.setup")).toHaveLength(1); expect(roundTrippedIds?.filter((id) => id === "runtime.export")).toHaveLength(1); }); it("derives deterministic child ids when mixed layout splits one authored section", () => { const createPanel = () => normalizeControlsPanelLayout({ sections: [ { controls: { opacity: opacityControl, palette: { target: "appearance.palette", type: "palette", }, }, id: "appearance", title: "Appearance", }, ], title: "Controls", }); const firstIds = createPanel().sections.map((section) => section.id); const secondIds = createPanel().sections.map((section) => section.id); expect(firstIds).toHaveLength(2); expect(new Set(firstIds).size).toBe(2); expect(firstIds).toEqual(secondIds); expect(firstIds.every((id) => id.startsWith("appearance.part-"))).toBe(true); }); it("round-trips deterministic mixed-layout child ids", () => { const resolved = defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { opacity: opacityControl, palette: { target: "appearance.palette", type: "palette", }, }, title: "Appearance", }, ], title: "Controls", }, }, }); const productIds = resolved.panels.controls?.sections .map((section) => section.id) .filter((id) => !id.startsWith("runtime.")); const roundTripped = defineToolcraft(resolved as unknown as ToolcraftAppSchema); const roundTrippedProductIds = roundTripped.panels.controls?.sections .map((section) => section.id) .filter((id) => !id.startsWith("runtime.")); expect(productIds).toHaveLength(2); expect(productIds?.every((id) => id.startsWith("legacy."))).toBe(true); expect(roundTrippedProductIds).toEqual(productIds); }); it("round-trips composed action-extracted and mixed-layout child ids", () => { const resolved = defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { opacity: opacityControl, palette: { target: "appearance.palette", type: "palette", }, export: { actions: [{ label: "Export PNG", value: "export" }], target: "output.export", type: "panelActions", }, }, id: "appearance", title: "Appearance", }, ], title: "Controls", }, }, }); const productIds = resolved.panels.controls?.sections .map((section) => section.id) .filter((id) => !id.startsWith("runtime.")); expect(productIds).toHaveLength(2); expect( productIds?.every((id) => (id.match(/\.part-/gu) ?? []).length === 2), ).toBe(true); const roundTripped = defineToolcraft(resolved as unknown as ToolcraftAppSchema); const roundTrippedProductIds = roundTripped.panels.controls?.sections .map((section) => section.id) .filter((id) => !id.startsWith("runtime.")); expect(roundTrippedProductIds).toEqual(productIds); }); });