summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar <oskar@mullvad.net>2025-09-02 14:59:53 +0200
committerOskar <oskar@mullvad.net>2025-09-05 11:47:01 +0200
commitf61e1331a5e0a40e4d351c36f9688cc1ee2d3487 (patch)
tree9908d2df1c769ae67496d8cf5b75d41ab68d26c3
parent985152f6ac87396c8ddac80db28e3aa5658efa3b (diff)
downloadmullvadvpn-f61e1331a5e0a40e4d351c36f9688cc1ee2d3487.tar.xz
mullvadvpn-f61e1331a5e0a40e4d351c36f9688cc1ee2d3487.zip
Add new ipc utility for mocked tests
-rw-r--r--desktop/packages/mullvad-vpn/src/shared/ipc-helpers.ts27
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/mocked-utils.ts53
2 files changed, 59 insertions, 21 deletions
diff --git a/desktop/packages/mullvad-vpn/src/shared/ipc-helpers.ts b/desktop/packages/mullvad-vpn/src/shared/ipc-helpers.ts
index db219f4535..c4f2c87c83 100644
--- a/desktop/packages/mullvad-vpn/src/shared/ipc-helpers.ts
+++ b/desktop/packages/mullvad-vpn/src/shared/ipc-helpers.ts
@@ -21,15 +21,16 @@ interface RendererToMain<T, R> {
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
-type AnyIpcCall = MainToRenderer<any> | RendererToMain<any, any>;
+export type AnyIpcCall = MainToRenderer<any> | RendererToMain<any, any>;
export type Schema = Record<string, Record<string, AnyIpcCall>>;
// Renames all IPC calls, e.g. `callName` to either `notifyCallName` or `handleCallName` depending
// on direction.
-type IpcMainKey<N extends string, I extends AnyIpcCall> = I['direction'] extends 'main-to-renderer'
- ? `notify${Capitalize<N>}`
- : `handle${Capitalize<N>}`;
+export type IpcMainKey<
+ N extends string,
+ I extends AnyIpcCall,
+> = I['direction'] extends 'main-to-renderer' ? `notify${Capitalize<N>}` : `handle${Capitalize<N>}`;
// Selects either the send or receive function depending on direction.
type IpcMainFn<I extends AnyIpcCall> = I['direction'] extends 'main-to-renderer'
@@ -61,11 +62,6 @@ export type IpcRenderer<S extends Schema> = {
};
};
-// Transforms the provided schema into containing only the event keys.
-export type IpcEvents<S> = {
- [G in keyof S]: { [C in keyof S[G]]: string };
-};
-
// Preforms the transformation of the main event channel in accordance with the above types.
export function createIpcMain<S extends Schema>(
schema: S,
@@ -104,15 +100,10 @@ export function createIpcRenderer<S extends Schema>(
});
}
-export function createIpcEvents<S extends Schema>(schema: S): IpcEvents<S> {
- return createIpc(schema, (event, key) => [key, event]);
-}
-
-export function createIpc<
- S extends Schema,
- T,
- R extends IpcMain<S> | IpcRenderer<S> | IpcEvents<S>,
->(ipc: S, fn: (event: string, key: string, spec: AnyIpcCall) => [newKey: string, newValue: T]): R {
+export function createIpc<S extends Schema, T, R>(
+ ipc: S,
+ fn: (event: string, key: string, spec: AnyIpcCall) => [newKey: string, newValue: T],
+): R {
return Object.fromEntries(
Object.entries(ipc).map(([groupKey, group]) => {
const newGroup = Object.fromEntries(
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/mocked-utils.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/mocked-utils.ts
index 0290536418..530b2c48a7 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/mocked-utils.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/mocked-utils.ts
@@ -1,6 +1,6 @@
import { ElectronApplication } from 'playwright';
-import { createIpcEvents, IpcEvents } from '../../../src/shared/ipc-helpers';
+import { AnyIpcCall, createIpc, Schema } from '../../../src/shared/ipc-helpers';
import { IpcSchema, ipcSchema } from '../../../src/shared/ipc-schema';
import { startApp, TestUtils } from '../utils';
@@ -16,7 +16,7 @@ export interface MockedTestUtils extends TestUtils {
mockIpcHandle: MockIpcHandle;
sendMockIpcResponse: SendMockIpcResponse;
expectIpcCall: ExpectIpcCall;
- ipcEvents: IpcEvents<IpcSchema>;
+ ipc: IpcMockedTest<IpcSchema>;
}
export const startMockedApp = async (): Promise<StartMockedAppResponse> => {
@@ -41,7 +41,7 @@ export const startMockedApp = async (): Promise<StartMockedAppResponse> => {
sendMockIpcResponse,
expectIpcCall,
- ipcEvents: createIpcEvents(ipcSchema),
+ ipc: createTestIpc(startAppResult.app),
},
};
};
@@ -101,6 +101,10 @@ export const generateExpectIpcCall = (electronApp: ElectronApplication) => {
return new Promise<T>((resolve) => {
ipcMain.handleOnce(channel, (_event, arg) => {
resolve(arg);
+ return {
+ type: 'success',
+ value: null,
+ };
});
});
},
@@ -108,3 +112,46 @@ export const generateExpectIpcCall = (electronApp: ElectronApplication) => {
);
};
};
+
+type IpcMockedTestKey<I extends AnyIpcCall> = I['direction'] extends 'main-to-renderer'
+ ? 'notify'
+ : 'handle';
+
+type IpcMockedTestExpectKey<I extends AnyIpcCall> = I['direction'] extends 'main-to-renderer'
+ ? never
+ : 'expect';
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+type Async<F extends (...args: any) => any> = (arg: Parameters<F>[0]) => Promise<ReturnType<F>>;
+
+type IpcMockedTestFn<I extends AnyIpcCall> = I['direction'] extends 'main-to-renderer'
+ ? Async<NonNullable<ReturnType<I['send']>>>
+ : Async<Parameters<ReturnType<I['receive']>>[0]>;
+
+export type IpcMockedTest<S extends Schema> = {
+ [G in keyof S]: {
+ [K in keyof S[G]]: {
+ [C in IpcMockedTestKey<S[G][K]>]: IpcMockedTestFn<S[G][K]>;
+ } & {
+ [C in IpcMockedTestExpectKey<S[G][K]>]: () => Promise<void>;
+ } & {
+ eventKey: string;
+ };
+ };
+};
+
+export function createTestIpc(electronApp: ElectronApplication): IpcMockedTest<IpcSchema> {
+ return createIpc(ipcSchema, (event, key, _spec) => {
+ return [
+ key,
+ {
+ eventKey: event,
+ notify: <T>(message: T) =>
+ generateSendMockIpcResponse(electronApp)({ channel: event, response: message }),
+ handle: <T>(response: T) =>
+ generateMockIpcHandle(electronApp)({ channel: event, response }),
+ expect: () => generateExpectIpcCall(electronApp)(event),
+ },
+ ];
+ });
+}