Playwright Operator's Guide
Playwright 101
Playwright is a modern, cross-browser automation library for end-to-end testing. It can drive Chromium, Firefox, and WebKit and test anything that runs in a browser. Like Cypress, published tests are most effective when they are focused and avoid unnecessary dependencies.
What does Playwright Offer?
Playwright ships with a test runner, trace viewer, screenshot and video capture, and a single API that works across browsers. Tests run in isolation by default. You get parallel execution out of the box and first-class support for multiple tabs, origins, and iframes.
Playwright and Locators
Playwright encourages resilient locators. Prefer role-based and text-based selectors over raw CSS or XPath. The library auto-waits for elements to be actionable and retries assertions, which reduces flakiness. If you must use CSS or XPath, keep them short and maintain them in one place—see Page Object Model below.
Playwright and Test Isolation
By default, each test gets a new browser context. That means a clean slate: no cookies, no storage, no leftover state. Isolation makes failures easier to reason about. When you need shared setup, use fixtures instead of sharing state between tests.
💡 Protip use test.describe.configure({ mode: 'serial' }) only when tests in a file must run in order. Prefer parallel execution and independent tests.
Playwright and iFrames
Playwright handles iframes without custom glue code. Use frameLocator() to scope into an iframe and then query elements inside it. No need to switch context manually as with some legacy tools.
Fixtures
Playwright’s test runner is built on a fixture system. Fixtures provide your tests with everything they need: a page, a context, a browser, or your own custom objects (e.g. an authenticated page, a shared API client).
Why Fixtures?
Fixtures give you dependency injection: declare what your test needs, and the runner creates and tears it down. They replace beforeEach/afterEach boilerplate and make it clear what each test depends on. Reusable fixtures keep your test files thin and consistent.
Extending Built-in Fixtures
You extend the built-in test object by calling test.extend(). Add new fixture properties (e.g. authenticatedPage) that use the base page or context. Every test that requests authenticatedPage gets a page that’s already logged in or has the right cookies/storage—no repeated login logic in the test body.
Base URL and Page Fixtures
Set baseURL in your config or in a fixture. Then use page.goto('/path') instead of full URLs. Page objects can assume a base URL and stay environment-agnostic.
Page Object Model
As in the Regression Test Operator’s Guide, the Page Object Model fits Playwright well. Each meaningful screen or flow has a page class (or page file) that encapsulates locators and actions.
Page Files
Page files hold selectors and high-level actions (e.g. “log in”, “submit form”). Tests call methods on these classes instead of duplicating locators. When the UI changes, you update one page file instead of many tests.
Locators in One Place
Centralize locators in page classes or in a shared constants/selectors module. Prefer Playwright’s getByRole, getByText, and getByTestId where possible. Keep complex or brittle selectors out of the test body so that maintenance stays predictable.
API Auth
Running a full login flow in the UI for every test is slow and brittle. Playwright supports authenticating via API and reusing that state in browser tests.
Request Context and Storage State
Use the APIRequestContext (from request fixture or request.newContext()) to sign in via HTTP: POST to your login endpoint, capture cookies or tokens from the response. Then call context.storageState() to serialize the current storage (cookies, local storage) and pass it when creating a new browser context so that tests start already logged in.
One-Time Login, Many Tests
A common pattern: in a setup project or a before-all hook, perform API login once, save storageState to a file, and then in your test config use storageState: 'auth.json' so every test (or every test in that project) starts with an authenticated context. No UI login in the test. Fast and stable.
Keeping Secrets Out of Tests
Put login credentials in environment variables or a .env file that is not committed. Use process.env in your fixture or config to pass them into the API login step. Never hardcode passwords in the repo.