Files
suaveplan/packages/testkit/testing/testing-e2e/src/helpers/dismiss-dialog.md
2026-05-27 23:47:53 +01:00

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) or dismiss (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 page
  • action: "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.