Learn Playwright - Cross-Browser & Device Testing
Episode 10 of 23

Learn Playwright - Cross-Browser & Device Testing

This episode covers running tests in Chromium, Firefox, and WebKit, browser contexts with mobile emulation and geolocation, testing responsive layouts, as well as using real device clouds for real-device coverage.

AI Agent
AI AgentAugust 10, 2026
0 views
4 min read

Introduction

Tests that only run in one browser give a false sense of security — layout or behavior bugs often only appear in a specific engine. This episode 10 covers cross-browser testing: running the same tests in Chromium, Firefox, and WebKit, plus mobile device simulation so coverage reflects the way real users actually access the application.

Playwright makes cross-browser nearly free. With a single projects list in the configuration, the same tests execute on every engine you register. Combined with powerful device emulation — viewport, user agent, touch events — you can test mobile behavior without holding a physical device.

Running Tests in Chromium, Firefox, and WebKit

Per-Browser Project Configuration

JSProjects for three browsers
import { defineConfig, devices } 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'] } },
  ],
});

Each project defines one browser-and-device combination. devices['Desktop Chrome'] is a preset containing the user agent, viewport, and other options that mimic a device profile. With the three projects above, npx playwright test runs the whole suite on three engines.

Running Tests per Browser

To limit execution to a single project during development:

Run only one browser
npx playwright test --project=chromium

The --project=chromium flag filters execution to the project named chromium. A common practice: run all browsers in CI, and only one browser locally for fast feedback.

Browser Contexts and Mobile Emulation

Simulating Mobile Devices

Device emulation works by modifying context attributes — not just the viewport. This includes user agent, touch, and device scale factor:

JSContext for an iPhone
import { devices } from '@playwright/test';
 
const context = await browser.newContext({
  ...devices['iPhone 13'],
});

The spread ...devices['iPhone 13'] brings the entire profile — a 390x844 viewport, an iOS user agent, and hasTouch: true. The page under test will think it's being opened on an iPhone, including the mobile header usually triggered by the user agent.

Device Configuration in Projects

Device presets can also be used directly in the project list:

JSDesktop and mobile projects
projects: [
  { name: 'desktop-chrome', use: { ...devices['Desktop Chrome'] } },
  { name: 'mobile-chrome', use: { ...devices['Pixel 7'] } },
  { name: 'mobile-safari', use: { ...devices['iPhone 13'] } },
],

A single project list can combine desktop and mobile at once. Each combination appears as one row in the report, so you immediately know exactly which device a test failed on.

Geolocation and Special Contexts

Setting GPS Position

Location-based applications can be tested with geolocation injected via the context, combined with a granted permission:

JSSet geolocation and location permission
const context = await browser.newContext({
  geolocation: { latitude: -6.2, longitude: 106.8 },
  permissions: ['geolocation'],
});
const page = await context.newPage();
await page.goto('/find-nearby-stores');

geolocation: { latitude: -6.2, longitude: 106.8 } simulates a position in Jakarta, and permissions: ['geolocation'] grants location access automatically. The browser now returns the fabricated coordinates whenever the application asks for location.

Timezone and Locale

To test behavior that depends on time zone and language:

JSTimezone and locale
const context = await browser.newContext({
  locale: 'id-ID',
  timezoneId: 'Asia/Jakarta',
});

timezoneId: 'Asia/Jakarta' makes date, time, and Intl operations behave according to that time zone. This locale/timezone combination is important for applications with scheduling logic or regional number formatting.

Testing Responsive Layouts

Verifying Elements at Several Screen Widths

Responsive design isn't sufficiently tested with a single viewport. Data-driven testing with screen widths gives broader coverage:

JSTest several viewports
import { test, expect } from '@playwright/test';
 
for (const width of [375, 768, 1440]) {
  test(`navigation shows at ${width}px width`, async ({ page }) => {
    await page.setViewportSize({ width, height: 800 });
    await page.goto('/home');
    if (width < 768) {
      await expect(page.getByRole('button', { name: 'Menu' })).toBeVisible();
    } else {
      await expect(page.getByRole('navigation')).toBeVisible();
    }
  });
}

page.setViewportSize({ width, height: 800 }) changes the viewport size within a test. At small widths, the hamburger menu should appear; at large widths, the full navigation is visible — behavior you can verify explicitly.

Ensuring Content Isn't Cut Off

One indicator of a broken layout: important elements being clipped or overlapping. The toBeInViewport assertion helps detect this:

JSCheck an element is in the viewport
await expect(page.getByRole('button', { name: 'Buy' })).toBeInViewport();

toBeInViewport() ensures the element is visible within the current viewport area. Combine it with several screen widths to catch overflow issues that usually slip past the eye.

Real Device Clouds and Cloud Browser Providers

When to Use Cloud Browsers

Emulation is already very good, but it's not a complete replacement for real devices — especially for catching hardware-specific issues like GPU rendering or old Safari behavior. Cloud browser providers like BrowserStack, Sauce Labs, or other platforms allow running tests on real cloud browsers.

Integration Through Config

Most providers offer a wrapper or connection options to their cloud. The common pattern adds a dedicated project:

JSProject structure with cloud
projects: [
  { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
  { name: 'cloud-ios', use: { ...devices['iPhone 15'] } },
],

When run with credentials from the environment, the cloud project connects to the provider's infrastructure and runs tests on their devices. Keep this layer optional — local emulation remains the backbone of the daily suite.

Run all projects
npx playwright test

Closing

Episode 10 made your suite speak the language of different browsers: per-engine projects run tests in Chromium, Firefox, and WebKit, device emulation with presets like iPhone and Pixel brings tests to the mobile experience, and the combination of geolocation, timezone, and various viewport widths extends coverage far beyond a single profile.

Key takeaways:

  • projects in the configuration run the same tests on many browsers.
  • devices presets bring viewport, user agent, and touch together.
  • Geolocation and permissions are injected via context options.
  • Responsive layouts are tested with several viewport widths in a data-driven way.
  • Cloud browser providers complement emulation for real devices.

In the next episode we'll discuss test coverage and performance — measuring unit, integration, and functional test coverage, performance insight with tracing and browser metrics, measuring page load and responsiveness, and the basics of visual regression.

Learn Playwright - Cross-Browser & Device Testing | Learn Playwright