summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar <oskar@mullvad.net>2025-09-05 11:47:10 +0200
committerOskar <oskar@mullvad.net>2025-09-05 11:47:10 +0200
commit5fa06507df5386a970a44ed14f71e7a289657b99 (patch)
tree5bcffd2a07f6d3fac68ec05ad5d08dbbbf45b81d
parent22eb93188789974397554453b52a2f265989535d (diff)
parent6ba919b6d0f594998259180489e30f79513420ef (diff)
downloadmullvadvpn-5fa06507df5386a970a44ed14f71e7a289657b99.tar.xz
mullvadvpn-5fa06507df5386a970a44ed14f71e7a289657b99.zip
Merge branch 'improve-mocked-tests'
-rw-r--r--.github/workflows/frontend.yml4
-rw-r--r--desktop/packages/mullvad-vpn/src/shared/ipc-helpers.ts31
-rw-r--r--desktop/packages/mullvad-vpn/src/shared/utility-types.ts3
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade/app-upgrade.spec.ts55
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade/helpers.ts41
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/expired-account-error-view.spec.ts11
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/feature-indicators.spec.ts59
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/launch.spec.ts (renamed from desktop/packages/mullvad-vpn/test/e2e/mocked/launch/launch.spec.ts)13
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/launch/ipc.ts17
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/login.spec.ts29
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/mocked-utils.ts175
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/notifications.spec.ts51
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/helpers.ts21
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/select-location.spec.ts11
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/settings.spec.ts16
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/tunnel-state.spec.ts55
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/setup/main.ts4
17 files changed, 265 insertions, 331 deletions
diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml
index b410fa6620..25fb01f3dd 100644
--- a/.github/workflows/frontend.yml
+++ b/.github/workflows/frontend.yml
@@ -68,10 +68,10 @@ jobs:
working-directory: desktop
# The sandbox is disabled as a workaround for lacking userns permisisons which is required
# since Ubuntu 24.04.
- run: NO_SANDBOX=1 npm run e2e:no-build -w mullvad-vpn
+ run: NO_SANDBOX=1 npm run e2e:no-build -w mullvad-vpn -- --reporter=line
- name: Run Playwright tests on Windows/macOS
if: runner.os != 'Linux'
working-directory: desktop
shell: bash
- run: npm run e2e:no-build --w mullvad-vpn
+ run: npm run e2e:no-build --w mullvad-vpn -- --reporter=line
diff --git a/desktop/packages/mullvad-vpn/src/shared/ipc-helpers.ts b/desktop/packages/mullvad-vpn/src/shared/ipc-helpers.ts
index db219f4535..559b48c22b 100644
--- a/desktop/packages/mullvad-vpn/src/shared/ipc-helpers.ts
+++ b/desktop/packages/mullvad-vpn/src/shared/ipc-helpers.ts
@@ -16,20 +16,22 @@ interface MainToRenderer<T> {
interface RendererToMain<T, R> {
direction: 'renderer-to-main';
+ type: 'send' | 'invoke';
send: (event: string, ipcRenderer: EIpcRenderer) => Sender<T, R>;
receive: (event: string, ipcMain: EIpcMain) => Handler<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 +63,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 +101,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(
@@ -127,6 +119,7 @@ export function createIpc<
export function send<T>(): RendererToMain<T, void> {
return {
direction: 'renderer-to-main',
+ type: 'send',
send: (event, ipcRenderer) => (newValue: T) => ipcRenderer.send(event, newValue),
receive: (event, ipcMain) => (handlerFn: (value: T) => void) => {
ipcMain.on(event, (_event, newValue: T) => {
@@ -140,6 +133,7 @@ export function send<T>(): RendererToMain<T, void> {
export function invokeSync<T, R>(): RendererToMain<T, R> {
return {
direction: 'renderer-to-main',
+ type: 'send',
send: (event, ipcRenderer) => (newValue: T) => ipcRenderer.sendSync(event, newValue),
receive: (event, ipcMain) => (handlerFn: (value: T) => R) => {
ipcMain.on(event, (ipcEvent, newValue: T) => {
@@ -153,6 +147,7 @@ export function invokeSync<T, R>(): RendererToMain<T, R> {
export function invoke<T, R>(): RendererToMain<T, Promise<R>> {
return {
direction: 'renderer-to-main',
+ type: 'invoke',
send: invokeImpl,
receive: handle,
};
diff --git a/desktop/packages/mullvad-vpn/src/shared/utility-types.ts b/desktop/packages/mullvad-vpn/src/shared/utility-types.ts
new file mode 100644
index 0000000000..1d6c41992f
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/src/shared/utility-types.ts
@@ -0,0 +1,3 @@
+export type Async<F extends (...args: unknown[]) => unknown> = (
+ ...args: Parameters<F>
+) => Promise<ReturnType<F>>;
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade/app-upgrade.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade/app-upgrade.spec.ts
index 6015f1b4fe..d06405428e 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade/app-upgrade.spec.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade/app-upgrade.spec.ts
@@ -3,12 +3,17 @@ import { Page } from 'playwright';
import { RoutePath } from '../../../../src/shared/routes';
import { MockedTestUtils, startMockedApp } from '../mocked-utils';
-import { createHelpers, createIpc, createSelectors, mockData, resolveIpcHandle } from './helpers';
+import {
+ createAppUpgradeEventIpcHelper,
+ createHelpers,
+ createSelectors,
+ mockData,
+} from './helpers';
let page: Page;
let util: MockedTestUtils;
let helpers: ReturnType<typeof createHelpers>;
-let ipc: ReturnType<typeof createIpc>;
+let upgradeEventIpc: ReturnType<typeof createAppUpgradeEventIpcHelper>;
let selectors: ReturnType<typeof createSelectors>;
test.describe('App upgrade', () => {
@@ -20,12 +25,12 @@ test.describe('App upgrade', () => {
({ page, util } = await startMockedApp());
helpers = createHelpers(page, util);
- ipc = createIpc(util);
+ upgradeEventIpc = createAppUpgradeEventIpcHelper(util);
selectors = createSelectors(page);
await util.waitForRoute(RoutePath.main);
- await ipc.send.upgradeVersion({
+ await util.ipc.upgradeVersion[''].notify({
supported: true,
suggestedIsBeta: false,
suggestedUpgrade: {
@@ -34,6 +39,8 @@ test.describe('App upgrade', () => {
},
});
+ await util.ipc.app.getUpgradeCacheDir.ignore();
+
await page.click('button[aria-label="Settings"]');
await util.waitForRoute(RoutePath.settings);
await page.getByRole('button', { name: 'App info' }).click();
@@ -96,7 +103,7 @@ test.describe('App upgrade', () => {
test('Should show download progress after receiving event', async () => {
const mockedProgress = 90;
- await ipc.send.appUpgradeEventDownloadProgress({
+ await upgradeEventIpc.send.appUpgradeEventDownloadProgress({
progress: mockedProgress,
server: 'cdn.mullvad.net',
timeLeft: 120,
@@ -109,7 +116,7 @@ test.describe('App upgrade', () => {
});
test('Should verify installer when download is complete', async () => {
- await ipc.send.appUpgradeEventVerifyingInstaller();
+ await upgradeEventIpc.send.appUpgradeEventVerifyingInstaller();
await expect(page.getByText('Verifying installer')).toBeVisible();
await expect(page.getByText('Download complete')).toBeVisible();
@@ -118,7 +125,7 @@ test.describe('App upgrade', () => {
});
test('Should show that it has verified the installer when verification is complete', async () => {
- await ipc.send.appUpgradeEventVerifiedInstaller();
+ await upgradeEventIpc.send.appUpgradeEventVerifiedInstaller();
await expect(page.getByText('Verification successful!')).toBeVisible();
await expect(page.getByText('Download complete')).toBeVisible();
@@ -131,7 +138,7 @@ test.describe('App upgrade', () => {
test.afterAll(() => restart());
test('Should handle failing to download upgrade', async () => {
- await ipc.send.appUpgradeError('DOWNLOAD_FAILED');
+ await util.ipc.app.upgradeError.notify('DOWNLOAD_FAILED');
await expect(
page.getByText(
@@ -149,7 +156,7 @@ test.describe('App upgrade', () => {
test('Should handle retrying download of upgrade', async () => {
const retryButton = selectors.retryButton();
- await resolveIpcHandle(ipc.handle.appUpgrade(), retryButton.click());
+ await Promise.all([util.ipc.app.upgrade.expect(), retryButton.click()]);
await expect(page.getByText('Downloading...')).toBeVisible();
await expect(page.getByText('Starting download...')).toBeVisible();
@@ -164,9 +171,9 @@ test.describe('App upgrade', () => {
// This test should fail due to the window not being focused,
// which is a pre-requisite for launching the installer automatically.
test('Should handle installer failing to start automatically', async () => {
- await ipc.send.windowFocus(false);
+ await util.ipc.window.focus.notify(false);
- await ipc.send.upgradeVersion({
+ await util.ipc.upgradeVersion[''].notify({
supported: true,
suggestedIsBeta: false,
suggestedUpgrade: {
@@ -176,7 +183,7 @@ test.describe('App upgrade', () => {
},
});
- await ipc.send.appUpgradeEventVerifiedInstaller();
+ await upgradeEventIpc.send.appUpgradeEventVerifiedInstaller();
const installUpdateButton = selectors.installButton();
@@ -188,10 +195,10 @@ test.describe('App upgrade', () => {
test('Should handle installer failing to start manually', async () => {
const installUpdateButton = selectors.installButton();
- await resolveIpcHandle(ipc.handle.appUpgradeInstallerStart(), installUpdateButton.click());
+ await Promise.all([util.ipc.app.upgradeInstallerStart.expect(), installUpdateButton.click()]);
- await ipc.send.appUpgradeEventExitedInstaller();
- await ipc.send.appUpgradeError('START_INSTALLER_FAILED');
+ await upgradeEventIpc.send.appUpgradeEventExitedInstaller();
+ await util.ipc.app.upgradeError.notify('START_INSTALLER_FAILED');
await expect(installUpdateButton).not.toBeVisible();
@@ -212,13 +219,13 @@ test.describe('App upgrade', () => {
// Call the retry button 2 additional times, to increase the total
// errorCount to 3 in order for the ManualDownloadLink to be shown.
- await resolveIpcHandle(ipc.handle.appUpgradeInstallerStart(), retryButton.click());
- await ipc.send.appUpgradeEventExitedInstaller();
- await ipc.send.appUpgradeError('START_INSTALLER_FAILED');
+ await Promise.all([util.ipc.app.upgradeInstallerStart.expect(), retryButton.click()]);
+ await upgradeEventIpc.send.appUpgradeEventExitedInstaller();
+ await util.ipc.app.upgradeError.notify('START_INSTALLER_FAILED');
- await resolveIpcHandle(ipc.handle.appUpgradeInstallerStart(), retryButton.click());
- await ipc.send.appUpgradeEventExitedInstaller();
- await ipc.send.appUpgradeError('START_INSTALLER_FAILED');
+ await Promise.all([util.ipc.app.upgradeInstallerStart.expect(), retryButton.click()]);
+ await upgradeEventIpc.send.appUpgradeEventExitedInstaller();
+ await util.ipc.app.upgradeError.notify('START_INSTALLER_FAILED');
const manualDownloadLink = selectors.manualDownloadLink();
await expect(manualDownloadLink).toBeVisible();
@@ -237,11 +244,11 @@ test.describe('App upgrade', () => {
test('Should pause upgrade when clicking the Pause button', async () => {
const pauseButton = selectors.pauseButton();
- await resolveIpcHandle(ipc.handle.appUpgradeAbort(), pauseButton.click());
+ await Promise.all([util.ipc.app.upgradeAbort.expect(), pauseButton.click()]);
// After the app upgrade abort RPC is sent we expect to receive an aborted
// event.
- await ipc.send.appUpgradeEventAborted();
+ await upgradeEventIpc.send.appUpgradeEventAborted();
await expect(pauseButton).toBeHidden();
@@ -253,7 +260,7 @@ test.describe('App upgrade', () => {
test('Should start upgrade again when clicking Resume button', async () => {
const resumeButton = selectors.resumeButton();
- await resolveIpcHandle(ipc.handle.appUpgrade(), resumeButton.click());
+ await Promise.all([util.ipc.app.upgrade.expect(), resumeButton.click()]);
await expect(resumeButton).toBeHidden();
});
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade/helpers.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade/helpers.ts
index 9c2570f04b..ffb11effb7 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade/helpers.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/app-upgrade/helpers.ts
@@ -1,32 +1,15 @@
import { expect } from '@playwright/test';
import { Page } from 'playwright';
-import { AppUpgradeError, AppUpgradeEvent } from '../../../../src/shared/app-upgrade';
-import {
- DaemonAppUpgradeEventStatusDownloadProgress,
- IAppVersionInfo,
-} from '../../../../src/shared/daemon-rpc-types';
+import { AppUpgradeEvent } from '../../../../src/shared/app-upgrade';
+import { DaemonAppUpgradeEventStatusDownloadProgress } from '../../../../src/shared/daemon-rpc-types';
import { MockedTestUtils } from '../mocked-utils';
-export const createIpc = (util: MockedTestUtils) => {
- const createMockHandle = <T>(channel: string, response?: T) =>
- util.mockIpcHandle<T | undefined>({ channel, response });
-
- const createMockResponse = <T>(channel: string, response: T) =>
- util.sendMockIpcResponse<T>({
- channel,
- response,
- });
-
+export const createAppUpgradeEventIpcHelper = (util: MockedTestUtils) => {
const createMockResponseAppUpgradeEvent = (event: AppUpgradeEvent) =>
- createMockResponse<AppUpgradeEvent>('app-upgradeEvent', event);
+ util.ipc.app.upgradeEvent.notify(event);
return {
- handle: {
- appUpgrade: () => createMockHandle('appUpgrade'),
- appUpgradeAbort: () => createMockHandle('appUpgradeAbort'),
- appUpgradeInstallerStart: () => createMockHandle('appUpgradeInstallerStart'),
- },
send: {
appUpgradeEventAborted: () =>
createMockResponseAppUpgradeEvent({
@@ -59,11 +42,6 @@ export const createIpc = (util: MockedTestUtils) => {
createMockResponseAppUpgradeEvent({
type: 'APP_UPGRADE_STATUS_EXITED_INSTALLER',
}),
- appUpgradeError: (error: AppUpgradeError) =>
- createMockResponse<AppUpgradeError>('app-upgradeError', error),
- upgradeVersion: (data: IAppVersionInfo) =>
- createMockResponse<IAppVersionInfo>('upgradeVersion-', data),
- windowFocus: (value: boolean) => createMockResponse<boolean>('window-focus', value),
},
};
};
@@ -110,21 +88,14 @@ export const mockData = {
version: '2100.1',
};
-export const resolveIpcHandle = async (test: Promise<void>, trigger: Promise<void>) => {
- // The promise is resolved when its handle has been called.
- // The handle should be called when the trigger is called.
- const promise = await Promise.all([test, trigger]);
- expect(promise).toBeTruthy();
-};
-
export const createHelpers = (page: Page, util: MockedTestUtils) => {
const selectors = createSelectors(page);
- const ipc = createIpc(util);
+ const ipc = createAppUpgradeEventIpcHelper(util);
const startAppUpgrade = async () => {
const downloadAndLaunchInstallerButton = selectors.downloadAndLaunchInstallerButton();
- await resolveIpcHandle(ipc.handle.appUpgrade(), downloadAndLaunchInstallerButton.click());
+ await Promise.all([util.ipc.app.upgrade.expect(), downloadAndLaunchInstallerButton.click()]);
await ipc.send.appUpgradeEventDownloadStarted();
};
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/expired-account-error-view.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/expired-account-error-view.spec.ts
index fc1c6d99e7..71c3443704 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/expired-account-error-view.spec.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/expired-account-error-view.spec.ts
@@ -2,7 +2,6 @@ import { expect, test } from '@playwright/test';
import { Page } from 'playwright';
import { colorTokens } from '../../../src/renderer/lib/foundations';
-import { IAccountData } from '../../../src/shared/daemon-rpc-types';
import { RoutePath } from '../../../src/shared/routes';
import { getBackgroundColor } from '../utils';
import { MockedTestUtils, startMockedApp } from './mocked-utils';
@@ -20,9 +19,8 @@ test.afterEach(async () => {
});
test('App should show Expired Account Error View', async () => {
- await util.sendMockIpcResponse<IAccountData>({
- channel: 'account-',
- response: { expiry: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString() },
+ await util.ipc.account[''].notify({
+ expiry: new Date(Date.now() - 10 * 24 * 60 * 60 * 1000).toISOString(),
});
await expect(page.locator('text=Out of time')).toBeVisible();
@@ -39,9 +37,6 @@ test('App should show out of time view after running out of time', async () => {
const expiryDate = new Date();
expiryDate.setSeconds(expiryDate.getSeconds() + 2);
- await util.sendMockIpcResponse<IAccountData>({
- channel: 'account-',
- response: { expiry: expiryDate.toISOString() },
- });
+ await util.ipc.account[''].notify({ expiry: expiryDate.toISOString() });
await util.waitForRoute(RoutePath.expired);
});
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/feature-indicators.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/feature-indicators.spec.ts
index 26509664b3..9145068c58 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/feature-indicators.spec.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/feature-indicators.spec.ts
@@ -1,12 +1,7 @@
import { expect, test } from '@playwright/test';
import { Page } from 'playwright';
-import {
- FeatureIndicator,
- ILocation,
- ITunnelEndpoint,
- TunnelState,
-} from '../../../src/shared/daemon-rpc-types';
+import { FeatureIndicator, ILocation, ITunnelEndpoint } from '../../../src/shared/daemon-rpc-types';
import { RoutePath } from '../../../src/shared/routes';
import { expectConnected } from '../shared/tunnel-state';
import { MockedTestUtils, startMockedApp } from './mocked-utils';
@@ -42,17 +37,10 @@ test.afterAll(async () => {
});
test('App should show no feature indicators', async () => {
- await util.mockIpcHandle<ILocation>({
- channel: 'location-get',
- response: mockDisconnectedLocation,
- });
- await util.sendMockIpcResponse<TunnelState>({
- channel: 'tunnel-',
- response: {
- state: 'connected',
- details: { endpoint, location: mockConnectedLocation },
- featureIndicators: undefined,
- },
+ await util.ipc.tunnel[''].notify({
+ state: 'connected',
+ details: { endpoint, location: mockConnectedLocation },
+ featureIndicators: undefined,
});
await expectConnected(page);
@@ -69,28 +57,21 @@ test('App should show no feature indicators', async () => {
});
test('App should show feature indicators', async () => {
- await util.mockIpcHandle<ILocation>({
- channel: 'location-get',
- response: mockDisconnectedLocation,
- });
- await util.sendMockIpcResponse<TunnelState>({
- channel: 'tunnel-',
- response: {
- state: 'connected',
- details: { endpoint, location: mockConnectedLocation },
- featureIndicators: [
- FeatureIndicator.daita,
- FeatureIndicator.udp2tcp,
- FeatureIndicator.customMssFix,
- FeatureIndicator.customMtu,
- FeatureIndicator.lanSharing,
- FeatureIndicator.serverIpOverride,
- FeatureIndicator.customDns,
- FeatureIndicator.lockdownMode,
- FeatureIndicator.quantumResistance,
- FeatureIndicator.multihop,
- ],
- },
+ await util.ipc.tunnel[''].notify({
+ state: 'connected',
+ details: { endpoint, location: mockConnectedLocation },
+ featureIndicators: [
+ FeatureIndicator.daita,
+ FeatureIndicator.udp2tcp,
+ FeatureIndicator.customMssFix,
+ FeatureIndicator.customMtu,
+ FeatureIndicator.lanSharing,
+ FeatureIndicator.serverIpOverride,
+ FeatureIndicator.customDns,
+ FeatureIndicator.lockdownMode,
+ FeatureIndicator.quantumResistance,
+ FeatureIndicator.multihop,
+ ],
});
// Make sure panel is collapsed before checking indicator visibility.
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/launch/launch.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/launch.spec.ts
index 30e793d6b8..b9614a29e8 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/launch/launch.spec.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/launch.spec.ts
@@ -1,24 +1,21 @@
import { expect, test } from '@playwright/test';
import { Page } from 'playwright';
-import { RoutePath } from '../../../../src/shared/routes';
-import { RoutesObjectModel } from '../../route-object-models';
-import { MockedTestUtils, startMockedApp } from '../mocked-utils';
-import { createIpc } from './ipc';
+import { RoutePath } from '../../../src/shared/routes';
+import { RoutesObjectModel } from '../route-object-models';
+import { MockedTestUtils, startMockedApp } from './mocked-utils';
let page: Page;
let util: MockedTestUtils;
let routes: RoutesObjectModel;
-let ipc: ReturnType<typeof createIpc>;
test.describe('Launch', () => {
test.beforeAll(async () => {
({ page, util } = await startMockedApp());
- ipc = createIpc(util);
routes = new RoutesObjectModel(page, util);
await util.waitForRoute(RoutePath.main);
- await ipc.send.daemonDisconnected();
+ await util.ipc.daemon.disconnected.notify();
await routes.launch.waitForRoute();
});
@@ -54,7 +51,7 @@ test.describe('Launch', () => {
await expect(defaultFooterText).toBeVisible();
});
test('Should display permission footer when daemon is not allowed', async () => {
- await ipc.send.daemonAllowed(false);
+ await util.ipc.daemon.daemonAllowed.notify(false);
const gotoSystemSettingsButton = routes.launch.selectors.gotoSystemSettingsButton();
await expect(gotoSystemSettingsButton).toBeVisible();
});
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/launch/ipc.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/launch/ipc.ts
deleted file mode 100644
index 6e790c444f..0000000000
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/launch/ipc.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { MockedTestUtils } from '../mocked-utils';
-
-export const createIpc = (util: MockedTestUtils) => {
- const createMockResponse = <T>(channel: string, response: T) =>
- util.sendMockIpcResponse<T>({
- channel,
- response,
- });
-
- return {
- handle: {},
- send: {
- daemonDisconnected: () => createMockResponse('daemon-disconnected', {}),
- daemonAllowed: (allowed: boolean) => createMockResponse('daemon-daemonAllowed', allowed),
- },
- };
-};
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/login.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/login.spec.ts
index 46f2dcd6a0..fb868a639e 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/login.spec.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/login.spec.ts
@@ -1,7 +1,6 @@
import { expect, test } from '@playwright/test';
import { Page } from 'playwright';
-import { DeviceEvent } from '../../../src/shared/daemon-rpc-types';
import { RoutesObjectModel } from '../route-object-models';
import { MockedTestUtils, startMockedApp } from './mocked-utils';
@@ -18,9 +17,9 @@ test.describe('Clear account history warnings', () => {
};
const logout = async () => {
- await util.sendMockIpcResponse<DeviceEvent>({
- channel: util.ipcEvents.account.device,
- response: { type: 'logged out', deviceState: { type: 'logged out' } },
+ await util.ipc.account.device.notify({
+ type: 'logged out',
+ deviceState: { type: 'logged out' },
});
await routes.login.waitForRoute();
@@ -40,20 +39,14 @@ test.describe('Clear account history warnings', () => {
});
const setAccountHistory = async () => {
- await util.sendMockIpcResponse({
- channel: util.ipcEvents.accountHistory[''],
- response: '1234123412341234',
- });
+ await util.ipc.accountHistory[''].notify('1234123412341234');
};
test('Should not warn about creating an account', async () => {
const accountHistoryItemButton = routes.login.getAccountHistoryItemButton();
await expect(accountHistoryItemButton).not.toBeVisible();
- await Promise.all([
- util.expectIpcCall(util.ipcEvents.account.create),
- routes.login.createNewAccount(),
- ]);
+ await Promise.all([util.ipc.account.create.expect(), routes.login.createNewAccount()]);
});
test('Should warn about creating an account', async () => {
@@ -68,10 +61,7 @@ test.describe('Clear account history warnings', () => {
await routes.login.createNewAccount();
- await Promise.all([
- util.expectIpcCall(util.ipcEvents.account.create),
- routes.login.confirmCreateNewAccount(),
- ]);
+ await Promise.all([util.ipc.account.create.expect(), routes.login.confirmCreateNewAccount()]);
});
test('Should warn about clearing account history', async () => {
@@ -90,14 +80,11 @@ test.describe('Clear account history warnings', () => {
await routes.login.clearAccountHistory();
await Promise.all([
- util.expectIpcCall(util.ipcEvents.accountHistory.clear),
+ util.ipc.accountHistory.clear.expect(),
routes.login.confirmClearAccountHistory(),
]);
- await util.sendMockIpcResponse({
- channel: util.ipcEvents.accountHistory[''],
- response: undefined,
- });
+ await util.ipc.accountHistory[''].notify(undefined);
await expect(accountHistoryItemButton).not.toBeVisible();
});
});
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..e6db357f99 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/mocked-utils.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/mocked-utils.ts
@@ -1,110 +1,187 @@
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 { Async } from '../../../src/shared/utility-types';
import { startApp, TestUtils } from '../utils';
// This option can be removed in the future when/if we're able to tun the tests with the sandbox
// enabled in GitHub actions (frontend.yml).
const noSandbox = process.env.NO_SANDBOX === '1';
+const showWindow = process.env.SHOW_WINDOW === '1';
interface StartMockedAppResponse extends Awaited<ReturnType<typeof startApp>> {
util: MockedTestUtils;
}
export interface MockedTestUtils extends TestUtils {
- mockIpcHandle: MockIpcHandle;
- sendMockIpcResponse: SendMockIpcResponse;
- expectIpcCall: ExpectIpcCall;
- ipcEvents: IpcEvents<IpcSchema>;
+ ipc: IpcMockedTest<IpcSchema>;
}
export const startMockedApp = async (): Promise<StartMockedAppResponse> => {
const args = ['.'];
+
if (noSandbox) {
console.log('Running tests without chromium sandbox');
args.unshift('--no-sandbox');
}
+
+ if (showWindow) {
+ args.unshift('--show-window');
+ }
+
// NOTE: Keep in sync with index.ts
args.push('--gtk-version=3');
const startAppResult = await startApp({ args });
- const mockIpcHandle = generateMockIpcHandle(startAppResult.app);
- const sendMockIpcResponse = generateSendMockIpcResponse(startAppResult.app);
- const expectIpcCall = generateExpectIpcCall(startAppResult.app);
return {
...startAppResult,
util: {
...startAppResult.util,
- mockIpcHandle,
- sendMockIpcResponse,
- expectIpcCall,
-
- ipcEvents: createIpcEvents(ipcSchema),
+ ipc: createTestIpc(startAppResult.app),
},
};
};
-type MockIpcHandleProps<T> = {
- channel: string;
- response: T;
+export const createMockIpcNotify = (electronApp: ElectronApplication, event: string) => {
+ return async <T>(arg: T) => {
+ await electronApp.evaluate(
+ ({ webContents }, { event, arg }) => {
+ webContents
+ .getAllWebContents()
+ // Select window that isn't devtools
+ .find((webContents) => webContents.getURL().startsWith('file://'))!
+ .send(event, arg);
+ },
+ { event, arg },
+ );
+ };
};
-export type MockIpcHandle = ReturnType<typeof generateMockIpcHandle>;
+export const createMockIpcHandle = (
+ electronApp: ElectronApplication,
+ event: string,
+ spec: AnyIpcCall,
+) => {
+ // This function resolves when the handle is registered. To await the event, use `expect()`.
+ return async <T>(response: T): Promise<void> => {
+ if ('type' in spec && spec.type === 'send') {
+ throw new Error(`No value can be returned on a send call (${event})`);
+ }
-export const generateMockIpcHandle = (electronApp: ElectronApplication) => {
- return async <T>({ channel, response }: MockIpcHandleProps<T>): Promise<void> => {
await electronApp.evaluate(
- ({ ipcMain }, { channel, response }) => {
- ipcMain.removeHandler(channel);
- ipcMain.handle(channel, () => {
+ ({ ipcMain }, { event, response }) => {
+ ipcMain.removeHandler(event);
+ ipcMain.handle(event, () => {
return Promise.resolve({
type: 'success',
value: response,
});
});
},
- { channel, response },
+ { event, response },
);
};
};
-type SendMockIpcResponseProps<T> = {
- channel: string;
- response: T;
+// Use when you want to wait for an IPC call to happen but don't need to respond. The returned
+// promise resolves when the IPC handle/on methods are triggered triggered.
+export const createMockIpcExpect = (
+ electronApp: ElectronApplication,
+ event: string,
+ spec: AnyIpcCall,
+) => {
+ const type = 'type' in spec ? spec.type : 'invoke';
+
+ return <T>(): Promise<T> => {
+ return electronApp.evaluate(
+ ({ ipcMain }, { event, type }) => {
+ return new Promise<T>((resolve) => {
+ if (type === 'send') {
+ ipcMain.once(event, (_event, arg) => resolve(arg));
+ } else {
+ ipcMain.handleOnce(event, (_event, arg) => {
+ resolve(arg);
+ return {
+ type: 'success',
+ value: null,
+ };
+ });
+ }
+ });
+ },
+ { event, type },
+ );
+ };
};
-export type SendMockIpcResponse = ReturnType<typeof generateSendMockIpcResponse>;
+// Use when you knowingly want to ignore when this IPC method is called. Useful to avoid unhandled
+// events from being printed and polluting the log output.
+export const createMockIpcIgnore = (
+ electronApp: ElectronApplication,
+ event: string,
+ spec: AnyIpcCall,
+) => {
+ const type = 'type' in spec ? spec.type : 'invoke';
-export const generateSendMockIpcResponse = (electronApp: ElectronApplication) => {
- return async <T>({ channel, response }: SendMockIpcResponseProps<T>) => {
+ return async (): Promise<void> => {
await electronApp.evaluate(
- ({ webContents }, { channel, response }) => {
- webContents
- .getAllWebContents()
- // Select window that isn't devtools
- .find((webContents) => webContents.getURL().startsWith('file://'))!
- .send(channel, response);
+ ({ ipcMain }, { event, type }) => {
+ if (type === 'send') {
+ ipcMain.removeAllListeners(event);
+ ipcMain.addListener(event, () => {});
+ } else {
+ ipcMain.removeHandler(event);
+ ipcMain.handle(event, () => ({
+ type: 'success',
+ value: null,
+ }));
+ }
},
- { channel, response },
+ { event, type },
);
};
};
-export type ExpectIpcCall = ReturnType<typeof generateExpectIpcCall>;
+type IpcMockedTestKey<I extends AnyIpcCall> = I['direction'] extends 'main-to-renderer'
+ ? 'notify'
+ : 'handle';
-export const generateExpectIpcCall = (electronApp: ElectronApplication) => {
- return <T>(channel: string): Promise<T> => {
- return electronApp.evaluate(
- ({ ipcMain }, { channel }) => {
- return new Promise<T>((resolve) => {
- ipcMain.handleOnce(channel, (_event, arg) => {
- resolve(arg);
- });
- });
- },
- { channel },
- );
+type IpcMockedTestExtraHandlerKey<
+ I extends AnyIpcCall,
+ K,
+> = I['direction'] extends 'main-to-renderer' ? never : K;
+
+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 IpcMockedTestExtraHandlerKey<S[G][K], 'expect'>]: () => Promise<void>;
+ } & {
+ [C in IpcMockedTestExtraHandlerKey<S[G][K], 'ignore'>]: () => Promise<void>;
+ } & {
+ eventKey: string;
+ };
};
};
+
+export function createTestIpc(electronApp: ElectronApplication): IpcMockedTest<IpcSchema> {
+ return createIpc(ipcSchema, (event, key, spec) => {
+ return [
+ key,
+ {
+ eventKey: event,
+ notify: createMockIpcNotify(electronApp, event),
+ handle: createMockIpcHandle(electronApp, event, spec),
+ expect: createMockIpcExpect(electronApp, event, spec),
+ ignore: createMockIpcIgnore(electronApp, event, spec),
+ },
+ ];
+ });
+}
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/notifications.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/notifications.spec.ts
index d432e0df4c..aa0ae73055 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/notifications.spec.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/notifications.spec.ts
@@ -3,14 +3,7 @@ import { Page } from 'playwright';
import { getDefaultSettings } from '../../../src/main/default-settings';
import { colorTokens } from '../../../src/renderer/lib/foundations';
-import {
- Constraint,
- ErrorStateCause,
- IAccountData,
- IRelayListWithEndpointData,
- ISettings,
- TunnelState,
-} from '../../../src/shared/daemon-rpc-types';
+import { Constraint, ErrorStateCause, TunnelState } from '../../../src/shared/daemon-rpc-types';
import { RoutePath } from '../../../src/shared/routes';
import { getBackgroundColor } from '../utils';
import { MockedTestUtils, startMockedApp } from './mocked-utils';
@@ -31,9 +24,8 @@ test.afterAll(async () => {
* Expires soon
*/
test('App should notify user about account expiring soon', async () => {
- await util.sendMockIpcResponse<IAccountData>({
- channel: 'account-',
- response: { expiry: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toISOString() },
+ await util.ipc.account[''].notify({
+ expiry: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toISOString(),
});
const title = page.getByTestId('notificationTitle');
@@ -46,16 +38,14 @@ test('App should notify user about account expiring soon', async () => {
const indicatorColor = await getBackgroundColor(indicator);
expect(indicatorColor).toBe(colorTokens.yellow);
- await util.sendMockIpcResponse<IAccountData>({
- channel: 'account-',
- response: { expiry: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toISOString() },
+ await util.ipc.account[''].notify({
+ expiry: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toISOString(),
});
subTitle = page.getByTestId('notificationSubTitle');
await expect(subTitle).toContainText(/2 days left\. buy more credit\./i);
- await util.sendMockIpcResponse<IAccountData>({
- channel: 'account-',
- response: { expiry: new Date(Date.now() + 1 * 24 * 60 * 60 * 1000).toISOString() },
+ await util.ipc.account[''].notify({
+ expiry: new Date(Date.now() + 1 * 24 * 60 * 60 * 1000).toISOString(),
});
subTitle = page.getByTestId('notificationSubTitle');
await expect(subTitle).toContainText(/less than a day left\. buy more credit\./i);
@@ -74,32 +64,23 @@ test.describe('Unsupported wireguard port', () => {
if ('normal' in settings.relaySettings) {
settings.relaySettings.normal.wireguardConstraints.port = port;
}
- await util.sendMockIpcResponse<ISettings>({
- channel: 'settings-',
- response: settings,
- });
+ await util.ipc.settings[''].notify(settings);
};
const updatePortRanges = async (portRanges: [number, number][]) => {
- await util.sendMockIpcResponse<IRelayListWithEndpointData>({
- channel: 'relays-',
- response: {
- relayList: {
- countries: [],
- },
- wireguardEndpointData: {
- portRanges,
- udp2tcpPorts: [],
- },
+ await util.ipc.relays[''].notify({
+ relayList: {
+ countries: [],
+ },
+ wireguardEndpointData: {
+ portRanges,
+ udp2tcpPorts: [],
},
});
};
const updateTunnelState = async (tunnelState: TunnelState) => {
- await util.sendMockIpcResponse<TunnelState>({
- channel: 'tunnel-',
- response: tunnelState,
- });
+ await util.ipc.tunnel[''].notify(tunnelState);
};
test.beforeAll(async () => {
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/helpers.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/helpers.ts
index ccfa8289f2..663116643b 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/helpers.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/helpers.ts
@@ -8,6 +8,7 @@ import {
IRelayListHostname,
ISettings,
Ownership,
+ RelaySettings,
} from '../../../../src/shared/daemon-rpc-types';
import { RoutePath } from '../../../../src/shared/routes';
import { RoutesObjectModel } from '../../route-object-models';
@@ -107,14 +108,8 @@ export const createHelpers = (page: Page, routes: RoutesObjectModel, utils: Mock
settings.relaySettings.normal.providers = providers;
}
}
- await utils.mockIpcHandle({
- channel: 'settings-setRelaySettings',
- response: {},
- });
- await utils.sendMockIpcResponse({
- channel: 'settings-',
- response: settings,
- });
+ await utils.ipc.settings.setRelaySettings.handle({} as RelaySettings);
+ await utils.ipc.settings[''].notify(settings);
};
const updateMockSettings = async (
@@ -139,10 +134,7 @@ export const createHelpers = (page: Page, routes: RoutesObjectModel, utils: Mock
if (directOnly !== undefined) settings.tunnelOptions.wireguard.daita.directOnly = directOnly;
}
- await utils.sendMockIpcResponse<ISettings>({
- channel: 'settings-',
- response: settings,
- });
+ await utils.ipc.settings[''].notify(settings);
return settings;
};
@@ -161,10 +153,7 @@ export const createHelpers = (page: Page, routes: RoutesObjectModel, utils: Mock
};
}
- await utils.sendMockIpcResponse<ISettings>({
- channel: 'settings-',
- response: settings,
- });
+ await utils.ipc.settings[''].notify(settings);
return settings;
};
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/select-location.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/select-location.spec.ts
index 2c0a413522..02a1a403bc 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/select-location.spec.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/select-location.spec.ts
@@ -3,7 +3,7 @@ import { Page } from 'playwright';
import { getDefaultSettings } from '../../../../src/main/default-settings';
import { colorTokens } from '../../../../src/renderer/lib/foundations';
-import { ISettings, ObfuscationType, Ownership } from '../../../../src/shared/daemon-rpc-types';
+import { ObfuscationType, Ownership } from '../../../../src/shared/daemon-rpc-types';
import { RoutePath } from '../../../../src/shared/routes';
import { mockData } from '../../mock-data';
import { RoutesObjectModel } from '../../route-object-models';
@@ -125,6 +125,9 @@ test.describe('Select location', () => {
});
test('Should disable entry server in exit list', async () => {
+ await util.ipc.tunnel.connect.ignore();
+ await util.ipc.settings.setRelaySettings.ignore();
+
const settings = await helpers.updateMockSettings({
multihop: true,
daita: true,
@@ -279,10 +282,8 @@ test.describe('Select location', () => {
if ('normal' in settings.relaySettings) {
settings.obfuscationSettings.selectedObfuscation = ObfuscationType.quic;
}
- await util.sendMockIpcResponse<ISettings>({
- channel: 'settings-',
- response: settings,
- });
+ await util.ipc.settings[''].notify(settings);
+
const locatedRelays = helpers.locateRelaysByObfuscation(relayList);
const relays = locatedRelays.map((locatedRelay) => locatedRelay.relay);
const relayNames = relays.map((relay) => relay.hostname);
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/settings.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/settings.spec.ts
index 472893a5c0..8648cf4e69 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/settings.spec.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/settings.spec.ts
@@ -1,7 +1,6 @@
import { expect, test } from '@playwright/test';
import { Page } from 'playwright';
-import { IAccountData } from '../../../src/shared/daemon-rpc-types';
import { RoutePath } from '../../../src/shared/routes';
import { MockedTestUtils, startMockedApp } from './mocked-utils';
@@ -30,27 +29,24 @@ test('Headerbar account info should be displayed correctly', async () => {
* 729 days left
* Add a one-second margin to the test, since it randomly fails in Github Actions otherwise
*/
- await util.sendMockIpcResponse<IAccountData>({
- channel: 'account-',
- response: { expiry: new Date(Date.now() + 730 * 24 * 60 * 60 * 1000 - 1000).toISOString() },
+ await util.ipc.account[''].notify({
+ expiry: new Date(Date.now() + 730 * 24 * 60 * 60 * 1000 - 1000).toISOString(),
});
await expect(expiryText).toContainText(/Time left: 729 days/i);
/**
* 2 years left
*/
- await util.sendMockIpcResponse<IAccountData>({
- channel: 'account-',
- response: { expiry: new Date(Date.now() + 731 * 24 * 60 * 60 * 1000).toISOString() },
+ await util.ipc.account[''].notify({
+ expiry: new Date(Date.now() + 731 * 24 * 60 * 60 * 1000).toISOString(),
});
await expect(expiryText).toContainText(/Time left: 2 years/i);
/**
* Expiry 1 day ago should show 'out of time'
*/
- await util.sendMockIpcResponse<IAccountData>({
- channel: 'account-',
- response: { expiry: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString() },
+ await util.ipc.account[''].notify({
+ expiry: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString(),
});
await expect(expiryText).not.toBeVisible();
});
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/tunnel-state.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/mocked/tunnel-state.spec.ts
index e8a706471b..d1790925c9 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/tunnel-state.spec.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/tunnel-state.spec.ts
@@ -1,12 +1,7 @@
import { test } from '@playwright/test';
import { Page } from 'playwright';
-import {
- ErrorStateCause,
- ILocation,
- ITunnelEndpoint,
- TunnelState,
-} from '../../../src/shared/daemon-rpc-types';
+import { ErrorStateCause, ILocation, ITunnelEndpoint } from '../../../src/shared/daemon-rpc-types';
import { RoutePath } from '../../../src/shared/routes';
import {
expectConnected,
@@ -41,14 +36,7 @@ test.afterAll(async () => {
* Disconnected state
*/
test('App should show disconnected tunnel state', async () => {
- await util.mockIpcHandle<ILocation>({
- channel: 'location-get',
- response: mockLocation,
- });
- await util.sendMockIpcResponse<TunnelState>({
- channel: 'tunnel-',
- response: { state: 'disconnected', lockedDown: false },
- });
+ await util.ipc.tunnel[''].notify({ state: 'disconnected', lockedDown: false });
await expectDisconnected(page);
});
@@ -56,14 +44,7 @@ test('App should show disconnected tunnel state', async () => {
* Connecting state
*/
test('App should show connecting tunnel state', async () => {
- await util.mockIpcHandle<ILocation>({
- channel: 'location-get',
- response: mockLocation,
- });
- await util.sendMockIpcResponse<TunnelState>({
- channel: 'tunnel-',
- response: { state: 'connecting', featureIndicators: undefined },
- });
+ await util.ipc.tunnel[''].notify({ state: 'connecting', featureIndicators: undefined });
await expectConnecting(page);
});
@@ -72,10 +53,6 @@ test('App should show connecting tunnel state', async () => {
*/
test('App should show connected tunnel state', async () => {
const location: ILocation = { ...mockLocation, mullvadExitIp: true };
- await util.mockIpcHandle<ILocation>({
- channel: 'location-get',
- response: location,
- });
const endpoint: ITunnelEndpoint = {
address: 'wg10:80',
@@ -84,9 +61,10 @@ test('App should show connected tunnel state', async () => {
tunnelType: 'wireguard',
daita: false,
};
- await util.sendMockIpcResponse<TunnelState>({
- channel: 'tunnel-',
- response: { state: 'connected', details: { endpoint, location }, featureIndicators: undefined },
+ await util.ipc.tunnel[''].notify({
+ state: 'connected',
+ details: { endpoint, location },
+ featureIndicators: undefined,
});
await expectConnected(page);
@@ -96,14 +74,7 @@ test('App should show connected tunnel state', async () => {
* Disconnecting state
*/
test('App should show disconnecting tunnel state', async () => {
- await util.mockIpcHandle<ILocation>({
- channel: 'location-get',
- response: mockLocation,
- });
- await util.sendMockIpcResponse<TunnelState>({
- channel: 'tunnel-',
- response: { state: 'disconnecting', details: 'nothing' },
- });
+ await util.ipc.tunnel[''].notify({ state: 'disconnecting', details: 'nothing' });
await expectDisconnecting(page);
});
@@ -111,13 +82,9 @@ test('App should show disconnecting tunnel state', async () => {
* Error state
*/
test('App should show error tunnel state', async () => {
- await util.mockIpcHandle<ILocation>({
- channel: 'location-get',
- response: mockLocation,
- });
- await util.sendMockIpcResponse<TunnelState>({
- channel: 'tunnel-',
- response: { state: 'error', details: { cause: ErrorStateCause.isOffline } },
+ await util.ipc.tunnel[''].notify({
+ state: 'error',
+ details: { cause: ErrorStateCause.isOffline },
});
await expectError(page);
});
diff --git a/desktop/packages/mullvad-vpn/test/e2e/setup/main.ts b/desktop/packages/mullvad-vpn/test/e2e/setup/main.ts
index de46f5e578..37bc6cdb6f 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/setup/main.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/setup/main.ts
@@ -114,6 +114,10 @@ class ApplicationMain {
await window.loadFile(path.join(__dirname, 'index.html'));
+ if (process.argv.includes('--show-window')) {
+ window.show();
+ }
+
if (DEBUG) {
window.webContents.openDevTools({ mode: 'detach' });
}