View all services
Talk to QA Advisor
Browse the Knowledge Hub211 resources
/Tools Library/Playwright
Tools Library · We run this

Playwright review: tutorial, alternatives and pricing

Playwright is Microsoft’s open-source browser automation framework. One API drives Chromium, Firefox and WebKit from JavaScript, TypeScript, Python, Java or .NET, and Playwright Test, its Node.js runner, adds retrying assertions, fixtures, parallel workers, sharding, tracing and an HTML report. Version 1.64, released on 7 October 2026, adds Locator.within, a --shuffle option and WebMCP support.

QAble runs this tool on client engagements. The assessment below is from that experience.

last reviewed
13 October 2026
version covered
Playwright 1.64
licence
Open source

Reviewed by Viral Patel

Co-Founder, QAbleLast reviewed 13 October 2026

At a glance

Playwright at a glance

Official site
Vendor
Microsoft
Category
Web automation, API testing, Visual testing
Licence
Open source (Apache 2.0)
Platforms
Chromium, Chrome, Edge, Firefox, WebKit
Languages
JavaScript, TypeScript, Python, Java, C#
Integrates with
GitHub Actions, Jenkins, Allure, Docker
First released
2020
Version reviewed
Playwright 1.64

Verdict

Our verdict on Playwright

Strengths

  • Every action waits for its element to be visible, stable, enabled and unobscured, which removes most hand-written waits
  • Web-first assertions such as toBeVisible retry until they pass, so tests state outcomes instead of polling
  • page.route stubs, delays or fails any request in a few lines, in all three engines
  • Workers, sharding, retries and an HTML report are built into the free runner, with no paid service
  • The trace viewer replays a failed CI run with DOM snapshots, network, console and source for every step
  • Browser contexts give each test an isolated session in milliseconds, and several users in one test

Weaknesses

  • WebKit is a patched build, not Safari, so Apple-specific behaviour still needs real Safari
  • No native mobile app automation, only device emulation in desktop browser engines
  • A release every month or so that pins new browsers and occasionally removes APIs
  • Sharding, UI mode and the HTML report belong to the Node.js runner, not the Python, Java or .NET ports

Best for

Most new web automation work, particularly with a TypeScript or JavaScript team. Auto-waiting removes the largest category of flakiness before you write any code, page.route makes stubbing a few lines, and workers, sharding, retries, tracing and the HTML report are included rather than bought or assembled. Browser contexts turn multi-user and multi-tab journeys into ordinary tests, and the bundled WebKit build is the cheapest way to catch Safari-engine problems in Linux CI. Python, Java and .NET teams get the same browser API through their own runners.

Not for

Native mobile applications, which need Appium or the platform frameworks. Journeys that must be proven in genuine Safari, since Playwright’s WebKit is a patched build rather than Apple’s browser. Teams with a large, stable Selenium estate, where a migration rarely pays back on stability alone. And Python, Java or .NET teams expecting the full Playwright Test experience: sharding, the HTML report and UI mode belong to the Node.js runner, so those languages rely on pytest, JUnit, NUnit and their plugins instead.

Setup cost

The lowest of the major tools. npm init playwright@latest writes the config, an example test and, if you accept, a GitHub Actions workflow, then downloads matching browser builds. A working parallel suite across three engines exists within the hour. The real decisions come next: a setup project that signs in once and saves storage state, a locator convention based on roles and test ids, how test data is created and isolated per worker, and whether CI runs one machine or several shards.

Maintenance burden

Materially lower than Selenium, mostly because auto-waiting and retrying assertions remove the explicit wait code that otherwise accumulates and rots. Locators remain your responsibility, and role and label locators survive refactoring better than CSS paths. The cost Playwright adds is pace: a minor release every month or so, each pinning new browser builds and sometimes removing APIs, as 1.57 did with page.accessibility and 1.58 with the _react and _vue selectors. Upgrade on a schedule, read each breaking changes section, and never let the version drift for a year.

Honest limitations

WebKit is not Safari, so it approximates rather than reproduces Safari behaviour, and genuine Safari bugs need a real device or a cloud session. No native mobile support. Bundled browsers add several hundred megabytes to an install, which matters in constrained CI. Component testing changed model in 1.62 and the experimental packages stopped receiving updates in 1.63, so most component testing advice online is stale. The richest tooling, from sharding to UI mode, is JavaScript-only.

How it works

How Playwright works

