import { act, render } from "@testing-library/react"; import * as React from "react"; import { describe, expect, it } from "vitest"; import type { ToolcraftModelRenderHost } from "./model-render-binding"; import { createLifetimeFixture, exportContext, exportRequest, } from "./model-render-lifetime.test-support"; import { ToolcraftModelRenderProvider, useToolcraftModelRenderHost, } from "./model-render-provider"; function Probe({ capture }: { capture(host: ToolcraftModelRenderHost): void }) { const host = useToolcraftModelRenderHost(); React.useEffect(() => capture(host), [capture, host]); return null; } describe("model provider export retention", () => { it.each(["unmount", "replace"] as const)( "waits for a paused renderer and host lease on real provider %s", async (operation) => { const original = createLifetimeFixture(); const replacement = createLifetimeFixture(); let current!: ToolcraftModelRenderHost; const capture = (host: ToolcraftModelRenderHost) => { current = host; }; const element = (fixture: typeof original) => ( ); const view = render(element(original)); await act(async () => {}); const oldHost = current; const release = oldHost.retain(); const outcome = oldHost.renderExport(exportRequest, exportContext()) .catch((error: unknown) => error); await original.entered.promise; if (operation === "unmount") view.unmount(); else view.rerender(element(replacement)); await act(async () => {}); expect(original.signals[0]!.aborted).toBe(true); expect(original.binding.dispose).not.toHaveBeenCalled(); expect(original.binding.disposePreparedPreview).not.toHaveBeenCalled(); expect(original.leases[0]!.root.children.length).toBeGreaterThan(0); original.pending.resolve(); expect(await outcome).toBe(original.signals[0]!.reason); expect(original.events).toEqual(["resource"]); release(); release(); expect(original.events).toEqual(["resource", "prepared"]); if (operation === "replace") { expect(current).not.toBe(oldHost); replacement.pending.resolve(); await current.renderExport(exportRequest, exportContext()); expect(replacement.binding.renderExport).toHaveBeenCalledTimes(1); view.unmount(); await act(async () => {}); expect(replacement.binding.disposePreparedPreview).toHaveBeenCalledTimes(1); } }, ); });