Playwright Browser Testing: A Comprehensive Guide for Chromium, Firefox, and WebKit

Your Playwright tests pass in Chromium. Cool. You merge and move on. Then someone messages you that checkout is broken in Safari. Another report comes in from Firefox, and the spacing is completely off. You check your tests again. Still green. Of course they are. You only ran them in one browser. That is the […]

Playwright Browser Testing: A Comprehensive Guide for Chromium, Firefox, and WebKit

Your Playwright tests pass in Chromium. Cool. You merge and move on.

Then someone messages you that checkout is broken in Safari. Another report comes in from Firefox, and the spacing is completely off. You check your tests again. Still green. Of course they are. You only ran them in one browser.

That is the problem with single-browser testing.

Different engines behave differently. Sometimes it is a layout. Sometimes it is focus handling, scrolling, date inputs, or just some weird browser behavior. If you are only testing in one browser, assume that other browsers will also behave the same way. But sadly, they do not.

That's where Playwright comes in. The nice thing about Playwright is that you don't need three separate setups to fix this issue. You just write the test once and run it on all browsers, Chromium, Firefox, and WebKit, etc.

  • Same test file

  • Same API

  • No browser-specific hacks

  • Just multiple projects in config

In this guide, we will look at how Playwright runs tests across all three engines, how to configure it properly, what usually breaks between them, and how to wire it into CI so browser bugs get caught before your users find them.

How does Playwright browser testing work across three engines?

Playwright runs tests across Chromium, Firefox, and WebKit using a single, unified API. You write one test, and Playwright executes it against all three rendering engines without requiring separate test scripts or driver configurations.

Here's what makes this possible. Playwright doesn't use your installed Chrome, Firefox, or Safari. Instead, it downloads its own patched versions of each browser engine during installation. This ensures consistent behavior across every machine, whether it's your laptop or a CI runner.

The three engines Playwright supports cover the vast majority of real-world browsers:

  • Chromium powers Chrome, Edge, Opera, Brave, and most Android browsers

  • Firefox (Gecko) powers Mozilla Firefox on all platforms

  • WebKit powers Safari on macOS and iOS

What is Rendering Engine?

The software component inside a browser that interprets HTML, CSS, and JavaScript to display web pages. Different engines (Blink, Gecko, WebKit) can render the same code differently.

When you install Playwright, it downloads all three engines by default:

terminal
npm init playwright@latest
# Downloads Chromium, Firefox, and WebKit automatically

This single command gives you everything you need. No Selenium grid. No WebDriver binaries. No version mismatches.

The API stays identical regardless of which browser runs the test. A page.click() call works the same in Chromium, Firefox, and WebKit. If your test breaks in one browser but passes in others, you've found a real browser compatibility issue, not a test framework bug.

How to configure Playwright projects for multi-browser testing?

Playwright uses a "projects" system in its configuration file to run tests across multiple browsers. Each project targets a specific browser or device, and they all run from the same test files.

The default playwright.config.ts already includes all three browsers:

playwright.config.ts
// playwright.config.ts
import { defineConfigdevices } from '@playwright/test';
export default defineConfig({
  projects: [
    {
      name'chromium',
      use: { ...devices['Desktop Chrome'] },
    },

    {
      name'firefox',
      use: { ...devices['Desktop Firefox'] },
    },

    {
      name'webkit',
      use: { ...devices['Desktop Safari'] },
    },
  ],
});

Running npx playwright test executes your entire test suite against all three browsers in parallel. The output clearly labels which browser each result belongs to:

terminal
Running 9 tests using 3 workers
  ✓ [chromium] › login.spec.ts:5:1 › should log in (1.2s)
  ✓ [firefox]  › login.spec.ts:5:1 › should log in (1.4s)
  ✓ [webkit]   › login.spec.ts:5:1 › should log in (1.3s)

You can also target a single browser when debugging:

terminal
# Run only in Firefox
npx playwright test --project=firefox

# Run only in WebKit
npx playwright test --project=webkit

Tip: During local development, run tests against Chromium only for speed. Save full cross-browser runs for your CI pipeline where all three engines matter.