A long-lived connection instead of a request per command

Playwright keeps one persistent, two-way connection to each browser it launches and sends commands over it, rather than issuing a separate HTTP request per instruction as classic WebDriver does. That removes per-command overhead and lets the browser push events, such as requests, console messages and new pages, to the test as they happen. Chromium is driven through its own DevTools protocol; for Firefox and WebKit, Playwright ships builds carrying its own patches, which is why it cannot drive branded Firefox or Safari. The Python, Java and .NET packages share the same underlying implementation as the Node.js library, so the browser API is the same in every language even though the test runners differ.

Actionability checks run before every action

Before a click, Playwright checks that the locator resolves to exactly one element, and that the element is visible, stable rather than animating, able to receive events because nothing covers it, enabled and not inert. Fill checks visibility, enabled and editable. It retries those checks until they pass or the action times out. Locators are strict, so a selector matching two elements fails rather than silently taking the first. Assertions such as toHaveText retry too, for 5 seconds by default inside a 30 second test timeout. Together these mean synchronisation is the default behaviour of the API rather than code you write, which is the main practical difference from Selenium.

Contexts isolate tests inside one browser

A browser context is an isolated session with its own cookies, storage, cache and permissions, created in milliseconds inside an already running browser. Playwright Test gives every test a fresh context and page, so tests are isolated without paying browser start-up time each time. Several contexts in one test are how you simulate two users, and several pages in one context are how you handle tabs and popups. Contexts are also how authentication is reused: sign in once, save the context’s storage state to a file, and start every later context from that file already signed in.

Each release pins its browsers

Every Playwright version is built and tested against specific browser builds. Version 1.64 pins Chromium 156, Firefox 157 and WebKit 27.2, and was also tested against Chrome and Edge 155 stable. Since 1.57 the Chromium builds are Chrome for Testing: headed runs use the full browser and headless runs use chrome-headless-shell. npx playwright install downloads the set for your version, so the browser moves only when you upgrade Playwright, which makes runs reproducible. If you need the browser your users run, set channel to chrome or msedge to drive the installed branded build instead.

Workers, projects and shards

Playwright Test runs test files in parallel in worker processes, by default half the machine’s logical CPU cores. Tests inside a file run in order in one worker unless fullyParallel is on. Each worker starts its own browser, and a worker is replaced after a test fails so later tests start clean. Projects define the matrix: the same tests under different browsers, devices or settings, with dependencies so a setup project runs first. Across machines, --shard=2/4 runs the second quarter of the suite, and the blob reporter plus merge-reports stitches the shards back into one HTML report.

Strengths

What it does well

Locators built around what users see

getByRole, getByLabel, getByText and getByTestId find elements the way a user or assistive technology would, so they survive the class and structure changes that break CSS paths. They chain and filter, and 1.64 adds Locator.within to combine two locators you already have. Codegen records interactions using these same locators, which makes it a useful first draft.

Network control in every engine

page.route can fulfil a request with a stub, modify it, delay it or abort it, so empty states, server errors and slow responses are testable without a proxy or a special backend. Recorded HAR files can be replayed as stubs, and the request fixture calls APIs directly in the same test, which is useful for seeding data before the UI steps.

Trace viewer and UI mode

A trace records every action with DOM snapshots before and after, network traffic, console output and the source line, and opens offline with npx playwright show-trace or inside the HTML report. Since 1.63 traces can also capture aria and screen snapshots. UI mode runs tests interactively with a watch mode and the same timeline, which shortens the write and debug loop considerably.

Several users, tabs and origins in one test

Two contexts in one test are two independent signed-in users, so chat, approvals and collaborative editing can be tested as they happen. Popups and new tabs are ordinary pages you can wait for, and cross-origin navigation needs no special handling. These are the journeys in-browser tools find hardest.

Emulation of devices, locales and passkeys

Device descriptors set viewport, pixel ratio, user agent and touch for dozens of phones and tablets. Contexts can fix geolocation, locale, time zone, colour scheme, permissions and offline mode. Since 1.61 a virtual authenticator answers WebAuthn passkey ceremonies in all three engines, and since 1.62 the clipboard is isolated from the host in headless runs.

Screenshot and aria snapshot assertions

toHaveScreenshot compares a page or element with a stored baseline and fails on pixel differences, with WebP baselines available since 1.62. toMatchAriaSnapshot asserts on the accessibility tree’s structure instead, which catches missing labels and changed headings without the brittleness of pixel comparison. Both run in the same tests as functional checks.

