Skip to content

BROWSER INTERACTIONS · LESSON 16 OF 24

Dialogs and frames

Handle modal browser dialogs and enter an iframe explicitly.

  • dialog
  • accept
  • dismiss
  • frameLocator

Your runnable checkpoint

Extract into a new practice folder. Each download contains tests, configuration, the complete local practice app and an offline lesson. Use Node.js 22 or 24. Each checkpoint is independent.

Install dependencies and browser binaries using the commands below. The test runner starts the practice server on port 4174. Tests target localhost, never the production EduPoly site.

1. Understand the concepts

JavaScript alert, confirm and prompt dialogs are different from HTML modal elements. A dialog listener must accept or dismiss the dialog; leaving it open can block the action that triggered it.

Attach one-time listeners before the triggering click when only one dialog is expected. Check the dialog’s type or message when that is part of the requirement.

An iframe has its own document. frameLocator enters that document before finding its controls. A page-level locator does not automatically search every frame.

Use the frame’s title or a stable selector to identify it. Avoid selecting a frame by its incidental position when several frames may be present.

2. Build and run

These are complete test files and configuration. The download also includes app/ and server.mjs; preserve their paths. Stop any manually started practice server before running the suite.

Terminal
npm install
npx playwright install chromium
npm run check
npm test

tests/lesson.spec.ts

tests/lesson.spec.ts
import { test, expect } from '@playwright/test';
test('confirm a reset and interact inside a frame', async ({ page }) => {
  await page.goto('/');
  page.once('dialog', async dialog => {
    expect(dialog.type()).toBe('confirm');
    await dialog.accept();
  });
  await page.getByRole('button', { name: 'Reset practice tasks' }).click();
  await expect(page.locator('#task-status')).toHaveText('Practice tasks reset');
  const frame = page.frameLocator('iframe[title="Practice frame"]');
  await frame.getByLabel('Frame name').fill('Asha');
  await frame.getByRole('button', { name: 'Greet' }).click();
  await expect(frame.getByRole('status')).toHaveText('Hello, Asha');
});

playwright.config.ts

playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  forbidOnly: !!process.env.CI,
  retries: process.env.CI ? 1 : 0,
  workers: process.env.CI ? 2 : undefined,
  timeout: 30000,
  expect: { timeout: 5000 },
  reporter: [['list'], ['html', { open: 'never' }]],
  use: { baseURL: 'http://127.0.0.1:4174', trace: 'retain-on-failure', screenshot: 'only-on-failure' },
  projects: [{ name: 'chromium', use: { ...devices['Desktop Chrome'] } }],
  webServer: { command: 'node server.mjs', url: 'http://127.0.0.1:4174', reuseExistingServer: false, timeout: 15000 }
});

package.json

package.json
{
  "name": "edupoly-playwright-dialogs-and-frames",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "test": "playwright test",
    "test:headed": "playwright test --headed",
    "test:ui": "playwright test --ui",
    "check": "tsc --noEmit",
    "report": "playwright show-report",
    "serve": "node server.mjs"
  },
  "devDependencies": {
    "@playwright/test": "1.56.1",
    "typescript": "5.9.3",
    "@types/node": "22.20.5"
  }
}

tsconfig.json

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "CommonJS",
    "moduleResolution": "Node",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "types": [
      "node"
    ],
    "noEmit": true
  },
  "include": [
    "tests/**/*.ts",
    "pages/**/*.ts",
    "playwright.config.ts"
  ]
}

.gitignore

.gitignore
node_modules/
playwright-report/
test-results/
playwright/.auth/
.env

3. Trace the code and check the result

  1. The listener handles the browser dialog rather than a DOM button.
  2. frameLocator scopes subsequent locators to the child document.
  3. The assertion reads the frame’s status element.

Verify your result

  • The reset confirmation is handled.
  • The iframe greeting reads Hello, Asha.

4. Practice independently

Change the entered frame name and verify the greeting changes.

Show the explanation

Fill Frame name with another synthetic name, click Greet and assert the exact greeting inside the frame.

When something goes wrong

If a browser executable is missing, run npx playwright install chromium in the project folder. If port 4174 is occupied, stop your manual practice server. Read assertion call logs for mismatched locators or expectations. Use npm run report to inspect failures; do not add fixed sleeps or broad resets to hide them.

Read the related official Playwright documentation ↗