From 28dee9b82bc79520f63e743cd6ecd04dcafdca30 Mon Sep 17 00:00:00 2001 From: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz> Date: Mon, 17 Aug 2026 20:14:41 -0400 Subject: [PATCH] test(e2e): use auto fixture for app bootstrap Signed-off-by: Brother Darryl <146fb160a3266e6165bfa385f6048c975eda9e21cf65da097a0b5ea7952532a5@buzz.block.builderlab.xyz> --- desktop/biome.json | 24 ++++++++- desktop/tests/helpers/test.ts | 91 +++++++++-------------------------- web/biome.json | 24 ++++++++- web/tests/helpers/test.ts | 91 +++++++++-------------------------- 4 files changed, 94 insertions(+), 136 deletions(-) diff --git a/desktop/biome.json b/desktop/biome.json index 12e778e4a9..70c87f8349 100644 --- a/desktop/biome.json +++ b/desktop/biome.json @@ -1,3 +1,25 @@ { - "extends": ["../biome.json"] + "extends": ["../biome.json"], + "overrides": [ + { + "includes": ["tests/e2e/**"], + "linter": { + "rules": { + "style": { + "noRestrictedImports": { + "level": "error", + "options": { + "paths": { + "@playwright/test": { + "importNames": ["test", "base", "default", "*"], + "message": "Import test from ../helpers/test so every E2E test receives automatic origin bootstrap." + } + } + } + } + } + } + } + } + ] } diff --git a/desktop/tests/helpers/test.ts b/desktop/tests/helpers/test.ts index d9adb294c8..ba02d9849b 100644 --- a/desktop/tests/helpers/test.ts +++ b/desktop/tests/helpers/test.ts @@ -1,11 +1,11 @@ import { expect, test as base } from "@playwright/test"; -import { bootstrapE2ePage } from "./bootstrap"; +import { E2E_APP_ORIGIN, bootstrapE2ePage } from "./bootstrap"; -export { expect }; export type * from "@playwright/test"; type E2eFixtures = { + automaticE2eBootstrap: void; /** * Set false only when a test must inspect or configure the page before its * first app navigation. @@ -13,71 +13,28 @@ type E2eFixtures = { e2eBootstrap: boolean; }; -type TestBody = ( - fixtures: Record, - testInfo: unknown, -) => unknown; - -const baseTest = base.extend({ +const test = base.extend({ e2eBootstrap: [true, { option: true }], + automaticE2eBootstrap: [ + async ({ page, baseURL, e2eBootstrap }, use) => { + if (e2eBootstrap) { + if (!baseURL) { + throw new Error( + "E2E app navigation requires Playwright use.baseURL.", + ); + } + const expectedOrigin = new URL(baseURL).origin; + if (expectedOrigin !== E2E_APP_ORIGIN) { + throw new Error( + `E2E app origin mismatch: Playwright baseURL resolves to ${expectedOrigin}, expected ${E2E_APP_ORIGIN}.`, + ); + } + await bootstrapE2ePage(page, { expectedOrigin }); + } + await use(); + }, + { auto: true }, + ], }); -function withAutomaticBootstrap(register: (...args: unknown[]) => unknown) { - return new Proxy(register, { - apply(target, thisArg, args: unknown[]) { - const bodyIndex = args.length - 1; - const body = args[bodyIndex]; - if (typeof body !== "function") - return Reflect.apply(target, thisArg, args); - - args[bodyIndex] = async ( - { - page, - baseURL, - e2eBootstrap, - browser, - context, - request, - }: { - page: import("@playwright/test").Page; - baseURL?: string; - e2eBootstrap: boolean; - browser: unknown; - context: unknown; - request: unknown; - }, - testInfo: unknown, - ) => { - if (e2eBootstrap) { - if (!baseURL) { - throw new Error( - "E2E app navigation requires Playwright use.baseURL.", - ); - } - await bootstrapE2ePage(page, { - expectedOrigin: new URL(baseURL).origin, - }); - } - return (body as TestBody)( - { page, baseURL, e2eBootstrap, browser, context, request }, - testInfo, - ); - }; - return Reflect.apply(target, thisArg, args); - }, - get(target, property, receiver) { - const value = Reflect.get(target, property, receiver); - return typeof property === "string" && - ["only", "skip", "fixme", "fail"].includes(property) && - typeof value === "function" - ? withAutomaticBootstrap(value) - : value; - }, - }); -} - -/** - * Playwright test API that bootstraps the app after suite and test hooks have - * registered their init scripts, but before each test body can use the page. - */ -export const test = withAutomaticBootstrap(baseTest) as typeof baseTest; +export { expect, test }; diff --git a/web/biome.json b/web/biome.json index 12e778e4a9..70c87f8349 100644 --- a/web/biome.json +++ b/web/biome.json @@ -1,3 +1,25 @@ { - "extends": ["../biome.json"] + "extends": ["../biome.json"], + "overrides": [ + { + "includes": ["tests/e2e/**"], + "linter": { + "rules": { + "style": { + "noRestrictedImports": { + "level": "error", + "options": { + "paths": { + "@playwright/test": { + "importNames": ["test", "base", "default", "*"], + "message": "Import test from ../helpers/test so every E2E test receives automatic origin bootstrap." + } + } + } + } + } + } + } + } + ] } diff --git a/web/tests/helpers/test.ts b/web/tests/helpers/test.ts index d9adb294c8..ba02d9849b 100644 --- a/web/tests/helpers/test.ts +++ b/web/tests/helpers/test.ts @@ -1,11 +1,11 @@ import { expect, test as base } from "@playwright/test"; -import { bootstrapE2ePage } from "./bootstrap"; +import { E2E_APP_ORIGIN, bootstrapE2ePage } from "./bootstrap"; -export { expect }; export type * from "@playwright/test"; type E2eFixtures = { + automaticE2eBootstrap: void; /** * Set false only when a test must inspect or configure the page before its * first app navigation. @@ -13,71 +13,28 @@ type E2eFixtures = { e2eBootstrap: boolean; }; -type TestBody = ( - fixtures: Record, - testInfo: unknown, -) => unknown; - -const baseTest = base.extend({ +const test = base.extend({ e2eBootstrap: [true, { option: true }], + automaticE2eBootstrap: [ + async ({ page, baseURL, e2eBootstrap }, use) => { + if (e2eBootstrap) { + if (!baseURL) { + throw new Error( + "E2E app navigation requires Playwright use.baseURL.", + ); + } + const expectedOrigin = new URL(baseURL).origin; + if (expectedOrigin !== E2E_APP_ORIGIN) { + throw new Error( + `E2E app origin mismatch: Playwright baseURL resolves to ${expectedOrigin}, expected ${E2E_APP_ORIGIN}.`, + ); + } + await bootstrapE2ePage(page, { expectedOrigin }); + } + await use(); + }, + { auto: true }, + ], }); -function withAutomaticBootstrap(register: (...args: unknown[]) => unknown) { - return new Proxy(register, { - apply(target, thisArg, args: unknown[]) { - const bodyIndex = args.length - 1; - const body = args[bodyIndex]; - if (typeof body !== "function") - return Reflect.apply(target, thisArg, args); - - args[bodyIndex] = async ( - { - page, - baseURL, - e2eBootstrap, - browser, - context, - request, - }: { - page: import("@playwright/test").Page; - baseURL?: string; - e2eBootstrap: boolean; - browser: unknown; - context: unknown; - request: unknown; - }, - testInfo: unknown, - ) => { - if (e2eBootstrap) { - if (!baseURL) { - throw new Error( - "E2E app navigation requires Playwright use.baseURL.", - ); - } - await bootstrapE2ePage(page, { - expectedOrigin: new URL(baseURL).origin, - }); - } - return (body as TestBody)( - { page, baseURL, e2eBootstrap, browser, context, request }, - testInfo, - ); - }; - return Reflect.apply(target, thisArg, args); - }, - get(target, property, receiver) { - const value = Reflect.get(target, property, receiver); - return typeof property === "string" && - ["only", "skip", "fixme", "fail"].includes(property) && - typeof value === "function" - ? withAutomaticBootstrap(value) - : value; - }, - }); -} - -/** - * Playwright test API that bootstraps the app after suite and test hooks have - * registered their init scripts, but before each test body can use the page. - */ -export const test = withAutomaticBootstrap(baseTest) as typeof baseTest; +export { expect, test };