Hero Background

Next-Gen App & Browser Testing Cloud

Trusted by 2 Mn+ QAs & Devs to accelerate their release cycles

Next-Gen App & Browser Testing Cloud

Why Playwright is not able to handle alerts, pop-ups, and other web page elements?

Playwright can handle alerts, pop-ups, and other web page elements. The reason it often appears not to is that, by default, Playwright auto-dismisses every JavaScript dialog. If no page.on('dialog') listener is attached, alerts, confirms, and prompts are silently dismissed, so your test moves on as if the dialog never appeared. The fix is to register a dialog handler before the action that triggers it, and to use the right event for each element type: filechooser for uploads, waitForEvent('popup') for new tabs, httpCredentials for auth boxes, and frameLocator for iframes.

Why It Looks Like Playwright Can't Handle Alerts

Playwright handles dialogs differently from Selenium, and that difference trips up most newcomers. There is no switchTo().alert() equivalent that you call after the dialog appears. Instead, Playwright is event-driven, and the defaults are aggressive.

  • Cause: By default, dialogs are auto-dismissed. With no listener for the dialog event, every alert, confirm, and prompt is automatically dismissed, so the script never pauses and you assume nothing was triggered.
  • Cause: The handler is registered too late. A dialog is modal and blocks page execution until it is handled, so the page.on('dialog') listener must be attached before the click or action that opens it, not after.
  • Cause: Treating non-DOM elements as DOM elements. File pickers, new-tab pop-ups, and basic-auth boxes are not part of the page DOM, so locator clicks will never find them. Each needs its own event or context option.
  • Cause: Targeting elements inside an iframe or shadow DOM with a plain page locator. iframes need a frame locator, and only open shadow roots are reachable.

Handle JavaScript Alerts, Confirms, and Prompts

Register a single listener before the triggering action. Use dialog.accept() to click OK and dialog.dismiss() to click Cancel. For a prompt, pass the input text to accept(). You can also inspect dialog.message() and dialog.type() to branch your logic.

// Accept an alert or confirm (click OK)
page.on('dialog', dialog => dialog.accept());
await page.getByRole('button', { name: 'Delete' }).click();

// Dismiss a confirm (click Cancel)
page.once('dialog', dialog => dialog.dismiss());
await page.getByRole('button', { name: 'Clear cart' }).click();

// Read the message, then enter text into a prompt
page.on('dialog', async dialog => {
  console.log(dialog.type(), dialog.message()); // e.g. "prompt" "Your name?"
  await dialog.accept('Ada Lovelace');           // type value + OK
});
await page.getByRole('button', { name: 'Ask name' }).click();

The listener must call accept() or dismiss(). If it does nothing, the click that opened the dialog will stall because the modal blocks execution. The same approach handles beforeunload dialogs, which fire when you call page.close({ runBeforeUnload: true }).

Handle File Upload Pop-ups (File Chooser)

The native operating-system file picker cannot be automated by clicking it. Playwright gives you two clean options: catch the filechooser event, or skip the dialog entirely with setInputFiles() on the underlying input.

// Option A: handle the OS file picker via the filechooser event
const [fileChooser] = await Promise.all([
  page.waitForEvent('filechooser'),
  page.getByRole('button', { name: 'Upload' }).click(),
]);
await fileChooser.setFiles('reports/result.pdf');

// Option B: set files directly on the input, no dialog needed
await page.setInputFiles('input[type=file]', 'reports/result.pdf');

Handle New Tabs and Pop-up Windows

Links with target="_blank" or window.open() create a brand-new page object. Listen for the popup event before the click, then drive the returned page just like the first one. Wrapping the click and the wait in Promise.all avoids a race where the pop-up opens before you start listening.

const [popup] = await Promise.all([
  page.waitForEvent('popup'),
  page.getByRole('link', { name: 'Open dashboard' }).click(),
]);

await popup.waitForLoadState();
await popup.getByRole('button', { name: 'Continue' }).click();
console.log(await popup.title());

Switching between several windows or tabs is a related but distinct topic, covered in Why Playwright Is Not Able to Handle Multiple Windows or Tabs?

Handle Auth Pop-ups, iframes, and Shadow DOM

