Playwright Lesson 1 of 6 ⏱️ 4 min read

Playwright Introduction

Installing Playwright Test, running your first cross-browser test, and recording tests with codegen.

Updated Oct 30, 2026

What is Playwright?

Playwright is an open-source browser automation and testing framework from Microsoft. One API drives Chromium (Chrome, Edge), Firefox and WebKit (Safari), and its test runner, Playwright Test, includes everything a modern suite needs:

  • Auto-waiting — actions wait for elements to be visible, stable and enabled.
  • Web-first assertions — expect(locator) retries until the condition is met.
  • Parallel execution by default, across files and browsers.
  • Isolation — every test gets a fresh browser context (like a new incognito profile) in milliseconds.
  • Built-in tools — codegen to record tests, UI mode, and the Trace Viewer for debugging CI failures.
  • API testing and network mocking in the same framework.

Playwright has official bindings for TypeScript/JavaScript, Python, Java and .NET. This track uses TypeScript with Playwright Test, the most common setup.

Installing Playwright

Bash
mkdir playwright-demo && cd playwright-demo
npm init playwright@latest

The installer asks a few questions (TypeScript, tests folder, GitHub Actions workflow, install browsers) and creates:

Text
tests/
  example.spec.ts
playwright.config.ts
package.json

Your first test

Playwright hosts a demo TodoMVC app for practice:

Typescript
// tests/todo.spec.ts
import { test, expect } from '@playwright/test';

test('adds a todo item', async ({ page }) => {
  await page.goto('https://demo.playwright.dev/todomvc');

  const newTodo = page.getByPlaceholder('What needs to be done?');
  await newTodo.fill('Write my first Playwright test');
  await newTodo.press('Enter');

  await expect(page.getByTestId('todo-title')).toHaveText(['Write my first Playwright test']);
  await expect(page.getByTestId('todo-count')).toContainText('1 item left');
});

test('completes a todo', async ({ page }) => {
  await page.goto('https://demo.playwright.dev/todomvc');

  await page.getByPlaceholder('What needs to be done?').fill('Buy milk');
  await page.getByPlaceholder('What needs to be done?').press('Enter');
  await page.getByTestId('todo-item').getByRole('checkbox').check();

  await expect(page.getByTestId('todo-item')).toHaveClass(/completed/);
});

The page argument is a fixture: Playwright creates a fresh page for each test and closes it afterwards.

Running tests

Bash
npx playwright test                         # all tests, all configured browsers, headless
npx playwright test tests/todo.spec.ts      # one file
npx playwright test --project=chromium      # one browser
npx playwright test --headed                # watch the browser
npx playwright test --ui                    # interactive UI mode (great for debugging)
npx playwright show-report                  # open the HTML report

By default the config runs every test in Chromium, Firefox and WebKit — three browsers from one test.

The config file

Typescript
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  retries: process.env.CI ? 2 : 0,
  reporter: 'html',
  use: {
    baseURL: 'https://demo.playwright.dev',
    trace: 'on-first-retry',          // record a trace when a test is retried
    screenshot: 'only-on-failure',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox',  use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit',   use: { ...devices['Desktop Safari'] } },
    { name: 'mobile',   use: { ...devices['Pixel 7'] } },
  ],
});

With baseURL set, tests can call page.goto('/todomvc').

Recording tests with codegen

Playwright can write a test while you click around:

Bash
npx playwright codegen https://demo.playwright.dev/todomvc

A browser opens alongside an inspector that generates code — including good locators — for every action. Codegen is excellent for learning locators and for scaffolding; always review and clean up the generated test before committing it.

Python version

The same test in Python with pytest-playwright:

Bash
pip install pytest-playwright
playwright install
Python
# test_todo.py — run with: pytest
import re
from playwright.sync_api import Page, expect


def test_adds_a_todo(page: Page):
    page.goto("https://demo.playwright.dev/todomvc")
    page.get_by_placeholder("What needs to be done?").fill("Learn Playwright")
    page.get_by_placeholder("What needs to be done?").press("Enter")

    expect(page.get_by_test_id("todo-title")).to_have_text(["Learn Playwright"])
    expect(page.get_by_test_id("todo-count")).to_have_text(re.compile("1 item left"))

Next: why Playwright tests rarely need waits — Locators & Auto-Waiting.

Finished this lesson?
Tick it off to track your Playwright progress.
Next lesson