summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2025-07-31 13:25:12 +0200
committerDavid Lönnhager <david.l@mullvad.net>2025-07-31 13:25:12 +0200
commitaecbd37675e8517354f73c0bccb25b38d885e9bb (patch)
treea402dd2f20798bf91ae605eac451b2c94d085afd
parent466d3d3e386518d0945945d78dce63bfed7326ab (diff)
parent0ab44b55ae30db9f32f7893096bf30acde2aa18f (diff)
downloadmullvadvpn-aecbd37675e8517354f73c0bccb25b38d885e9bb.tar.xz
mullvadvpn-aecbd37675e8517354f73c0bccb25b38d885e9bb.zip
Merge branch 'automate-remaining-parts-of-daitamultihop-manual-test-card-des-2210'
-rw-r--r--desktop/packages/mullvad-vpn/src/main/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/installed/state-dependent/daita-settings/daita-settings.spec.ts44
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/installed/state-dependent/multihop-settings/multihop-settings.spec.ts43
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mock-data.ts (renamed from desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/mock-data.ts)21
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/mocked-utils.ts2
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/helpers.ts66
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/select-location.spec.ts151
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/route-object-models/daita-settings/daita-settings-route-object-model.ts29
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/route-object-models/daita-settings/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/route-object-models/daita-settings/selectors.ts5
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/route-object-models/main/main-route-object-model.ts5
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/route-object-models/main/selectors.ts1
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/route-object-models/multihop-settings/index.ts1
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/route-object-models/multihop-settings/multihop-settings-route-object-model.ts29
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/route-object-models/multihop-settings/selectors.ts5
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/route-object-models/routes-object-model.ts6
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/route-object-models/settings/selectors.ts2
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/route-object-models/settings/settings-route-object-model.ts10
-rw-r--r--desktop/packages/mullvad-vpn/test/e2e/setup/main.ts37
-rw-r--r--test/test-manager/src/tests/ui.rs24
20 files changed, 395 insertions, 88 deletions
diff --git a/desktop/packages/mullvad-vpn/src/main/index.ts b/desktop/packages/mullvad-vpn/src/main/index.ts
index 457a192a1a..9c354f4dd4 100644
--- a/desktop/packages/mullvad-vpn/src/main/index.ts
+++ b/desktop/packages/mullvad-vpn/src/main/index.ts
@@ -157,6 +157,7 @@ class ApplicationMain
}
if (process.platform === 'linux') {
+ // NOTE: Keep in sync with mocked-utils.ts
app.commandLine.appendSwitch('gtk-version', '3');
}
diff --git a/desktop/packages/mullvad-vpn/test/e2e/installed/state-dependent/daita-settings/daita-settings.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/installed/state-dependent/daita-settings/daita-settings.spec.ts
new file mode 100644
index 0000000000..dd3335059f
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/test/e2e/installed/state-dependent/daita-settings/daita-settings.spec.ts
@@ -0,0 +1,44 @@
+import { expect, test } from '@playwright/test';
+import { Page } from 'playwright';
+
+import { RoutesObjectModel } from '../../../route-object-models';
+import { TestUtils } from '../../../utils';
+import { startInstalledApp } from '../../installed-utils';
+
+let page: Page;
+let util: TestUtils;
+let routes: RoutesObjectModel;
+
+test.describe('DAITA settings', () => {
+ const startup = async () => {
+ ({ page, util } = await startInstalledApp());
+ routes = new RoutesObjectModel(page, util);
+
+ await routes.main.waitForRoute();
+ await routes.main.gotoSettings();
+ await routes.settings.gotoDaitaSettings();
+ await routes.daitaSettings.setEnableDaitaSwitch(false);
+ };
+
+ test.beforeAll(async () => {
+ await startup();
+ });
+
+ test.afterAll(async () => {
+ await page.close();
+ });
+
+ test.afterEach(async () => {
+ await routes.daitaSettings.setEnableDaitaSwitch(false);
+ const daitaSwitch = routes.daitaSettings.getEnableDaitaSwitch();
+
+ await expect(daitaSwitch).toHaveAttribute('aria-checked', 'false');
+ });
+
+ test('Should enable DAITA when clicking switch', async () => {
+ await routes.daitaSettings.setEnableDaitaSwitch(true);
+ const daitaSwitch = routes.daitaSettings.getEnableDaitaSwitch();
+
+ await expect(daitaSwitch).toHaveAttribute('aria-checked', 'true');
+ });
+});
diff --git a/desktop/packages/mullvad-vpn/test/e2e/installed/state-dependent/multihop-settings/multihop-settings.spec.ts b/desktop/packages/mullvad-vpn/test/e2e/installed/state-dependent/multihop-settings/multihop-settings.spec.ts
new file mode 100644
index 0000000000..0cc819828b
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/test/e2e/installed/state-dependent/multihop-settings/multihop-settings.spec.ts
@@ -0,0 +1,43 @@
+import { expect, test } from '@playwright/test';
+import { Page } from 'playwright';
+
+import { RoutesObjectModel } from '../../../route-object-models';
+import { TestUtils } from '../../../utils';
+import { startInstalledApp } from '../../installed-utils';
+
+let page: Page;
+let util: TestUtils;
+let routes: RoutesObjectModel;
+
+test.describe('Multihop settings', () => {
+ const startup = async () => {
+ ({ page, util } = await startInstalledApp());
+ routes = new RoutesObjectModel(page, util);
+
+ await routes.main.waitForRoute();
+ await routes.main.gotoSettings();
+ await routes.settings.gotoMultihopSettings();
+ };
+
+ test.beforeAll(async () => {
+ await startup();
+ });
+
+ test.afterAll(async () => {
+ await page.close();
+ });
+
+ test.afterEach(async () => {
+ await routes.multihopSettings.setEnableMultihopSwitch(false);
+ const multihopSwitch = routes.multihopSettings.getEnableMultihopSwitch();
+
+ await expect(multihopSwitch).toHaveAttribute('aria-checked', 'false');
+ });
+
+ test('Should enable multihop when clicking switch', async () => {
+ await routes.multihopSettings.setEnableMultihopSwitch(true);
+ const multihopSwitch = routes.multihopSettings.getEnableMultihopSwitch();
+
+ await expect(multihopSwitch).toHaveAttribute('aria-checked', 'true');
+ });
+});
diff --git a/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/mock-data.ts b/desktop/packages/mullvad-vpn/test/e2e/mock-data.ts
index f3039c2537..efbac88f83 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/select-location/mock-data.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mock-data.ts
@@ -1,4 +1,4 @@
-import { IRelayList } from '../../../../src/shared/daemon-rpc-types';
+import { IRelayList } from '../../src/shared/daemon-rpc-types';
const relayList: IRelayList = {
countries: [
@@ -13,7 +13,7 @@ const relayList: IRelayList = {
longitude: 12,
relays: [
{
- hostname: 'my-test-relay1',
+ hostname: 'mullvad-wireguard-1',
provider: 'mullvad',
ipv4AddrIn: '10.0.0.1',
includeInCountry: true,
@@ -24,7 +24,7 @@ const relayList: IRelayList = {
daita: true,
},
{
- hostname: 'my-test-relay2',
+ hostname: 'mullvad-wireguard-23',
provider: 'mullvad',
ipv4AddrIn: '10.0.0.2',
includeInCountry: true,
@@ -35,7 +35,7 @@ const relayList: IRelayList = {
daita: true,
},
{
- hostname: 'se-got-wg-103',
+ hostname: 'another-provider-wireguard-1',
provider: 'another-provider',
ipv4AddrIn: '10.0.0.3',
includeInCountry: true,
@@ -46,7 +46,7 @@ const relayList: IRelayList = {
daita: true,
},
{
- hostname: 'se-got-wg-104',
+ hostname: 'mullvad-wireguard-quic-1',
provider: 'mullvad',
ipv4AddrIn: '10.0.0.4',
includeInCountry: true,
@@ -61,6 +61,17 @@ const relayList: IRelayList = {
token: '',
},
},
+ {
+ hostname: 'mullvad-openvpn-1',
+ provider: 'mullvad',
+ ipv4AddrIn: '10.0.0.2',
+ includeInCountry: true,
+ active: true,
+ weight: 0,
+ owned: true,
+ endpointType: 'openvpn',
+ daita: true,
+ },
],
},
],
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 474786aebc..171cc1cc50 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/mocked/mocked-utils.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/mocked/mocked-utils.ts
@@ -21,6 +21,8 @@ export const startMockedApp = async (): Promise<StartMockedAppResponse> => {
console.log('Running tests without chromium sandbox');
args.unshift('--no-sandbox');
}
+ // NOTE: Keep in sync with index.ts
+ args.push('--gtk-version=3');
const startAppResult = await startApp({ args });
const mockIpcHandle = generateMockIpcHandle(startAppResult.app);
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 d04dac2f3b..ccfa8289f2 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
@@ -6,6 +6,7 @@ import {
IRelayListCity,
IRelayListCountry,
IRelayListHostname,
+ ISettings,
Ownership,
} from '../../../../src/shared/daemon-rpc-types';
import { RoutePath } from '../../../../src/shared/routes';
@@ -33,6 +34,16 @@ export const createHelpers = (page: Page, routes: RoutesObjectModel, utils: Mock
}
};
+ const locateRelaysByHostnames = (relayList: IRelayList, hostnames: string[]): LocatedRelay[] => {
+ return relayList.countries.flatMap((country) =>
+ country.cities.flatMap((city) =>
+ city.relays
+ .filter((relay) => hostnames.includes(relay.hostname))
+ .map((relay) => ({ country, city, relay })),
+ ),
+ );
+ };
+
const locateRelaysByProvider = (relayList: IRelayList, provider?: string): LocatedRelay[] =>
relayList.countries.flatMap((country) =>
country.cities.flatMap((city) =>
@@ -106,9 +117,62 @@ export const createHelpers = (page: Page, routes: RoutesObjectModel, utils: Mock
});
};
+ const updateMockSettings = async (
+ {
+ daita,
+ directOnly,
+ multihop,
+ }: {
+ multihop?: boolean;
+ daita?: boolean;
+ directOnly?: boolean;
+ },
+ settings?: ISettings,
+ ) => {
+ if (!settings) {
+ settings = getDefaultSettings();
+ }
+ if ('normal' in settings.relaySettings && settings.tunnelOptions.wireguard.daita) {
+ if (multihop !== undefined)
+ settings.relaySettings.normal.wireguardConstraints.useMultihop = multihop;
+ if (daita !== undefined) settings.tunnelOptions.wireguard.daita.enabled = daita;
+ if (directOnly !== undefined) settings.tunnelOptions.wireguard.daita.directOnly = directOnly;
+ }
+
+ await utils.sendMockIpcResponse<ISettings>({
+ channel: 'settings-',
+ response: settings,
+ });
+
+ return settings;
+ };
+
+ const updateEntryLocation = async (relay: LocatedRelay, settings?: ISettings) => {
+ if (!settings) {
+ settings = getDefaultSettings();
+ }
+ if ('normal' in settings.relaySettings && settings.tunnelOptions.wireguard.daita) {
+ settings.relaySettings.normal.wireguardConstraints.entryLocation = {
+ only: {
+ hostname: relay.relay.hostname,
+ country: relay.country.code,
+ city: relay.city.code,
+ },
+ };
+ }
+
+ await utils.sendMockIpcResponse<ISettings>({
+ channel: 'settings-',
+ response: settings,
+ });
+
+ return settings;
+ };
+
return {
areAllCheckboxesChecked,
expandLocatedRelays,
+ locateRelaysByHostnames,
locateRelaysByProvider,
locateRelaysByOwner,
locateRelaysByObfuscation,
@@ -116,6 +180,8 @@ export const createHelpers = (page: Page, routes: RoutesObjectModel, utils: Mock
resetProviders,
resetView,
updateMockRelayFilter,
+ updateMockSettings,
+ updateEntryLocation,
};
};
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 ab4cd3736d..2c0a413522 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,43 +3,33 @@ import { Page } from 'playwright';
import { getDefaultSettings } from '../../../../src/main/default-settings';
import { colorTokens } from '../../../../src/renderer/lib/foundations';
-import {
- IRelayListWithEndpointData,
- ISettings,
- IWireguardEndpointData,
- ObfuscationType,
- Ownership,
-} from '../../../../src/shared/daemon-rpc-types';
+import { ISettings, 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';
import { MockedTestUtils, startMockedApp } from '../mocked-utils';
import { createHelpers, SelectLocationHelpers } from './helpers';
-import { mockData } from './mock-data';
-const wireguardEndpointData: IWireguardEndpointData = {
- portRanges: [],
- udp2tcpPorts: [],
-};
+const { relayList } = mockData;
let page: Page;
let util: MockedTestUtils;
let routes: RoutesObjectModel;
let helpers: SelectLocationHelpers;
-const { relayList } = mockData;
test.describe('Select location', () => {
test.beforeAll(async () => {
({ page, util } = await startMockedApp());
routes = new RoutesObjectModel(page, util);
helpers = createHelpers(page, routes, util);
+
await util.waitForRoute(RoutePath.main);
- await page.getByLabel('Select location').click();
- await util.waitForRoute(RoutePath.selectLocation);
+ });
- await util.sendMockIpcResponse<IRelayListWithEndpointData>({
- channel: 'relays-',
- response: { relayList, wireguardEndpointData },
- });
+ test.beforeEach(async () => {
+ if ((await util.currentRoute()) === RoutePath.main) {
+ await routes.main.gotoSelectLocation();
+ }
});
test.afterAll(async () => {
@@ -48,14 +38,8 @@ test.describe('Select location', () => {
test.describe('Multihop enabled', () => {
test.beforeAll(async () => {
- const settings = getDefaultSettings();
- if ('normal' in settings.relaySettings) {
- settings.relaySettings.normal.wireguardConstraints.useMultihop = true;
- }
-
- await util.sendMockIpcResponse<ISettings>({
- channel: 'settings-',
- response: settings,
+ await helpers.updateMockSettings({
+ multihop: true,
});
});
@@ -81,16 +65,10 @@ test.describe('Select location', () => {
});
test("App shouldn't show entry selection when daita is enabled without direct only", async () => {
- const settings = getDefaultSettings();
- if ('normal' in settings.relaySettings && settings.tunnelOptions.wireguard.daita) {
- settings.relaySettings.normal.wireguardConstraints.useMultihop = true;
- settings.tunnelOptions.wireguard.daita.enabled = true;
- settings.tunnelOptions.wireguard.daita.directOnly = false;
- }
-
- await util.sendMockIpcResponse<ISettings>({
- channel: 'settings-',
- response: settings,
+ await helpers.updateMockSettings({
+ multihop: true,
+ daita: true,
+ directOnly: false,
});
const entryButton = routes.selectLocation.getEntryButton();
@@ -101,16 +79,10 @@ test.describe('Select location', () => {
});
test('App should show entry selection when daita is enabled with direct only', async () => {
- const settings = getDefaultSettings();
- if ('normal' in settings.relaySettings && settings.tunnelOptions.wireguard.daita) {
- settings.relaySettings.normal.wireguardConstraints.useMultihop = true;
- settings.tunnelOptions.wireguard.daita.enabled = true;
- settings.tunnelOptions.wireguard.daita.directOnly = true;
- }
-
- await util.sendMockIpcResponse<ISettings>({
- channel: 'settings-',
- response: settings,
+ await helpers.updateMockSettings({
+ multihop: true,
+ daita: true,
+ directOnly: true,
});
const entryButton = routes.selectLocation.getEntryButton();
@@ -119,6 +91,78 @@ test.describe('Select location', () => {
const sweden = page.getByText('Sweden');
await expect(sweden).toBeVisible();
});
+
+ test('Should show only wireguard servers in entry list', async () => {
+ const entryButton = routes.selectLocation.getEntryButton();
+ await entryButton.click();
+
+ const wireguardRelays = relayList.countries[0].cities[0].relays.filter(
+ (relay) => relay.endpointType === 'wireguard',
+ );
+ const hostnames = wireguardRelays.map((relay) => relay.hostname);
+ const locatedRelays = helpers.locateRelaysByHostnames(relayList, hostnames);
+
+ await helpers.expandLocatedRelays(locatedRelays);
+
+ const buttons = routes.selectLocation.getRelaysMatching(hostnames);
+ await expect(buttons).toHaveCount(wireguardRelays.length);
+ });
+
+ test('Should show only wireguard servers in exit list', async () => {
+ const exitButton = routes.selectLocation.getExitButton();
+ await exitButton.click();
+
+ const wireguardRelays = relayList.countries[0].cities[0].relays.filter(
+ (relay) => relay.endpointType === 'wireguard',
+ );
+ const hostnames = wireguardRelays.map((relay) => relay.hostname);
+ const locatedRelays = helpers.locateRelaysByHostnames(relayList, hostnames);
+
+ await helpers.expandLocatedRelays(locatedRelays);
+
+ const buttons = routes.selectLocation.getRelaysMatching(hostnames);
+ await expect(buttons).toHaveCount(wireguardRelays.length);
+ });
+
+ test('Should disable entry server in exit list', async () => {
+ const settings = await helpers.updateMockSettings({
+ multihop: true,
+ daita: true,
+ directOnly: true,
+ });
+
+ const entryButton = routes.selectLocation.getEntryButton();
+ await entryButton.click();
+
+ // Get first wireguard relay
+ const [entryRelay, exitRelay] = relayList.countries[0].cities[0].relays.filter(
+ (relay) => relay.endpointType === 'wireguard',
+ );
+
+ if (!entryRelay) {
+ throw new Error('No wireguard relay found in mocked data');
+ }
+
+ const locatedEntryRelay = helpers.locateRelaysByHostnames(relayList, [entryRelay.hostname]);
+
+ await helpers.expandLocatedRelays(locatedEntryRelay);
+
+ await routes.selectLocation.getRelaysMatching([entryRelay.hostname]).first().click();
+
+ await helpers.updateEntryLocation(locatedEntryRelay[0], settings);
+
+ await helpers.expandLocatedRelays(locatedEntryRelay);
+ const entryRelayButton = routes.selectLocation.getRelaysMatching([entryRelay.hostname]);
+ await expect(entryRelayButton).toBeDisabled();
+
+ const locatedExitRelay = helpers.locateRelaysByHostnames(relayList, [exitRelay.hostname]);
+ await helpers.expandLocatedRelays(locatedExitRelay);
+
+ // Clicking exit relay should navigate to main route
+ const exitRelayButton = routes.selectLocation.getRelaysMatching([exitRelay.hostname]);
+ await exitRelayButton.click();
+ await util.waitForRoute(RoutePath.main);
+ });
});
test.describe('Filter', () => {
@@ -143,9 +187,18 @@ test.describe('Select location', () => {
await routes.filter.expandProviders();
await routes.filter.checkAllProvidersCheckbox();
expect(await helpers.areAllCheckboxesChecked()).toBe(false);
+ const wireguardRelays = {
+ countries: relayList.countries.map(({ cities, ...country }) => ({
+ ...country,
+ cities: cities.map(({ relays, ...city }) => ({
+ ...city,
+ relays: relays.filter((relay) => relay.endpointType === 'wireguard'),
+ })),
+ })),
+ };
// Select one provider
- const provider = relayList.countries[0].cities[0].relays[0].provider;
+ const provider = wireguardRelays.countries[0].cities[0].relays[0].provider;
await routes.filter.checkProviderCheckbox(provider);
await helpers.updateMockRelayFilter({
@@ -157,7 +210,7 @@ test.describe('Select location', () => {
const providerFilterChip = routes.selectLocation.getFilterChip('Providers: 1');
await expect(providerFilterChip).toBeVisible();
- const locatedRelays = helpers.locateRelaysByProvider(relayList, provider);
+ const locatedRelays = helpers.locateRelaysByProvider(wireguardRelays, provider);
const relays = locatedRelays.map((locatedRelay) => locatedRelay.relay);
const relayNames = relays.map((relay) => relay.hostname);
diff --git a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/daita-settings/daita-settings-route-object-model.ts b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/daita-settings/daita-settings-route-object-model.ts
new file mode 100644
index 0000000000..ba9b73a019
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/daita-settings/daita-settings-route-object-model.ts
@@ -0,0 +1,29 @@
+import { Page } from 'playwright';
+
+import { TestUtils } from '../../utils';
+import { createSelectors } from './selectors';
+
+export class DaitaSettingsRouteObjectModel {
+ readonly page: Page;
+ readonly utils: TestUtils;
+ readonly selectors: ReturnType<typeof createSelectors>;
+
+ constructor(page: Page, utils: TestUtils) {
+ this.page = page;
+ this.utils = utils;
+ this.selectors = createSelectors(page);
+ }
+
+ getEnableDaitaSwitch() {
+ return this.selectors.enableDaitaSwitch();
+ }
+
+ async setEnableDaitaSwitch(enabled: boolean) {
+ const enableDaitaSwitch = this.selectors.enableDaitaSwitch();
+ const ariaChecked = await enableDaitaSwitch.getAttribute('aria-checked');
+
+ if ((enabled && ariaChecked === 'false') || (!enabled && ariaChecked === 'true')) {
+ await enableDaitaSwitch.click();
+ }
+ }
+}
diff --git a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/daita-settings/index.ts b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/daita-settings/index.ts
new file mode 100644
index 0000000000..458dc4cafc
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/daita-settings/index.ts
@@ -0,0 +1 @@
+export * from './daita-settings-route-object-model';
diff --git a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/daita-settings/selectors.ts b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/daita-settings/selectors.ts
new file mode 100644
index 0000000000..4c8f8c51bd
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/daita-settings/selectors.ts
@@ -0,0 +1,5 @@
+import { Page } from 'playwright';
+
+export const createSelectors = (page: Page) => ({
+ enableDaitaSwitch: () => page.getByLabel('Enable'),
+});
diff --git a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/main/main-route-object-model.ts b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/main/main-route-object-model.ts
index 6e1c2bb099..ea76183ac3 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/main/main-route-object-model.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/main/main-route-object-model.ts
@@ -23,4 +23,9 @@ export class MainRouteObjectModel {
await this.selectors.settingsButton().click();
await this.utils.waitForRoute(RoutePath.settings);
}
+
+ async gotoSelectLocation() {
+ await this.selectors.selectLocationButton().click();
+ await this.utils.waitForRoute(RoutePath.selectLocation);
+ }
}
diff --git a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/main/selectors.ts b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/main/selectors.ts
index a2f120a4ef..077a34db2d 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/main/selectors.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/main/selectors.ts
@@ -2,4 +2,5 @@ import { Page } from 'playwright';
export const createSelectors = (page: Page) => ({
settingsButton: () => page.locator('button[aria-label="Settings"]'),
+ selectLocationButton: () => page.getByLabel('Select location'),
});
diff --git a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/multihop-settings/index.ts b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/multihop-settings/index.ts
new file mode 100644
index 0000000000..e9de351d62
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/multihop-settings/index.ts
@@ -0,0 +1 @@
+export * from './multihop-settings-route-object-model';
diff --git a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/multihop-settings/multihop-settings-route-object-model.ts b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/multihop-settings/multihop-settings-route-object-model.ts
new file mode 100644
index 0000000000..402ff09d71
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/multihop-settings/multihop-settings-route-object-model.ts
@@ -0,0 +1,29 @@
+import { Page } from 'playwright';
+
+import { TestUtils } from '../../utils';
+import { createSelectors } from './selectors';
+
+export class MultihopSettingsRouteObjectModel {
+ readonly page: Page;
+ readonly utils: TestUtils;
+ readonly selectors: ReturnType<typeof createSelectors>;
+
+ constructor(page: Page, util: TestUtils) {
+ this.page = page;
+ this.utils = util;
+ this.selectors = createSelectors(page);
+ }
+
+ getEnableMultihopSwitch() {
+ return this.selectors.enableMultihopSwitch();
+ }
+
+ async setEnableMultihopSwitch(enabled: boolean) {
+ const enableMultihopSwitch = this.selectors.enableMultihopSwitch();
+ const ariaChecked = await enableMultihopSwitch.getAttribute('aria-checked');
+
+ if ((enabled && ariaChecked === 'false') || (!enabled && ariaChecked === 'true')) {
+ await enableMultihopSwitch.click();
+ }
+ }
+}
diff --git a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/multihop-settings/selectors.ts b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/multihop-settings/selectors.ts
new file mode 100644
index 0000000000..217ed920a2
--- /dev/null
+++ b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/multihop-settings/selectors.ts
@@ -0,0 +1,5 @@
+import { Page } from 'playwright';
+
+export const createSelectors = (page: Page) => ({
+ enableMultihopSwitch: () => page.getByRole('checkbox', { name: 'Enable' }),
+});
diff --git a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/routes-object-model.ts b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/routes-object-model.ts
index dbb0e39803..d85c698984 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/routes-object-model.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/routes-object-model.ts
@@ -1,9 +1,11 @@
import { Page } from 'playwright';
import { TestUtils } from '../utils';
+import { DaitaSettingsRouteObjectModel } from './daita-settings';
import { FilterRouteObjectModel } from './filter';
import { LaunchRouteObjectModel } from './launch';
import { MainRouteObjectModel } from './main';
+import { MultihopSettingsRouteObjectModel } from './multihop-settings';
import { SelectLanguageRouteObjectModel } from './select-language';
import { SelectLocationRouteObjectModel } from './select-location';
import { SettingsRouteObjectModel } from './settings/settings-route-object-model';
@@ -19,6 +21,8 @@ export class RoutesObjectModel {
readonly filter: FilterRouteObjectModel;
readonly selectLocation: SelectLocationRouteObjectModel;
readonly vpnSettings: VpnSettingsRouteObjectModel;
+ readonly multihopSettings: MultihopSettingsRouteObjectModel;
+ readonly daitaSettings: DaitaSettingsRouteObjectModel;
constructor(page: Page, utils: TestUtils) {
this.selectLanguage = new SelectLanguageRouteObjectModel(page, utils);
@@ -29,5 +33,7 @@ export class RoutesObjectModel {
this.filter = new FilterRouteObjectModel(page, utils);
this.selectLocation = new SelectLocationRouteObjectModel(page, utils);
this.vpnSettings = new VpnSettingsRouteObjectModel(page, utils);
+ this.multihopSettings = new MultihopSettingsRouteObjectModel(page, utils);
+ this.daitaSettings = new DaitaSettingsRouteObjectModel(page, utils);
}
}
diff --git a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/settings/selectors.ts b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/settings/selectors.ts
index ac227d0732..abf5527eda 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/settings/selectors.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/settings/selectors.ts
@@ -1,6 +1,8 @@
import { Page } from 'playwright';
export const createSelectors = (page: Page) => ({
+ multihopSettingsButton: () => page.getByRole('button', { name: 'Multihop' }),
+ daitaSettingsButton: () => page.getByRole('button', { name: 'Daita' }),
userInterfaceButton: () => page.getByRole('button', { name: 'User interface settings' }),
vpnSettingsButton: () => page.getByRole('button', { name: 'VPN settings' }),
});
diff --git a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/settings/settings-route-object-model.ts b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/settings/settings-route-object-model.ts
index e85af07bd2..59b8609260 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/route-object-models/settings/settings-route-object-model.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/route-object-models/settings/settings-route-object-model.ts
@@ -24,4 +24,14 @@ export class SettingsRouteObjectModel {
await this.selectors.vpnSettingsButton().click();
await this.utils.waitForRoute(RoutePath.vpnSettings);
}
+
+ async gotoMultihopSettings() {
+ await this.selectors.multihopSettingsButton().click();
+ await this.utils.waitForRoute(RoutePath.multihopSettings);
+ }
+
+ async gotoDaitaSettings() {
+ await this.selectors.daitaSettingsButton().click();
+ await this.utils.waitForRoute(RoutePath.daitaSettings);
+ }
}
diff --git a/desktop/packages/mullvad-vpn/test/e2e/setup/main.ts b/desktop/packages/mullvad-vpn/test/e2e/setup/main.ts
index d23d85cfa6..de46f5e578 100644
--- a/desktop/packages/mullvad-vpn/test/e2e/setup/main.ts
+++ b/desktop/packages/mullvad-vpn/test/e2e/setup/main.ts
@@ -9,13 +9,13 @@ import {
IAccountData,
IAppVersionInfo,
ILocation,
- IRelayList,
IWireguardEndpointData,
} from '../../../src/shared/daemon-rpc-types';
import { messages, relayLocations } from '../../../src/shared/gettext';
import { IGuiSettingsState } from '../../../src/shared/gui-settings-state';
import { ITranslations, MacOsScrollbarVisibility } from '../../../src/shared/ipc-schema';
import { ICurrentAppVersionInfo } from '../../../src/shared/ipc-types';
+import { mockData } from '../mock-data';
const DEBUG = false;
@@ -74,36 +74,6 @@ class ApplicationMain {
mullvadExitIp: false,
};
- private relayList: IRelayList = {
- countries: [
- {
- name: 'Sweden',
- code: 'se',
- cities: [
- {
- name: 'Gothenburg',
- code: 'got',
- latitude: 58,
- longitude: 12,
- relays: [
- {
- hostname: 'se-got-wg-101',
- provider: 'mullvad',
- ipv4AddrIn: '127.0.0.1',
- includeInCountry: true,
- active: true,
- weight: 0,
- owned: true,
- endpointType: 'wireguard',
- daita: false,
- },
- ],
- },
- ],
- },
- ],
- };
-
private wireguardEndpointData: IWireguardEndpointData = {
portRanges: [],
udp2tcpPorts: [],
@@ -159,9 +129,8 @@ class ApplicationMain {
settings: this.settings,
isPerformingPostUpgrade: false,
deviceState: this.deviceState,
- relayListPair: {
- relays: this.relayList,
- bridges: this.relayList,
+ relayList: {
+ relayList: mockData.relayList,
wireguardEndpointData: this.wireguardEndpointData,
},
currentVersion: this.currentVersion,
diff --git a/test/test-manager/src/tests/ui.rs b/test/test-manager/src/tests/ui.rs
index 1260bfeadc..7790de9306 100644
--- a/test/test-manager/src/tests/ui.rs
+++ b/test/test-manager/src/tests/ui.rs
@@ -319,3 +319,27 @@ pub async fn test_settings_ui(
assert!(ui_result.success());
Ok(())
}
+
+/// Test DAITA UI
+#[test_function]
+pub async fn test_daita_ui(
+ _: TestContext,
+ rpc: ServiceClient,
+ _: MullvadProxyClient,
+) -> Result<(), Error> {
+ let ui_result = run_test(&rpc, &["daita-settings.spec"]).await?;
+ assert!(ui_result.success());
+ Ok(())
+}
+
+/// Test multihop UI
+#[test_function]
+pub async fn test_multihop_ui(
+ _: TestContext,
+ rpc: ServiceClient,
+ _: MullvadProxyClient,
+) -> Result<(), Error> {
+ let ui_result = run_test(&rpc, &["multihop-settings.spec"]).await?;
+ assert!(ui_result.success());
+ Ok(())
+}