'use strict'; // Copyright 2019-2024 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT function mockInternals() { var _a, _b; window.__TAURI_INTERNALS__ = (_a = window.__TAURI_INTERNALS__) !== null && _a !== void 0 ? _a : {}; window.__TAURI_EVENT_PLUGIN_INTERNALS__ = (_b = window.__TAURI_EVENT_PLUGIN_INTERNALS__) !== null && _b !== void 0 ? _b : {}; } /** * Intercepts all IPC requests with the given mock handler. * * This function can be used when testing tauri frontend applications or when running the frontend in a Node.js context during static site generation. * * # Examples * * Testing setup using Vitest: * ```ts * import { mockIPC, clearMocks } from "@tauri-apps/api/mocks" * import { invoke } from "@tauri-apps/api/core" * * afterEach(() => { * clearMocks() * }) * * test("mocked command", () => { * mockIPC((cmd, payload) => { * switch (cmd) { * case "add": * return (payload.a as number) + (payload.b as number); * default: * break; * } * }); * * expect(invoke('add', { a: 12, b: 15 })).resolves.toBe(27); * }) * ``` * * The callback function can also return a Promise: * ```js * import { mockIPC, clearMocks } from "@tauri-apps/api/mocks" * import { invoke } from "@tauri-apps/api/core" * * afterEach(() => { * clearMocks() * }) * * test("mocked command", () => { * mockIPC((cmd, payload) => { * if(cmd === "get_data") { * return fetch("https://example.com/data.json") * .then((response) => response.json()) * } * }); * * expect(invoke('get_data')).resolves.toBe({ foo: 'bar' }); * }) * ``` * * `listen` can also be mocked with direct calls to the `emit` function. This functionality is opt-in via the `shouldMockEvents` option: * ```js * import { mockIPC, clearMocks } from "@tauri-apps/api/mocks" * import { emit, listen } from "@tauri-apps/api/event" * * afterEach(() => { * clearMocks() * }) * * test("mocked event", () => { * mockIPC(() => {}, { shouldMockEvents: true }); // enable event mocking * * const eventHandler = vi.fn(); * listen('test-event', eventHandler); // typically in component setup or similar * * emit('test-event', { foo: 'bar' }); * expect(eventHandler).toHaveBeenCalledWith({ * event: 'test-event', * payload: { foo: 'bar' } * }); * }) * ``` * `emitTo` is currently **not** supported by this mock implementation. * * @since 1.0.0 */ function mockIPC(cb, options) { mockInternals(); function isEventPluginInvoke(cmd) { return cmd.startsWith('plugin:event|'); } function handleEventPlugin(cmd, args) { switch (cmd) { case 'plugin:event|listen': return handleListen(args); case 'plugin:event|emit': return handleEmit(args); case 'plugin:event|unlisten': return handleRemoveListener(args); } } const listeners = new Map(); function handleListen(args) { if (!listeners.has(args.event)) { listeners.set(args.event, []); } listeners.get(args.event).push(args.handler); return args.handler; } function handleEmit(args) { const eventListeners = listeners.get(args.event) || []; for (const handler of eventListeners) { runCallback(handler, args); } return null; } function handleRemoveListener(args) { const eventListeners = listeners.get(args.event); if (eventListeners) { const index = eventListeners.indexOf(args.id); if (index !== -1) { eventListeners.splice(index, 1); } } } // eslint-disable-next-line @typescript-eslint/require-await async function invoke(cmd, args, _options) { if ((options === null || options === void 0 ? void 0 : options.shouldMockEvents) && isEventPluginInvoke(cmd)) { return handleEventPlugin(cmd, args); } return cb(cmd, args); } const callbacks = new Map(); function registerCallback(callback, once = false) { const identifier = window.crypto.getRandomValues(new Uint32Array(1))[0]; callbacks.set(identifier, (data) => { if (once) { unregisterCallback(identifier); } return callback && callback(data); }); return identifier; } function unregisterCallback(id) { callbacks.delete(id); } function runCallback(id, data) { const callback = callbacks.get(id); if (callback) { callback(data); } else { // eslint-disable-next-line no-console console.warn(`[TAURI] Couldn't find callback id ${id}. This might happen when the app is reloaded while Rust is running an asynchronous operation.`); } } function unregisterListener(event, id) { unregisterCallback(id); } window.__TAURI_INTERNALS__.invoke = invoke; window.__TAURI_INTERNALS__.transformCallback = registerCallback; window.__TAURI_INTERNALS__.unregisterCallback = unregisterCallback; window.__TAURI_INTERNALS__.runCallback = runCallback; window.__TAURI_INTERNALS__.callbacks = callbacks; window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener = unregisterListener; } /** * Mocks one or many window labels. * In non-tauri context it is required to call this function *before* using the `@tauri-apps/api/window` module. * * This function only mocks the *presence* of windows, * window properties (e.g. width and height) can be mocked like regular IPC calls using the `mockIPC` function. * * # Examples * * ```js * import { mockWindows } from "@tauri-apps/api/mocks"; * import { getCurrentWindow } from "@tauri-apps/api/window"; * * mockWindows("main", "second", "third"); * * const win = getCurrentWindow(); * * win.label // "main" * ``` * * ```js * import { mockWindows } from "@tauri-apps/api/mocks"; * * mockWindows("main", "second", "third"); * * mockIPC((cmd, args) => { * if (cmd === "plugin:event|emit") { * console.log('emit event', args?.event, args?.payload); * } * }); * * const { emit } = await import("@tauri-apps/api/event"); * await emit('loaded'); // this will cause the mocked IPC handler to log to the console. * ``` * * @param current Label of window this JavaScript context is running in. * * @since 1.0.0 */ function mockWindows(current, ..._additionalWindows) { mockInternals(); window.__TAURI_INTERNALS__.metadata = { currentWindow: { label: current }, currentWebview: { windowLabel: current, label: current } }; } /** * Mock `convertFileSrc` function * * * @example * ```js * import { mockConvertFileSrc } from "@tauri-apps/api/mocks"; * import { convertFileSrc } from "@tauri-apps/api/core"; * * mockConvertFileSrc("windows") * * const url = convertFileSrc("C:\\Users\\user\\file.txt") * ``` * * @param osName The operating system to mock, can be one of linux, macos, or windows * * @since 1.6.0 */ function mockConvertFileSrc(osName) { mockInternals(); window.__TAURI_INTERNALS__.convertFileSrc = function (filePath, protocol = 'asset') { const path = encodeURIComponent(filePath); return osName === 'windows' ? `http://${protocol}.localhost/${path}` : `${protocol}://localhost/${path}`; }; } /** * Clears mocked functions/data injected by the other functions in this module. * When using a test runner that doesn't provide a fresh window object for each test, calling this function will reset tauri specific properties. * * # Example * * ```js * import { mockWindows, clearMocks } from "@tauri-apps/api/mocks" * * afterEach(() => { * clearMocks() * }) * * test("mocked windows", () => { * mockWindows("main", "second", "third"); * * expect(window.__TAURI_INTERNALS__).toHaveProperty("metadata") * }) * * test("no mocked windows", () => { * expect(window.__TAURI_INTERNALS__).not.toHaveProperty("metadata") * }) * ``` * * @since 1.0.0 */ function clearMocks() { if (typeof window.__TAURI_INTERNALS__ !== 'object') { return; } // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case delete window.__TAURI_INTERNALS__.invoke; // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case delete window.__TAURI_INTERNALS__.transformCallback; // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case delete window.__TAURI_INTERNALS__.unregisterCallback; // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case delete window.__TAURI_INTERNALS__.runCallback; // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case delete window.__TAURI_INTERNALS__.callbacks; // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case delete window.__TAURI_INTERNALS__.convertFileSrc; // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case delete window.__TAURI_INTERNALS__.metadata; if (typeof window.__TAURI_EVENT_PLUGIN_INTERNALS__ !== 'object') { return; } // @ts-expect-error "The operand of a 'delete' operator must be optional." does not matter in this case delete window.__TAURI_EVENT_PLUGIN_INTERNALS__.unregisterListener; } exports.clearMocks = clearMocks; exports.mockConvertFileSrc = mockConvertFileSrc; exports.mockIPC = mockIPC; exports.mockWindows = mockWindows;