beginner19 min

Playwright Architecture, Setup, and the Test Lifecycle

Browser, context, and page — the three-layer model Playwright is built on — and how a Playwright project discovers and runs a test from start to finish.

What you'll learn

  • Explain the relationship between a Browser, a BrowserContext, and a Page
  • Set up a Playwright project and explain what npx playwright install actually does
  • Describe the lifecycle of a single test file from discovery to teardown

Explanation

Playwright's object model has three layers, and understanding the relationship between them explains most of Playwright's behavior. A Browser is one launched browser process (Chromium, Firefox, or WebKit) — expensive to start, so a test suite typically launches one per test file or worker, not one per test. A BrowserContext is an isolated, incognito-like session within that browser: its own cookies, storage, and cache, created cheaply and quickly, which is why Playwright's default test runner gives every single test its own fresh BrowserContext — no cookie or localStorage state leaks between tests, without needing to manually clear anything. A Page is one tab within a context; a context can hold multiple pages, which is exactly how Playwright models a real user opening a link in a new tab or handling a popup (covered later in this course).

npx playwright install downloads the actual browser binaries Playwright drives — Chromium, Firefox, and WebKit builds Playwright has tested against, kept separate from any browser already installed on your machine, so a test's behavior doesn't depend on whichever version of Chrome happens to be installed locally. This is a one-time (or per-Playwright-version) setup step, run once per machine or CI environment, distinct from npm install, which only installs the @playwright/test package itself.

A Playwright test file's lifecycle: the test runner discovers files matching a configured pattern (typically *.spec.ts), loads each file to find its test(...) calls, then executes each test — creating a fresh context/page via the built-in page fixture (fixtures are covered in depth in Module 4), running the test body, then automatically tearing down that context after the test finishes, regardless of whether it passed or failed. This automatic, guaranteed teardown — closing pages and contexts even after a failure or a thrown exception — is a large part of why Playwright test suites don't accumulate leaked browser processes over a long CI run the way hand-rolled automation scripts often do.

Example

Modeling the Browser -> Context -> Page hierarchy and its isolation guarantee, without launching a real browser -- the real syntax and behavior are covered in this lesson's guided local lab.

class FakeBrowser {
  newContext() {
    return new FakeContext();
  }
}
class FakeContext {
  constructor() { this.cookies = new Set(); this.pages = []; }
  newPage() {
    const page = new FakePage(this);
    this.pages.push(page);
    return page;
  }
}
class FakePage {
  constructor(context) { this.context = context; }
  setCookie(name) { this.context.cookies.add(name); }
}

const browser = new FakeBrowser();
const contextA = browser.newContext();
const contextB = browser.newContext();
contextA.newPage().setCookie("session=abc");

console.log(contextA.cookies.has("session=abc")); // true
console.log(contextB.cookies.has("session=abc")); // false -- contexts are isolated, exactly like real Playwright contexts

Try it yourself

Add a second page to contextA and confirm both pages within the SAME context share its cookies.

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Guided exercise

Guided exercise

Write isolatedContexts(actions) modeling context isolation: actions is an array of {contextId, cookie} objects. Return an object mapping each contextId to the Set of cookies set within it -- cookies set in one context must never appear in another.

Checks: cookies set in different contexts remain isolated from each other · handles an empty actions list

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Independent exercise

Independent exercise

Write countPagesPerContext(pageOpenEvents) where pageOpenEvents is an array of contextId strings (one entry per page opened in that context). Return an object mapping each contextId to how many pages were opened in it -- modeling how one context can hold multiple pages (tabs/popups).

Checks: counts multiple pages within the same context correctly · handles an empty event list

Code editor. Press Escape then Tab to leave the editor if keyboard focus becomes trapped. Press Control+Shift+M inside the editor to toggle Tab-key focus trapping.

Loading editor…

Stuck? Get a hint.

Guided local lab

Create and Run a Multi-Browser Playwright Project Locally

Runs on your computer
This lab runs on your own computer, in your own terminal and editor — not in your browser. VisaSparkSchools does not execute, run, or verify these commands for you. Follow the verification steps yourself to confirm your result.

