fix: bound browser scenario initial navigation during startup

This commit is contained in:
morluto
2026-10-24 02:22:34 +08:00
parent 597aecfb7d
commit 5cded74b6d
3 changed files with 36 additions and 3 deletions
+4 -2
View File
@@ -27,10 +27,12 @@ Duration, action, and navigation timeouts remain separate from CDP command
deadlines. `command_timeout_ms` (default 20000; omit it to keep that default
and leave it out of Evidence) bounds launch startup, the connect-over-CDP
handshake, target lookup, environment commands, and storage CDP commands,
including replies from attached child targets. Set it in the scenario JSON.
including replies from attached child targets. It also bounds the initial
`start_url` navigation through the load event; a redirect or replacement
navigation cannot leave startup waiting indefinitely. Set it in the scenario JSON.
`capture-browser-scenario` has no `--command-timeout-ms` option.
It does not change action `timeout_ms`, `wait_for_timeout.duration_ms`, or
navigation. Service-worker restoration keeps a fixed 5000 ms grace and does
action navigation. Service-worker restoration keeps a fixed 5000 ms grace and does
not inherit a raised `command_timeout_ms`. CDP discovery stays on its own
fixed deadline. When service workers are blocked, the registration check
before discovery has its own fixed 10000 ms deadline; `command_timeout_ms`
@@ -1453,3 +1453,34 @@ it("retains late opening failure cleanup after cancellation until the exact prof
await provider.close();
}
});
it.each(["launch", "connect"] as const)(
"bounds never-settling initial navigation and awaits %s cleanup",
async (mode) => {
vi.useFakeTimers();
const { cleanup, page, storage } = fixture();
const request = browserScenarioSchema.parse({
...(mode === "launch" ? scenario : connectedScenario("allow")),
command_timeout_ms: 250,
});
page.goto.mockImplementation(() => new Promise<never>(() => {}));
try {
const opening = PlaywrightScenarioSession.open(request, {}, {}).catch(
(cause: unknown) => cause,
);
await vi.advanceTimersByTimeAsync(250);
const failure = await opening;
expect(failure).toMatchObject({
_tag: "AnalysisTimeoutError",
operation: "capture_browser_scenario",
timeoutMs: 250,
});
expect(cleanup.close).toHaveBeenCalledOnce();
expect(storage?.close).toHaveBeenCalledWith(true);
expect(page.goto).toHaveBeenCalledOnce();
} finally {
await cleanup.cleanupLease.release();
vi.useRealTimers();
}
},
);
+1 -1
View File
@@ -221,7 +221,7 @@ export class PlaywrightScenarioSession implements BrowserScenarioSessionPort {
waitUntil: "load",
timeout: 0,
}),
undefined,
scenario.command_timeout_ms,
options.signal,
);
await withPlaywrightExecutionBoundary(