Learn Playwright in 10 DaysDay 1: Welcome to Playwright

Day 1: Welcome to Playwright

What You'll Learn Today

  • What E2E testing is and why it matters
  • The testing pyramid concept (Unit, Integration, E2E)
  • What Playwright is, its features, and advantages
  • Playwright vs other tools (Cypress, Selenium) comparison
  • Playwright architecture (browser automation via protocols)
  • Installation and initial setup
  • Writing and running your first test
  • Understanding test results

What Is E2E Testing?

When building web applications, you need to verify that entire user workflows function correctly β€” things like "Can a user log in with valid credentials?" or "Can a customer add items to their cart and complete checkout?" Manually checking all of this after every code change is slow, tedious, and error-prone.

E2E (End-to-End) testing is a testing approach that automates real browser interactions to verify your application works correctly from the user's perspective, covering the full stack from the UI down to the database.

flowchart LR
    subgraph Manual["Manual Testing"]
        M1["Open the browser"]
        M2["Fill in the form"]
        M3["Click the button"]
        M4["Visually verify results"]
    end
    subgraph E2E["E2E Testing (Automated)"]
        E1["page.goto()"]
        E2["page.fill()"]
        E3["page.click()"]
        E4["expect() auto-verifies"]
    end
    M1 --> M2 --> M3 --> M4
    E1 --> E2 --> E3 --> E4
    style Manual fill:#f59e0b,color:#fff
    style E2E fill:#22c55e,color:#fff

Why Do We Need E2E Testing?

Problem How E2E Testing Helps
Manual testing is time-consuming Automated tests finish in minutes
Humans make mistakes Tests execute the same steps accurately every time
Regressions are hard to detect Automatically checks the entire app after every change
Cross-browser verification is tedious Run automatically across Chromium, Firefox, and WebKit
Quality assurance in CI/CD Validates quality automatically before deployment

The Testing Pyramid

Software testing comes in several types depending on scope and purpose. The testing pyramid provides a systematic framework for thinking about how many tests of each type you should write.

flowchart TB
    subgraph Pyramid["Testing Pyramid"]
        E2E["E2E Tests\n(Few Β· High cost Β· High confidence)"]
        INT["Integration Tests\n(Moderate)"]
        UNIT["Unit Tests\n(Many Β· Low cost Β· Fast)"]
    end
    E2E --> INT --> UNIT
    style E2E fill:#ef4444,color:#fff
    style INT fill:#f59e0b,color:#fff
    style UNIT fill:#22c55e,color:#fff
Test Type Scope Speed Cost Example
Unit Tests Individual functions/components Fast Low Testing an input validation function
Integration Tests Multiple components working together Moderate Moderate Testing API and database interaction
E2E Tests The entire application Slow High User registration through login flow

E2E tests sit at the top of the pyramid. While you write fewer of them, they cover the most critical user scenarios. Playwright is the tool that makes writing and running these E2E tests efficient and reliable.


What Is Playwright?

Playwright is an open-source E2E testing and browser automation framework developed by Microsoft. Released in 2020, it has rapidly gained adoption and is now one of the leading E2E testing tools in the industry.

Key Features of Playwright

flowchart TB
    subgraph Features["Playwright Features"]
        A["Multi-browser Support\nChromium / Firefox / WebKit"]
        B["Auto-waiting\nBuilt-in Smart Waits"]
        C["Test Isolation\nBrowser Contexts"]
        D["Parallel Execution\nWorker Processes"]
        E["Powerful Debugging\nTrace Viewer / Inspector"]
        F["Multi-language\nTS / JS / Python / Java / C#"]
    end
    style Features fill:#3b82f6,color:#fff
    style A fill:#8b5cf6,color:#fff
    style B fill:#8b5cf6,color:#fff
    style C fill:#8b5cf6,color:#fff
    style D fill:#8b5cf6,color:#fff
    style E fill:#8b5cf6,color:#fff
    style F fill:#8b5cf6,color:#fff
  1. Multi-browser support: Test on Chromium (Chrome, Edge), Firefox, and WebKit (Safari) with a single API
  2. Auto-waiting: Playwright automatically waits for elements to be actionable before performing actions β€” no explicit waits or sleeps needed
  3. Test isolation: Each test runs in its own Browser Context, ensuring tests never interfere with each other
  4. Parallel execution: Tests run in parallel across worker processes, dramatically reducing total test time
  5. Powerful debugging: Trace Viewer, UI Mode, and Playwright Inspector provide deep insight into test execution
  6. Multi-language support: Available for TypeScript/JavaScript, Python, Java, and C#

Playwright vs Other Tools

Selenium / Cypress / Playwright Comparison

