/* Copyright 2026 Marimo. All rights reserved. */ /* oxlint-disable no-console -- for debugging */ import { type Mock, vi } from "vitest"; import { invariant } from "@/utils/invariant"; interface MockLogger { debug: Mock; log: Mock; warn: Mock; error: Mock; trace: Mock; get: Mock; disabled: Mock; } // Common mock factories export const Mocks = { quietLogger: (): MockLogger => ({ debug: vi.fn(), log: vi.fn(), warn: vi.fn(), error: vi.fn(), trace: vi.fn(), get: vi.fn().mockImplementation(() => Mocks.quietLogger()), disabled: vi.fn(), }), logger: (): MockLogger => ({ debug: vi.fn().mockImplementation(console.debug), log: vi.fn().mockImplementation(console.log), warn: vi.fn().mockImplementation(console.warn), error: vi.fn().mockImplementation(console.error), trace: vi.fn().mockImplementation(console.trace), get: vi.fn().mockImplementation(() => Mocks.logger()), disabled: vi.fn(), }), toast: vi.fn(), clipboard: () => ({ write: vi.fn(), writeText: vi.fn(), read: vi.fn(), readText: vi.fn(), }), clipboardItem: (data: Record) => ({ types: Object.keys(data), getType: vi .fn() .mockImplementation((type: string) => Promise.resolve(new Blob([data[type]?.toString() || ""], { type })), ), supports: vi.fn().mockReturnValue(true), }), blob: (parts?: string[], options?: { type?: string }) => ({ text: () => Promise.resolve(parts?.[0] || ""), type: options?.type || "text/plain", }), event: (props: Partial = {}) => ({ preventDefault: vi.fn(), continuePropagation: vi.fn(), target: document.createElement("div"), currentTarget: document.createElement("div"), stopPropagation: vi.fn(), ...props, }), keyboardEvent: ( props: Partial = {}, ): React.KeyboardEvent & { continuePropagation: () => void; } => Mocks.event({ ...props, }) as unknown as React.KeyboardEvent & { continuePropagation: () => void; }, }; // Common mock modules for vi.mock() calls export const MockModules = { toast: () => ({ toast: vi.fn() }), toastWithControls: () => { const dismiss = vi.fn(); const update = vi.fn(); return { mock: { toast: vi.fn(() => ({ dismiss, update })) }, dismiss, update, }; }, }; // Global mock setup functions export const SetupMocks = { resizeObserver: () => { const observe = vi.fn(); const unobserve = vi.fn(); const disconnect = vi.fn(); global.ResizeObserver = class MockResizeObserver { observe = observe; unobserve = unobserve; disconnect = disconnect; } as unknown as typeof ResizeObserver; return { observe, unobserve, disconnect }; }, clipboard: (mockClipboard = Mocks.clipboard()) => { Object.defineProperty(navigator, "clipboard", { value: mockClipboard, writable: true, }); // Mock ClipboardItem global.ClipboardItem = Object.assign( vi.fn().mockImplementation((data) => Mocks.clipboardItem(data)), { supports: vi.fn().mockReturnValue(true) }, ) as unknown as typeof ClipboardItem; // Mock Blob global.Blob = vi .fn() .mockImplementation((parts, options) => Mocks.blob(parts, options)); return mockClipboard; }, localStorage: () => { const store: Record = {}; Object.defineProperty(window, "localStorage", { value: { getItem: vi.fn((key: string) => store[key] || null), setItem: vi.fn((key: string, value: string) => { store[key] = value; }), removeItem: vi.fn((key: string) => { // oxlint-disable-next-line typescript/no-dynamic-delete delete store[key]; }), clear: vi.fn(() => { for (const key of Object.keys(store)) { // oxlint-disable-next-line typescript/no-dynamic-delete delete store[key]; } }), key: vi.fn((index: number) => Object.keys(store)[index] || null), get length() { return Object.keys(store).length; }, }, writable: true, }); }, fetch: (mockImplementation?: Mock) => { const mockFetch = vi.fn(mockImplementation); global.fetch = mockFetch; return mockFetch; }, websocket: () => { const mockWs = { close: vi.fn(), send: vi.fn(), addEventListener: vi.fn(), removeEventListener: vi.fn(), onopen: null, onclose: null, onmessage: null, onerror: null, readyState: WebSocket.CONNECTING, }; global.WebSocket = vi.fn(() => mockWs) as unknown as typeof WebSocket; return mockWs; }, }; // oxlint-disable-next-line typescript/no-explicit-any export function asMock unknown>(fn: T): Mock { invariant( "mock" in fn, "fn must be a mock function, use vi.fn() to create one", ); return fn as unknown as Mock; } export function partialImplementation( partial: Partial, ): T { return partial as unknown as T; }