import { act, cleanup, fireEvent, render, screen, } from "@testing-library/react"; import * as React from "react"; import { afterEach, describe, expect, it, vi } from "vitest"; import { defineToolcraft } from "../../../schema/define-toolcraft"; import type { ToolcraftExternalStore } from "../../../state/toolcraft-external-store"; import { ToolcraftRoot } from "../../app-shell/toolcraft-root"; import { useToolcraftStore } from "../../app-shell/toolcraft-store-context"; import { ControlsPanel } from "../controls-panel"; const counters = vi.hoisted(() => ({ commits: new Map(), executions: new Map(), })); function increment(counter: Map, id: string): void { counter.set(id, (counter.get(id) ?? 0) + 1); } function CommitProbe({ id }: { id: string }): null { React.useLayoutEffect(() => { increment(counters.commits, id); }); return null; } vi.mock("@repo/ui", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, PanelSection: (props: React.ComponentProps) => { const id = `section:${String(props.title)}`; increment(counters.executions, id); return ( <> ); }, }; }); vi.mock( "../renderers/controls-panel-basic-renderers", async (importOriginal) => { const actual = await importOriginal< typeof import("../renderers/controls-panel-basic-renderers") >(); return { ...actual, renderBasicControl: ( args: Parameters[0], ): React.ReactNode => { increment(counters.executions, `control:${args.id}`); return ( <> {actual.renderBasicControl(args)} ); }, }; }, ); function CaptureStore({ capture, }: { capture: (store: ToolcraftExternalStore) => void; }): null { capture(useToolcraftStore()); return null; } function resetCounters(): void { counters.commits.clear(); counters.executions.clear(); } function count(kind: "commits" | "executions", id: string): number { return counters[kind].get(id) ?? 0; } function renderPanel( schema: ReturnType, props: Pick, "onPanelAction"> = {}, ): ToolcraftExternalStore { let store: ToolcraftExternalStore | undefined; render( { store = value; }} /> , ); if (!store) { throw new Error("Expected Toolcraft store to be captured."); } resetCounters(); return store; } function createTwoSliderSchema(): ReturnType { return defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: true }, panels: { controls: { sections: [ { id: "test-section-1", controls: { alpha: { applicability: { mode: "always" as const }, defaultValue: 10, label: "Alpha", max: 100, min: 0, target: "shape.alpha", type: "slider", }, beta: { applicability: { mode: "always" as const }, defaultValue: 20, label: "Beta", max: 100, min: 0, target: "shape.beta", type: "slider", }, }, title: "Transform", }, ], title: "Controls", }, }, }, modules: [], }); } afterEach(() => { cleanup(); resetCounters(); window.localStorage.clear(); }); describe("ControlsPanel section isolation", () => { it("distinguishes a control named Reset controls from an actual reset", () => { const schema = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: false }, panels: { controls: { sections: [ { id: "test-section-2", controls: { resetLabel: { applicability: { mode: "always" as const }, defaultValue: 10, label: "Reset controls", max: 100, min: 0, target: "shape.resetLabel", type: "slider", }, }, title: "Transform", }, ], title: "Controls", }, }, }, modules: [], }); const control = schema.panels.controls?.sections.find( (section) => section.controls.resetLabel !== undefined, )?.controls.resetLabel; const store = renderPanel(schema); if (!control || typeof control.label !== "string") { throw new Error("Expected the Reset controls control."); } const controlLabel = control.label; act(() => { store.dispatch({ label: controlLabel, target: control.target, type: "controls.setValue", value: 30, }); }); expect(count("executions", "control:resetLabel")).toBe(1); expect(count("commits", "section:Transform")).toBe(0); resetCounters(); act(() => { store.dispatch({ type: "controls.reset" }); }); expect(count("commits", "section:Transform")).toBe(1); expect(count("commits", "control:resetLabel")).toBe(1); resetCounters(); act(() => { store.dispatch({ label: controlLabel, target: control.target, type: "controls.setValue", value: 40, }); }); expect(count("executions", "control:resetLabel")).toBe(1); expect(count("commits", "section:Transform")).toBe(0); act(() => { store.dispatch({ type: "history.undo" }); }); resetCounters(); act(() => { store.dispatch({ type: "history.undo" }); }); expect(store.getState().values[control.target]).toBe(30); expect(count("commits", "section:Transform")).toBe(1); resetCounters(); act(() => { store.dispatch({ type: "history.redo" }); }); expect(store.getState().values[control.target]).toBe(10); expect(count("commits", "section:Transform")).toBe(1); }); it("keeps the reset generation stable across a later ordinary edit", () => { const schema = createTwoSliderSchema(); const alphaTarget = schema.panels.controls?.sections.find( (section) => section.controls.alpha !== undefined, )?.controls.alpha?.target; const store = renderPanel(schema); if (!alphaTarget) { throw new Error("Expected alpha target."); } act(() => { store.dispatch({ target: alphaTarget, type: "controls.setValue", value: 30, }); store.dispatch({ type: "controls.reset" }); }); expect(count("commits", "section:Transform")).toBeGreaterThan(0); resetCounters(); act(() => { store.dispatch({ target: alphaTarget, type: "controls.setValue", value: 40, }); }); expect(count("executions", "control:alpha")).toBe(1); expect(count("commits", "control:alpha")).toBe(1); expect(count("executions", "control:beta")).toBe(0); expect(count("commits", "control:beta")).toBe(0); expect(count("executions", "section:Transform")).toBe(0); expect(count("commits", "section:Transform")).toBe(0); act(() => { store.dispatch({ type: "history.undo" }); }); resetCounters(); act(() => { store.dispatch({ type: "history.undo" }); }); expect(store.getState().values[alphaTarget]).toBe(30); expect(count("commits", "section:Transform")).toBeGreaterThan(0); resetCounters(); act(() => { store.dispatch({ type: "history.redo" }); }); expect(store.getState().values[alphaTarget]).toBe(10); expect(count("commits", "section:Transform")).toBeGreaterThan(0); }); it("keeps unaffected section shells cold for collapse and footer progress state", async () => { const schema = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: false }, panels: { controls: { sections: [ { id: "test-section-3", controls: { first: { applicability: { mode: "always" as const }, defaultValue: 1, label: "First value", max: 10, min: 0, target: "first.value", type: "slider", }, }, title: "First", }, { id: "test-section-4", controls: { second: { applicability: { mode: "always" as const }, defaultValue: 2, label: "Second value", max: 10, min: 0, target: "second.value", type: "slider", }, }, title: "Second", }, { id: "test-section-5", actionGroup: "primary", controls: { export: { applicability: { mode: "always" as const }, actions: [{ label: "Export", value: "export" }], target: "actions.export", type: "panelActions", }, }, title: "Actions", }, ], title: "Controls", }, }, }, modules: [], }); let resolveAction: (() => void) | undefined; const pendingAction = new Promise((resolve) => { resolveAction = resolve; }); let reportActionProgress: ((progress: number) => void) | undefined; renderPanel(schema, { onPanelAction: ({ reportProgress }) => { reportActionProgress = reportProgress; return pendingAction; }, }); fireEvent.click( screen.getByRole("button", { name: "Collapse First section" }), ); expect(count("executions", "section:First")).toBe(1); expect(count("commits", "section:First")).toBe(1); expect(count("executions", "section:Second")).toBe(0); expect(count("commits", "section:Second")).toBe(0); resetCounters(); fireEvent.click(screen.getByRole("button", { name: "Export" })); expect(count("executions", "section:First")).toBe(0); expect(count("commits", "section:First")).toBe(0); expect(count("executions", "section:Second")).toBe(0); expect(count("commits", "section:Second")).toBe(0); resetCounters(); act(() => { reportActionProgress?.(0.5); }); expect(count("executions", "section:First")).toBe(0); expect(count("commits", "section:First")).toBe(0); expect(count("executions", "section:Second")).toBe(0); expect(count("commits", "section:Second")).toBe(0); await act(async () => { resolveAction?.(); await pendingAction; }); expect(count("executions", "section:First")).toBe(0); expect(count("commits", "section:Second")).toBe(0); }); it("mounts a visibleWhen section without executing an unaffected section", () => { const schema = defineToolcraft({ base: { identity: { id: "runtime-test", title: "Toolcraft App" }, canvas: { enabled: false }, panels: { controls: { sections: [ { id: "test-section-6", controls: { mode: { applicability: { mode: "always" as const }, defaultValue: false, label: "Advanced mode", target: "mode.advanced", type: "switch", }, }, title: "Always", }, { id: "test-section-7", controls: { amount: { applicability: { mode: "always" as const }, defaultValue: 5, label: "Amount", max: 10, min: 0, target: "advanced.amount", type: "slider", }, }, title: "Advanced", visibleWhen: { equals: true, target: "mode.advanced" }, }, ], title: "Controls", }, }, }, modules: [], }); const modeTarget = schema.panels.controls?.sections.find( (section) => section.controls.mode !== undefined, )?.controls.mode?.target; const store = renderPanel(schema); if (!modeTarget) { throw new Error("Expected mode target."); } act(() => { store.dispatch({ target: modeTarget, type: "controls.setValue", value: true, }); }); expect(count("executions", "section:Advanced")).toBe(1); expect(count("commits", "section:Advanced")).toBe(1); expect(count("executions", "section:Always")).toBe(0); expect(count("commits", "section:Always")).toBe(0); }); });