Playwright’s multi-browser testing is less about running tests in different browsers and more about running them against different browser engines.
Here’s a simple example of how you’d set up Playwright to run tests across Chrome, Firefox, and WebKit (Safari’s engine):
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
// Look for tests in any '.spec.ts' file.
testDir: './tests',
// Run tests in parallel.
fullyParallel: true,
// Fail the build on test failures.
failOnScan: true,
// Limit the number of parallel test runs.
workers: 3,
// Reporter to use. See https://playwright.dev/docs/test-reporters
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api-configuration. */
use: {
/* Base URL to use in actions like `await page.goto('/')`. */
// baseURL: 'http://localhost:3000',
/* Collect trace when an iteration of a test fails. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
},
{
name: 'firefox',
use: {
...devices['Desktop Firefox'],
},
},
{
name: 'webkit',
use: {
...devices['Desktop Safari'],
},
},
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: {
// ...devices['Pixel 5'],
// },
// },
// {
// name: 'Mobile Safari',
// use: {
// ...devices['iPhone 12'],
// },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: {
// channel: 'msedge',
// },
// },
// {
// name: 'Google Chrome',
// use: {
// channel: 'chrome',
// },
// },
],
// Run your local dev server before starting the tests
// webServer: {
// command: 'npm run start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: true,
// },
});
When you run npx playwright test, Playwright will execute your tests against each project defined in playwright.config.ts. This means a single test file like example.spec.ts will be run three times, once for Chromium, once for Firefox, and once for WebKit. The devices object provides pre-configured settings for common device emulations, including browser versions and viewports. You can also specify a specific channel like 'chrome' or 'msedge' if you have multiple versions of a browser installed and want to target a particular one.
The core problem Playwright solves here is the fragmentation of browser rendering engines and JavaScript implementations. Historically, developers had to write and maintain separate test suites or manually test across different browsers, a tedious and error-prone process. Playwright abstracts this away by providing a unified API that interacts with each browser engine through its own specific protocol (like Chrome DevTools Protocol for Chromium, or its own protocol for WebKit and Firefox). This allows you to write tests once and have them execute consistently across the major browser families.
Internally, Playwright launches separate browser instances for each configured project. The workers setting controls how many of these browser instances can run tests in parallel. If workers is set to 3, and you have 3 projects defined (like chromium, firefox, webkit), all three browser types will be running tests simultaneously. If you had 5 projects, only 3 would run at a time, with the others waiting for a worker to become free. The fullyParallel: true setting ensures that tests within a single project can also run in parallel if you have enough workers.
The use block within each project is where you specify browser-specific configurations. For example, you might want to set a different locale or timezone for testing in different regions, or use specific userAgent strings. The devices object is a convenience wrapper for common configurations, abstracting away the need to manually set viewport, userAgent, and other properties.
A key detail often overlooked is how Playwright handles browser launches and closures. By default, Playwright will launch a new browser instance for each test run per project. This ensures test isolation. However, for performance, especially in CI environments, you can configure Playwright to reuse existing browser instances. This is done by setting launchOptions within the use block of a project, but it’s crucial to understand that this can lead to test pollution if not managed carefully. For instance, await context.close() or await browser.close() within your test setup or teardown hooks becomes essential when reusing browser contexts or instances.
When you run npx playwright test --project=chromium, Playwright will only execute tests against the Chromium project. This is useful for debugging or when you know a specific browser engine is the source of an issue.
The next logical step after mastering multi-browser testing is understanding how to leverage Playwright’s tracing capabilities for debugging cross-browser issues.