Handle a popup window

Wait for a popup around the click that opens it, then act on the new page.

When an action opens a new tab or window, you need the popup as its own Page before you can act on it. waitForPopup() registers the wait around the triggering action, avoiding the race between two separate statements.

Wait for the popup around the click

Pass the action as a callable. waitForPopup() arms the wait, runs your callable, and returns the popup Page.

php
$popup = $page->waitForPopup(function () use ($page): void {
    $page->getByRole('link', ['name' => 'Open dashboard'])->click();
});

The action goes inside the callable. That ordering is the point: the wait is already listening when the click fires.

Act on the new page

The returned $popup is a full Page. Wait for it to settle, then use locators on it exactly as you would on the main page.

php
$popup->waitForLoadState();

expect($popup->getByRole('heading', ['name' => 'Dashboard']))->toBeVisible();

$popup->getByRole('button', ['name' => 'Refresh'])->click();

Keep each page named for its role

Assign the popup to a variable that says what it is, so every later assertion makes clear which page it targets.

php
expect($popup->getByText('Signed in as Ada'))->toBeVisible();
expect($page->getByText('Session opened in a new window'))->toBeVisible();

Pitfalls

  • Do not click first and then wait. A popup opened before the wait is armed is gone, and waitForPopup() will time out.
  • If a flow depends on the popup, assert on the popup itself. Asserting only on the original page hides a popup that never opened.

Expected result

waitForPopup() returns the new page, and assertions on that page prove the popup loaded the expected content. Keep a separate assertion on the original page only when it should also change.

Go next

← All recipes