You can extend projects to include mobile emulation as well. Playwright ships with a library of preconfigured devices:

playwright.config.ts
projects: [
  // Desktop browsers
  { name'chromium'use: { ...devices['Desktop Chrome'] } },
  { name'firefox'use: { ...devices['Desktop Firefox'] } },
  { name'webkit'use: { ...devices['Desktop Safari'] } },

  // Mobile devices
  { name'Mobile Chrome'use: { ...devices['Pixel 5'] } },
  { name'Mobile Safari'use: { ...devices['iPhone 12'] } },

  // Branded browsers
  { name'Google Chrome'use: { ...devices['Desktop Chrome'], channel'chrome' } },
  { name'Microsoft Edge'use: { ...devices['Desktop Edge'], channel'msedge' } },
],

This configuration gives you desktop coverage, mobile coverage, and branded browser coverage from a single config file. No additional tools needed.

Chromium, Firefox, and WebKit: What each engine covers

Not all browsers are created equal. Understanding what each Playwright engine covers helps you decide where to focus your testing effort.

1. Chromium: The majority of your users

Chromium is the open-source engine behind Chrome, Edge, Opera, Brave, and most Android browsers. As of early 2026, Chrome alone holds over 65% of the global desktop browser market share.

When you include Edge (around 13% desktop share) and other Chromium-based browsers, you're covering roughly 80% of desktop users with one engine.

Playwright stays ahead of Chrome releases. When Chrome is on version N, Playwright already supports Chromium N+1. This means you're testing against features your users will see in a few weeks, not features from months ago.

A few things to know about Chromium in Playwright:

  • Playwright uses open-source Chromium builds by default, not branded Chrome

  • Enterprise policies (proxies, mandatory extensions) can interfere with branded Chrome, so Chromium is safer for local testing

  • You can opt into branded Chrome using the channel: 'chrome' option if needed

  • Headless mode uses chrome-headless-shell for lighter resource usage in CI

2. Firefox: The privacy-focused segment

Playwright's Firefox version matches the recent Firefox Stable build. Unlike Chromium, Playwright uses a patched version of Firefox's Gecko engine because it needs deeper integration than Firefox's public APIs allow.

Firefox holds around 5-7% of the desktop market share, depending on the source. That sounds small, but it's still hundreds of millions of users. And Firefox users tend to be more technical, which means they're often power users of your product.

Common issues that only show up in Firefox include differences in form validation behavior, CSS grid rendering, and how fetch handles certain CORS scenarios.

Note: Playwright doesn't work with the branded version of Firefox you have installed. It downloads its own patched build. This means Firefox extension testing isn't supported through Playwright.

3. WebKit: Your Safari coverage

WebKit is the engine behind Safari on macOS and all browsers on iOS (yes, even Chrome on iOS uses WebKit under the hood). Safari holds roughly 18 to 20% of the global browser market, and it's the dominant mobile browser in the US.

Playwright's WebKit build is derived from the latest WebKit main branch sources. This often includes changes that haven't shipped in Safari yet, giving you early access to upcoming behavior.

There are platform-specific differences to keep in mind:

  • Media codecs vary between Linux, macOS, and Windows

  • Video playback testing is most accurate on macOS

  • Linux CI is the cheapest option for WebKit testing, but some media features may behave differently from real Safari

For the closest-to-Safari experience, run WebKit tests on macOS. For general layout and JavaScript testing, Linux works fine and saves CI costs.

Engine Browsers Covered Desktop Share Key Differences
Chromium Chrome, Edge, Opera, Brave ~80% combined Fastest Playwright engine, ahead of stable Chrome
Firefox (Gecko) Firefox ~5-7% Patched build, different CSS/form behavior
WebKit Safari, all iOS browsers ~18-20% Media codec differences by OS, closest to Safari on macOS

Sources: StatCounter (2025-2026), Backlinko Browser Market Share Report

Setting up cross-browser tests (step-by-step)

Getting Playwright browser testing running across all three engines takes about 5 minutes. Here's the setup from scratch.

Step 1 - Install Playwright and all browsers

terminal
npm init playwright@latest

