ScreenshotNeo

BlogGuides

Cross-Platform UI Testing: How to Test Across Devices and Browsers

Build a risk-based browser and device matrix with Playwright, emulation, branded browsers, and real-device checks. Learn what each run can—and cannot—prove.

By the ScreenshotNeo team4 October 202611 min read

Cross-platform UI testing means running the same important user journeys against a deliberate set of browser, operating system, viewport, and device configurations. Start with the browsers you support and the flows where a failure would matter most. Use Playwright projects for repeatable Chromium, Firefox, and WebKit coverage; add branded Chrome or Edge when their stable releases matter; use emulation for fast responsive checks; and reserve physical devices for behavior that depends on real hardware or mobile OS integration. No single automation run—or single phone—proves compatibility everywhere.

This guide builds a risk-based test matrix, configures Playwright projects, explains the limits of emulation and WebKit, and shows when to add real-device testing.

1. Define the support policy and the risks

Before choosing browsers, write down what the product promises to support. That policy is the boundary for the test matrix; without it, “test every browser” has no practical stopping point.

  1. List supported browser families and versions, operating systems, and any explicit exclusions. Include desktop and mobile where the product supports both.
  2. Identify critical flows: sign-in, checkout, navigation, forms, uploads, media, and any workflow tied to revenue or access.
  3. Note platform-specific risks: touch interactions, permissions, viewport resizing, media codecs, keyboard behavior, or browser/OS integration.
  4. Use audience analytics, support tickets, and incident history to order the combinations. If those data are unavailable, document the assumptions and review them after release.
  5. Assign each combination a purpose: fast pull-request smoke coverage, broader scheduled coverage, or a targeted real-device check.

A useful matrix is small enough to run consistently and explicit enough that a failure says what environment broke. Do not select every possible combination without regard to risk.

Coverage layer Typical use What it establishes What it does not establish
Local Playwright engines Pull requests and repeatable regression suites Behavior in the installed Chromium, Firefox, and WebKit builds Exact behavior of every branded browser release or physical device
Emulated device profiles Responsive layout and basic touch-oriented workflows Behavior under configured viewport, screen, user agent, and touch parameters Real hardware, mobile OS integration, or every phone model
Branded Chrome or Edge channels Regressions against public stable browser releases Behavior in the selected installed branded browser channel Enterprise-specific policies unless configured and tested
Physical device testing High-risk mobile, media, permission, or hardware-sensitive paths Behavior on the selected device, OS, and browser combination All other models, OS versions, or browser configurations

2. Configure repeatable Playwright projects

A Playwright project groups tests under a configuration such as browser, device profile, environment, timeout, or retry policy. Projects let a suite run against the configurations that matter, and the command line can select one project or all of them. Playwright documents Chromium, Firefox, WebKit, emulated devices, and branded Chrome and Edge project examples in its projects guide.

Install the test runner

In a Node.js project, install Playwright Test and its browser binaries:

npm init -y
npm install --save-dev @playwright/test
npx playwright install

Commit the lockfile so local and CI installs use the same dependency resolution. Playwright recommends updating the package and installing the corresponding browsers together; browser binaries are tied to Playwright versions. See the official browser documentation.

Create a cross-browser configuration

The following config includes three engines and two emulated mobile profiles. Device descriptors supply a set of emulation parameters. Project names are used with --project.

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

