test(ui): run e2e against the minified vite build, not the dev server
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 17s
CI / ui (pull_request) Successful in 46s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m9s

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.
This commit is contained in:
Ilia Denisov
2026-06-14 12:54:44 +02:00
parent 553152e195
commit abe1038333
2 changed files with 14 additions and 4 deletions
+1
View File
@@ -1,5 +1,6 @@
node_modules/ node_modules/
dist/ dist/
dist-e2e/
.svelte-kit/ .svelte-kit/
*.tsbuildinfo *.tsbuildinfo
test-results/ test-results/
+13 -4
View File
@@ -1,7 +1,11 @@
import { defineConfig, devices } from '@playwright/test'; import { defineConfig, devices } from '@playwright/test';
// Hermetic e2e: Playwright boots the Vite dev server in `mock` mode (the in-memory // Hermetic e2e: Playwright builds the app in `mock` mode (the in-memory fake transport, so the
// fake transport), so the smoke needs no backend/gateway/Postgres. // 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({ export default defineConfig({
testDir: './e2e', testDir: './e2e',
fullyParallel: true, fullyParallel: true,
@@ -13,10 +17,15 @@ export default defineConfig({
trace: 'on-first-retry', trace: 'on-first-retry',
}, },
webServer: { 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', url: 'http://localhost:4173',
reuseExistingServer: !process.env.CI, 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 // 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 // exercised in both rendering/JS engines. Note: desktop WebKit on Linux does not