import { act, renderHook, waitFor } from "@testing-library/react"; import { beforeEach, describe, expect, it, vi } from "vitest"; import type { RuntimeSnapshot, Workspace, WorkspaceSummary } from "./types"; const mocks = vi.hoisted(() => ({ invoke: vi.fn(), listeners: {} as Record void>, listen: vi.fn(), })); vi.mock("@tauri-apps/api/core", () => ({ invoke: mocks.invoke })); vi.mock("@tauri-apps/api/event", () => ({ listen: mocks.listen })); vi.mock("@tauri-apps/api/window", () => ({ getCurrentWindow: () => ({}), })); vi.mock("./persistence", () => ({ legacyPreferences: () => ({}), loadPresentation: async () => ({ version: 1, directoryOrder: [], runtimeOrderByDirectory: {}, drafts: {}, scroll: {}, lastSeen: {}, }), presentationState: vi.fn(), removeLegacyPreferences: vi.fn(), savePresentation: vi.fn().mockResolvedValue(undefined), })); import { useWorkspace } from "./useWorkspace"; const normalRuntime = { runtimeId: "normal", worktreePath: "/repo", state: "idle", label: "Normal", attention: false, queueCount: 0, lastActivity: "now", openedAt: "now", }; const quickRuntime = { ...normalRuntime, runtimeId: "quick", label: "Quick" }; function workspace(runtimes: (typeof normalRuntime)[]): Workspace { return { bridgeInstanceId: "bridge", latestSeq: 1, directories: [ { worktreePath: "/repo", isHome: true, openCount: runtimes.length, workingCount: 0, attentionCount: 0, recoveringCount: 0, errorCount: 0, runtimes, }, ], }; } const summary: WorkspaceSummary = { bridgeInstanceId: "bridge", latestSeq: 1, openCount: 1, workingCount: 0, attentionCount: 0, recoveringCount: 0, errorCount: 0, directoryCount: 1, resourceWarning: false, }; function snapshot(runtime: typeof normalRuntime): RuntimeSnapshot { return { bridgeInstanceId: "bridge", latestSeq: 1, runtime, extensions: [], }; } describe("useWorkspace Quick runtime isolation", () => { beforeEach(() => { vi.clearAllMocks(); for (const key of Object.keys(mocks.listeners)) delete mocks.listeners[key]; mocks.listen.mockImplementation((event, listener) => { mocks.listeners[event] = listener; return Promise.resolve(() => delete mocks.listeners[event]); }); }); it("ignores unknown Quick events and exposes promoted runtime once", async () => { let promoted = false; mocks.invoke.mockImplementation( (command: string, payload?: { runtimeId?: string }) => { if (command === "get_workspace") return Promise.resolve( workspace(promoted ? [normalRuntime, quickRuntime] : [normalRuntime]), ); if (command === "get_workspace_summary") return Promise.resolve(summary); if (command === "get_session_runtime_snapshot") return Promise.resolve( snapshot(payload?.runtimeId === "quick" ? quickRuntime : normalRuntime), ); if (command === "get_pending_promoted_runtime") return Promise.resolve(null); return Promise.resolve(undefined); }, ); const { result } = renderHook(() => useWorkspace()); await waitFor(() => expect(result.current.state.runtimesById.normal).toBeDefined(), ); await waitFor(() => expect(mocks.listeners["workspace-bridge"]).toBeDefined(), ); const workspaceCalls = () => mocks.invoke.mock.calls.filter(([command]) => command === "get_workspace"); await act(async () => { mocks.listeners["workspace-bridge"]({ payload: { kind: "event", event: { bridgeInstanceId: "bridge", seq: 2, type: "runtime_event", data: { runtimeId: "quick", runtime: quickRuntime, eventType: "transcript", eventData: { event: { type: "message_end" } }, }, }, }, }); }); expect(result.current.state.runtimesById.quick).toBeUndefined(); expect(workspaceCalls()).toHaveLength(1); promoted = true; await act(async () => { mocks.listeners["workspace-promoted-runtime"]({ payload: { runtimeId: "quick" }, }); }); await waitFor(() => expect(result.current.state.runtimesById.quick).toBeDefined(), ); expect(result.current.state.runtimeOrderByDirectory["/repo"]).toEqual([ "normal", "quick", ]); expect(result.current.state.selectedRuntimeId).toBe("quick"); expect(workspaceCalls()).toHaveLength(2); }); });