Summary
BrowserPage.evaluate() does not currently provide Playwright-like page-context evaluation:
- String expressions run through host
eval(), wheredocumentandwindoware not the frame's globals. - Function arguments are not accepted or forwarded.
- A function that expects an argument silently receives
undefined.
Reproduction
import { Browser } from 'very-happy-dom'
const page = new Browser().newPage()
page.content = '<head><title>Page title</title></head><body></body>'
page.evaluate('document.title')
// ReferenceError: document is not defined
;(page.evaluate as any)((value: number) => value + 1, 41)
// NaN (the argument is discarded)Confirmed against main at 59d2e7c3 with Bun 1.4.1.
Current implementation
BrowserFrame.evaluate() creates a wrapper with window and document only for function input and invokes the function with no arguments. String input uses direct eval(code), so it executes in the module/host realm instead of the frame realm.
Expected behavior
Support a signature equivalent to:
evaluate<R, Arg>(fn: string | ((arg: Arg) => R), arg?: Arg): Promise<R>Both forms should resolve window, document, location, and other exposed globals from the target frame. Function arguments and return values should cross the boundary predictably, with clear errors for unsupported values.
Playwright documents page-context execution and serializable arguments here: https://playwright.dev/docs/evaluating
Acceptance cases
expect(await page.evaluate('document.title')).toBe('Page title')
expect(await page.evaluate(value => value + 1, 41)).toBe(42)
expect(await page.evaluate(() => window.location.href)).toBe(page.url)Frame evaluation should use the selected frame's globals rather than the process globals.
Why this matters
Evaluation is the escape hatch for browser automation. Without page globals and arguments, callers cannot inspect application state or execute common Playwright migration patterns, even when the virtual DOM already implements the relevant browser API.