This command does three things. It installs the @playwright/test package, downloads Chromium, Firefox, and WebKit binaries, and creates a starter config file.

If you only want specific browsers to save disk space:

terminal
# Install only Chromium and WebKit
npx playwright install chromium webkit

Step 2 - Write a browser-agnostic test

Your test file doesn't need to know which browser it runs on. Playwright handles that through the config:

example.spec.ts
import { testexpect } from '@playwright/test';
test('has title'async ({ page }) => {
  await page.goto('https://playwright.dev/');
  // Expect a title "to contain" a substring.
  await expect(page).toHaveTitle(/Playwright/);
});

test('get started link'async ({ page }) => {
  await page.goto('https://playwright.dev/');
  // Click the get started link.
  await page.getByRole('link', { name'Get started' }).click();
  // Expects page to have a heading with the name of Installation.
  await expect(page.getByRole('heading', { name'Installation' })).toBeVisible();
});

This test runs identically in Chromium, Firefox, and WebKit. No browser-specific code needed.

Step 3 - Configure projects in playwright.config.ts

The default config works for most teams. Customize timeouts and retries based on your app:

playwright.config.ts
import { defineConfigdevices } from '@playwright/test';
export default defineConfig({
  testDir'./tests',
  timeout30_000,
  retries1,
  reporter'html',
  use: {
    trace'on-first-retry',
    screenshot'only-on-failure',
  },

  projects: [
    { name'chromium'use: { ...devices['Desktop Chrome'] } },
    { name'firefox'use: { ...devices['Desktop Firefox'] } },
    { name'webkit'use: { ...devices['Desktop Safari'] } },
  ],
});

Step 4 - Run tests across all browsers

terminal
# Run against all configured browsers
npx playwright test

# Run with visible browser windows for debugging
npx playwright test --headed

# Run and open the HTML report
npx playwright test --reporter=html

Step 5 - Add to CI with GitHub Actions

.github/workflows/playwright.yml
# .github/workflows/playwright.yml
namePlaywright Tests
on: [pushpull_request]
jobs:
  test:
    runs-onubuntu-latest
    steps:
      - usesactions/checkout@v4
      - usesactions/setup-node@v4
        with:
          node-version20

      - runnpm ci
      - runnpx playwright install --with-deps
      - runnpx playwright test
      - usesactions/upload-artifact@v4

        if${{ !cancelled() }}
        with:
          nameplaywright-report
          pathplaywright-report/

The --with-deps flag installs system-level dependencies that Firefox and WebKit need on Linux. Without it, you'll see cryptic errors in CI.

Tip: Use retries: 2 in CI and retries: 0 locally. CI environments are more prone to timing issues, and retries catch genuine flakes without hiding real bugs during development.

Common cross-browser issues and how to fix them

Writing one test for three engines sounds ideal until a test passes in Chromium but fails in WebKit. These are the most common browser-specific issues teams run into with Playwright, and how to handle them.

CSS rendering differences

Each engine interprets CSS slightly differently. Flexbox gaps, grid alignment, and font rendering are the usual suspects.

For example, gap in flexbox containers have been supported in Chromium since 2020, but were inconsistent in older WebKit versions. If your layout breaks in WebKit but looks fine in Chrome, CSS feature support is the first place to check.

Fix: Use the Can I Use database to verify feature support. Write CSS that degrades gracefully, and use Playwright's screenshot comparison to catch visual regressions.

Form and input behavior

Firefox handles form validation differently from Chromium. Date pickers, autofill behavior, and input masking can all produce different results.

Fix: Use getByRole and getByLabel locators instead of CSS selectors. These are based on accessibility attributes that behave consistently across browsers:

code
// More reliable across browsers
await page.getByLabel('Email').fill('[email protected]');
await page.getByRole('button', { name'Submit' }).click();

Timing and animation differences

WebKit and Firefox may render animations at different speeds than Chromium. Tests that rely on animation completion can fail intermittently.

Fix: Playwright's auto-wait handles most timing issues. For animations, wait for the final state rather than a fixed timeout:

