import { cleanup, fireEvent, render, screen, waitFor, } from "@testing-library/react"; import { afterEach, describe, expect, it } from "vitest"; import { defineToolcraft } from "../../schema/define-toolcraft"; import { ToolcraftRoot } from "./toolcraft-root"; import { useToolcraft } from "./use-toolcraft"; function Probe() { const { dispatch, state } = useToolcraft(); return ( ); } function ResetProbe() { const { dispatch, state } = useToolcraft(); return (
); } function PanelPersistenceProbe() { const { dispatch, state } = useToolcraft(); return ( ); } function createPersistentSchema() { return defineToolcraft({ base: { identity: { id: "root-test", title: "Root Test" }, canvas: { enabled: true }, panels: { controls: { sections: [ { id: "test-section-1", controls: { opacity: { applicability: { mode: "always" as const }, defaultValue: 75, target: "selectedLayer.opacity", type: "slider", }, }, }, ], title: "Controls", }, }, persistence: { storage: "localStorage", }, }, modules: [], }); } function createHistorySchema(history = true) { return defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true }, panels: { controls: { sections: [ { id: "test-section-2", controls: { opacity: { applicability: { mode: "always" as const }, defaultValue: 75, target: "selectedLayer.opacity", type: "slider", }, }, }, ], title: "Controls", }, }, toolbar: { history }, }, modules: [], }); } afterEach(() => { cleanup(); window.localStorage.clear(); }); describe("ToolcraftRoot", () => { it("provides editor state and dispatch to children", async () => { render( , ); fireEvent.click(screen.getByRole("button", { name: "75" })); expect(await screen.findByRole("button", { name: "50" })).toBeTruthy(); }); it("restores persisted values before first render", () => { const schema = createPersistentSchema(); window.localStorage.setItem( "toolcraft:root-test:state:v2", JSON.stringify({ state: { values: { "selectedLayer.opacity": 33 } }, version: 2, }), ); render( , ); expect(screen.getByRole("button", { name: "33" })).toBeTruthy(); }); it("persists runtime values and reset results through schema policy", async () => { const schema = createPersistentSchema(); render( , ); fireEvent.click(screen.getByRole("button", { name: "75" })); await waitFor(() => { expect( JSON.parse( window.localStorage.getItem("toolcraft:root-test:state:v2") ?? "{}", ), ).toMatchObject({ state: { values: { "selectedLayer.opacity": 50 } }, version: 2, }); }); fireEvent.click(screen.getByRole("button", { name: "Reset" })); await waitFor(() => { expect( JSON.parse( window.localStorage.getItem("toolcraft:root-test:state:v2") ?? "{}", ), ).toMatchObject({ state: { values: { "selectedLayer.opacity": 75 } }, version: 2, }); }); }); it("restores dragged panel offsets from the app persistence policy before first render", async () => { const schema = createPersistentSchema(); const firstRender = render( , ); fireEvent.click(screen.getByRole("button", { name: "0,0" })); await waitFor(() => { expect( JSON.parse( window.localStorage.getItem("toolcraft:root-test:state:v2") ?? "{}", ), ).toMatchObject({ state: { panels: { controls: { offset: { x: 72, y: -24 } } } }, version: 2, }); }); firstRender.unmount(); render( , ); expect(screen.getByRole("button", { name: "72,-24" })).toBeTruthy(); }); it("binds runtime undo and redo to keyboard shortcuts when history is enabled", async () => { render( , ); fireEvent.click(screen.getByRole("button", { name: "75" })); expect(await screen.findByRole("button", { name: "50" })).toBeTruthy(); fireEvent.keyDown(document, { key: "z", metaKey: true }); expect(screen.getByRole("button", { name: "75" })).toBeTruthy(); fireEvent.keyDown(document, { key: "z", metaKey: true, shiftKey: true }); expect(screen.getByRole("button", { name: "50" })).toBeTruthy(); fireEvent.keyDown(document, { key: "z", ctrlKey: true }); expect(screen.getByRole("button", { name: "75" })).toBeTruthy(); fireEvent.keyDown(document, { key: "y", ctrlKey: true }); expect(screen.getByRole("button", { name: "50" })).toBeTruthy(); }); it("binds runtime history shortcuts from focused non-text inputs", async () => { render(
, ); fireEvent.click(screen.getByRole("button", { name: "75" })); expect(await screen.findByRole("button", { name: "50" })).toBeTruthy(); const slider = screen.getByRole("slider", { name: "Opacity slider" }); const checkbox = screen.getByRole("checkbox", { name: "Include background", }); fireEvent.keyDown(slider, { key: "z", metaKey: true }); expect(screen.getByRole("button", { name: "75" })).toBeTruthy(); fireEvent.keyDown(slider, { key: "z", metaKey: true, shiftKey: true }); expect(screen.getByRole("button", { name: "50" })).toBeTruthy(); fireEvent.keyDown(checkbox, { key: "z", ctrlKey: true }); expect(screen.getByRole("button", { name: "75" })).toBeTruthy(); fireEvent.keyDown(checkbox, { key: "y", ctrlKey: true }); expect(screen.getByRole("button", { name: "50" })).toBeTruthy(); }); it("does not hijack undo shortcuts while the user is typing into editable controls", async () => { render(