diff options
| author | Oskar <oskar@mullvad.net> | 2025-09-29 13:00:22 +0200 |
|---|---|---|
| committer | Oskar <oskar@mullvad.net> | 2025-09-30 09:37:18 +0200 |
| commit | 944dfd4f434e4abd68049588316d7f3f7cf18e55 (patch) | |
| tree | d73721ad96c6550c9de8d6b6675792655430ecf7 /desktop | |
| parent | f0d4a04761dfe942bf85c270581efc1d7935c211 (diff) | |
| download | mullvadvpn-944dfd4f434e4abd68049588316d7f3f7cf18e55.tar.xz mullvadvpn-944dfd4f434e4abd68049588316d7f3f7cf18e55.zip | |
Add path matcher for tests
Diffstat (limited to 'desktop')
6 files changed, 81 insertions, 2 deletions
diff --git a/desktop/packages/mullvad-vpn/scripts/build-test-executable.sh b/desktop/packages/mullvad-vpn/scripts/build-test-executable.sh index 3950f548ab..cd1f83db70 100755 --- a/desktop/packages/mullvad-vpn/scripts/build-test-executable.sh +++ b/desktop/packages/mullvad-vpn/scripts/build-test-executable.sh @@ -15,6 +15,7 @@ ASSETS=( "build-standalone/src/shared/constants/*.js" "build-standalone/src/shared/routes.js" "build-standalone/test/e2e/utils.js" + "build-standalone/test/e2e/lib/*.js" "build-standalone/test/e2e/route-object-models/**/*.js" "build-standalone/test/e2e/shared/*.js" "build-standalone/test/e2e/installed/**/*.js" diff --git a/desktop/packages/mullvad-vpn/test/e2e/lib/path-helpers.ts b/desktop/packages/mullvad-vpn/test/e2e/lib/path-helpers.ts new file mode 100644 index 0000000000..5fe0573084 --- /dev/null +++ b/desktop/packages/mullvad-vpn/test/e2e/lib/path-helpers.ts @@ -0,0 +1,42 @@ +import { expect } from '@playwright/test'; + +// Match the actual path against against the expected path where the expected can contain parameters +function toMatchPath(actual: string, expected: string | null) { + const pass = matchPaths(expected, actual); + const message = () => + pass + ? `Expected path to be "${expected}"` + : `Expected path "${expected}", but found "${actual}"`; + return { pass, message }; +} + +expect.extend({ toMatchPath }); + +function trimTrailingSlash(value: string): string { + return value.replaceAll(/\/$/g, ''); +} + +// Match b against a where a can contain parameters +export function matchPaths(a: string | null, b: string | null): boolean { + if (b?.includes(':')) { + throw new Error('Only a is allowed to contain parameters'); + } + + if (a === null || b === null) { + return a === b; + } + + const aParts = trimTrailingSlash(a).split('/'); + const bParts = trimTrailingSlash(b).split('/'); + + return ( + aParts.length >= bParts.length && + aParts.every((aPart, i) => { + if (aPart.startsWith(':')) { + return aPart.endsWith('?') ? true : bParts[i] !== undefined; + } else { + return aPart === bParts[i]; + } + }) + ); +} diff --git a/desktop/packages/mullvad-vpn/test/e2e/types/index.d.ts b/desktop/packages/mullvad-vpn/test/e2e/types/index.d.ts new file mode 100644 index 0000000000..21befea35d --- /dev/null +++ b/desktop/packages/mullvad-vpn/test/e2e/types/index.d.ts @@ -0,0 +1,9 @@ +import '@playwright/test'; + +declare global { + namespace PlaywrightTest { + interface Matchers<R> { + toMatchPath(template: string | null): Promise<R>; + } + } +} diff --git a/desktop/packages/mullvad-vpn/test/e2e/utils.ts b/desktop/packages/mullvad-vpn/test/e2e/utils.ts index 7c5461b48f..9534c4474a 100644 --- a/desktop/packages/mullvad-vpn/test/e2e/utils.ts +++ b/desktop/packages/mullvad-vpn/test/e2e/utils.ts @@ -1,3 +1,5 @@ +import './lib/path-helpers'; + import { expect } from '@playwright/test'; import fs from 'fs'; import { _electron as electron, ElectronApplication, Locator, Page } from 'playwright'; @@ -48,14 +50,14 @@ function getCurrentRoute(page: Page): Promise<string | null> { // Returns a promise which resolves when the provided route is reached. async function expectRoute(page: Page, expectedRoute: RoutePath): Promise<void> { - await expect.poll(async () => getCurrentRoute(page)).toBe(expectedRoute); + await expect.poll(async () => getCurrentRoute(page)).toMatchPath(expectedRoute); } // Returns a promise which resolves when the route changes. async function expectRouteChange(page: Page, trigger: TriggerFn) { const initialRoute = await getCurrentRoute(page); await trigger(); - await expect.poll(async () => getCurrentRoute(page)).not.toBe(initialRoute); + await expect.poll(async () => getCurrentRoute(page)).not.toMatchPath(initialRoute); } const getStyleProperty = (locator: Locator, property: string) => { diff --git a/desktop/packages/mullvad-vpn/test/unit/path-helpers.spec.ts b/desktop/packages/mullvad-vpn/test/unit/path-helpers.spec.ts new file mode 100644 index 0000000000..0c0936c8aa --- /dev/null +++ b/desktop/packages/mullvad-vpn/test/unit/path-helpers.spec.ts @@ -0,0 +1,24 @@ +import { expect } from 'chai'; +import { describe, it } from 'mocha'; + +import { matchPaths } from '../e2e/lib/path-helpers'; + +describe('E2E test path helper', () => { + it('should identify matching paths', () => { + expect(matchPaths('/a/b/c', '/a/b/c')).to.be.true; + expect(matchPaths('/a/b/c', '/a/b/c/')).to.be.true; + expect(matchPaths('/a/b/:param', '/a/b/c')).to.be.true; + expect(matchPaths('/a/:param/:param', '/a/b/c')).to.be.true; + expect(matchPaths('/a/:param/:param?', '/a/b/c')).to.be.true; + expect(matchPaths('/a/:param/:param?', '/a/b')).to.be.true; + expect(matchPaths('/a/:param?/:param?', '/a')).to.be.true; + + expect(matchPaths('/a/b/c', '/a/b')).to.be.false; + expect(matchPaths('/a/b/c', '/a/b/d')).to.be.false; + expect(matchPaths('/a/b/c', '/a/b/c/d')).to.be.false; + expect(matchPaths('/a/b/c', 'a/b/c')).to.be.false; + expect(matchPaths('/a/b/:param', '/a/b')).to.be.false; + + expect(() => matchPaths('/a/b/c', '/a/b/:param')).to.throw(); + }); +}); diff --git a/desktop/packages/mullvad-vpn/tsconfig.json b/desktop/packages/mullvad-vpn/tsconfig.json index 04a6153f76..fbd82c8b4c 100644 --- a/desktop/packages/mullvad-vpn/tsconfig.json +++ b/desktop/packages/mullvad-vpn/tsconfig.json @@ -17,6 +17,7 @@ ], "typeRoots": [ "./types", + "./test/e2e/types", "node_modules/@types", "../../node_modules/@types" ] |