code
// Bad: fixed timeout
await page.waitForTimeout(2000);
// Good: wait for the actual state
await expect(page.getByText('Success')).toBeVisible();

Network and CORS handling

Firefox enforces stricter CORS policies in some scenarios. API calls that work in Chromium might fail in Firefox due to preflight request differences.

Fix: Test your API endpoints separately using Playwright's request fixture, and ensure your server sends proper CORS headers for all methods.

Note: If a test fails in only one browser, don't add a browser-specific skip. Instead, investigate whether the failure is a real bug in your app that only surfaces in that engine. These are often the most valuable bugs to catch.

Best practices for Playwright browser testing

After setting up cross-browser tests, these practices keep your suite fast, reliable, and useful as it grows.

Use semantic locators everywhere. Locators like getByRole, getByText, and getByLabel work consistently across browsers because they're based on the accessibility tree, not the DOM structure. This makes tests more stable and more meaningful.

code
// Reliable across all browsers
page.getByRole('button', { name'Add to cart' });

// Fragile - may break with CSS changes
page.locator('.btn-primary.add-cart-btn');

Run Chromium locally, all browsers in CI. Full cross-browser runs triple your test time. During development, test against Chromium for fast feedback. Let CI run the full matrix overnight or on pull requests.

Set up browser-specific traces. When a test fails in WebKit but passes in Chromium, Playwright's trace viewer shows you exactly what happened. Enable traces on first retry so you get debugging data without slowing down passing tests:

playwright.config.ts
use: {
  trace'on-first-retry',
}

Don't skip tests per browser without documenting why. It's tempting to add test.skip(browserName === 'webkit') when something fails. Instead, file an issue, add a comment with the reason, and revisit it. Skipped tests accumulate quickly and become blind spots.

Keep browser versions updated. Run npx playwright install regularly. Playwright ships updates that include new browser versions, bug fixes, and performance improvements. Falling behind means your tests run on engines that don't match what your users have.

For teams running large Playwright test suites across multiple browsers, keeping track of browser-specific failures manually gets overwhelming fast. Tools like TestDino show which browsers have the most failures and track patterns across runs, so you can prioritize fixes based on actual data instead of gut feeling.

Track, Analyze, and Fix Browser Breaks Fast
TestDino shows you exactly which browsers fail, why, and where to focus to ship confidently.
Try Now CTA Graphic

Conclusion

Playwright browser testing gives you real cross-browser coverage without the complexity that older tools required. One API, three engines, and a config file that takes minutes to set up.

The key takeaway is straightforward: Chromium covers the majority of your users, Firefox catches standards compliance issues, and WebKit protects your Safari audience. Running all three in CI, with Chromium as your local default, gives you the right balance between speed and coverage.

For teams scaling their Playwright suites, TestDino adds AI-powered failure classification across browser projects, so you can quickly spot whether a failure is browser-specific, flaky, or a real regression without digging through logs manually.

FAQs

Does Playwright test real Chrome and Safari?
Not by default. Playwright tests Chromium (Chrome's open-source base) and WebKit (Safari's engine). You can opt into branded Chrome and Edge using the channel option, but Safari itself isn't directly supported. WebKit testing covers Safari's rendering behavior.
Can I run Playwright tests on only one browser?
Yes. Use the --project flag to target a specific browser: npx playwright test --project=firefox. This is useful for debugging browser-specific issues without running the full suite.
Why does my test pass in Chromium but fail in WebKit?
Common causes include CSS rendering differences, different timing for animations, stricter security policies in WebKit, and media codec variations. Playwright's trace viewer helps pinpoint the exact step that diverges between browsers.
Do I need macOS to test WebKit?
No. Playwright runs WebKit on Linux and Windows too. However, some features like video playback and media codecs behave differently across operating systems. For the most accurate Safari simulation, macOS is recommended.
How does Playwright handle browser updates?
Playwright bundles specific browser versions with each release. Running npx playwright install downloads the correct versions. Playwright's Chromium is typically one version ahead of stable Chrome, giving you early warning of potential issues.

Get started fast

Step-by-step guides, real-world examples, and proven strategies to maximize your test reporting success