Rust backend: SQLite (WAL mode, 8 tables), vault encryption (Argon2id + AES-256-GCM), settings/connections/credentials services, 19 Tauri command wrappers. 46/46 tests passing. Vue 3 frontend: unlock/create vault flow, Pinia app store, Tailwind CSS v4 dark theme with Wraith branding. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
298 lines
9.6 KiB
JavaScript
298 lines
9.6 KiB
JavaScript
'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;
|