From abe10383337652bbb555e2c2fa48eb5894ca04fa Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Sun, 14 Jun 2026 12:54:44 +0200 Subject: [PATCH] test(ui): run e2e against the minified vite build, not the dev server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The e2e booted the unminified Vite dev server, so production-minification bugs were invisible to it — exactly how a minifier dropping a bare `void recenter` reactive read slipped through. Build the app in mock mode and serve the minified artifact via `vite preview` instead, so the smoke exercises the same bundle the contour ships. Build to dist-e2e/ (gitignored) so it never clobbers the dist/ the bundle-size gate measures, and with `--base /` so the SPA-fallback also boots a subpath like /telegram/ (the production relative base needs the gateway's path mapping, absent under a plain preview). All 118 specs pass against the build on both engines, including the hint-recenter spec — confirming the hardened dependency survives minification. --- ui/.gitignore | 1 + ui/playwright.config.ts | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ui/.gitignore b/ui/.gitignore index 612bbcc..4917c73 100644 --- a/ui/.gitignore +++ b/ui/.gitignore @@ -1,5 +1,6 @@ node_modules/ dist/ +dist-e2e/ .svelte-kit/ *.tsbuildinfo test-results/ diff --git a/ui/playwright.config.ts b/ui/playwright.config.ts index cc28f70..78e5d74 100644 --- a/ui/playwright.config.ts +++ b/ui/playwright.config.ts @@ -1,7 +1,11 @@ import { defineConfig, devices } from '@playwright/test'; -// Hermetic e2e: Playwright boots the Vite dev server in `mock` mode (the in-memory -// fake transport), so the smoke needs no backend/gateway/Postgres. +// Hermetic e2e: Playwright builds the app in `mock` mode (the in-memory fake transport, so the +// smoke needs no backend/gateway/Postgres) and serves the MINIFIED artifact via `vite preview`, +// rather than the dev server. Running against the production-minified bundle is what the contour +// ships, so the e2e now catches minification-only regressions — e.g. a reactive dependency the +// minifier drops — which the unminified dev server silently hid. The build goes to dist-e2e/ so it +// never clobbers the dist/ the bundle-size gate measures. export default defineConfig({ testDir: './e2e', fullyParallel: true, @@ -13,10 +17,15 @@ export default defineConfig({ trace: 'on-first-retry', }, webServer: { - command: 'pnpm exec vite --mode mock --port 4173 --strictPort', + // `--base /` overrides the production relative base (`./`, which lets the gateway serve the SPA + // under /app/ and /telegram/): served from the preview root, an absolute base keeps assets at + // /assets/ so the SPA-fallback also boots a subpath like /telegram/. Base only prefixes asset + // URLs, so the minified JS under test is identical to the contour's. + command: + 'pnpm exec vite build --mode mock --base / --outDir dist-e2e --emptyOutDir && pnpm exec vite preview --outDir dist-e2e --port 4173 --strictPort', url: 'http://localhost:4173', reuseExistingServer: !process.env.CI, - timeout: 60_000, + timeout: 120_000, }, // Run the same hermetic specs in Chromium and WebKit (Safari's engine) so the UI is // exercised in both rendering/JS engines. Note: desktop WebKit on Linux does not