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";
const previousToolcraftNamespace = ["creative", "apps", "kit"].join("-");
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({
canvas: { enabled: true },
panels: {
controls: {
sections: [
{
controls: {
opacity: {
defaultValue: 75,
target: "selectedLayer.opacity",
type: "slider",
},
},
},
],
title: "Controls",
},
},
persistence: {
include: ["values", "panels"],
key: "toolcraft:root-test:state:v1",
storage: "localStorage",
version: 1,
},
});
}
function createHistorySchema(history = true) {
return defineToolcraft({
canvas: { enabled: true },
panels: {
controls: {
sections: [
{
controls: {
opacity: {
defaultValue: 75,
target: "selectedLayer.opacity",
type: "slider",
},
},
},
],
title: "Controls",
},
},
toolbar: { history },
});
}
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:v1",
JSON.stringify({
state: { values: { "selectedLayer.opacity": 33 } },
version: 1,
}),
);
render(
,
);
expect(screen.getByRole("button", { name: "33" })).toBeTruthy();
});
it("migrates persisted values from the previous storage namespace", async () => {
const schema = createPersistentSchema();
const previousStorageKey = `${previousToolcraftNamespace}:root-test:state:v1`;
window.localStorage.setItem(
previousStorageKey,
JSON.stringify({
state: { values: { "selectedLayer.opacity": 44 } },
version: 1,
}),
);
render(
,
);
expect(screen.getByRole("button", { name: "44" })).toBeTruthy();
await waitFor(() => {
expect(
JSON.parse(
window.localStorage.getItem("toolcraft:root-test:state:v1") ?? "{}",
),
).toMatchObject({
state: { values: { "selectedLayer.opacity": 44 } },
version: 1,
});
expect(window.localStorage.getItem(previousStorageKey)).toBeNull();
});
});
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:v1") ?? "{}"),
).toMatchObject({
state: { values: { "selectedLayer.opacity": 50 } },
version: 1,
});
});
fireEvent.click(screen.getByRole("button", { name: "Reset" }));
await waitFor(() => {
expect(
JSON.parse(window.localStorage.getItem("toolcraft:root-test:state:v1") ?? "{}"),
).toMatchObject({
state: { values: { "selectedLayer.opacity": 75 } },
version: 1,
});
});
});
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:v1") ?? "{}"),
).toMatchObject({
state: { panels: { controls: { offset: { x: 72, y: -24 } } } },
version: 1,
});
});
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(
,
);
fireEvent.click(screen.getByRole("button", { name: "75" }));
expect(await screen.findByRole("button", { name: "50" })).toBeTruthy();
fireEvent.keyDown(screen.getByRole("textbox", { name: "Text value" }), {
key: "z",
metaKey: true,
});
expect(screen.getByRole("button", { name: "50" })).toBeTruthy();
fireEvent.keyDown(screen.getByRole("textbox", { name: "Notes" }), {
key: "z",
metaKey: true,
});
expect(screen.getByRole("button", { name: "50" })).toBeTruthy();
fireEvent.keyDown(screen.getByRole("combobox", { name: "Mode" }), {
key: "z",
metaKey: true,
});
expect(screen.getByRole("button", { name: "50" })).toBeTruthy();
fireEvent.keyDown(screen.getByRole("textbox", { name: "Editable value" }), {
key: "z",
metaKey: true,
});
expect(screen.getByRole("button", { name: "50" })).toBeTruthy();
});
it("does not register history shortcuts when toolbar history is disabled", 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: "50" })).toBeTruthy();
});
});