lgtm 2
Some checks failed
Build and publish Docker image / build-and-push (push) Failing after 2m43s
CI / Check (push) Failing after 3m7s

This commit is contained in:
Andras Schmelczer 2026-05-14 22:39:41 +01:00
parent a8de0a614d
commit 3fa95819e3
30 changed files with 907 additions and 205 deletions

View file

@ -1,4 +1,11 @@
import { chromium, type Browser, type BrowserContext, type Page, type Route } from 'playwright';
import {
chromium,
type Browser,
type BrowserContext,
type Page,
type Request,
type Route,
} from 'playwright';
import { existsSync, readdirSync } from 'fs';
import { NetworkCache } from './network-cache.js';
@ -234,13 +241,13 @@ export async function takeScreenshot(url: string, authHeader?: string): Promise<
const page = await acquirePage();
const t0 = performance.now();
let postLoadNavigations = 0;
let postLoadDocumentNavigations = 0;
let navigationLoopDetected = false;
const mainFrame = page.mainFrame();
const onNavigated = (frame: ReturnType<typeof page.mainFrame>) => {
if (frame !== mainFrame) return;
postLoadNavigations += 1;
if (postLoadNavigations > MAX_POST_LOAD_NAVIGATIONS) {
const onNavigationRequest = (request: Request) => {
if (request.frame() !== mainFrame || !request.isNavigationRequest()) return;
postLoadDocumentNavigations += 1;
if (postLoadDocumentNavigations > MAX_POST_LOAD_NAVIGATIONS) {
navigationLoopDetected = true;
}
};
@ -265,10 +272,11 @@ export async function takeScreenshot(url: string, authHeader?: string): Promise<
if (response) {
console.log(` Navigate: ${(t1 - t0).toFixed(0)}ms (status ${response.status()})`);
}
// Start counting only AFTER the initial navigation completes — the
// initial `goto` itself fires framenavigated, so we'd double-count.
postLoadNavigations = 0;
page.on('framenavigated', onNavigated);
// Start counting only AFTER the initial navigation completes. Count
// document navigation requests rather than `framenavigated`, because
// SPA history/query updates fire frame navigation events in dev mode.
postLoadDocumentNavigations = 0;
page.on('request', onNavigationRequest);
// Wait for the frontend to signal that data is loaded and layers created
try {
@ -281,7 +289,7 @@ export async function takeScreenshot(url: string, authHeader?: string): Promise<
if (navigationLoopDetected) {
throw new Error(
`Navigation loop detected (${postLoadNavigations} post-load navigations)`,
`Navigation loop detected (${postLoadDocumentNavigations} post-load document navigations)`,
);
}
const t2 = performance.now();
@ -302,7 +310,7 @@ export async function takeScreenshot(url: string, authHeader?: string): Promise<
return Buffer.from(screenshot);
} finally {
page.off('framenavigated', onNavigated);
page.off('request', onNavigationRequest);
// Remove page-level auth route before returning page to pool
// so the next screenshot doesn't inherit stale credentials
if (authHeader) {