// SimpleSyrup - workflow-focused ComfyUI extensions for image generation // Copyright (C) 2026 Artificial Sweetener and contributors // SPDX-License-Identifier: AGPL-3.0-or-later import { describe, expect, it, vi } from "vitest"; import { InteractiveInspectorController, type AsyncInspectorView, type PreparedInspectorState } from "../src/interactiveInspector"; describe("InteractiveInspectorController", () => { it("discards stale prepared documents and commits only the latest", async () => { const first = deferred(); const second = deferred(); const view = fakeView([first.promise, second.promise]); const controller = new InteractiveInspectorController(view, (output) => typeof output === "string" ? output : undefined ); const stale = preparedState(); const latest = preparedState(); controller.update("first"); controller.update("second"); second.resolve(latest); await vi.waitFor(() => { expect(latest.commitSpy).toHaveBeenCalledOnce(); }); first.resolve(stale); await vi.waitFor(() => { expect(stale.disposeSpy).toHaveBeenCalledOnce(); }); expect(stale.commitSpy).not.toHaveBeenCalled(); }); it("disposes committed state and blocks pending publication", async () => { const pending = deferred(); const view = fakeView([pending.promise]); const controller = new InteractiveInspectorController(view, () => "document"); const prepared = preparedState(); controller.update({}); controller.dispose(); pending.resolve(prepared); await vi.waitFor(() => { expect(prepared.disposeSpy).toHaveBeenCalledOnce(); }); expect(prepared.commitSpy).not.toHaveBeenCalled(); expect(view.disposeSpy).toHaveBeenCalledOnce(); }); }); interface TestInspectorView extends AsyncInspectorView { disposeSpy: ReturnType void>>; } function fakeView( preparations: Array> ): TestInspectorView { let index = 0; const disposeSpy = vi.fn<() => void>(); return { element: document.createElement("div"), setLoading: vi.fn(), prepare: vi.fn(() => { const preparation = preparations[index]; index += 1; if (!preparation) throw new Error("Missing test preparation."); return preparation; }), showError: vi.fn(), dispose: () => { disposeSpy(); }, disposeSpy }; } interface TestPreparedState extends PreparedInspectorState { commitSpy: ReturnType void>>; disposeSpy: ReturnType void>>; } function preparedState(): TestPreparedState { const commitSpy = vi.fn<() => void>(); const disposeSpy = vi.fn<() => void>(); return { commit: () => { commitSpy(); }, dispose: () => { disposeSpy(); }, commitSpy, disposeSpy }; } function deferred(): { promise: Promise; resolve: (value: T) => void; } { let resolver: ((value: T) => void) | undefined; const promise = new Promise((resolve) => { resolver = resolve; }); return { promise, resolve(value: T): void { if (!resolver) throw new Error("Deferred resolver was not initialized."); resolver(value); } }; }