/* Copyright 2026 Marimo. All rights reserved. */ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; const { mockNotebookReadFile, rpcListeners } = vi.hoisted(() => ({ mockNotebookReadFile: vi.fn(), rpcListeners: {} as Record void>, })); // Mock browser globals before any imports vi.stubGlobal("crossOriginIsolated", false); vi.stubGlobal( "Worker", vi.fn(() => ({ addEventListener: vi.fn(), postMessage: vi.fn(), terminate: vi.fn(), })), ); class MockURL extends URL { static override createObjectURL = vi.fn(() => "blob:mock-url"); static override revokeObjectURL = vi.fn(); } vi.stubGlobal("URL", MockURL); vi.mock("@/core/wasm/rpc", () => ({ getWorkerRPC: () => ({ proxy: { request: { bridge: vi.fn(), startSession: vi.fn(), readFile: vi.fn(), readNotebook: vi.fn(), saveNotebook: vi.fn(), }, send: { consumerReady: vi.fn() }, }, addMessageListener: (event: string, cb: () => void) => { rpcListeners[event] = cb; }, }), })); vi.mock("@/core/meta/globals", () => ({ getMarimoVersion: () => "0.0.0-test", })); vi.mock("@/core/wasm/utils", () => ({ isWasm: () => true, })); vi.mock("@/core/wasm/store", () => ({ fallbackFileStore: { readFile: vi.fn(), saveFile: vi.fn() }, notebookFileStore: { readFile: mockNotebookReadFile, saveFile: vi.fn() }, })); // Import after all mocks are set up import { store } from "@/core/state/jotai"; import { initialModeAtom } from "@/core/mode"; import { PyodideBridge } from "../bridge"; // Access INSTANCE once at module level so the constructor runs (and // addMessageListener populates rpcListeners) before any test executes. void PyodideBridge.INSTANCE; describe("PyodideBridge.readCode", () => { beforeEach(() => { vi.clearAllMocks(); }); afterEach(() => { store.set(initialModeAtom, undefined); }); it("reads from notebookFileStore in read mode", async () => { store.set(initialModeAtom, "read"); // Trigger getSaveWorker — it reads the current mode and returns a stub // whose readNotebook delegates to notebookFileStore. rpcListeners.initialized(); mockNotebookReadFile.mockResolvedValue( "import numpy as np\nprint(np.__version__)", ); const result = await PyodideBridge.INSTANCE.readCode(); expect(result).toEqual({ contents: "import numpy as np\nprint(np.__version__)", }); expect(mockNotebookReadFile).toHaveBeenCalledTimes(1); }); it("returns empty string when notebookFileStore has no code in read mode", async () => { store.set(initialModeAtom, "read"); rpcListeners.initialized(); mockNotebookReadFile.mockResolvedValue(null); const result = await PyodideBridge.INSTANCE.readCode(); expect(result).toEqual({ contents: "" }); }); it("does not call notebookFileStore in edit mode", async () => { store.set(initialModeAtom, "edit"); // getSaveWorker in edit mode creates a real worker (mocked); readNotebook // goes to the RPC proxy, not notebookFileStore. rpcListeners.initialized(); await PyodideBridge.INSTANCE.readCode(); expect(mockNotebookReadFile).not.toHaveBeenCalled(); }); });