iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
Handle a JavaScript dialog in Playwright by registering a page.on('dialog') listener before the action that opens it, then calling dialog.accept() or dialog.dismiss(). If you do nothing, Playwright automatically dismisses dialogs. A new browser tab is different: wait for the opener page’s popup event. A print dialog is tested by intercepting window.print(), not as a normal JavaScript dialog.
The distinction matters because an alert, confirm, prompt, or beforeunload dialog blocks page execution. Once you install a listener, that listener owns the dialog; if it only logs the message and never closes it, the click that triggered it can wait indefinitely.
What counts as a dialog in Playwright?
Playwright exposes the browser’s JavaScript modal APIs through the Dialog object. The dialog’s type(), message(), and defaultValue() methods let a test inspect what appeared before choosing a response.
Recommended Free Tools
| Browser UI | dialog.type() |
Typical response | Important detail |
|---|---|---|---|
alert() |
alert |
accept() |
There is no user-entered value. |
confirm() |
confirm |
accept() or dismiss() |
Accept represents OK; dismiss represents Cancel. |
prompt() |
prompt |
accept('text') or dismiss() |
accept() can supply the text returned to the page. |
| Unload confirmation | beforeunload |
accept() or dismiss() |
It is only triggered when the page is closed with runBeforeUnload: true. |
These are browser JavaScript dialogs, not HTML elements that can be located with CSS or role selectors.
#1 Best Overall
Accept or dismiss an alert, confirm, or prompt
Install the handler first, then perform the click or other action that opens the modal:
page.on('dialog', async dialog => {
console.log(dialog.type(), dialog.message());
await dialog.accept();
});
await page.getByRole('button', { name: 'Continue' }).click();
The order is essential. Registering the listener after the click creates a race: the page can already be blocked by the modal before Playwright starts listening.
Choose a response from the dialog contents
Use the type and message to apply different behavior to different dialogs. This example rejects a destructive confirmation, supplies text to a prompt, and accepts anything else:
page.on('dialog', async dialog => {
if (dialog.type() === 'confirm' && dialog.message().includes('delete')) {
await dialog.dismiss();
} else if (dialog.type() === 'prompt') {
await dialog.accept('approved value');
} else {
await dialog.accept();
}
});
await page.getByText('Delete account').click();
For assertions, inspect dialog.type() and dialog.message() before closing it. dialog.defaultValue() is available when the test needs to verify the dialog’s supplied default.
Accepting versus dismissing
await dialog.accept(): presses OK. For a prompt, pass the string that the page should receive.await dialog.dismiss(): presses Cancel and makes a confirm or prompt return its canceled result.- Always await the call: the modal must be closed before the page can continue.
Why a click hangs when an alert appears
With no page.on('dialog') or browserContext.on('dialog') listener, Playwright auto-dismisses dialogs. That default is useful when the dialog is irrelevant to a test.
Once a listener exists, Playwright stops applying the automatic behavior. The listener must call accept() or dismiss(). A handler that only prints dialog.message() leaves the modal open, so the browser cannot execute more page JavaScript and the triggering action appears to freeze.
Rank #2
If the test already has a global listener, check that every branch closes the dialog. A conditional branch that does not match the message must still end with an explicit accept or dismiss.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Scan for outdated or missing drivers - takes under a minute3Repair Windows errors before they cause bigger problemsTest a beforeunload confirmation
Playwright does not run a page’s unload handlers during an ordinary page.close(). Pass runBeforeUnload: true when the purpose of the test is to exercise that confirmation:
page.on('dialog', async dialog => {
expect(dialog.type()).toBe('beforeunload');
await dialog.dismiss();
});
await page.close({ runBeforeUnload: true });
Use accept() instead when the scenario is intended to leave the page. The listener must be installed before the close operation.
Handle a real popup window or new tab
A window opened by window.open(), a link with a new target, or similar browser behavior is a new Page, not a JavaScript Dialog. Wait on the opener page’s popup event before clicking:
const popupPromise = page.waitForEvent('popup');
await page.getByText('open the popup').click();
const popup = await popupPromise;
console.log(await popup.evaluate(() => location.href));
The returned page is available once its initial navigation response has started. Use that Page for locators, assertions, screenshots, and further navigation. Do not try to accept or dismiss a tab with a dialog listener.
Test a print dialog
window.print() is handled separately from alert, confirm, prompt, and beforeunload. Replace the page’s print function with a promise before triggering the print control:
await page.goto('<url>');
await page.evaluate(() => {
(window as any).waitForPrintDialog = new Promise<void>(resolve => {
window.print = resolve;
});
});
await page.getByText('Print it!').click();
await page.waitForFunction(() => (window as any).waitForPrintDialog);
This verifies that the application called window.print(). It does not attempt to automate the operating-system print window as though it were a Playwright JavaScript dialog.
Apply one dialog policy to every page in a context
When the same rule should cover every page belonging to a browser context, listen on the context rather than each page:
context.on('dialog', async dialog => {
await dialog.dismiss();
});
The BrowserContext dialog event covers dialogs from every page in that context (documented as available from Playwright v1.34). Choose a page listener when only one tab has the policy; choose the context listener when popups and additional pages should behave identically.
A reliable dialog-handling checklist
- Identify the UI: decide whether it is a JavaScript dialog, a new
Page, or a print invocation. - Install the observer first: put
page.on('dialog'),context.on('dialog'), orwaitForEvent('popup')before the triggering action. - Inspect when necessary: branch on
type()andmessage(); usedefaultValue()for prompt assertions. - Close every dialog: every listener path must await
accept()ordismiss(). - Keep scope intentional: a page listener is local; a context listener affects all pages in that context.
- Use the right assertion: assert a returned popup page for a new tab, or wait on the replacement promise for print.
Troubleshooting common failures
The click or navigation never finishes
Cause: a dialog listener was registered, but one code path only logs or performs an assertion.
Fix: make every branch await dialog.accept() or dialog.dismiss(). If the dialog is irrelevant, remove the listener and allow Playwright’s automatic dismissal.
The test never sees the alert
Cause: the listener was added after the action, or the page has no listener because the dialog was auto-dismissed.
Rank #4
Fix: register the listener before the click, navigation, or script evaluation that opens the dialog. If you only need the page to continue, do not add a listener.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
A new tab is not handled
Cause: a popup page was treated as a JavaScript dialog.
Fix: create page.waitForEvent('popup') before the click, await it, and use the resulting Page.
The unload handler does not run
Cause: the page was closed without the opt-in flag.
Fix: call page.close({ runBeforeUnload: true }) and install a listener that handles the resulting beforeunload event.
Free tools Windows power users keep installed
One-click scans. No signup required.
The print test waits forever
Cause: the test is waiting for a normal dialog event or replaced window.print after clicking.
Fix: install the promise replacement first, trigger the control, then wait for the page promise.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.One complete JavaScript example
The following script shows the ordering for a confirm, a prompt, a new tab, and a print call. Replace the URL and selectors with those from your application:
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage();
page.on('dialog', async dialog => {
console.log(`${dialog.type()}: ${dialog.message()}`);
if (dialog.type() === 'prompt') {
await dialog.accept('test value');
} else {
await dialog.dismiss();
}
});
await page.goto('https://example.com/app');
await page.getByRole('button', { name: 'Open confirmation' }).click();
const popupPromise = page.waitForEvent('popup');
await page.getByRole('link', { name: 'Open report' }).click();
const popup = await popupPromise;
await popup.waitForLoadState();
await page.evaluate(() => {
(window as any).printCalled = new Promise<void>(resolve => {
window.print = resolve;
});
});
await page.getByRole('button', { name: 'Print' }).click();
await page.waitForFunction(() => (window as any).printCalled);
await browser.close();
If your project is plain JavaScript, remove the TypeScript-only type annotations such as Promise<void> and the as any casts.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallOr skip the browser setup
If the goal is a clean screenshot or PDF rather than testing dialog behavior, ScreenshotNeo provides a single request. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.
Use the API documentation at https://screenshotneo.com/docs/ for the full option list, including full-page lazy-image loading, CSS-selector element capture, dark mode, device presets, custom viewport and retina scale, PDF paper settings, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, selectable cache TTLs, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, usage data, and the OpenAPI specification.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also has an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Every feature is included on every plan. The current monthly options are:
| Plan | Price | Included shots |
|---|---|---|
| Free | $0 | 1,000; no card required |
| Starter | $5 | 3,000 |
| Growth | $15 | 15,000 |
| Pro | $39 | 60,000 |
| Scale | $99 | 250,000 |
| Business | $249 | 1,000,000 |
Yearly billing provides two months free. Start with 1,000 free screenshots a month with no card; paid plans start at $5 for 3,000 shots.
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Frequently Asked Questions
Does ScreenshotNeo replace Playwright dialog tests?
No. ScreenshotNeo returns screenshots or PDFs through an API and MCP server; Playwright remains the tool for asserting alert, confirm, prompt, beforeunload, popup-page, and print behavior.
Can I use a context listener when a popup page is created later?
Yes. A browserContext.on('dialog') listener applies to dialogs from every page in that context, including pages opened after the listener is registered.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

