Different browser engines may behave differently. Device emulation is useful like a simulator, but it is not every real device.
same test → varied environment → compare behavior
Series: Playwright Zero to Expert | Demo app used throughout this series: saucedemo.com
Part 16 briefly showed multiple browser projects in playwright.config.ts. This part goes deeper — why cross-browser differences genuinely still matter, how mobile emulation actually works, and the additional dimensions (geolocation, permissions) real applications increasingly depend on.
Why Cross-Browser Testing Still Matters
It’s worth being honest about something upfront: the days of browsers rendering the same HTML/CSS in wildly, unpredictably different ways are largely behind us compared to the past. But “largely behind us” is not “eliminated entirely,” and the differences that remain tend to be genuinely subtle, and genuinely easy to miss if you only ever test in one browser.
Different browser engines — Chromium (which powers Chrome and Edge), Firefox’s Gecko, and WebKit (which powers Safari, and critically, all browsers on iOS, including “Chrome for iOS,” which is actually WebKit underneath due to Apple’s platform requirements) — can still handle specific CSS features, JavaScript APIs, form input behaviors, and even date/time formatting differently in real, occasionally significant ways.
Recall Part 6’s projects array — running the exact same test suite against multiple engines costs you nothing in terms of writing new tests; it’s purely a configuration decision:
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
npx playwright test # runs the entire suite three times, once per project
Mobile Device Emulation
Playwright ships pre-configured device profiles — accurate viewport sizes, pixel density, user agent strings, and touch-event support — for real, specific devices:
projects: [
{
name: 'mobile-chrome',
use: { ...devices['Pixel 7'] },
},
{
name: 'mobile-safari',
use: { ...devices['iPhone 14'] },
},
],
It’s worth being precise about what this actually is, and isn’t: this is emulation, not a genuine physical device. Playwright runs a real desktop browser engine, but configures it to report the viewport size, user agent, and touch capabilities of the named device, and renders at that viewport — which is accurate for the overwhelming majority of layout, responsive-design, and touch-interaction testing needs.
It is not, however, a substitute for testing on genuinely real hardware when something depends on true, physical device-specific behavior (actual GPU rendering quirks, real battery/performance characteristics, or platform-specific native browser chrome) — a distinction worth being able to articulate honestly in an interview, rather than overstating what emulation actually guarantees.
Analogy: Translators at Three Different Embassies Imagine submitting a standard visa application written in a universal layout (HTML/CSS):
- Chromium (The American Embassy): The officer reads the text, processes the form fields, and issues the visa standardly.
- Firefox / Gecko (The British Embassy): The officer uses a slightly different dictionary, reading specific date values (
YYYY-MM-DDinputs) or styling margins with their own minor dialect formatting rules.- WebKit (The French Embassy): The WebKit engine (which is the only translator allowed inside the iOS borders, meaning even Chrome/Firefox on iOS must use WebKit’s rules) translates specific styling scripts and swipe events differently. To ensure your application works worldwide, you send the identical application form to all three translation officers (the
projectsmatrix setup) to confirm no one gets stuck due to localized translation nuances.
📊 Visual Flowchart: The Cross-Browser and Device Emulation Matrix
Here is how Playwright maps projects to browser engines and overrides headers/layouts to emulate mobile devices:
graph TD
Config["playwright.config.ts Projects Matrix"] --> P1["Project: desktop-chrome"]
Config --> P2["Project: mobile-safari"]
Config --> P3["Project: desktop-firefox"]
P1 --> EngineC["Engine: Chromium"]
P2 --> EngineW["Engine: WebKit"]
P3 --> EngineF["Engine: Gecko"]
EngineW --> Emulate{"Emulation Settings Applied"}
Emulate -->|Device: iPhone 14| VP["Viewport: 390x844<br>Device Scale Factor: 3"]
Emulate -->|UA Header| UA["User Agent: 'Mozilla/... Mobile/Safari...'"]
Emulate -->|Capabilities| Touch["Touch Events: Enabled<br>Mouse Pointer: Coarse"]
VP --> Execute["Execute Spec Actions (click, fill)"]
UA --> Execute
Touch --> Execute
Custom Viewports
For testing responsive breakpoints that don’t correspond to any specific named device:
test.use({ viewport: { width: 375, height: 667 } }); // custom small viewport, applied to this test file
test("mobile menu appears at narrow viewport", async ({ page }) => {
await page.goto("/");
await expect(page.getByRole("button", { name: "Menu" })).toBeVisible();
});
test.use({...}) overrides configuration for the tests that follow it within a given file or describe block — a targeted way to test a specific responsive scenario without needing a whole separate project just for one custom size.
Geolocation and Permissions
Real applications increasingly depend on browser permissions and device capabilities — location-based features, camera or microphone access, notifications. Playwright can simulate all of these deliberately, without needing a real, physical device or a genuinely granted permission dialog interrupted by a real user:
test.use({
geolocation: { latitude: 12.9716, longitude: 77.5946 }, // Bengaluru, for example
permissions: ["geolocation"],
});
test("shows nearby stores based on location", async ({ page }) => {
await page.goto("/store-locator");
await expect(page.getByText("Stores near you")).toBeVisible();
});
This is genuinely valuable for a real, practical reason: testing a location-dependent feature by physically traveling to different locations obviously isn’t feasible, and manually clicking through a real browser’s permission dialog on every test run would be slow, awkward to automate reliably, and would block genuinely unattended CI execution entirely. Simulating both the permission grant and the specific location value directly, in code, makes an entire category of otherwise very difficult-to-test features straightforward and fully repeatable.
How It Works in a Real Test Run
Playwright projects run the same scenario under different engines and context settings. Device descriptors combine viewport, user agent, touch, scale factor, and related emulation settings; they do not turn a desktop browser into real phone hardware.
Use emulation for broad coverage and real devices for risks involving mobile operating systems, hardware keyboards, sensors, browser chrome, or performance. Cross-browser breadth should follow product usage and risk, not an automatic Cartesian product of every test and browser.
Interview Questions
Q: Why does cross-browser testing still matter today, given that browsers are far more standardized than they used to be?
Ans: While major, obvious rendering inconsistencies are far less common than in the past, different browser engines — Chromium, Gecko, and WebKit — can still handle certain CSS features, JavaScript APIs, and input behaviors subtly differently. These differences tend to be easy to miss if testing is only ever done in a single browser, and they’re precisely the kind of thing that can slip through unnoticed without deliberate cross-browser coverage.
Q: Why is it specifically important to test with WebKit, even for a team whose users primarily use Chrome or Edge?
Ans: Because WebKit powers not just desktop Safari but every browser on iOS — including apps that are branded “Chrome” or “Firefox” on iPhones, which are actually required by Apple’s platform policies to use WebKit underneath regardless of their name. A significant share of real mobile users could be affected by WebKit-specific issues even if they believe they’re using a different browser entirely.
Q: What is the actual difference between Playwright’s mobile device emulation and testing on a genuine, physical device?
Ans: Emulation runs a real desktop browser engine configured to report a specific device’s viewport size, user agent, and touch capabilities, and renders at that viewport — which is accurate for the vast majority of layout, responsive, and touch-interaction testing. It is not a substitute for testing on genuinely real hardware when something depends on true physical device behavior, like actual GPU rendering characteristics, real performance under real hardware constraints, or platform-specific native browser chrome.
Q: Why is simulating geolocation and permissions in code preferable to manually testing a location-dependent feature with a real device in a real location?
Ans: Physically traveling to different real-world locations to test location-based behavior isn’t practical, and manually interacting with a real permission dialog would be slow to automate reliably and would block genuinely unattended CI execution. Simulating both the permission grant and the specific location value directly in code makes the feature fully testable, repeatable, and automatable, without needing any real, physical travel or manual interaction at all.
Q: When would you use test.use({ viewport: {...} }) instead of adding a whole new named project in playwright.config.ts?
Ans: test.use({...}) is appropriate for testing a specific, targeted responsive scenario — like one particular custom breakpoint — scoped to just the tests in a specific file or describe block, without needing to run your entire suite an additional time under that configuration. A whole new project makes more sense when you want an entire, broad set of tests to run consistently under a given configuration, like a full named device profile.
Exercises — Part 26
Understand: Explain, in your own words, why WebKit deserves specific attention in a cross-browser testing strategy beyond “just another browser to check,” tying your answer to iOS’s platform requirements.
Simple Practice: Add Chromium, Firefox, and WebKit projects to a Playwright config, and run an existing SauceDemo login test across all three, noting in a sentence whether you observed any differences in behavior or timing.
Real-World Scenario:
Design a test for a hypothetical “mobile menu” feature that only appears below a certain viewport width. Write it using test.use({ viewport: {...} }) for a mobile-sized viewport, and a second test confirming the mobile menu is correctly absent at a standard desktop viewport size.
Challenge: Find a real site with a location-based feature (a store locator, a “delivery available near you” checker). Using Playwright’s geolocation and permissions simulation, write a test asserting on the location-specific content shown for two different, deliberately chosen coordinate values.
Next: Part 27 — Parallel Execution and Scaling
— workers, sharding, and how Playwright actually runs large suites fast without sacrificing test isolation.
- Author
- TechByteByByte Editorial Team
- Reviewed by
- TechByteByByte Admin
- Published
- Last reviewed