Playwright documentation, in reading order
Microsoft's browser automation library, and the official documentation for it, arranged in the order it is worth reading.
Playwright drives Chromium, Firefox and WebKit through one API, from JavaScript, TypeScript, Python, Java or .NET. It is open source under Apache 2.0 and maintained by Microsoft. It is also what QAright runs on, so treat everything below as a recommendation from an interested party — though the pages linked are Playwright's own, not ours.
Three design decisions account for most of the difference between a Playwright test and the browser tests people remember disliking. They are worth understanding before writing anything, because each one removes a category of code that older frameworks required you to write by hand.
| The decision | What it removes |
|---|---|
| Every action re-checks that the element is actionable, and retries until it is or the timeout expires | Explicit waits, sleeps, and the retry helper every team used to write |
Assertions are web-first: expect(locator).toBeVisible() polls rather than sampling once | The race between asserting and rendering |
| Each test gets a fresh browser context — its own cookies, storage and cache — in milliseconds | Cross-test pollution, and the cost of a browser launch per test |
The practical consequence is that a correct Playwright test tends to be shorter than the equivalent elsewhere, and that when it fails the failure usually means something. A test full of waitForTimeout calls is a test written against the wrong mental model, and the documentation says so directly.
import { test, expect } from '@playwright/test';
test('a signed-in customer can reach checkout', async ({ page }) => {
await page.goto('https://example.com/');
await page.getByRole('link', { name: 'Sign in' }).click();
await page.getByLabel('Email').fill('ada@example.com');
await page.getByLabel('Password').fill(process.env.PASSWORD);
await page.getByRole('button', { name: 'Sign in' }).click();
// Web-first: polls until the heading appears, or the timeout expires.
await expect(page.getByRole('heading', { name: 'Basket' })).toBeVisible();
});
playwright.dev serves a separate copy of every page per language, and the code samples change with it. The test runner — fixtures, projects, sharding, the HTML reporter — is richest in the JavaScript and TypeScript edition; the Python, Java and .NET bindings give you the same browser automation and integrate with that ecosystem's own runner instead.
Getting the first test running
4 entriesAn hour, start to finish, including the install.
-
Installation and first run
Official docsScaffolds a project, installs the browser binaries and runs an example. Read the note about
playwright.dev--with-deps: on Linux the browsers ship with Playwright but the system libraries they link against do not, and that is the single most common first-run failure. -
Writing tests
Official docsActions, assertions, and test isolation, with the reasoning attached. The clearest short statement of what a Playwright test is made of.
playwright.dev -
Generating tests with Codegen
Official docsRecords your clicks and emits code using the recommended locators. Best treated as a way to discover the right locator for an awkward control rather than as a way to author whole suites — generated code needs editing before it is worth keeping.
playwright.dev -
UI mode
Official docsA watch-mode runner with a time-travelling trace of every run, a DOM picker and a locator explorer. If you write tests daily and have not tried it, this is the page on this list most likely to change your afternoon.
playwright.dev
The execution model
4 entriesThe four pages that explain why a test behaves the way it does.
-
Locators
Official docsHow to find elements, in the recommended order of preference, plus filtering, chaining and strict mode. The most load-bearing page in the whole documentation.
playwright.dev -
Auto-waiting and actionability
Official docsThe list of checks every action performs first — attached, visible, stable, receives events, enabled — and which actions perform which. Read it once and most timing questions answer themselves.
playwright.dev -
Assertions
Official docsEvery matcher, split into the auto-retrying ones and the ones that check a value immediately. Using a non-retrying matcher on something the page renders asynchronously is the classic self-inflicted flake.
playwright.dev -
Timeouts
Official docsTest timeout, expect timeout, action timeout, navigation timeout, global timeout — what each covers and how they compose. Worth reading before raising any of them.
playwright.dev
Structure, once there is more than one test
5 entries-
Fixtures
Official docsPlaywright's answer to setup and teardown, and a better one than before-each hooks: a fixture is requested by name, built only for the tests that ask for it, and torn down automatically. This is how you share a logged-in state or a seeded record without a web of globals.
playwright.dev -
Page object models
Official docsThe official take, including the unfashionable but correct point that a page object is worth writing when it removes duplication and not before.
playwright.dev -
Projects
Official docsOne config, several named runs: browsers, viewports, environments, and setup projects that other projects depend on. This is how a smoke subset and a full matrix live in one repository.
playwright.dev -
Parameterising tests
Official docsData-driven tests done properly: a list of cases, a test per case, and a distinct title for each so a failure names the row that failed. Also covers reading the cases from a CSV or JSON file, and parameterising a whole project through the config rather than the test.
playwright.dev -
Configuration reference
Official docsEvery option in
playwright.devplaywright.config, with defaults. The place to check before copying a config block from a blog post.
When it fails
4 entriesThe pages that turn “it broke on CI” into a fixable statement.
-
Trace viewer
Official docsA recorded trace of a run: DOM snapshot at every step, network, console, source, and a timeline you can scrub. Turning traces on for retries costs almost nothing and is the highest-value line in most CI configs.
playwright.dev -
Debugging tests
Official docsThe Playwright Inspector, VS Code integration,
playwright.devpage.pause(), and headed mode. Covers the browser developer tools route as well, which people forget is available. -
Retries
Official docsHow to configure retries per project or per run, and the reporting rule that matters more than the configuration: a test that passes on a retry is reported as flaky, not as a pass. That is what makes retries survivable rather than corrosive.
playwright.dev -
Screenshots and video
Official docsCapture on failure only, capture always, full-page or viewport, and how the artefacts attach to the report. A full-page screenshot is usually the right default: the error banner is often below the fold.
playwright.dev
Beyond clicking things
6 entries-
API testing
Official docsPlaywright can issue HTTP requests directly, with the browser's cookies or without them. Use it to set up state in two seconds instead of twenty, and to assert on the response a UI action produced.
playwright.dev -
Network and mocking
Official docsIntercept, modify, stub or record and replay any request the page makes. The honest way to test an error state you cannot make a real backend produce on demand.
playwright.dev -
Authentication
Official docsSign in once, save the storage state, reuse it across every test. This single technique removes more runtime from a typical suite than any other change on this page.
playwright.dev -
Visual comparisons
Official docsScreenshot assertions with a stored baseline, tolerance settings, and the masking you will need to make them survivable. Read the caveats before adopting these widely.
playwright.dev -
Emulation
Official docsDevices, viewports, locale, timezone, colour scheme, geolocation and permissions. How to test the mobile layout without a mobile device farm.
playwright.dev -
Accessibility testing
Official docsThe official guide to running axe inside a Playwright test, including how to scope a scan and how to handle known violations without switching the check off.
playwright.dev
The other bindings, and the project itself
6 entries-
Playwright for Python
Official docsThe same browser automation with pytest integration, sync and async APIs. The natural choice if the rest of your tooling is Python.
playwright.dev -
Playwright for .NET
Official docsNUnit and MSTest base classes, the idiomatic C# API, and the same trace viewer and codegen tooling. This is the binding QAright's exported test code targets, so the pages here describe the code a downloaded bundle actually contains.
playwright.dev -
Playwright for Java
Official docsThe Java API surface and JUnit integration, for teams whose build is already Maven or Gradle and whose testers would rather not maintain a second toolchain in a second language just to drive a browser.
playwright.dev -
Release notes
Official docsPlaywright ships often and the release notes are written to be read. Scanning them once a month is the cheapest way to find out that the workaround you maintain is now a built-in.
playwright.dev -
The source, the issue tracker, and the place to check whether the behaviour surprising you is a known bug. Issues here are answered by the maintainers unusually often.
github.com -
Playwright community
CommunityDiscord, the community-maintained tool list, and the ambassador programme. Useful for the questions that are too vague for an issue.
playwright.dev
What to take away
- Read actionability once.Almost every “why do I need a wait here” question is answered by that one page, and the answer is usually that you do not.
- Turn on traces for retries.One config line, and it converts “only fails in CI” from an argument into a recording.
- Save the signed-in state.Authenticating once and reusing storage state is the biggest single runtime saving available to most suites.
Reading is the slow way to learn how a suite behaves. Record one against your own app, free, and watch what the report says.