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) => (
),
);
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,
});
});
});