export default defineConfig({
  testDir: './tests',
  fullyParallel: true,
  reporter: [['list'], ['html', { open: 'never' }]],
  use: {
    baseURL: process.env.BASE_URL ?? 'http://127.0.0.1:3000',
    trace: 'retain-on-failure',
    screenshot: 'only-on-failure',
  },
  projects: [
    { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
    { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
    { name: 'webkit', use: { ...devices['Desktop Safari'] } },
    {
      name: 'mobile-chrome-emulated',
      use: { ...devices['Pixel 7'] },
    },
    {
      name: 'mobile-safari-emulated',
      use: { ...devices['iPhone 13'] },
    },
  ],
});

Device descriptor names are part of the installed Playwright package and can change between releases. Check the descriptors available to your installed version before copying a device name into a long-lived config. Keep project names descriptive: “mobile-safari-emulated” makes the nature of the run clearer than “phone”.

Add a small test that runs in every project

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

test('primary navigation opens the account page', async ({ page }) => {
  await page.goto('/');
  await page.getByRole('link', { name: 'Sign in' }).click();
  await expect(page).toHaveURL(/\/sign-in/);
  await expect(page.getByRole('heading', { name: 'Sign in' })).toBeVisible();
});

Use role-based locators and observable outcomes so the same test has a fair chance of working across engines. If a browser exposes a real difference, keep the assertion and investigate it instead of weakening it just to make the matrix green.

Run all projects or select a layer

# Run every configured project
npx playwright test

# Run a single browser project
npx playwright test --project=webkit

# Run only the emulated mobile projects
npx playwright test --project=mobile-chrome-emulated --project=mobile-safari-emulated

# Run one spec in the desktop Chromium project
npx playwright test tests/navigation.spec.ts --project=chromium

For a fast pull-request gate, teams often select a compact subset and run broader coverage on a schedule. That is a policy choice: base it on suite runtime and failure risk, and retain a path that eventually exercises every supported project.

Add stable Chrome or Edge when the release channel matters

Playwright’s bundled Chromium can be ahead of branded stable browsers. A Chrome or Edge stable channel can be relevant for regressions against public releases or enterprise environments. Add these projects only if that target is part of your support or incident risk:

// Add to projects in playwright.config.ts
{
  name: 'chrome-stable',
  use: { ...devices['Desktop Chrome'], channel: 'chrome' },
},
{
  name: 'msedge-stable',
  use: { ...devices['Desktop Edge'], channel: 'msedge' },
},

The branded browser must be available on the runner. Install it through your CI image or runner setup and verify the channel name and installation for that environment. Chromium coverage alone is not proof against every Chrome or Edge release configuration.

3. Use emulation for breadth, with its limits in view

Playwright emulation can set browser parameters such as user agent, viewport, screen size, and touch behavior. This makes it useful for responsive layouts and inexpensive mobile-oriented checks in CI. The emulation guide describes these configurable parameters and device presets.

Emulation is a configured simulation, not a physical phone. It does not establish how every device behaves with its actual hardware, OS integration, browser build, or media stack. Playwright also notes that device profiles carry platform assumptions; for example, a desktop Chrome preset can include a Windows user-agent string. Inspect the descriptor and override values deliberately where your target differs.

Use emulation for:

  • Responsive breakpoints, overflow, and content visibility.
  • Touch-oriented controls and mobile navigation behavior.
  • Quick comparison of selected screen sizes and orientations.
  • Repeatable CI coverage before a change reaches a device lab.

Promote a case to real hardware when its outcome depends on actual touch input, mobile browser/OS integration, permissions, media, viewport behavior, or another hardware-sensitive feature. A phone is a useful sample of one configuration, not a replacement for the rest of the matrix.

4. Understand WebKit, Safari, and operating-system differences

Playwright’s WebKit project uses upstream WebKit; it is not the branded Safari application. WebKit tests are valuable engine coverage, but they should not be described as proof that a specific Safari release works identically.

When the exact public browser release matters, add a branded browser project where supported. For Safari-specific checks that depend on macOS behavior—for example, some video playback cases—Playwright’s browser guidance points to running WebKit on macOS. Host operating systems can also affect features such as media codecs, so a Linux run and a macOS run may not be interchangeable. Review the current Playwright browser guidance for the behavior you need to validate.

Record the host OS and browser versions with failures. “WebKit failed” is not enough information to reproduce a media issue if the run’s operating system and browser revision are unknown.

5. Add real-device checks for platform-specific risks

Use physical Android or iOS devices when the risk warrants evidence from actual hardware and OS behavior. A focused device suite can cover touch interactions, orientation or viewport edge cases, permissions, media playback, and the browser integration that emulation cannot establish.

Cloud device services can provide access to selected real devices and can run Playwright, but support depends on provider, OS, browser, device, and version. BrowserStack documents its supported Playwright browser and OS combinations and mobile-device workflow in its compatibility documentation and mobile testing guide. Verify the current compatibility list and the exact automation path before making it a release dependency.

BrowserStack announced real iPhone and iPad Safari support for Playwright in June 2025. Its announcement attributed a “28% of global web traffic” figure to its CTO; treat that number as the company’s stated figure, not an independent traffic measurement. See the announcement.

Keep cloud-device selection purposeful. Evaluate supported browser and OS versions, device models and orientations, physical versus emulated execution, CI integration, parallel capacity, run time, debugging evidence, privacy constraints, and total cost. The cited documentation establishes capabilities, not a neutral vendor benchmark or current price comparison.

6. Keep the matrix maintainable

  • Run the highest-value checks first. Put critical journeys in the fastest dependable layer, then add broader combinations where the risk justifies them.
  • Make environment identity visible. Record Playwright version, browser revision or channel, host OS, project name, and device profile in CI output or artifacts.
  • Preserve failure evidence. Configure traces and screenshots for failures; add video or network and console logs only where your runner and retention policy support them.
  • Separate product failures from setup failures. A missing browser binary, unavailable cloud device, or broken test account is not a UI regression.
  • Review on change. Revisit the matrix after browser support changes, major incidents, audience shifts, and platform-specific feature launches.
  • Control concurrency. More projects and workers can reduce wall time but consume more CI capacity and can expose shared test data or rate limits. Isolate accounts and data before increasing parallelism.

7. Capture screenshots for visual review

Cross-browser automation answers behavioral questions; screenshots help inspect layout and visual differences at a specific viewport. Keep screenshot comparisons tied to a named browser, OS, viewport, and device profile. A difference can come from fonts, rendering engines, operating-system rasterization, or animation timing, so establish stable baselines per environment rather than treating every pixel as universally identical.

For a screenshot captured from your own test, Playwright can save a page image directly:

import { test } from '@playwright/test';

test('save the home page screenshot', async ({ page }) => {
  await page.goto('/');
  await page.screenshot({ path: 'artifacts/home.png', fullPage: true });
});

To inspect a public URL without setting up a local browser, ScreenshotNeo is a website screenshot API and MCP server from ScreenshotNeo. Its API accepts one GET request and returns an image or PDF; the full option list is in the ScreenshotNeo documentation. This is useful for capturing a reference page, but an image capture is not a replacement for interactive cross-browser automation.

8. Troubleshooting cross-platform test failures

Symptom Likely cause What to do
Browser executable is missing Playwright package and browser binaries are out of sync, or CI did not install browsers. Install the browser binaries for the locked Playwright version with npx playwright install; keep package and browser updates together.
Emulated mobile passes but a phone fails Emulation does not reproduce all physical hardware, OS integration, or browser behavior. Reproduce on the named device and browser; add that real-device case to the risk matrix if consequential.
WebKit passes but Safari fails Playwright WebKit is not branded Safari, and OS-specific behavior can differ. Run the relevant branded/OS check where supported; for Safari-sensitive media scenarios, use WebKit on macOS as appropriate.
Only one CI operating system fails Host-dependent browser behavior, fonts, media codecs, or environment configuration. Capture the OS and browser revision, compare runner setup, and reproduce on that OS before changing assertions.
Tests pass alone but fail in parallel Shared account state, mutable fixtures, server capacity, or rate limits. Give each worker isolated data and accounts, reset state, or reduce worker count for the affected project.
Mobile layout assertion is flaky Unstable content, animation, late fonts/images, or an assertion tied to timing. Wait for a meaningful visible state, disable or account for animation in the test setup, and avoid arbitrary sleeps where a condition can be awaited.
Screenshot differs across runs Dynamic content, timestamps, ads, fonts, animation, or environment-specific rasterization. Stabilize the page and compare baselines within the same browser/OS/viewport configuration.
Cloud device job cannot start The requested device/browser/version is not supported or available under the chosen provider setup. Check the provider’s live compatibility documentation and adjust to a documented device and Playwright path.
ScreenshotNeo returns an unexpected page The target may show a bot check, blank page, failed load, or another page verdict. Inspect the response’s X-Page-Verdict and X-Billed headers, then check the target URL and capture configuration. Only clean shots are billed; bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing.

9. Cost and reliability trade-offs

Local Playwright runs use your own CI capacity; adding projects increases the number of browser executions. Emulation is inexpensive in setup terms and repeatable, while physical device coverage adds provider or device-lab capacity and may be slower or less available. The reviewed sources do not establish neutral provider prices or performance benchmarks, so estimate from your own suite, required concurrency, and current provider terms.

For reliability, pin dependencies, install the matching browsers, track runner OS versions, isolate test data, and retry only failures that are understood as transient. Retries can provide diagnostic signal, but a test that only passes on retry still needs investigation. Schedule matrix reviews because browser and cloud-device compatibility changes over time.

10. Or skip the browser setup

For a clean screenshot of a public page, call ScreenshotNeo’s API. It returns a PNG, JPEG, WebP, or PDF based on the requested options. The API is useful for visual references and capture workflows; use Playwright or device testing when you need to interact with and verify the application.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));

See the API documentation for parameters and response details. ScreenshotNeo accepts cookie banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are never billed, and the response indicates the page verdict and billing status. An MCP server gives AI agents tools to take screenshots, inspect page info, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.

Sign up free for 1,000 screenshots a month, no card required.

FAQ

How do I test my UI across browsers and devices?

Define supported environments and critical journeys, configure Playwright projects for the highest-risk browser and device profiles, then add real-device checks where physical behavior matters.

Is mobile emulation enough?

It is enough for many responsive layout and basic touch checks. It does not verify every physical device, mobile OS integration, or hardware-dependent behavior.

Does Playwright WebKit mean Safari passed?

No. Playwright WebKit is not branded Safari. Choose a Safari-specific environment when the browser or operating-system behavior is part of the requirement.

Should every test run on every project?

Not necessarily. Run critical paths broadly and distribute the remaining matrix according to risk, runtime, and support policy. Ensure each supported configuration is exercised on a schedule.

Can a screenshot API replace cross-browser testing?

No. A screenshot API captures a page image; it does not exercise the full interactive journey across browser engines and physical devices.