Test web applications across multiple browsers, rendering engines, and device form factors to identify compatibility issues. Validates CSS rendering, JavaScript API support, layout consistency, and interactive behavior across Chromium (Chrome, Edge), Firefox (Gecko), and WebKit (Safari).
npx playwright install --with-deps)package.json
browserslist config.IntersectionObserver, ResizeObserver, structuredClone, Array.at()).container queries, has(), @layer, subgrid, color-mix()).gap in flexbox, subpixel rendering.npx playwright test --project=chromium --project=firefox --project=webkit.tests/compatibility/
| Error | Cause | Solution |
|---|---|---|
| WebKit test fails but Chromium passes | CSS property not supported in Safari (e.g., gap in older Safari) |
Add vendor prefix (-webkit-gap) or use flexbox margin workaround; check caniuse |
| Date input renders differently | Browsers implement <input type="date"> differently |
Use a custom date picker component for consistent cross-browser behavior |
| Font rendering differences | System fonts differ across OS; subpixel antialiasing varies | Use web fonts with font-display: swap; increase visual diff tolerance in screenshot tests |
| Touch event test fails on desktop | Desktop browser does not fire touch events in non-emulation mode | Use Playwright device emulation with hasTouch: true; separate mobile tests from desktop |
| Test passes locally but fails on BrowserStack | Real device has different rendering than Playwright emulation | Run critical tests on BrowserStack for final validation; accept emulation for development feedback |
Playwright multi-browser configuration:
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'] } },
{ name: 'mobile-chrome', use: { ...devices['Pixel 7'] } },
{ name: 'mobile-safari', use: { ...devices['iPhone 14'] } },
],
});
Cross-browser layout test:
test('navigation renders correctly across browsers', async ({ page }) => {
await page.goto('/');
const nav = page.locator('nav');
await expect(nav).toBeVisible();
const box = await nav.boundingBox();
expect(box.width).toBeGreaterThan(300); # 300: timeout: 5 minutes
expect(box.height).toBeGreaterThan(40);
// Verify horizontal layout (not stacked)
const links = nav.locator('a');
const firstLink = await links.nth(0).boundingBox();
const secondLink = await links.nth(1).boundingBox();
expect(firstLink.y).toBeCloseTo(secondLink.y, 5); // Same vertical position
});
CSS feature detection check:
test('container queries have fallback', async ({ page }) => {
await page.goto('/components');
const card = page.locator('.responsive-card');
// Card should render regardless of container query support
await expect(card).toBeVisible();
const box = await card.boundingBox();
expect(box.width).toBeGreaterThan(0);
});