Skip to content

Software Testing & QA Tools

Playwright

Playwright is an open-source library, first released by Microsoft in 2020, for automating real browsers — Chromium, WebKit (the engine behind Safari), and Firefox — through a single API available in TypeScript/JavaScript, Python, Java, and .NET. Several of its original authors had previously built Puppeteer at Google, and Playwright extends that lineage to more than one browser engine and adds a purpose-built test runner (Playwright Test) with parallel execution, auto-waiting, and trace recording. It is used for end-to-end UI testing, browser-based API testing, and general automation such as scraping.

Why it matters

It was the first mainstream tool to test against WebKit on Linux, and still has the most complete support for it
Every browser on iOS — Chrome, Firefox, whatever — is required by Apple to use the WebKit engine underneath, so testing against WebKit is the closest thing to real iOS Safari coverage a Linux CI runner can produce without a Mac. Playwright's WebKit target is stable and first-class; Cypress has since added an experimental WebKit mode built on Playwright's own WebKit binaries, but it still lacks features like cy.origin() and Test Replay that Playwright's WebKit support has had from day one.
Auto-waiting removes the single biggest source of flaky UI tests
Before most actions, Playwright waits for the target element to be attached, visible, stable (not mid-animation), and able to receive events, so the manual sleep() or explicit wait-for-selector calls common in older Selenium suites are largely unnecessary.
Parallel workers and trace recording shorten the debug loop
Playwright Test spreads spec files across worker processes by default and can capture a full trace — DOM snapshots, network calls, console output — for a failed test, replayable in a trace viewer without rerunning anything.
One API covers UI, API, and visual testing
The same test file can drive the browser, make a raw HTTP request with the built-in request fixture, and assert a screenshot pixel-match, so a suite doesn't need a second tool bolted on for API checks.

How it actually talks to the browser

Playwright does not use the W3C WebDriver protocol that Selenium is built on. Instead it drives each browser through that browser's own automation protocol — the Chrome DevTools Protocol (CDP) for Chromium, and analogous internal protocols for WebKit and Firefox that the Playwright team patches into those engines and maintains themselves. This is also why Playwright ships its own downloaded browser binaries by default rather than automating the browser already installed on the machine: the WebKit and Firefox builds it uses include the patches its protocol bridge depends on. It can be pointed at a system-installed Chrome or Edge instead (channel: 'chrome'), but the WebKit and Firefox builds are Playwright's own.

JavaScript
import { test, expect } from '@playwright/test';

test('search returns results', async ({ page }) => {
  await page.goto('https://example.com');
  await page.getByRole('searchbox').fill('playwright');
  await page.getByRole('button', { name: 'Search' }).click();
  await expect(page.getByRole('list')).toContainText('Playwright');
});

Locators, auto-waiting, and web-first assertions

A Playwright Locator (page.getByRole, getByText, getByTestId, and so on) is a lazy reference — it doesn't resolve to an element until an action or assertion runs against it, and at that point Playwright re-queries the DOM and waits for the element to satisfy the actionability checks (visible, stable, enabled, receives events) before proceeding. expect(locator).toHaveText(...) and similar 'web-first' assertions retry internally for a configurable timeout instead of failing on the first check, which is what removes most of the manual waiting logic older frameworks need. This makes tests noticeably more resistant to timing issues without hand-written retry code, though it also means a genuinely broken selector fails slowly — only after the full timeout — rather than immediately.

Isolation, tracing, and what the test runner adds

Playwright Test gives each test a fresh browser context by default — an isolated, cookie-free environment roughly like a private window — so tests don't leak state into each other even when the underlying browser process is reused for speed. Its trace viewer records a timeline of actions, DOM snapshots before and after each step, network requests, and console logs for a failing test, and that trace file can be opened later (or by a teammate, or in CI artifact storage) without needing to reproduce the failure live. It also has built-in support for visual regression (toHaveScreenshot), API mocking (page.route), and running tests against multiple browser/viewport combinations from one config file.

