Problem
typeof page.setContent // 'undefined'Putting markup in a page means reaching through three objects to the document:
// Playwright
await page.setContent('<button>Save</button>')
// here
page.mainFrame.window.document.body.innerHTML = '<button>Save</button>'page.content exists as a getter, so the read side is there and the write side is not.
Why it matters
Almost every Playwright example that is not about navigation starts with setContent, so its absence is hit on the first line of a port. It is also the first thing tried when writing a test for this library: both test/locator-visibility.test.ts and test/browser-page-events.test.ts open with a local helper
function render(css: string, markup: string): void {
document.head.innerHTML = ...
document.body.innerHTML = markup
}which is a setContent with a different name, written twice because there was nothing to call.
The reach-through is not just verbose. page.mainFrame.window.document.body assumes there is a body, which is not true of a page whose document was replaced — the bug fixed in #1595 — and it skips whatever setContent should be doing around the assignment: replacing the whole document rather than only the body, and settling before it resolves.
Proposed scope
page.setContent(html, { timeout })— replace the document, not onlybody.innerHTML, so a full<!DOCTYPE html><html><head>…string lands where it belongs and a bare fragment still becomes the body.- Resolve once parsing has settled, and fire
domcontentloadedandloadas a navigation does. This is what makessetContentfollowed by an assertion safe without a wait. frame.setContenttoo, sinceBrowserFrameowns the document.- Reuse the parse path
BrowserFrame._navigatealready uses for an HTML response rather than adding a second one — two HTML entry points that disagree about how a fragment is wrapped would be a new class of bug.
Once it exists, the two local render() helpers in the test suite should be replaced by it, which is also the proof it covers the real use.
Acceptance
await page.setContent('<button>Save</button>')leavespage.getByRole('button')matching, with no wait in between.- A full document string sets
document.titlefrom its<head>, proving the whole document was replaced rather than the body filled. loadanddomcontentloadedfire.page.contentround-trips what was set.- The existing
render()helpers intest/locator-visibility.test.tsandtest/browser-page-events.test.tsare replaced withsetContent, and those suites still pass.
Found while assessing whether wildloop's Playwright specs could port here.