Agents and MCP for AI-assisted authoring

Playwright Test Agents, added in 1.56 and installed with npx playwright init-agents, give coding assistants a planner, a generator and a healer for writing and repairing tests. Since 1.62 the Playwright MCP server ships in the package as npx playwright mcp. Treat generated tests as drafts to review, especially their locators and assertions.

Pricing

Playwright pricing and licensing

Playwright is free and open source under the Apache 2.0 licence, maintained by Microsoft, with no paid edition: the runner, parallel workers, sharding, tracing and reporting are all in the open-source packages. The only Microsoft product with a price is Playwright Workspaces in Azure App Testing, an optional service that runs your tests against cloud browsers. Most teams pay only for CI machines and the engineering time that regular upgrades take.

  • Licence: Apache 2.0 for the library, the test runner and the Python, Java and .NET packages, as stated in the microsoft/playwright repository and on npm.
  • No tiers: parallelism, sharding across machines, retries, the trace viewer and the HTML report need no account or licence key.
  • Playwright Workspaces: Azure’s managed cloud browsers, billed per minute of browser time with separate Linux and Windows rates, plus a 30-day trial covering the first 100 test minutes. It replaced Microsoft Playwright Testing, retired on 8 March 2026.
  • How cost scales: by CI compute. Sharding shortens wall-clock time but not total machine time, and every shard repeats the install and browser download.
  • Hidden costs: browser downloads on every CI job unless cached or run in the official Docker image, and engineering time for an upgrade cycle that moves every month or so.

Checked against the vendor’s own pages on 13 October 2026. Pricing changes; confirm on the official site before buying.

Getting started

Playwright tutorial: getting started

Setup, step by step

  1. Install Node.js 22, 24 or 26, the versions the current installation guide lists.
  2. Run npm init playwright@latest and accept TypeScript, the tests folder and the GitHub Actions workflow.
  3. Let it download the browser builds, or run npx playwright install later.
  4. Set use.baseURL in playwright.config.ts to your application’s address.
  5. Save the test below in the tests folder and run npx playwright test.
  6. Open the results with npx playwright show-report, and try npx playwright test --ui for interactive runs.

Install

npm init playwright@latest
# writes the config and an example test, then downloads browsers
npx playwright test
npx playwright show-report

A first example

// tests/login.spec.ts
import { test, expect } from '@playwright/test'

test('a valid user can sign in', async ({ page }) => {
  await page.goto('https://the-internet.herokuapp.com/login')

  // Label and role locators survive markup changes better than CSS paths
  await page.getByLabel('Username').fill('tomsmith')
  await page.getByLabel('Password').fill('SuperSecretPassword!')

  // click waits until the button is visible, stable, enabled and unobscured
  await page.getByRole('button', { name: 'Login' }).click()

  // Web-first assertions retry for up to 5 seconds by default
  await expect(page).toHaveURL(/\/secure/)
  await expect(page.locator('#flash')).toContainText('You logged into a secure area!')
})

What trips people up first

Reaching for CSS selectors and fixed waits out of habit. Playwright’s recommended locators, getByRole, getByLabel and getByText, are tied to how a user perceives the page, and strict mode fails any locator that matches more than one element. Teams migrating from Selenium port their selectors verbatim, hit strict mode violations, silence them with first(), then add waitForTimeout where a race appears, and conclude Playwright is no more stable. Rewrite locators by role or test id, let actions and expect assertions do the waiting, and use the locator picker in UI mode or codegen to find a good locator quickly.

In practice

Using Playwright in practice

Organising specs with fixtures and page objects

Group specs by feature, keep page objects in their own folder, and expose them to tests through fixtures rather than constructing them in every test. A fixture declared with test.extend is created only for tests that ask for it, can run setup before the test and cleanup after it, and keeps tests free of plumbing. Page objects should hold locators, which are lazy and re-evaluated on every use, rather than element handles, so they never go stale. Keep assertions in tests, except for the check that a page has finished loading, which belongs in the page object’s navigation method.

// tests/pages/orders-page.ts
import { type Page, type Locator, expect } from '@playwright/test'

export class OrdersPage {
  readonly page: Page
  readonly rows: Locator
  readonly newOrder: Locator

