Alerts & Dialogs Automation Practice
Master dialog interactions — open, close, confirm, cancel, backdrop dismiss, Escape key, accessibility assertions, and scoped dialog locators in Selenium, Playwright, and Cypress.
Interactive Scenarios
| ID | Scenario | Type | Priority | |
|---|---|---|---|---|
| ALD_001 | Dialog opens after trigger button click Expected: Dialog element is visible with role=dialog and aria-modal=true ✅ positivehigh | positive | high | |
| ALD_002 | Dialog heading matches expected title Expected: Heading inside dialog reads "Session Notice" exactly ✅ positivehigh | positive | high | |
| ALD_003 | Close button dismisses the info dialog Expected: Dialog disappears from the DOM after clicking the × button ✅ positivehigh | positive | high | |
| ALD_004 | Cancel button keeps dialog closed without triggering the action Expected: Dialog closes and the confirm result is not updated ❌ negativemedium | negative | medium | |
| ALD_005 | Confirm button triggers the expected action and closes dialog Expected: Result reads "Submission confirmed!" and dialog disappears ✅ positivehigh | positive | high | |
| ALD_006 | Destructive confirm button located by aria-label (no data-testid) Expected: Result reads "Account deleted!" after clicking the danger button ✅ positivehigh | positive | high | |
| ALD_007 | Backdrop click closes the modal dialog Expected: Dialog closes when clicking the overlay behind the dialog box ✅ positivemedium | positive | medium | |
| ALD_008 | Escape key dismisses the dialog Expected: Result confirms dialog closed via keyboard; dialog disappears ✅ positivehigh | positive | high | |
| ALD_009 | Dialog body text is assertable without data-testid Expected: Text 'Sunday' is found inside the dialog body ✅ positivemedium | positive | medium | |
| ALD_010 | Dialog has correct aria attributes for accessibility Expected: role=dialog, aria-modal=true, and aria-labelledby are present ✅ positivemedium | positive | medium | |
| ALD_011 | aria-labelledby attribute references the visible heading Expected: The heading element ID matches the dialog aria-labelledby value ✅ positivemedium | positive | medium | |
| ALD_012 | Correct notification targeted from repeated Dismiss buttons Expected: The 'Session Expiring Soon' notification is dismissed, not the others ✅ positivehigh | positive | high | |
| ALD_013 | Dismiss confirm dialog scoped by data-notif-id Expected: Confirm button inside scoped dialog is clicked without ambiguity ✅ positivemedium | positive | medium | |
| ALD_014 | Clicking dialog box does not fire backdrop close handler Expected: Dialog remains open after clicking inside the dialog box ⚠️ edgelow | edge | low | |
| ALD_015 | Escape key has no effect when no dialog is open Expected: Page state remains unchanged when pressing Escape with no active dialog ⚠️ edgelow | edge | low | |
| ALD_016 | Page loads without console errors Expected: No uncaught errors are logged during initial load ✅ positivehigh | positive | high |
Dialogs appear in real applications for confirmations, warnings, and notifications. Automating them well means knowing how to open, assert, interact, and close them — including edge cases like backdrop clicks and keyboard dismissal.
Always assert that the dialog is visible before interacting with it. Race conditions can cause tests to fail if you click before the dialog renders.
// Playwright — open dialog and assert it is visible
await page.getByTestId('open-info-dialog').click();
const dialog = page.getByRole('dialog', { name: 'Session Notice' });
await expect(dialog).toBeVisible();Scope into the dialog before targeting the confirm or cancel button. Unscoped button clicks are fragile when similar buttons exist elsewhere on the page.
// Playwright — confirm action inside dialog
await page.getByTestId('open-confirm-dialog').click();
const dialog = page.getByTestId('confirm-action-dialog');
await expect(dialog.getByRole('heading')).toHaveText('Confirm Submission');
await dialog.getByTestId('confirm-ok-btn').click();
await expect(dialog).not.toBeVisible();When a button has no data-testid, check for an aria-label. It is the most stable fallback when test ids are missing — more reliable than text alone.
// Playwright — locate button by aria-label (no data-testid)
await page.getByTestId('open-delete-dialog').click();
const dialog = page.getByRole('dialog', { name: 'Delete Account' });
// The Delete button has aria-label but no data-testid
await dialog.getByRole('button', {
name: 'Confirm account deletion'
}).click();Clicking the backdrop requires targeting a position outside the dialog box. A plain click() lands on the dialog box center, which stops propagation and does not close the dialog.
// Playwright — click backdrop (outside dialog box)
await page.getByTestId('open-backdrop-dialog').click();
const backdrop = page.getByTestId('backdrop-dismiss-dialog');
await expect(backdrop).toBeVisible();
// Click at top-left corner — outside the centered dialog box
await backdrop.click({ position: { x: 5, y: 5 } });
await expect(backdrop).not.toBeVisible();Keyboard dismissal is common in accessible UIs. Always verify the focus state before pressing Escape — some frameworks require focus on the dialog element.
// Playwright — press Escape to dismiss dialog
await page.getByTestId('open-escape-dialog').click();
const dialog = page.getByTestId('escape-dismiss-dialog');
await expect(dialog).toBeVisible();
await page.keyboard.press('Escape');
await expect(dialog).not.toBeVisible();Repeated elements with the same data-testid require scoping to a unique parent. The data-notif-id attribute is the stable anchor for identifying the right item in the list.
// Playwright — scope to the right notification before dismissing
// All Dismiss buttons share data-testid — scope to the unique parent
const targetNotif = page.locator(
'[data-testid="notif-item"][data-notif-id="notif-2"]'
);
await targetNotif.getByTestId('notif-dismiss-btn').click();
// Confirm in the dialog scoped by data-notif-id
const dialog = page.locator(
'[data-testid="dismiss-confirm-dialog"][data-notif-id="notif-2"]'
);
await dialog.getByRole('button', { name: /Confirm dismiss/i }).click();Quick reference across all three frameworks.
| Action | Selenium | Playwright JS | Playwright PY | Cypress |
|---|---|---|---|---|
| Open dialog | element.click() | locator.click() | locator.click() | .click() |
| Assert visible | isDisplayed() | expect(dialog).toBeVisible() | expect(dialog).to_be_visible() | should('be.visible') |
| Scope to dialog | findElement(By.css('[role="dialog"]')) | getByRole('dialog') | get_by_role('dialog') | get('[role="dialog"]') |
| Escape key | sendKeys(Keys.ESCAPE) | keyboard.press('Escape') | keyboard.press('Escape') | type('{esc}') |
| Backdrop click | Actions.moveToElement(el, x, y).click() | click({ position: {x,y} }) | click(position={'x':5,'y':5}) | .click('topLeft') |
| Aria-label button | findElement(By.css("[aria-label='...']")) | getByRole('button', { name: '...' }) | get_by_role('button', name='...') | find("[aria-label='...']") |