Set up a real Playwright project from scratch, install real browser binaries, and run your first test across multiple real browser engines.

Required tools

  • Node.js (20.x or 22.x LTS)
  • npm (10.x or newer (bundled with Node.js))
  • A terminal (any)

Setup

  1. Create a project folder: `mkdir pw-learning-lab && cd pw-learning-lab`.
  2. Initialize it and install Playwright's test runner: `npm init -y && npm install -D @playwright/test`.
  3. Install real browser binaries for Chromium, Firefox, and WebKit: `npx playwright install`.

Project structure

pw-learning-lab/
  playwright.config.ts
  tests/
    homepage.spec.ts
  package.json

Starter files

playwright.config.ts

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

export default defineConfig({
  testDir: "./tests",
  projects: [
    // TODO: add a project for chromium, firefox, and webkit,
    // each using devices["Desktop Chrome"] / devices["Desktop Firefox"] / devices["Desktop Safari"]
  ],
});

tests/homepage.spec.ts

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

test("a real public page loads and has a heading", async ({ page }) => {
  // TODO: navigate to a real, stable public URL of your choice (e.g. https://playwright.dev)
  // TODO: assert that at least one heading (role "heading") is visible on the page
});

Requirements

  • playwright.config.ts defines three projects: chromium, firefox, and webkit.
  • tests/homepage.spec.ts navigates to a real URL and asserts a heading is visible.
  • The test passes when run against all three configured browser projects.

Commands to run

  • Run the test suite across all configured browser projects

    npx playwright test
  • Run only the Firefox project

    npx playwright test --project=firefox
  • Run with the visible (headed) browser, useful while learning

    npx playwright test --headed

Expected behavior

Running `npx playwright test` launches the test three times — once per configured browser project — and reports all three passing, each having genuinely loaded the page in a different real browser engine (Chromium, Firefox, WebKit).

Verify it yourself

  • npx playwright test

    Expected: 3 passed (one per browser project), 0 failed

  • npx playwright test --project=webkit

    Expected: 1 passed — confirms WebKit specifically ran, not just Chromium

Troubleshooting

  • `browserType.launch: Executable doesn't exist``npx playwright install` was skipped or didn't finish — re-run it; it downloads real browser binaries and needs a working internet connection the first time.
  • Only one project's test runs, not threeCheck playwright.config.ts's `projects` array actually lists all three entries — a missing entry silently means that browser is never tested.
  • Test times out waiting for the headingConfirm the URL is real, publicly reachable, and that the page genuinely renders a heading element — try loading it manually in a browser first.

Stuck? Get a hint.

Extension challenge

Add a fourth, mobile-emulating project using devices['iPhone 13'], and confirm the same test passes there too, on a genuinely different viewport and user agent.

When you've verified this locally, use the "Mark lesson complete" button below to record your progress.

Common mistakes

  • Launching one Browser per test instead of reusing one per worker/file and creating a fresh BrowserContext per test -- launching a full browser process per test is far slower than Playwright's default context-per-test isolation model.
  • Confusing `npm install` (installs the @playwright/test package) with `npx playwright install` (downloads actual browser binaries) -- skipping the second step is the most common first-run setup failure.
  • Assuming a Page and a BrowserContext are the same thing -- a context can hold multiple pages (tabs/popups), and cookies/storage belong to the context, not to any single page within it.

Knowledge check

Knowledge check

1. Why does Playwright's test runner give every single test its own fresh BrowserContext by default?
2. What does `npx playwright install` actually do?
3. Can one BrowserContext hold more than one Page?

Takeaway

Browser is an expensive, shared process; BrowserContext is a cheap, isolated session (one per test by default); Page is a tab within a context — understanding this hierarchy explains both Playwright's default isolation and how it models multi-tab scenarios.

Summary

Playwright's Browser → BrowserContext → Page hierarchy gives every test a fresh, isolated context by default. npx playwright install downloads real browser binaries, separate from npm install. A test's lifecycle is discover → load → execute (with automatic context teardown) → report.

References

Your notes

Notes save automatically.

Finished this lesson?

Mark it complete to track your progress and schedule a future review.