Feature Selenium Cypress Playwright
Developer ThoughtWorks (2004) Cypress.io (2017) Microsoft (2020)
Browsers All major browsers Chromium, Firefox, WebKit Chromium, Firefox, WebKit
Languages Java, Python, C#, JS, etc. JavaScript/TypeScript JS/TS, Python, Java, C#
Architecture WebDriver protocol Runs inside the browser CDP / browser protocols
Auto-waiting No (manual implementation) Yes Yes (more precise)
Parallel execution Requires external tools Limited Built-in
Multiple tabs/windows Supported Not supported Supported
iframe handling Possible (verbose) Possible (limited) Easy
Network interception Limited Powerful Powerful
Execution speed Slow Fast Very fast
Learning curve Moderate Low Low

Why Choose Playwright?

  • Over Selenium: Auto-waiting, simple setup, faster execution, modern API
  • Over Cypress: Multiple tabs and iframe support, true multi-browser testing, built-in parallel execution

Playwright Architecture

Understanding how Playwright controls browsers under the hood helps you write better tests and debug issues more effectively.

flowchart LR
    subgraph TestProcess["Test Process"]
        T["Test Code\n(TypeScript)"]
        PW["Playwright\nLibrary"]
    end
    subgraph BrowserProcess["Browser Process"]
        B["Browser Engine\n(Chromium/Firefox/WebKit)"]
        P["Page"]
    end
    T --> PW
    PW -->|"CDP / Browser Protocol"| B
    B --> P
    style TestProcess fill:#3b82f6,color:#fff
    style BrowserProcess fill:#8b5cf6,color:#fff

Communication Protocols

Playwright uses each browser engine's native protocol for communication.

Browser Protocol Description
Chromium Chrome DevTools Protocol (CDP) The same protocol used by Chrome DevTools
Firefox Playwright protocol (custom patches) Optimized protocol for Firefox
WebKit Playwright protocol (custom patches) Optimized protocol for WebKit

How It Differs from Cypress

Cypress runs test code directly inside the browser in an iframe. Playwright, on the other hand, keeps the test process and browser process separate. This architectural decision enables:

  • Multiple browser control: Since browsers run as separate processes, Playwright can control multiple browsers simultaneously
  • Multiple tabs and windows: Controlling from outside the browser means no restrictions on tab or window management
  • Network-level control: HTTP requests and responses can be intercepted from outside the browser

Installation and Initial Setup

Prerequisites

  • Node.js: Version 18 or higher
  • npm: Bundled with Node.js
  • OS: Windows, macOS, or Linux

Creating a New Project

Let's create a new project and set up Playwright.

# Create a new directory
mkdir my-playwright-project
cd my-playwright-project

# Initialize a Playwright project
npm init playwright@latest

The initialization wizard will ask:

? Do you want to use TypeScript or JavaScript? Β· TypeScript
? Where to put your end-to-end tests? Β· tests
? Add a GitHub Actions workflow? Β· false
? Install Playwright browsers? Β· true

Generated File Structure

my-playwright-project/
β”œβ”€β”€ tests/
β”‚   └── example.spec.ts        # Sample test file
β”œβ”€β”€ tests-examples/
β”‚   └── demo-todo-app.spec.ts  # Todo app demo test
β”œβ”€β”€ playwright.config.ts        # Playwright configuration
β”œβ”€β”€ package.json
β”œβ”€β”€ package-lock.json
└── tsconfig.json

Understanding playwright.config.ts

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