  constructor(page: Page) {
    this.page = page
    this.rows = page.getByTestId('order-row')
    this.newOrder = page.getByRole('button', { name: 'New order' })
  }

  async goto() {
    await this.page.goto('/orders')
    await expect(this.page.getByRole('heading', { name: 'Orders' })).toBeVisible()
  }
}

// tests/fixtures.ts
import { test as base } from '@playwright/test'
import { OrdersPage } from './pages/orders-page'

export const test = base.extend<{ ordersPage: OrdersPage }>({
  ordersPage: async ({ page }, use) => {
    const ordersPage = new OrdersPage(page)
    await ordersPage.goto()
    await use(ordersPage)
  },
})
export { expect } from '@playwright/test'

// tests/orders.spec.ts
import { test, expect } from './fixtures'

test('lists existing orders', async ({ ordersPage }) => {
  await expect(ordersPage.rows).toHaveCount(3)
})

Signing in once with a setup project

The documentation’s recommended pattern for a shared account is a setup project: a small test that signs in through the UI, waits until the app has set its cookies, and saves the context’s storage state to a file. Every other project lists setup as a dependency and loads that file, so every test starts signed in without repeating the login. Keep the file in playwright/.auth and add that folder to .gitignore, because it holds live session cookies. If tests change server-side state that other tests read, use one account per worker instead, as the authentication guide describes. The webServer option starts your application before the run and reuses a running one locally.

// tests/auth.setup.ts
import { test as setup, expect } from '@playwright/test'

setup('sign in', async ({ page }) => {
  await page.goto('/login')
  await page.getByLabel('Email').fill(process.env.E2E_EMAIL!)
  await page.getByLabel('Password').fill(process.env.E2E_PASSWORD!)
  await page.getByRole('button', { name: 'Sign in' }).click()
  // Wait until the app has finished setting cookies before saving them
  await expect(page.getByRole('button', { name: 'Account' })).toBeVisible()
  await page.context().storageState({ path: 'playwright/.auth/user.json' })
})

// playwright.config.ts
import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
  fullyParallel: true,
  retries: process.env.CI ? 2 : 0,
  reporter: process.env.CI ? 'blob' : 'html',
  use: { baseURL: 'http://localhost:3000', trace: 'on-first-retry' },
  webServer: { command: 'npm run start', url: 'http://localhost:3000', reuseExistingServer: !process.env.CI },
  projects: [
    { name: 'setup', testMatch: /.*\.setup\.ts/ },
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'], storageState: 'playwright/.auth/user.json' },
      dependencies: ['setup'],
    },
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'], storageState: 'playwright/.auth/user.json' },
      dependencies: ['setup'],
      default: false, // new in 1.64: runs only when selected, for example --project="*"
    },
  ],
})

Stubbing the backend with page.route

The states users complain about, empty lists, failed requests and slow responses, are the hardest to produce against a real backend. page.route makes each one a few lines: register the route before navigating so the first request is caught, then fulfil it with JSON, an error status or a delay. To synchronise with a real request rather than a stub, start waitForResponse before the click that triggers it, then await it afterwards; awaiting it first misses fast responses. Keep a smaller set of unstubbed journeys running against the real API, so stubs cannot drift from the contract unnoticed.

// tests/orders-states.spec.ts
import { test, expect } from '@playwright/test'

test('shows an empty state when there are no orders', async ({ page }) => {
  // Register routes before navigating so the first request is caught
  await page.route('**/api/orders', route => route.fulfill({ json: [] }))
  await page.goto('/orders')
  await expect(page.getByText('No orders yet')).toBeVisible()
})

test('offers a retry when the API fails', async ({ page }) => {
  await page.route('**/api/orders', route =>
    route.fulfill({ status: 500, json: { message: 'down' } })
  )
  await page.goto('/orders')
  await expect(page.getByRole('button', { name: 'Try again' })).toBeVisible()
})

test('saves a new order through the real API', async ({ page }) => {
  await page.goto('/orders/new')
  // Start waiting before the click, then await after it
  const saved = page.waitForResponse(
    r => r.url().endsWith('/api/orders') && r.request().method() === 'POST'
  )
  await page.getByRole('button', { name: 'Save' }).click()
  expect((await saved).status()).toBe(201)
})

Sharding across CI machines and merging reports

