VALIDATION LAB•JAVASCRIPT ALERTS
Overlays
JavaScript Alerts
Handle native alert, confirm, and prompt dialogs with Playwright dialog listeners, accept/dismiss flows, and message assertions.
Scenario
Native browser dialogs freeze page execution. Tests hang unless `page.on('dialog', ...)` or `page.once('dialog', ...)` is registered before the triggering click.
Native dialogs block the page until Playwright handles the dialog event.
Type: none
Action: waiting
Message: No dialog handled yet.
Live widget · interact freely
Manual test checklist
- 1Trigger the alert and accept it
- 2Trigger confirm and test both OK and Cancel behavior
- 3Trigger prompt and submit a custom release tag
- 4Confirm the dialog message is reflected in the result panel
- 5Confirm the page cannot be interacted with while the native dialog is open
Expected result
Alert accepts, confirm can accept or dismiss, prompt accepts custom input, and the result panel records dialog type, action, message, and prompt value.
Automation challenge
Register the dialog handler before the click, assert `dialog.message()`, then use `dialog.accept()`, `dialog.dismiss()`, and prompt text across separate tests.
Stable selectors
- Alert trigger
[data-testid="js-alert-trigger"] - Confirm trigger
[data-testid="js-confirm-trigger"] - Prompt trigger
[data-testid="js-prompt-trigger"] - Dialog result
[data-testid="js-dialog-result"] - Prompt value
[data-testid="js-prompt-value"]
Locator strategy
Three levels from simple IDs to scoped Playwright locators. IDs and names are easy to learn but are not always the best long-term choice when labels change or components repeat.
Use simple IDs and names to understand how locating elements works.
1234
await page.goto('https://lab.hakdogan.com/practice/javascript-alerts');
page.once('dialog', (dialog) => dialog.accept());
await page.locator('#js-alert-trigger').click();Avoid as primary strategies
XPath (unless there is no alternative), long CSS chains, Tailwind-style utility class selectors, generated or unstable IDs, and volatile framework internals break when layout, styling, or DOM structure shifts.
12
await page.locator('.w-full.rounded-xl.border.bg-blue-500').fill('demo@example.com');
await page.locator('//div[2]/form/div[1]/input').fill('demo@example.com');Reference Playwright spec
1234567891011121314151617181920212223242526272829303132333435363738
import { test, expect } from '@playwright/test';
test.describe('javascript alerts', () => {
test('accepts alert and reads message', async ({ page }) => {
await page.goto('https://lab.hakdogan.com/practice/javascript-alerts');
page.on('dialog', async (dialog) => {
expect(dialog.message()).toBe('Deployment queued.');
await dialog.accept();
});
await page.getByTestId('js-alert-trigger').click();
await expect(page.getByTestId('js-dialog-result')).toContainText('accepted');
});
test('dismisses confirm', async ({ page }) => {
await page.goto('https://lab.hakdogan.com/practice/javascript-alerts');
page.once('dialog', async (dialog) => {
expect(dialog.message()).toContain('Approve production');
await dialog.dismiss();
});
await page.getByTestId('js-confirm-trigger').click();
await expect(page.getByTestId('js-dialog-result')).toContainText('dismissed');
});
test('accepts prompt input', async ({ page }) => {
await page.goto('https://lab.hakdogan.com/practice/javascript-alerts');
page.once('dialog', async (dialog) => {
await dialog.accept('v2.4.1');
});
await page.getByTestId('js-prompt-trigger').click();
await expect(page.getByTestId('js-prompt-value')).toContainText('v2.4.1');
});
});Headed Test Playback
Simulated headed-browser flow — no real browser is launched.
Automation-style playback (Playwright-shaped logs). No real browser; no commands run on your machine.
javascript-alerts.spec.ts- Registering dialog listener
- Accepting alert
- Dismissing confirm
- Accepting prompt value
- Asserting dialog result