export default defineConfig({
  // Directory containing test files
  testDir: './tests',

  // Timeout for each test (30 seconds)
  timeout: 30000,

  // Run tests in parallel
  fullyParallel: true,

  // Enable retries in CI
  retries: process.env.CI ? 2 : 0,

  // Number of parallel workers
  workers: process.env.CI ? 1 : undefined,

  // Test reporter
  reporter: 'html',

  // Shared settings for all tests
  use: {
    // Base URL for navigation
    baseURL: 'http://localhost:3000',

    // Record trace on first retry
    trace: 'on-first-retry',

    // Screenshot settings
    screenshot: 'only-on-failure',
  },

  // Browser configurations
  projects: [
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'] },
    },
    {
      name: 'firefox',
      use: { ...devices['Desktop Firefox'] },
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

Key Configuration Options

Option Default Description
testDir './tests' Directory containing test files
timeout 30000 Timeout per test in milliseconds
fullyParallel true Enable fully parallel test execution
retries 0 Number of retries on failure
workers 50% of CPU cores Number of parallel worker processes
reporter 'list' Test reporter type
use.baseURL none Base URL for page.goto('/')
use.trace 'off' When to record traces

Writing Your First Test

Basic Test Structure

Playwright tests use the test function and expect assertions.

// tests/my-first-test.spec.ts
import { test, expect } from '@playwright/test';

test('can access the Playwright website', async ({ page }) => {
  // 1. Navigate to the page
  await page.goto('https://playwright.dev/');

  // 2. Verify the title contains "Playwright"
  await expect(page).toHaveTitle(/Playwright/);

  // 3. Verify the "Get started" link is visible
  const getStarted = page.getByRole('link', { name: 'Get started' });
  await expect(getStarted).toBeVisible();
});

Running Tests

# Run all tests
npx playwright test

# Run a specific test file
npx playwright test tests/my-first-test.spec.ts

# Run with a specific browser
npx playwright test --project=chromium

# Run in UI mode (interactive)
npx playwright test --ui

# Run in headed mode (browser visible)
npx playwright test --headed

Writing Multiple Tests

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

test.describe('Playwright website', () => {
  test('can access the homepage', async ({ page }) => {
    await page.goto('https://playwright.dev/');
    await expect(page).toHaveTitle(/Playwright/);
  });

  test('can navigate to Get Started page', async ({ page }) => {
    await page.goto('https://playwright.dev/');

    // Click the "Get started" link
    await page.getByRole('link', { name: 'Get started' }).click();

    // Verify the Installation heading is visible
    await expect(
      page.getByRole('heading', { name: 'Installation' })
    ).toBeVisible();
  });

  test('search functionality works', async ({ page }) => {
    await page.goto('https://playwright.dev/');

    // Click the search button
    await page.getByRole('button', { name: 'Search' }).click();

    // Verify the search dialog appears
    await expect(page.getByRole('dialog')).toBeVisible();
  });
});

Understanding Test Results

Command Line Output

When you run tests, you'll see output like this:

Running 3 tests using 3 workers

  βœ“  1 [chromium] β€Ί example.spec.ts:4:3 β€Ί Playwright website β€Ί can access the homepage (1.2s)
  βœ“  2 [chromium] β€Ί example.spec.ts:9:3 β€Ί Playwright website β€Ί can navigate to Get Started page (1.8s)
  βœ“  3 [chromium] β€Ί example.spec.ts:20:3 β€Ί Playwright website β€Ί search functionality works (1.5s)

  3 passed (4.2s)

Test Result Statuses

Status Symbol Meaning
Passed βœ“ Test succeeded
Failed βœ— Test failed
Skipped - Test was skipped
Timed out βœ— Failed due to timeout
Flaky βœ“ Passed after retry (unstable test)

HTML Report

Playwright can generate detailed HTML reports for your test runs.

# View the HTML report
npx playwright show-report

The HTML report provides:

  • An overview of all test results
  • Execution time for each test
  • Error messages for failed tests
  • Screenshots (when configured)
  • Trace information (when configured)

Trace Viewer

The Trace Viewer is an invaluable tool for investigating why tests fail.

// Enable trace recording in playwright.config.ts
use: {
  trace: 'on-first-retry',  // Record trace on first retry
}

The Trace Viewer shows:

  • Screenshots at each step of the test
  • DOM snapshots at the time of each action
  • Network requests and responses
  • Console logs from the browser
# Open a trace file
npx playwright show-trace trace.zip

Summary

Concept Description
E2E Testing Tests that automate user interactions to verify the entire application
Testing Pyramid Three-layer structure: Unit β†’ Integration β†’ E2E. Focus E2E on critical scenarios
Playwright Microsoft's multi-browser E2E testing framework
Auto-waiting Playwright automatically waits for elements to be actionable
Browser Context Isolated browser environment ensuring test independence
CDP Chrome DevTools Protocol β€” how Playwright communicates with Chromium
Trace Viewer Debugging tool that provides detailed step-by-step test execution analysis

Key Points

  1. E2E tests are essential for automating manual testing and preventing regressions
  2. Playwright controls browsers from outside the process, enabling multiple tabs and iframe manipulation
  3. Auto-waiting eliminates the need for explicit sleep or wait calls in your test code
  4. npm init playwright@latest provides a quick and easy project setup

Practice Exercises

Exercise 1: Basic

Explain the three layers of the testing pyramid, providing specific examples of tests you would write at each layer. Why are E2E tests placed at the top of the pyramid?

Exercise 2: Intermediate

Compare the architectures of Playwright and Cypress. List three advantages that Playwright's out-of-process architecture provides, and explain specifically why it enables multiple tab operations.

Challenge Exercise

Set up a new project using npm init playwright@latest and create tests that meet the following requirements:

  1. Modify playwright.config.ts to run tests only on Chromium
  2. Write a test that navigates to a website of your choice
  3. Include assertions for the page title and visibility of a specific element
  4. Run the tests with npx playwright test and review the HTML report

Reference Links


Next Preview: In Day 2, we'll explore Test Structure Fundamentals. You'll learn about test.describe, test.beforeEach, test.afterEach, and how Playwright's fixture system works.