Skip to content

Commit 7e04c07

Browse files
feat: add mock functions for testing and SSG (#3437)
Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.studio>
1 parent 49955ea commit 7e04c07

2 files changed

Lines changed: 151 additions & 0 deletions

File tree

.changes/mock-functions.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri": minor
3+
---
4+
5+
Provide functions to mock IPC calls during testing and static site generation.

tooling/api/src/mocks.ts

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
interface IPCMessage {
2+
cmd: string
3+
callback: number
4+
error: number
5+
[key: string]: unknown
6+
}
7+
8+
/**
9+
* Intercepts all IPC requests with the given mock handler.
10+
*
11+
* This function can be used when testing tauri frontend applications or when running the frontend in a Node.js context during static site generation.
12+
*
13+
* # Examples
14+
*
15+
* Testing setup using vitest:
16+
* ```js
17+
* import { mockIPC, clearMocks } from "@tauri-apps/api/mocks"
18+
* import { invoke } from "@tauri-apps/api/tauri"
19+
*
20+
* afterEach(() => {
21+
* clearMocks()
22+
* })
23+
*
24+
* test("mocked command", () => {
25+
* mockIPC((cmd, args) => {
26+
* switch (cmd) {
27+
* case "add":
28+
* return (args.a as number) + (args.b as number);
29+
* default:
30+
* break;
31+
* }
32+
* });
33+
*
34+
* expect(invoke('add', { a: 12, b: 15 })).resolves.toBe(27);
35+
* })
36+
* ```
37+
*
38+
* @param cb
39+
*/
40+
export function mockIPC(
41+
cb: (cmd: string, args: Record<string, unknown>) => any
42+
): void {
43+
window.__TAURI_IPC__ = async ({
44+
cmd,
45+
callback,
46+
error,
47+
...args
48+
}: IPCMessage) => {
49+
try {
50+
// @ts-expect-error The function key is dynamic and therefore not typed
51+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
52+
window[`_${callback}`](await cb(cmd, args))
53+
} catch (err) {
54+
// @ts-expect-error The function key is dynamic and therefore not typed
55+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
56+
window[`_${error}`](err)
57+
}
58+
}
59+
}
60+
61+
/**
62+
* Mocks one or many window labels.
63+
* In non-tauri context it is required to call this function *before* using the `@tauri-apps/api/window` module.
64+
*
65+
* This function only mocks the *presence* of windows,
66+
* window properties (e.g. width and height) can be mocked like regular IPC calls using the `mockIPC` function.
67+
*
68+
* # Examples
69+
*
70+
* ```js
71+
* import { mockWindows } from "@tauri-apps/api/mocks";
72+
* import { getCurrent } from "@tauri-apps/api/window";
73+
*
74+
* mockWindows("main", "second", "third");
75+
*
76+
* const win = getCurrent();
77+
*
78+
* win.label // "main"
79+
* ```
80+
*
81+
* ```js
82+
* import { mockWindows } from "@tauri-apps/api/mocks";
83+
*
84+
* mockWindows("main", "second", "third");
85+
*
86+
* mockIPC((cmd, args) => {
87+
* if (cmd === "tauri") {
88+
* if (
89+
* args?.__tauriModule === "Window" &&
90+
* args?.message?.cmd === "manage" &&
91+
* args?.message?.data?.cmd?.type === "close"
92+
* ) {
93+
* console.log('closing window!');
94+
* }
95+
* }
96+
* });
97+
*
98+
* const { getCurrent } = await import("@tauri-apps/api/window");
99+
*
100+
* const win = getCurrent();
101+
* await win.close(); // this will cause the mocked IPC handler to log to the console.
102+
* ```
103+
*
104+
* @param current Label of window this JavaScript context is running in.
105+
* @param additionalWindows Label of additional windows the app has.
106+
*/
107+
export function mockWindows(
108+
current: string,
109+
...additionalWindows: string[]
110+
): void {
111+
window.__TAURI_METADATA__ = {
112+
__windows: [current, ...additionalWindows].map((label) => ({ label })),
113+
__currentWindow: { label: current }
114+
}
115+
}
116+
117+
/**
118+
* Clears mocked functions/data injected by the other functions in this module.
119+
* When using a test runner that doesn't provide a fresh window object for each test, calling this function will reset tauri specific properties.
120+
*
121+
* # Example
122+
*
123+
* ```js
124+
* import { mockWindows, clearMocks } from "@tauri-apps/api/mocks"
125+
*
126+
* afterEach(() => {
127+
* clearMocks()
128+
* })
129+
*
130+
* test("mocked windows", () => {
131+
* mockWindows("main", "second", "third");
132+
*
133+
* expect(window).toHaveProperty("__TAURI_METADATA__")
134+
* })
135+
*
136+
* test("no mocked windows", () => {
137+
* expect(window).not.toHaveProperty("__TAURI_METADATA__")
138+
* })
139+
* ```
140+
*/
141+
export function clearMocks(): void {
142+
// @ts-expect-error
143+
delete window.__TAURI_IPC__
144+
// @ts-expect-error
145+
delete window.__TAURI_METADATA__
146+
}

0 commit comments

Comments
 (0)