2.6 KiB
dismiss-dialog
Purpose
dismissDialog attaches a one-shot handler to the next page.on("dialog") event, with the caller choosing between accept and dismiss. It's the small piece of glue that turns Playwright's event-driven dialog model into an imperative one-liner, which matches how most tests actually think about modal dialogs.
Features
- Installs a single
page.on("dialog", …)listener; auto-removes after it fires - Choice of
accept(OK/Allow) ordismiss(Cancel/Deny) - Safe to register before the action that triggers the dialog
- Returns an unsubscribe function so the test can detach manually if the dialog never appears
Usage: Basic Example
import { dismissDialog } from "@suaveplan/testing-e2e/helpers";
const off = dismissDialog(page, "accept");
await page.click("text=Delete");
// If a confirm() pops, it is accepted.
off(); // optional — the handler auto-removes after firing
Usage: Advanced Example
import { dismissDialog } from "@suaveplan/testing-e2e/helpers";
import { expect } from "@playwright/test";
test("destructive action requires confirm", async ({ page }) => {
await page.goto("/account");
dismissDialog(page, "dismiss"); // cancel the confirm
await page.click("text=Delete account");
await expect(page.getByRole("alert")).toHaveText(/cancelled/i);
dismissDialog(page, "accept"); // now accept it
await page.click("text=Delete account");
await expect(page).toHaveURL("/goodbye");
});
API Reference
dismissDialog(page, action): () => void
page: Page— Playwright pageaction: "accept" | "dismiss"— what to do when the dialog appears- Returns an unsubscribe function; safe to call after the handler has already auto-removed
Implementation Notes
Per the project-wide guidance around browser dialogs, tests should avoid triggering dialogs when possible — they block the entire Playwright event stream until dismissed. dismissDialog is for the cases where dismissal is unavoidable (e.g. testing a beforeunload prompt). The handler uses { once: true } semantics so a stale handler from a prior test never intercepts the current one.
Race Protection
dismissDialog(page) registers a one-shot page.once("dialog", d => d.dismiss()) listener and immediately returns a disposer that removes the listener if the dialog never fires. Doing it as one-shot avoids leaking listeners across test boundaries — a repeated bug in the previous iteration. The helper also logs the dialog's type and message through the structured logger so trace viewers show exactly what was dismissed.