One CI machine runs as many workers as its cores allow; beyond that, shard. Each matrix job runs --shard=n/4, writes a blob report and uploads it, and a final job downloads every blob and merges them into one HTML report. Turn on fullyParallel so shards are balanced by test rather than by file, and keep fail-fast off so one failing shard does not cancel the others. The workflow installs browsers with their system dependencies on each job; on large suites, the official Playwright Docker image as a job container saves that time. Upload reports even when tests fail, since that is when you need them.

# .github/workflows/playwright.yml
name: playwright
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shard: [1, 2, 3, 4]
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: lts/*
          cache: npm
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test --shard=${{ matrix.shard }}/4
      - uses: actions/upload-artifact@v7
        if: ${{ !cancelled() }}
        with:
          name: blob-report-${{ matrix.shard }}
          path: blob-report
          retention-days: 1
  report:
    if: ${{ !cancelled() }}
    needs: [test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: lts/*
      - run: npm ci
      - uses: actions/download-artifact@v8
        with:
          path: all-blob-reports
          pattern: blob-report-*
          merge-multiple: true
      - run: npx playwright merge-reports --reporter html ./all-blob-reports
      - uses: actions/upload-artifact@v7
        with:
          name: html-report
          path: playwright-report

Debugging failures and upgrading to 1.64

Set trace to on-first-retry, as the generated config does, so a failing test is retried with a full trace and passing runs record nothing. Open the merged HTML report, click the failed test and step through the trace before trying to reproduce anything locally. Use the new --shuffle option occasionally to flush out tests that depend on execution order, and test locks, added in 1.63, for tests that must not run alongside each other. When upgrading to 1.64, check four breaking changes: device descriptors now forward screen size, so window.screen and media queries see the emulated screen; elements inside hidden iframes count as hidden; JSX in test files follows tsconfig.json; and --update-snapshots=missing now passes the run.

Pitfalls

Common pitfalls

Using page.waitForTimeout to fix a race

Why it happens: It feels like the familiar sleep, and it appears to work. But actions already wait for actionability, so a timeout means you are waiting for something you have not expressed, and the fixed duration is either too short under load or dead time.

The fix: Wait for the real condition: an expect assertion on the state you need, waitForResponse on the request you depend on, or waitForURL after a navigation.

Strict mode violations after migrating from Selenium

Why it happens: Playwright fails when a locator matches more than one element instead of taking the first. Selectors ported from Selenium were often ambiguous and nobody knew.

The fix: Treat each violation as a finding. Narrow the locator by role and name, scope it inside a parent, or use Locator.within in 1.64. Adding first() reintroduces the ambiguity strict mode is catching.

Signing in through the UI in every test

Why it happens: It is the obvious way to start, and it costs several seconds per test, which on a large suite becomes most of the runtime.

The fix: Sign in once in a setup project, save storageState to a file, and load it in each project’s use block. Use one account per worker if tests change shared server-side state.

Treating WebKit results as Safari results

Why it happens: Playwright’s WebKit is built from WebKit sources with Playwright’s patches. It is not Safari and lacks Apple-specific behaviour, so genuine Safari bugs can pass it cleanly.

The fix: Use WebKit for broad engine coverage in CI, ideally on macOS for media features, and verify Safari-critical journeys on a real device or a cloud Safari session before release.

Recording traces and video for every run

Why it happens: Setting trace to on feels thorough and produces large artefacts for every test, which slows CI and fills storage while nobody looks at passing traces.

The fix: Use on-first-retry or retain-on-failure for traces and retain-on-failure for video. You get full diagnostics when something fails and pay almost nothing when it does not.

Copying APIs that recent releases removed

Why it happens: Older articles use page.accessibility, removed in 1.57, the _react and _vue selectors and the devtools launch option, removed in 1.58, and the videosPath context option, removed in 1.60. page.type has been deprecated since 1.38.

The fix: Use an axe integration for accessibility checks, role and test id locators instead of framework selectors, recordVideo for video, and fill or pressSequentially instead of type.

Unbalanced shards

Why it happens: Without fullyParallel, Playwright assigns whole files to shards, so one large spec file can make a single shard run far longer than the rest.

The fix: Turn on fullyParallel so tests are split individually, or keep spec files small and similar in size. Check shard durations in CI after adding large files.

Compared

Playwright alternatives compared

ToolLanguagesBrowsersAuto-waitingParallel across machinesMulti-tab and multi-userNative mobile apps
PlaywrightJS, TS, Python, Java, C#Chromium, Chrome, Edge, Firefox, WebKitYes, actionability checksBuilt-in sharding, freeYes, contexts and pagesNo
CypressJavaScript, TypeScriptChrome, Edge, Firefox; WebKit experimentalYes, retry-abilityVia Cypress Cloud, free plan cappedNo; beta plugin for Chromium tabsNo
SeleniumJava, Python, C#, Ruby, JSChrome, Edge, Firefox, SafariNo, explicit waitsRunner plus Grid or cloudYes, windows and separate sessionsVia Appium
WebdriverIOJavaScript, TypeScriptChrome, Edge, Firefox, SafariYes, for actions and matchersBuilt-in --shard, freeYes, plus multiremoteYes, via Appium service
PuppeteerJavaScript, TypeScriptChrome, FirefoxLocators onlyNo runner; bring your ownYes, pages and browser contextsNo

FAQ

Frequently asked questions

What are the best Playwright alternatives?

The alternatives most often weighed against Playwright are Cypress, Selenium, WebdriverIO and Puppeteer. The comparison on this page sets them side by side on languages, browsers, auto-waiting, parallel across machines, multi-tab and multi-user and native mobile apps, and the verdict above says where Playwright is the wrong choice and which of them to pick instead.

Is Playwright free?

Yes. Playwright and Playwright Test are open source under the Apache 2.0 licence, and everything from parallel workers and sharding to the trace viewer and HTML report is included without an account. Microsoft’s only paid offering is Playwright Workspaces in Azure App Testing, an optional cloud browser service billed per minute of browser time. Most teams run Playwright entirely on their own CI machines.

Should I choose Playwright or Selenium?

For most new web projects, Playwright, on stability and developer experience: auto-waiting, network interception, tracing and parallel execution are built in. Choose Selenium when you need Ruby, real Safari rather than WebKit, a W3C standard rather than a vendor-maintained tool, or when a healthy Selenium suite already exists. Write new coverage in Playwright rather than scheduling a big-bang migration.

Should I choose Playwright or Cypress?

Choose Playwright when you need several tabs or users in one test, WebKit coverage, languages beyond JavaScript, or free parallel execution across machines. Choose Cypress when a JavaScript team values its interactive runner and in-browser debugging, tests one user in one tab, and wants component and end-to-end tests in one familiar tool. For a new suite with no preference, Playwright has fewer structural limits.

Does Playwright test real Safari?

No. It bundles WebKit built from the WebKit main branch with Playwright’s own patches, and the documentation states it does not work with branded Safari. WebKit catches most engine-level differences, and running it on macOS gets closest to Safari for media features. Safari-critical journeys still need a real device or a cloud Safari session.

Can Playwright test mobile apps?

Not native apps. It emulates mobile devices in desktop engines, including viewport, pixel ratio, user agent, touch and geolocation, which is useful for responsive and mobile web testing. Native and hybrid iOS and Android apps need Appium, the platform frameworks, or a tool such as WebdriverIO with its Appium service.

Can I use Playwright with Python, Java or C#?

Yes. The same browser API is published for Python, Java and .NET. Python teams use the official pytest-playwright plugin and pytest-xdist for parallelism; Java teams use JUnit or TestNG; .NET has base classes for MSTest, NUnit and xUnit. Playwright Test, with its sharding, UI mode and HTML report, is Node.js only, while tracing is available in every language.

What changed in Playwright 1.64?

Playwright 1.64 was released on 7 October 2026. It adds Locator.within for combining locators, a --shuffle option for random test order, projects that run only when selected, WebMCP access from page.webmcp, and smaller VP9 videos with a configurable frame rate. Breaking changes: device descriptors forward screen size, elements in hidden iframes count as hidden, JSX follows tsconfig.json, and --update-snapshots=missing now passes.

What is the difference between Playwright and Playwright Test?

Playwright is the browser automation library: launching browsers, contexts, pages, locators and network control. Playwright Test, the @playwright/test package, is the Node.js test runner built on it, adding fixtures, retrying expect assertions, parallel workers, projects, sharding, retries, reporters and UI mode. Most JavaScript teams use the runner; the library alone suits scripts and other frameworks.

Alternatives

Playwright alternatives worth weighing

Resources

Where to go next

Keep going

Prepare, or get it done

Ready

Picking the wrong tool costs more than picking late.

QAble advises on toolchain decisions and then implements them, with engineers who have run these tools on real delivery. Start with a free QA audit.

Talk to QA Advisor