Problem
BrowserPage advertises six page events:
export type PageEventType = 'console' | 'request' | 'response' | 'error' | 'load' | 'domcontentloaded'Three of them can never fire. _emitConsole (BrowserPage.ts:730), _emitResponse (:746) and _emitError (:754) are declared and have zero callers anywhere in src/. _emitRequest (:738) has one caller at :914 and works, as do load and domcontentloaded, which BrowserFrame._commit emits.
const page = new Browser().newPage()
const fired: string[] = []
for (const event of ['console', 'response', 'error', 'load', 'domcontentloaded'] as const)
page.on(event, () => fired.push(event))
page.mainFrame.window.console.log('hello from the page')
page.mainFrame.window.document.body.innerHTML = '<div></div>'
console.log(fired) // [] — the console.log printed, but nothing was emittedWhy it matters
page.on() accepts these names without complaint and returns normally, so the failure is silent. Registering a handler looks like it worked; it simply never runs.
The cost lands on the waiting patterns these events exist for. Code that awaits a console message or a response before asserting does not fail — it hangs until a timeout, and reports as a timeout rather than as a missing feature, which sends whoever is debugging it looking in the wrong place.
console is the one people reach for first, to assert a page logged no errors. A test doing that passes unconditionally today: no events, no errors, nothing checked.
Proposed scope
Either wire them up or stop advertising them. Wiring is the better answer, and each has an obvious source:
response—BrowserFrame._navigatealready has the main-resourceResponse, andRouteRegistrysees every intercepted request's response.console— theWindow's console is already a distinct object; the page can forward through it the way_emitRequestis already forwarded.error— page-level errors surfacing fromevaluateand from navigation failures.
If any cannot be sourced meaningfully, remove it from PageEventType so page.on('console', ...) is a type error rather than a silent no-op. A missing feature that announces itself is better than one that pretends.
Acceptance
- The reproduction above reports every event it registered for, or the unsupported names no longer type-check.
- A test asserts each advertised event actually fires, so a future handler cannot quietly lose its only caller — which is how this happened.
request,loadanddomcontentloadedkeep working.
Found while assessing whether wildloop's Playwright specs could port here: several await a response event before asserting, and would have hung rather than failed.