import fs from "node:fs"; import path from "node:path"; import { defineConfig, devices } from "@playwright/test"; import { portFor } from "yt-dlp-transcript-common/lib/ports.mjs"; import { FIXTURE_SUMMARY_NAME, writeFixtureSummary, } from "./e2e/fixture-summary"; import { FIXTURE_SETTINGS_NAME, FIXTURE_SOCIAL_TRIO, writeFixtureSettings, } from "./e2e/fixture-social"; import { FIXTURE_SOURCE_NAME, clearFixtureSource } from "./e2e/fixture-source"; // Homepage e2e. Runs against `next dev` (default mode) so it reflects uncommitted // source. Kill any stale dev server on the port between runs. // // The port comes from HOMEPAGE_E2E_PORT, the block scripts/worktree.mjs already // allocates per worktree (common/lib/ports.mjs; 3040 on main). It used to read // `PORT`, which is the EDITOR's port variable — so a worktree run pointed this // suite at whatever the editor had been given, and the per-worktree offset never // applied here. // // Test-only environment, declared here (one-core Phase 4 slice 3): // E2E_HOMEPAGE_SUMMARY_FILE set below on the dev server: the synthetic // summary it reads instead of // public/homepage-summary.json (app/lib/summary.ts, // ignored by a production build) // SETTINGS_FILE set below on the dev server: a settings.json // holding only the fixture's social links // (e2e/fixture-social.ts), never the checkout's // own — the header and footer render them // SITES_DIR set below on the dev server: an EMPTY // directory, so no homepage.json (whose // socialLinks would win over settings.json's) // is read from the checkout // E2E_EXPECT_SOURCE `1` (set by whoever runs the suite, after // `archilyzer source publish`): e2e/source.spec.ts // FAILS on the /source/ page's empty state instead // of accepting it // E2E_SOURCE_PUBLIC_DIR set below on the dev server: the directory the // /source/ page reads the publish from WHILE it // holds a manifest (app/lib/source.ts, ignored by // a production build). e2e/source-history.spec.ts // writes a fixture publish there and removes it; // empty, the page reads public/ const PORT = portFor("HOMEPAGE_E2E_PORT"); const baseURL = `http://localhost:${PORT}`; // The dev server reads a SYNTHETIC summary (e2e/fixture-summary.ts), never // public/homepage-summary.json: a fresh clone has none, and the operator's // numbers move. Built by the real summary builder, deterministic, written // here to e2e/.e2e-summary.json (gitignored) before the server starts; the // specs that read numbers read the same file. const FIXTURE_SUMMARY = path.resolve(process.cwd(), "e2e", FIXTURE_SUMMARY_NAME); writeFixtureSummary(FIXTURE_SUMMARY); // The same for settings.json: the operator's social links move, and a worktree's // settings.json is a copy of theirs. The e2e reads the fixture's (the trio). const FIXTURE_SETTINGS = path.resolve(process.cwd(), "e2e", FIXTURE_SETTINGS_NAME); writeFixtureSettings(FIXTURE_SETTINGS, FIXTURE_SOCIAL_TRIO); const FIXTURE_SITES_DIR = path.resolve(process.cwd(), "e2e", ".e2e-sites"); fs.mkdirSync(FIXTURE_SITES_DIR, { recursive: true }); // The fixture publish (e2e/fixture-source.ts): none until a spec writes one, // so a killed run's leftover never stands in for public/. const FIXTURE_SOURCE_DIR = path.resolve(process.cwd(), "e2e", FIXTURE_SOURCE_NAME); clearFixtureSource(FIXTURE_SOURCE_DIR); // E2E_MODE (start | dev), the editor's and umtool's switch: those suites // default to `start`, a production build the stamp vouches for // (scripts/e2e-stamp.mjs). This suite always runs under `next dev`: the homepage is a // STATIC export, and its fixture summary and fixture publish // (E2E_HOMEPAGE_SUMMARY_FILE, E2E_SOURCE_PUBLIC_DIR) are read only outside a // production build, by design. // `E2E_MODE=start` is accepted and says so; anything else is refused. { const mode = (process.env.E2E_MODE ?? "").trim().toLowerCase(); if (mode !== "" && mode !== "start" && mode !== "dev") { throw new Error(`E2E_MODE=${process.env.E2E_MODE} is not a mode: use start or dev`); } if (mode === "start" && !process.env.E2E_BUILD_CHECKED) { process.stderr.write( "homepage e2e: E2E_MODE=start has no build to run here (a static export bakes its fixtures in at build time) — running next dev\n", ); process.env.E2E_BUILD_CHECKED = "1"; } } export default defineConfig({ testDir: "./e2e", timeout: 30_000, retries: process.env.CI ? 2 : 0, // `json` beside `list`: the run's timing record, which // scripts/e2e-timings.mjs totals per spec against the branch's last run. reporter: process.env.CI ? "github" : [["list"], ["json", { outputFile: "test-results/timings.json" }]], outputDir: "test-results/", fullyParallel: false, workers: 1, webServer: { command: `pnpm exec next dev --port ${PORT}`, url: baseURL, timeout: 120_000, reuseExistingServer: !process.env.CI, env: { E2E_HOMEPAGE_SUMMARY_FILE: FIXTURE_SUMMARY, SETTINGS_FILE: FIXTURE_SETTINGS, SITES_DIR: FIXTURE_SITES_DIR, E2E_SOURCE_PUBLIC_DIR: FIXTURE_SOURCE_DIR, }, }, use: { baseURL, trace: "on-first-retry", screenshot: "only-on-failure", }, projects: [ { name: "chromium", use: { ...devices["Desktop Chrome"], viewport: { width: 1100, height: 1100 } }, }, ], });