diff --git a/e2e/src/screenshots/run-scenarios.ts b/e2e/src/screenshots/run-scenarios.ts index d951309335..5525bb61d2 100644 --- a/e2e/src/screenshots/run-scenarios.ts +++ b/e2e/src/screenshots/run-scenarios.ts @@ -97,8 +97,15 @@ for (const scenario of allScenarios) { }); } - // Navigate to the page - await page.goto(scenario.url, { waitUntil: 'networkidle' }); + // Navigate to the page. Use networkidle so SvelteKit hydrates and API + // calls complete, but fall back to domcontentloaded if it times out + // (e.g. a persistent connection the catch-all mock didn't cover). + try { + await page.goto(scenario.url, { waitUntil: 'networkidle', timeout: 15_000 }); + } catch { + console.warn(`networkidle timed out for ${scenario.name}, falling back to current state`); + // Page has already navigated, just continue with what we have + } // Wait for specific selector if specified if (scenario.waitForSelector) { diff --git a/e2e/src/ui/mock-network/base-network.ts b/e2e/src/ui/mock-network/base-network.ts index f23202ca77..98a521c649 100644 --- a/e2e/src/ui/mock-network/base-network.ts +++ b/e2e/src/ui/mock-network/base-network.ts @@ -10,6 +10,27 @@ export const setupBaseMockApiRoutes = async (context: BrowserContext, adminUserI path: '/', }, ]); + + // Block socket.io connections — these are persistent WebSocket connections + // that prevent networkidle from resolving since there's no real server. + await context.route('**/api/socket.io**', async (route) => { + return route.abort('connectionrefused'); + }); + + // Catch-all for any /api/ endpoint not explicitly mocked below. + // Registered FIRST so specific routes (registered after) take priority + // (Playwright checks routes in reverse registration order). + // Without this, unmocked API calls hit the static preview server which + // either hangs or returns HTML, preventing networkidle and causing timeouts. + await context.route('**/api/**', async (route) => { + const method = route.request().method(); + return route.fulfill({ + status: 200, + contentType: 'application/json', + json: method === 'GET' ? [] : {}, + }); + }); + await context.route('**/api/users/me', async (route) => { return route.fulfill({ status: 200,