Three more elements regularly get labelled as unhandleable, when in fact each has a one-line answer.

  • Basic-auth box: The HTTP authentication prompt is browser chrome, not a DOM node, so no locator can reach it. Supply credentials when you create the context.
  • iframes: Elements inside a frame are invisible to a plain page locator. Enter the frame first with frameLocator(), and chain it for nested frames.
  • Shadow DOM: Playwright locators pierce open shadow roots automatically, so getByRole, getByText, and CSS selectors work with no extra API. Closed shadow roots are intentionally off-limits.
// Basic auth: pass credentials at the context level
const context = await browser.newContext({
  httpCredentials: { username: 'admin', password: 's3cret' },
});
const page = await context.newPage();
await page.goto('https://app.example.com/secure');

// iframe: enter the frame before locating
await page.frameLocator('#payment-frame')
          .getByRole('button', { name: 'Pay now' })
          .click();

// shadow DOM: locators pierce open shadow roots automatically
await page.getByRole('button', { name: 'Add to cart' }).click();

Element Type, Why It "Fails", and the Correct Fix

ElementWhy it seems unhandledCorrect Playwright approach
Alert / confirm / promptAuto-dismissed by defaultpage.on('dialog', d => d.accept()) before the action
File upload pickerOS dialog, not in the DOMfilechooser event or setInputFiles()
New tab / pop-up windowOpens a separate page objectpage.waitForEvent('popup')
Basic-auth pop-upBrowser chrome, not a DOM nodenewContext({ httpCredentials })
iframe elementPlain locator can't enter the framepage.frameLocator(selector)
Shadow DOM elementAssumed to need special APIStandard locators pierce open shadow roots

Once your dialog and pop-up handling is correct, the next variable is the browser itself, behaviour can differ across Chromium, Firefox, and WebKit. You can run the same Playwright suite across real browsers and operating systems on the TestMu AI Playwright Testing to confirm your dialog, file-chooser, and iframe logic holds up everywhere.

Frequently Asked Questions

Why does Playwright seem to ignore my alert or confirm dialog?

Because Playwright auto-dismisses dialogs by default. With no listener for the dialog event, every alert, confirm, and prompt is automatically dismissed, so the test continues as if nothing happened. Attach a page.on('dialog') handler before the action that triggers the dialog and call accept() or dismiss().

How do I accept or dismiss a Playwright dialog?

Use page.on('dialog', dialog => dialog.accept()) to click OK, or dialog.dismiss() to click Cancel, set up before the triggering click. For a prompt, pass text to accept: dialog.accept('my value'). The listener must call accept or dismiss, otherwise the action stalls because dialogs are modal and block page execution.

How does Playwright handle file upload pop-ups?

The native OS file picker cannot be clicked. Either listen for the filechooser event and call setFiles() on it, or bypass the dialog completely with page.setInputFiles() against the file input. The second approach is faster and more reliable for most uploads.

How do I handle a new tab or pop-up window in Playwright?

Set up page.waitForEvent('popup') before the click that opens the new tab, then work with the returned page object exactly like the original. Wrap the click and the wait in Promise.all so you don't miss the event.

Can Playwright interact with elements inside an iframe or shadow DOM?

Yes. For iframes, use page.frameLocator() to enter the frame before locating elements, and chain it for nested frames. For shadow DOM, Playwright locators automatically pierce open shadow roots, so getByRole, getByText, and CSS selectors work without any special API. Closed shadow roots are intentionally inaccessible.

How do I handle the browser's basic authentication pop-up?

The HTTP basic-auth box is browser chrome, not a DOM element, so you cannot click it with a locator. Pass credentials at the context level with browser.newContext({ httpCredentials: { username, password } }) and the prompt is satisfied automatically.

Related Questions

Test Your Website on 3000+ Browsers

Get 100 minutes of automation test minutes FREE!!

Test Now...

KaneAI - Testing Assistant

World’s first AI-Native E2E testing agent.

...

TestMu AI forEnterprise

Get access to solutions built on Enterprise
grade security, privacy, & compliance

  • Advanced access controls
  • Advanced data retention rules
  • Advanced Local Testing
  • Premium Support options
  • Early access to beta features
  • Private Slack Channel
  • Unlimited Manual Accessibility DevTools Tests