import { act, cleanup, fireEvent, render, screen } from "@testing-library/react"; import * as React from "react"; import { afterEach, beforeAll, describe, expect, it, vi } from "vitest"; import { registerToolcraftRendererPipeline, createToolcraftRendererPipelineRuntimeOwner, type ToolcraftRendererPipelinePassContract, type ToolcraftRendererPipelineClient, } from "../../rendering"; import { defineToolcraft } from "../../schema/define-toolcraft"; import { ToolcraftApp } from "./toolcraft-app"; import { ToolcraftPipelineEvidenceBridge } from "./toolcraft-pipeline-evidence"; import { useToolcraftPipeline } from "./use-toolcraft-pipeline"; beforeAll(() => { Object.defineProperty(window, "matchMedia", { value: () => ({ addEventListener: () => undefined, addListener: () => undefined, matches: false, removeEventListener: () => undefined, removeListener: () => undefined, }), writable: true, }); }); afterEach(cleanup); const schema = defineToolcraft({ canvas: { enabled: true }, panels: { controls: { sections: [ { controls: { actions: { actions: [{ label: "Render", value: "render" }], target: "panel.actions", type: "panelActions", }, power: { defaultValue: 10, label: "Power", target: "generation.power", type: "pipelinePower", }, }, title: "Renderer", }, ], title: "Controls", }, }, }); function createRendererPipelineRegistration(runtimeId = "app-shell-test-v1") { return registerToolcraftRendererPipeline<{ preview: ToolcraftRendererPipelinePassContract; }>()({ interactionInvalidation: [], passes: [ { cacheKey: ["generation.power"], id: "preview", inputs: ["generation.power"], invalidatedBy: ["generation.power"], kind: "composite", lifecycle: { cache: "memoized", resourceScope: "renderer" }, output: "preview", quality: "full", runsOn: "main", }, ], runtimeId, }); } describe("Toolcraft pipeline context", () => { it("exposes pull-only immutable evidence without subscribing or serializing", async () => { const registration = createRendererPipelineRegistration("pull-evidence-v1"); const owner = createToolcraftRendererPipelineRuntimeOwner(registration); const subscribe = vi.fn(owner.client.subscribe); const client: ToolcraftRendererPipelineClient = Object.freeze({ ...owner.client, subscribe, }); const symbol = Symbol.for("toolcraft.renderer-pipeline-evidence.snapshot"); render(); const evidence = document.querySelector( '[data-toolcraft-pipeline-evidence="pull-evidence-v1"]', ) as HTMLOutputElement & Record; expect(subscribe).not.toHaveBeenCalled(); expect(evidence.getAttribute("data-toolcraft-pipeline-snapshot")).toBeNull(); expect(evidence.propertyIsEnumerable(symbol)).toBe(false); const getSnapshot = evidence[symbol]; expect(getSnapshot).toBeTypeOf("function"); await owner.client.runPass( registration.getPass("preview"), { "generation.power": 10 }, () => "preview", ); const snapshot = (getSnapshot as () => ReturnType)(); expect(snapshot.passes.preview.executions).toBe(1); expect(Object.isFrozen(snapshot)).toBe(true); await owner.dispose(); }); it("owns one StrictMode-safe runtime for output, custom controls, and actions", async () => { const registration = createRendererPipelineRegistration(); const observedRuntimes: ToolcraftRendererPipelineClient[] = []; let actionRuntime: ToolcraftRendererPipelineClient | null | undefined; function PipelineConsumer(): React.JSX.Element { const runtime = useToolcraftPipeline(); if (runtime) { observedRuntimes.push(runtime); } return Pipeline consumer; } const view = render( } controlRenderers={{ pipelinePower: PipelineConsumer }} onPanelAction={({ rendererPipeline }) => { actionRuntime = rendererPipeline; return rendererPipeline?.runPass( registration.getPass("preview"), { "generation.power": 10 }, () => "preview-model", ); }} rendererPipelineRegistration={registration} schema={schema} /> , ); view.rerender( } controlRenderers={{ pipelinePower: PipelineConsumer }} onPanelAction={({ rendererPipeline }) => { actionRuntime = rendererPipeline; return rendererPipeline?.runPass( registration.getPass("preview"), { "generation.power": 10 }, () => "preview-model", ); }} rendererPipelineRegistration={registration} schema={schema} /> , ); fireEvent.click(screen.getByRole("button", { name: "Render" })); await act(() => Promise.resolve()); const runtime = observedRuntimes[0]; expect(runtime).toBeDefined(); expect(observedRuntimes.every((candidate) => candidate === runtime)).toBe(true); expect(runtime).not.toHaveProperty("dispose"); expect(actionRuntime).toBe(runtime); expect(runtime?.getSnapshot().disposed).toBe(false); expect(Object.isFrozen(runtime?.getSnapshot())).toBe(true); const evidence = document.querySelector( '[data-toolcraft-pipeline-evidence="app-shell-test-v1"]', ); expect(evidence).toBeInstanceOf(HTMLOutputElement); const getSnapshot = ( evidence as HTMLOutputElement & Record )[Symbol.for("toolcraft.renderer-pipeline-evidence.snapshot")]; const snapshot = (getSnapshot as () => { disposed: boolean; passes: { preview: { executions: number } }; })(); expect(snapshot).toMatchObject({ disposed: false, passes: { preview: { executions: 1 } }, }); view.unmount(); await act(() => Promise.resolve()); expect(runtime?.getSnapshot().disposed).toBe(true); }); it("is absent when an app does not register a pipeline", () => { let observedRuntime: unknown = "not-rendered"; function PipelineProbe(): React.JSX.Element { observedRuntime = useToolcraftPipeline(); return Pipeline probe; } render(} schema={schema} />); expect(observedRuntime).toBeNull(); expect( document.querySelector("[data-toolcraft-pipeline-evidence]"), ).toBeNull(); }); it("isolates one immutable evidence bridge per provider lifecycle", async () => { const first = createRendererPipelineRegistration("first-pipeline-v1"); const second = createRendererPipelineRegistration("second-pipeline-v1"); const view = render( <> First} rendererPipelineRegistration={first} schema={schema} /> Second} rendererPipelineRegistration={second} schema={schema} /> , ); await act(() => Promise.resolve()); const evidence = document.querySelectorAll( "[data-toolcraft-pipeline-evidence]", ); expect(evidence).toHaveLength(2); expect( [...evidence].map((element) => element.getAttribute("data-toolcraft-pipeline-evidence"), ), ).toEqual(["first-pipeline-v1", "second-pipeline-v1"]); for (const element of evidence) { const getSnapshot = (element as HTMLElement & Record)[ Symbol.for("toolcraft.renderer-pipeline-evidence.snapshot") ]; const snapshot = (getSnapshot as () => { runtimeId: string })(); expect(snapshot.runtimeId).toBe( element.getAttribute("data-toolcraft-pipeline-evidence"), ); } view.unmount(); expect( document.querySelector("[data-toolcraft-pipeline-evidence]"), ).toBeNull(); }); });