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 type { ToolcraftCustomControlRendererProps } from "../control-renderers"; import { ControlsPanel } from "../controls-panel"; const extensionsSchema = defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { title: { defaultValue: "Built in", target: "document.title", type: "text", }, live: { defaultValue: 10, label: "Live", target: "document.live", type: "liveCustom", }, export: { actions: [{ label: "Export", value: "export" }], target: "panel.actions", type: "panelActions", }, }, title: "Extensions", }, ], title: "Controls", }, }, }); function CaptureStore({ capture, }: { capture: (store: ToolcraftExternalStore) => void; }): null { capture(useToolcraftStore()); return null; } afterEach(() => { cleanup(); vi.restoreAllMocks(); }); describe("ControlsPanel extension live state", () => { it("keeps built-in structure cold while a custom renderer receives effective state", () => { const renderCustom = vi.fn( ({ state, value }: ToolcraftCustomControlRendererProps) => ( {state.timeline.currentTimeSeconds}:{state.canvas.offset.x}: {state.canvas.offset.y}:{state.canvas.zoom}:{String(value)} ), ); let store: ToolcraftExternalStore | undefined; render( { store = value; }} /> , ); const builtInInput = screen.getByDisplayValue("Built in"); const initialCustomRenders = renderCustom.mock.calls.length; act(() => { store?.dispatchTransient({ currentTimeSeconds: 3, type: "timeline.setCurrentTime", }); store?.dispatchTransient({ offset: { x: 24, y: -12 }, type: "canvas.setViewport", zoom: 150, }); }); expect(screen.getByDisplayValue("Built in")).toBe(builtInInput); expect(screen.getByTestId("live-custom-state").textContent).toBe( "3:24:-12:150:10", ); expect(renderCustom.mock.calls.length).toBeGreaterThan(initialCustomRenders); }); it("reads effective state when an app action starts", () => { const onPanelAction = vi.fn(); let store: ToolcraftExternalStore | undefined; render( { store = value; }} /> , ); act(() => { store?.dispatchTransient({ currentTimeSeconds: 2.5, type: "timeline.setCurrentTime", }); store?.dispatchTransient({ offset: { x: 18, y: -9 }, type: "canvas.setViewport", zoom: 125, }); }); fireEvent.click(screen.getByRole("button", { name: "Export" })); const actionState = onPanelAction.mock.calls[0]?.[0].state; expect(actionState.timeline.currentTimeSeconds).toBe(2.5); expect(actionState.canvas).toMatchObject({ offset: { x: 18, y: -9 }, zoom: 125, }); }); });