Mistakes people make here

Assuming Playwright's WebKit is identical to the Safari a user actually has
Playwright's WebKit build tracks upstream WebKit closely but is not Apple's shipped Safari binary, and it doesn't reproduce macOS/iOS-specific behavior, extensions, or Safari's own release cadence — it's a strong proxy for Safari-engine quirks, not a guarantee of pixel- or behavior-identical results.
Using arbitrary CSS selectors instead of role- or testid-based locators
A selector like div.card > button:nth-child(2) breaks the moment a designer reorders markup, while getByRole('button', { name: 'Submit' }) survives most refactors and, as a side effect, checks that the element is actually accessible the way a screen reader would see it.
Sharing one browser context across tests to save time
It's faster, but cookies, localStorage, and login state leak between tests, so a failure in test 3 can be caused by state test 1 left behind — exactly the kind of order-dependent flakiness isolated contexts exist to prevent.
Ignoring trace files when a CI-only failure won't reproduce locally
CI environments often differ in timing, viewport, or resource contention from a developer machine, and the trace captured on that failing CI run — not a local rerun — is usually the fastest way to see what the page actually looked like at the moment of failure.

Strengths and trade-offs

Where it is strong

  • Real multi-engine coverage — Chromium, WebKit, and Firefox — from one API and one config file.
  • Auto-waiting and web-first assertions eliminate most manual timing code and the flakiness it causes.
  • Trace viewer, video, and screenshot capture on failure make CI-only failures debuggable after the fact.
  • Actively developed by a well-resourced team at Microsoft, with fast adoption of new web platform features.

The trade-offs

  • Younger ecosystem than Selenium's: fewer decades of Stack Overflow answers, third-party integrations, and grid/cloud vendors, though this gap has narrowed a lot since 2020.
  • Its WebKit and Firefox builds are Playwright-maintained forks of those engines, not the exact browsers end users run — real device/browser testing still has a place for final sign-off.
  • No built-in support for older browsers (no real Internet Explorer story), which matters only for the shrinking set of teams that still need it.
  • The auto-wait model means a permanently-missing element fails only after its timeout elapses, which can make a genuinely broken test slower to fail than an explicit assertion would be.

Who needs this

Any team writing new browser end-to-end tests today should at least evaluate it — it's a common default for greenfield JS/TS projects. Teams with a large, working Selenium suite don't need to migrate just to migrate; the calculus changes if they're fighting flakiness or need real cross-engine coverage.

Questions about playwright

Is Playwright faster than Selenium?
In most head-to-head comparisons, yes, mainly because it talks to the browser over a fast native protocol instead of the WebDriver wire protocol's HTTP round trips, and because Playwright Test parallelizes across workers by default. Selenium can also run in parallel (via Selenium Grid or a test runner's own parallelization), so the gap is more about default ergonomics than a hard ceiling on either tool.
Can Playwright test a real Safari browser, not just WebKit?
Not directly, and not on Windows or Linux at all — Playwright's WebKit build only runs on the desktop platforms it supports (Windows, Linux, macOS), and even on macOS it is Playwright's own WebKit build, not Apple's Safari.app. For sign-off against the exact shipped Safari, a real macOS/iOS device or a cloud device farm is still necessary.
Does Playwright replace Selenium entirely?
For new projects, it's the more common choice today, but Selenium remains extremely widely deployed, has broader language bindings, and is the tool most enterprise test grids and legacy suites are already built on — 'replace' overstates it; 'increasingly the default for new work' is closer to accurate.
Do I need to know JavaScript to use Playwright?
No — official bindings exist for Python, Java, and .NET as well as JavaScript/TypeScript, and they expose the same core API (locators, auto-waiting, contexts), though the test-runner tooling (Playwright Test) is most mature in the JS/TS ecosystem.

The primary source

Related concepts

← All concept guides