Playwright Cross-Browser Testing: Config + CI Examples
Alex Chen · Updated 11 min read
Find out how easy it is to capture and share pixel-perfect screenshots at scale using Allscreenshots. Sign up for a free account and start integrating your first screenshot API call today.
AC
Alex Chen
Full-stack developer and technical writer who loves browser automation and making complex tools accessible.
Run the same tests in Chromium, Firefox and WebKit by defining a Playwright project for each engine. Start with this working example, then choose a smaller test matrix for your application.
Quickstart: run a complete cross-browser example
Download the example ZIP, extract it, and open the playwright-cross-browser-example directory. The source repository includes the fixture, tests, lockfile and CI workflow.
npm ci
npx playwright install --with-deps
npmtest# Debug one engine or inspect the HTML report:npmtest -- --project=webkit
npx playwright show-report
No API key is needed. The test runner starts a local server for a responsive page with a form and a marker below the fold.
The config uses three desktop engines and two mobile profiles. webServer starts the example automatically; for your own app, change its command and baseURL together. The pinned Playwright version and lockfile keep the browser tooling reproducible.
A test with user-visible assertions
import{ test, expect }from'@playwright/test';test('valid form submission and responsive layout',async({ page })=>{await page.goto('/');awaitexpect(page.getByRole('heading',{ name:'Website screenshots across browsers'})).toBeVisible();await page.getByLabel('Email',{ exact:true}).fill('[email protected]');await page.getByRole('button',{ name:'Continue'}).click();awaitexpect(page.getByRole('status')).toHaveText('Ready to continue');expect(await page.evaluate(()=> document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);});
The second test in the repository verifies native required-field validation and captures a full-page PNG after checking the end marker. Keep different engines' visual baselines separate.
Evidence from an actual run
On October 8, 2026, Node 24.8.0 and Playwright 1.64.0 on macOS arm64 ran 10 tests: 10 passed, 0 failed, 0 skipped, in 17.8 seconds. Both tests passed in all five projects. Download the recorded results.
Project
Tests passed
What it covers
Chromium
2 / 2
Desktop Chromium engine
Firefox
2 / 2
Playwright's patched Firefox
WebKit
2 / 2
Desktop WebKit build
Mobile Chromium
2 / 2
Pixel 7 device profile
Mobile WebKit
2 / 2
iPhone 13 device profile
Full-page WebKit output showing the form, two-column layout and end-of-capture marker
Reading the capture: the form near the top tests native validation; the two cards expose layout overflow; the END OF CAPTURE marker verifies the image extends below the viewport. Chromium output, Firefox output, and mobile WebKit output are available for comparison.
These results validate this fixture on one machine. Mobile profiles are emulation, and Playwright WebKit is not the Safari application or a real iPhone. They do not prove your production checkout works.
GitHub Actions for this example
Use the included workflow when placing the example at the root of its own repository:
The public example repository runs this workflow directly. The local result above is measured; the matching five-project suite also passed in the website’s CI on October 8, 2026. New runs are available in the public example’s GitHub Actions.
Plan the coverage for your app
Playwright provides browser projects and CI installation guidance. Once the configuration runs, choose which user journeys need every engine and which can run only in Chromium.
What Cross-Browser Testing Should Prove
Start by deciding what risk you are trying to reduce. A good cross-browser suite usually answers these questions:
Can users sign up, log in, search, buy, save, export, or complete the core workflow?
Does the app render usable layouts in Chromium, Firefox, and WebKit?
Do mobile navigation, dialogs, sticky headers, and forms work at phone-sized viewports?
Do browser APIs such as downloads, clipboard access, file uploads, media playback, or date inputs behave as expected?
Do important pages still look sane after CSS, dependency, or design system changes?
It should not try to duplicate every unit test in every browser. That creates more runtime than signal. Use Playwright for browser behavior and real user flows. Keep business logic, pure functions, and edge-case permutations in cheaper test layers.
Make the cross-browser suite intentionally small at first. A fast suite that runs on every pull request is more valuable than a complete suite that developers only run before major releases.
Browser Engines Playwright Can Cover
Playwright's core browser projects map to three engines:
Project
Engine
Useful coverage
Chromium
Blink/V8
Chrome, Edge, many desktop users, most Android browser behavior
Firefox
Gecko/SpiderMonkey
Firefox-specific layout, event, storage, and media behavior
Playwright can also run against branded Google Chrome and Microsoft Edge through browser channels, and it can emulate selected desktop, tablet, and mobile device profiles.
There are two important caveats:
Playwright's WebKit is not the branded Safari app. It is a WebKit build that gives strong Safari-like coverage, but the closest Safari signal still comes from running WebKit on macOS when platform-specific behavior matters.
Playwright's Firefox support uses its own patched Firefox build. Do not assume it is the same as launching a user's installed Firefox binary.
Those caveats do not make the coverage weak. They just mean you should treat Playwright as a practical automation layer, not as a perfect substitute for every physical device and browser combination.
Configure a Browser Matrix
Most teams should start with one Playwright config that defines browser projects. The project names become the handles you use locally and in CI.
npx playwright test tests/e2e/checkout.spec.ts --project=firefox
That last command is the one developers use most often when debugging a browser-specific failure.
Choose the Right Test Matrix
Running every test in every browser sounds thorough, but it usually becomes expensive quickly. A better matrix separates critical coverage from broad coverage.
Suite
Browser coverage
When to run
Smoke flows
Chromium, Firefox, WebKit, mobile Chrome, mobile Safari
Then run the smoke folder across every project and the full suite in Chromium:
npx playwright test tests/e2e/smoke
npx playwright test tests/e2e/full --project=chromium
This gives you browser breadth where it matters most and keeps routine CI practical.
Write Tests That Survive Browser Differences
Cross-browser tests are less forgiving than single-browser tests. Small assumptions that pass in Chromium can fail in Firefox or WebKit.
Prefer user-facing locators
Role, label, placeholder, and text locators are usually more resilient than CSS selectors.
import{ test, expect }from'@playwright/test';test('visitor can request a product demo',async({ page })=>{await page.goto('/pricing');await page.getByRole('link',{ name:/book a demo/i}).click();awaitexpect(page.getByRole('heading',{ name:/schedule a demo/i})).toBeVisible();});
CSS selectors often depend on implementation details that vary after refactors:
// Fragileawait page.locator('.pricing-card:nth-child(2) .button.primary').click();// Betterawait page.getByRole('link',{ name:/start pro trial/i}).click();
Avoid timing assumptions
Hard waits hide real readiness problems and behave differently across engines and CI machines.
// Avoidawait page.waitForTimeout(2000);// Prefer a user-visible signalawaitexpect(page.getByRole('heading',{ name:'Dashboard'})).toBeVisible();awaitexpect(page.getByText('Project created')).toBeVisible();
Be careful with hover-only interactions
Hover behavior differs across desktop and touch-style projects. If a menu is essential on mobile, the product should expose a touch-friendly path. Your test should reflect the real control for that viewport.
Fonts, antialiasing, form controls, scrollbars, focus rings, and media codecs can differ between engines and operating systems. Functional tests should assert user outcomes, not exact CSS internals.
For visual checks, compare screenshots inside a stable environment and keep browser-specific baselines separate. A WebKit screenshot should not usually be compared against a Chromium baseline.
Use Project-Specific Settings
Some browser projects need different settings. For example, you may want a stricter reduced-motion environment for visual tests, or a branded Chrome channel for codec-heavy workflows.
Use branded Chrome or Edge when you specifically need the installed browser channel, media codecs, enterprise browser policy behavior, or parity with a customer support environment. For normal regression testing, the bundled browser projects are simpler and more reproducible.
Run Cross-Browser Tests in CI
For CI, install the Playwright browsers and operating system dependencies before running the suite.
Do not hide browser-specific failures by rerunning the whole matrix until it passes. Use retries to collect a cleaner signal, but treat repeat failures in one browser as product or test bugs that need investigation.
Debug Browser-Specific Failures
When a test fails in only one browser, resist the urge to guess. Capture the browser's view of the page.
Start with the failing project:
npx playwright test tests/e2e/checkout.spec.ts --project=webkit --headed
Missing OS dependency, font difference, slower hardware, hidden race condition
Works on desktop, fails on mobile project
Different navigation pattern, covered button, viewport-specific layout, touch target issue
Fails only in Firefox
CSS support difference, event handling difference, storage or download behavior
When a test exposes a real browser bug in your app, keep the regression test. Narrow it to the behavior that failed so the next person understands why it exists.
Screenshots are useful for debugging and for visual checks around high-value pages. But screenshot-heavy workflows become expensive when every browser, route, state, and viewport runs inside the same end-to-end suite.
A practical split is:
Use Playwright to prove that flows work across browsers.
Use screenshots on failure so developers can debug quickly.
Use a dedicated screenshot workflow for broad page coverage, production monitoring, documentation snapshots, and visual baselines.
That keeps the Playwright suite focused on behavior while screenshot automation handles repeatable capture, visual regression testing, and scheduled page monitoring.
A Practical Rollout Plan
If you are adding cross-browser testing to an existing app, do it in stages:
Add Playwright with Chromium only.
Write three to five smoke tests for critical workflows.
Add Firefox and WebKit projects for those smoke tests.
Add mobile Chrome and mobile Safari projects for responsive workflows.
Capture traces, screenshots, and videos on retry or failure.
Move the full suite to Chromium and keep the broad matrix focused.
Add nightly or release-gated full-matrix runs if the app needs it.
This sequence gives you useful coverage early and avoids making browser testing feel like a tax on every change.
Conclusion
Playwright makes cross-browser testing approachable, but the value comes from the matrix you choose. Run your most important flows across Chromium, Firefox, WebKit, and mobile projects. Keep the full suite concentrated where it gives the best signal. Use traces to debug browser-only failures, and use screenshot automation when the job is broad visual coverage rather than interaction testing.
Done well, cross-browser testing becomes a focused release safety net instead of an expensive checklist.