diff --git a/tests/browser/typeahead.spec.mjs b/tests/browser/typeahead.spec.mjs index 278f309d63..7efbac1bc7 100644 --- a/tests/browser/typeahead.spec.mjs +++ b/tests/browser/typeahead.spec.mjs @@ -1,6 +1,25 @@ -import { test, expect } from "./source-fixture.mjs"; +import { test, expect } from "./playwright-test.mjs"; +import { previewFixtures } from "./vite-server.mjs"; import { watchPageErrors } from "./page-errors.mjs"; +let fixtures; +// A dev server serves these fixtures as about 1,600 modules, and a worker's +// first navigation outlasted the navigation timeout on a busy runner. +test.beforeAll(async () => { + fixtures = await previewFixtures([ + "tests/fixtures/emoji.html", + "tests/fixtures/mentions.html", + "tests/fixtures/messages.html", + "tests/fixtures/typeahead.html", + ]); +}); +test.afterAll(() => fixtures?.close()); +// Journeys open the pages by path. The bundle's port is known only after +// beforeAll, which runs before each test's fixtures. Playwright also resolves +// baseURL for beforeAll itself, before the bundle exists. +// biome-ignore lint/correctness/noEmptyPattern: Playwright requires a destructured fixture argument. +test.use({ baseURL: async ({}, use) => use(fixtures?.origin) }); + const open = async (page) => { await page.goto("/tests/fixtures/mentions.html?test-controls"); return page.getByRole("textbox", { name: "Message #General" }); diff --git a/tests/browser/vite-server.mjs b/tests/browser/vite-server.mjs index 9caa841733..8c52306b8c 100644 --- a/tests/browser/vite-server.mjs +++ b/tests/browser/vite-server.mjs @@ -136,6 +136,49 @@ export const preview = (config) => }), ); +// A dev server transforms a page's modules on first request, so a worker's +// first navigation to a large fixture can outlast the navigation timeout on a +// busy runner. This builds the repository-relative pages once and serves the +// bundle. Development React, as a dev server serves, keeps StrictMode's checks. +// close() stops the server and removes the build. +export async function previewFixtures(pages) { + const directory = await mkdtemp(join(tmpdir(), "buzz-fixture-preview-")); + try { + const config = { + root, + configFile: false, + envDir: false, + logLevel: "error", + plugins: [react()], + define: { + "import.meta.env.DEV": "true", + "process.env.NODE_ENV": '"development"', + }, + build: { + rollupOptions: { input: pages.map((page) => join(root, page)) }, + outDir: join(directory, "dist"), + emptyOutDir: true, + target: "esnext", + }, + }; + await build(config); + const server = await preview({ + ...config, + preview: { host: "127.0.0.1", port: 0, strictPort: true }, + }); + return { + origin: `http://127.0.0.1:${server.httpServer.address().port}`, + async close() { + await new Promise((resolve) => server.httpServer.close(resolve)); + await rm(directory, { recursive: true, force: true }); + }, + }; + } catch (error) { + await rm(directory, { recursive: true, force: true }); + throw error; + } +} + // Run once per test run, before any test: it scans and bundles the fixture // dependencies so no server does that work inside a navigation budget. export async function prepareFixtureDependencies() { diff --git a/tests/browser/voice-notes.spec.mjs b/tests/browser/voice-notes.spec.mjs index b796f231b1..f47dd49cb7 100644 --- a/tests/browser/voice-notes.spec.mjs +++ b/tests/browser/voice-notes.spec.mjs @@ -1,49 +1,13 @@ import { watchPageErrors } from "./page-errors.mjs"; import { test, expect } from "./playwright-test.mjs"; -import { build, preview } from "./vite-server.mjs"; -import react from "@vitejs/plugin-react"; -import { mkdtemp, rm } from "node:fs/promises"; -import { tmpdir } from "node:os"; -import { join } from "node:path"; -import { fileURLToPath } from "node:url"; -let server, directory, url; -// A dev server serves this fixture as about 800 modules. On a busy runner, -// WebKit's first navigation took longer than the navigation timeout to load -// them, so build the page once and serve the bundle. +import { previewFixtures } from "./vite-server.mjs"; +let fixtures; +// A dev server serves this fixture as about 800 modules, and WebKit's first +// navigation outlasted the navigation timeout on a busy runner. test.beforeAll(async () => { - const root = fileURLToPath(new URL("../../", import.meta.url)); - directory = await mkdtemp(join(tmpdir(), "buzz-voice-notes-")); - const config = { - root, - configFile: false, - envDir: false, - logLevel: "error", - plugins: [react()], - // Development React, as the dev server served, keeps StrictMode's checks. - define: { - "import.meta.env.DEV": "true", - "process.env.NODE_ENV": '"development"', - }, - build: { - rollupOptions: { - input: join(root, "tests/fixtures/voice-notes.html"), - }, - outDir: join(directory, "dist"), - emptyOutDir: true, - target: "esnext", - }, - }; - await build(config); - server = await preview({ - ...config, - preview: { host: "127.0.0.1", port: 0, strictPort: true }, - }); - url = `http://127.0.0.1:${server.httpServer.address().port}/tests/fixtures/voice-notes.html`; -}); -test.afterAll(async () => { - if (server) await new Promise((resolve) => server.httpServer.close(resolve)); - if (directory) await rm(directory, { recursive: true, force: true }); + fixtures = await previewFixtures(["tests/fixtures/voice-notes.html"]); }); +test.afterAll(() => fixtures?.close()); test.beforeEach(async ({ page }) => { await page.route("**/api/relay/media?*", async (route) => { const url = new URL(route.request().url()).searchParams.get("url"); @@ -59,7 +23,7 @@ test.beforeEach(async ({ page }) => { body: Buffer.from(bytes), }); }); - await page.goto(url); + await page.goto(`${fixtures.origin}/tests/fixtures/voice-notes.html`); await page.evaluate(() => { const mic = { mode: "allow",