Decide the dialog answer before the click.
Handle native confirmation deliberately, then verify what acceptance and cancellation actually change.


The test clicks Remove demo draft. A browser confirmation opens. A listener prints its message and then does nothing. The click never finishes. The application is waiting for an answer, not for another timeout. A dialog handler that observes without responding can turn an otherwise simple test into a stalled journey.
This guide follows Playwright's dialogs documentation using an invented local draft. There is no real document deletion or backend request. We test both answers to a native confirm dialog and the visible result of each answer. The test policy is decided before the action opens the dialog.
Native confirmation is not a page modal
A JavaScript confirm dialog is browser UI. It is not an HTML element that getByRole can click inside the page. An application-designed modal is different: that interface may have a dialog role and ordinary buttons. First identify which mechanism the feature uses.
Playwright dismisses native dialogs automatically when no listener is registered. Once a dialog listener exists, the handler must accept or dismiss the dialog. A listener that only logs the message can block the triggering action. That difference explains why adding debug output can change a test's behavior.
Register the answer before the action
Attach the handler before clicking. Use a one-time listener when the case expects exactly one dialog, and keep the expected answer visible in the code. For the cancellation case, preserve the draft:
let observed;
page.once('dialog', async dialog => {
observed = { type: dialog.type(), message: dialog.message() };
await dialog.dismiss();
});
await page.getByRole('button', { name: 'Remove demo draft' }).click();
expect(observed).toEqual({ type: 'confirm', message: 'Remove demo draft?' });
await expect(page.getByRole('status')).toHaveText('Demo draft kept');This snippet assumes the controlled fixture and an imported Playwright expect. The handler answers first and the test checks the observed type and message after the click. That order avoids leaving a mismatched dialog open because an assertion inside the handler failed before it could respond.
For an approved acceptance case, attach a fresh handler that calls accept(), repeat the action on a fresh fixture, and assert the resulting local state. Do not reuse a blanket accept-all listener across unrelated journeys. A future confirmation might ask for a different destructive action, and the listener would silently answer yes.
Cancellation needs a business expectation
Dismissing the dialog proves the test supplied the cancellation answer. It does not prove the application respected it. Check the draft remains available and that the next useful action still works. In a real product, add the appropriate supported evidence that the server record was not removed.
Acceptance needs the same discipline. A confirmation disappearing is not proof of deletion. Establish the controlled record, permitted staging environment and final result before writing the positive assertion. Our demo changes a status only, so its screenshot cannot stand in for server-side evidence.
Unexpected dialogs deserve a clear failure rather than a broad handler that makes them disappear. Record enough context to understand the mismatch, while avoiding private text in published artifacts. Dialog messages can include record names, account details or other content that is not safe to distribute.
Keep the handler's lifetime narrow
A persistent on() listener stays active until removed or the page closes. That can be appropriate when a deliberate sequence expects multiple dialogs, but the sequence should name the expected types, messages and answers. A once() handler expresses the simpler one-dialog case more clearly.
Prompt dialogs add an input value to acceptance. beforeunload behavior has its own documented handling and close options. Do not assume a confirmation example covers those cases. Start from the actual feature and read the matching API before expanding the suite.
The outcome note can be short: expected confirm message, cancellation keeps the draft, approved acceptance removes it, and server evidence is separate. This is more useful than a log containing only the moment the dialog appeared.
AnyTest describes reviewable end-to-end tests built after application exploration. During review, ask whether a proposed journey answers the expected confirmation or accepts every dialog it sees. No AnyTest dialog-handling feature is claimed here. Deliberate answers protect the meaning of the test; post-answer assertions protect the feature.
Common questions
Why can a logging-only dialog listener stall a click?
Native dialogs block page execution. A registered listener must accept or dismiss the dialog.
Does dismissing a confirmation prove cancellation worked?
No. Verify that the application kept the intended state, including server evidence when the real feature needs it.