import type { PreviewViewportSetting } from "@t3tools/contracts"; import { describe, expect, it, vi } from "vite-plus/test"; import { BROWSER_VIEWPORT_COMMIT_TIMEOUT_MS, commitBrowserViewportChange, runBrowserViewportMutation, subscribeBrowserViewportChange, } from "./browserViewportActions"; describe("browserViewportActions", () => { it("routes drag commits to the visible tab handler and cleans up exactly that handler", async () => { const first = vi.fn(async () => undefined); const second = vi.fn(async () => undefined); const unsubscribeFirst = subscribeBrowserViewportChange("tab-1", first); const unsubscribeSecond = subscribeBrowserViewportChange("tab-1", second); unsubscribeFirst(); await commitBrowserViewportChange("tab-1", { _tag: "freeform", width: 900, height: 700, }); expect(first).not.toHaveBeenCalled(); expect(second).toHaveBeenCalledWith({ _tag: "freeform", width: 900, height: 700 }); unsubscribeSecond(); await expect( commitBrowserViewportChange("tab-1", { _tag: "freeform", width: 800, height: 600, }), ).rejects.toThrow("No visible browser viewport handler"); }); it("commits viewport changes in order for each tab", async () => { let releaseFirst: (() => void) | undefined; let markFirstStarted: (() => void) | undefined; const firstPending = new Promise((resolve) => { releaseFirst = resolve; }); const firstStarted = new Promise((resolve) => { markFirstStarted = resolve; }); const calls: Array = []; const unsubscribe = subscribeBrowserViewportChange("tab-serial", async (setting) => { if (setting._tag === "fill") return; calls.push(setting.width); if (setting.width === 800) { markFirstStarted?.(); await firstPending; } }); const first = commitBrowserViewportChange("tab-serial", { _tag: "freeform", width: 800, height: 600, }); const second = commitBrowserViewportChange("tab-serial", { _tag: "freeform", width: 900, height: 700, }); await firstStarted; expect(calls).toEqual([800]); releaseFirst?.(); await Promise.all([first, second]); expect(calls).toEqual([800, 900]); unsubscribe(); }); it("serializes background mutations with visible viewport commits", async () => { let releaseBackground: (() => void) | undefined; const backgroundPending = new Promise((resolve) => { releaseBackground = resolve; }); const calls: string[] = []; const background = runBrowserViewportMutation("tab-shared", async () => { calls.push("background"); await backgroundPending; }); const unsubscribe = subscribeBrowserViewportChange("tab-shared", async () => { calls.push("visible"); }); const visible = commitBrowserViewportChange("tab-shared", { _tag: "freeform", width: 900, height: 700, }); await vi.waitFor(() => expect(calls).toEqual(["background"])); releaseBackground?.(); await Promise.all([background, visible]); expect(calls).toEqual(["background", "visible"]); unsubscribe(); }); it("does not let a timed-out handler overtake a newer viewport commit", async () => { vi.useFakeTimers(); try { let releaseFirst: (() => void) | undefined; const delayed = new Promise((resolve) => { releaseFirst = resolve; }); const handler = vi.fn(async (_setting: PreviewViewportSetting): Promise => undefined); handler.mockImplementationOnce(() => delayed).mockResolvedValueOnce(undefined); const unsubscribe = subscribeBrowserViewportChange("tab-timeout", handler); const first = commitBrowserViewportChange("tab-timeout", { _tag: "freeform", width: 800, height: 600, }); const firstResult = expect(first).rejects.toThrow( "Timed out committing the browser viewport for tab tab-timeout", ); const second = commitBrowserViewportChange("tab-timeout", { _tag: "freeform", width: 900, height: 700, }); await vi.advanceTimersByTimeAsync(BROWSER_VIEWPORT_COMMIT_TIMEOUT_MS); await firstResult; expect(handler).toHaveBeenCalledTimes(1); releaseFirst?.(); await second; expect(handler).toHaveBeenCalledTimes(2); expect(handler.mock.calls[1]?.[0]).toMatchObject({ width: 900, height: 700 }); unsubscribe(); } finally { vi.useRealTimers(); } }); });