I have the following test in Playwright:
test(‘should open help page’, async ({ page }) => {
await page.locator(‘body’).click();
const popupPromise = page.waitForEvent(‘popup’);
await page.keyboard.press(‘Control+Shift+F1’);
const helpPage = await popupPromise;
await expect(helpPage).toHaveURL(‘/help’);
});
But I receive this error:
Error: page.waitForEvent: Page closed
Execution log waiting for event “popup”
It seems the script is waiting for the ‘popup’ event, but the page closes before the popup fires. According to Playwright’s docs, page.waitForEvent(‘popup’) expects a popup to open in response to some action.
Playwright
+1
What could be causing this timeout and “Page closed” error? How can I correctly wait for a popup window (new tab/page) that’s triggered, especially with keyboard shortcuts or non-standard triggers?
I’ve run into this issue a few times in my projects, and from my experience, the “Page closed” error usually happens because Playwright never gets the actual popup event. With playwright waitforevent, timing matters a lot. If the popup doesn’t open exactly when Playwright is listening or if the parent page closes too early the call just times out.
This especially happens with keyboard shortcuts like Ctrl+Shift+F1, since browsers may treat them differently from click-based triggers.
What has consistently worked for me is wrapping the listener and the action together, like this:
const [helpPage] = await Promise.all([
page.waitForEvent('popup'),
page.keyboard.press('Control+Shift+F1')
]);
This way Playwright listens for the popup at the exact moment you trigger it usually eliminating the timeout completely.
Yeah, totally agree with @akanshasrivastava.1121 here. I’ve dealt with similar debugging sessions, and the key pattern I noticed is that if your main page navigates, refreshes, or closes even slightly earlier than expected, playwright waitforevent immediately throws the “Page closed” error.
The Promise.all approach is honestly the safest bet because it guarantees Playwright starts listening before the shortcut fires. I had cases where the popup depended on a handler that took a moment to respond, and without Promise.all, Playwright missed it entirely.
So if your page behavior is unpredictable, wrapping both the waiting and the triggering action together is the cleanest and most reliable fix.
I’ll add one more thing based on some testing I’ve done recently. Sometimes keyboard-triggered popups especially ones powered by browser extensions or system-level shortcuts don’t register as a true ‘popup’ from the original page. That’s when playwright waitforevent for 'popup' still fails, even inside Promise.all.
In those cases, I’ve had better luck listening for any new page inside the browser context:
const [helpPage] = await Promise.all([
page.context().waitForEvent('page'),
page.keyboard.press('Control+Shift+F1')
]);
This catches everything new windows, new tabs, whatever the browser decides to create and isn’t tied to the lifespan of the original page. If the popup isn’t directly connected to the current page, this approach usually solves it."