Fundamentals

2 min read
Rapid overview

Playwright Fundamentals (Interview-Ready)

What Playwright is (and why it wins)

  • One API, multiple browsers: Chromium, Firefox, WebKit.
  • Auto-waiting: actions wait for element to be actionable (visible, stable, enabled).
  • Web-first assertions: expect() retries until timeout, reducing flake.
  • Great debugging: trace viewer, screenshots, videos, UI mode.

The minimum setup you should know

Install (example):

npm i -D @playwright/test
npx playwright install

Typical structure:

e2e/
  playwright.config.ts
  tests/
  fixtures/
  pages/

playwright.config.ts essentials

What to configure early:

  • testDir, timeout, expect.timeout
  • retries (CI only)
  • use.baseURL, use.trace, use.video, use.screenshot
  • projects (desktop/mobile, chromium/firefox/webkit)
  • workers (cap parallelism for stability in CI)

Example (minimal):

import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  timeout: 30_000,
  expect: { timeout: 5_000 },
  retries: process.env.CI ? 2 : 0,
  use: {
    baseURL: process.env.BASE_URL ?? 'http://localhost:5173',
    trace: 'on-first-retry',
    screenshot: 'only-on-failure',
    video: 'retain-on-failure',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
  ],
});

Actions + assertions: the core loop

Pattern: 1) navigate quickly 2) do actions 3) assert with web-first expectations

import { test, expect } from '@playwright/test';

test('login works', async ({ page }) => {
  await page.goto('/login', { waitUntil: 'commit' });
  await page.getByTestId('email').fill('user@example.com');
  await page.getByTestId('password').fill('pw');
  await page.getByTestId('submit').click();

  await expect(page).toHaveURL(/\\/dashboard/);
  await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});

Fixtures (how to keep tests clean)

Use fixtures to share setup without sharing state.

import { test as base } from '@playwright/test';
import { LoginPage } from './pages/login-page';

export const test = base.extend<{ loginPage: LoginPage }>({
  loginPage: async ({ page }, use) => {
    await use(new LoginPage(page));
  },
});

Debugging basics

  • npx playwright test --ui for interactive mode
  • PWDEBUG=1 npx playwright test to step through
  • --headed to see the browser
  • --trace on (or config trace: 'on-first-retry') to investigate flake

Common failure modes (and what to do instead)

  • “Element not found”: fix locator strategy (prefer role/testid), not sleeps.
  • “Timeout”: assert the right thing (URL/response/state), reduce ambiguity.
  • “Flaky in CI”: make test data deterministic, reduce cross-test coupling, cap workers.