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 contextsTry 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.
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.
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.
Stuck? Get a hint.
Guided local lab
Create and Run a Multi-Browser Playwright Project Locally
Runs on your computerSet 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
- Create a project folder: `mkdir pw-learning-lab && cd pw-learning-lab`.
- Initialize it and install Playwright's test runner: `npm init -y && npm install -D @playwright/test`.
- 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.jsonStarter 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 testRun only the Firefox project
npx playwright test --project=firefoxRun 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 testExpected: 3 passed (one per browser project), 0 failed
npx playwright test --project=webkitExpected: 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 three — Check 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 heading — Confirm 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
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.
AI tutor
The optional AI tutor isn't enabled in this deployment. All lessons, exercises, quizzes, and search work fully without it.