Mobile web
Emulating a device
Device descriptors, what the six or seven properties they set actually change, and the one that emulation cannot give you.
A viewport gets you the layout. A device descriptor gets you the rest: the user agent the page sees, whether it thinks it has a touch screen, and how sharp the screen is.
Playwright ships descriptors for the common handsets and tablets, plus a few desktop profiles, and Browser exposes them as keywords.
Using one
Get Device returns a dictionary, and &{device} expands it into New Context
as named arguments. That is the whole idiom — nothing to unpack by hand.
To find out what is available:
The list is Playwright's, so it grows when Playwright updates. Names are exact
and case-sensitive: iPhone 13, iPhone 13 Pro Max, Pixel 7, Galaxy S9+,
iPad Mini, and the landscape variants such as iPhone 13 landscape.
What a descriptor actually sets
Six or seven properties — most phone and tablet descriptors also carry
screen, what window.screen reports. On a portrait descriptor it is taller
than the viewport, which excludes browser chrome; a landscape descriptor keeps
the portrait screen, so iPhone 13 landscape is a 750×342 viewport on a
390×844 screen. It is worth knowing them individually, because each one changes
something different, and you can set any of them yourself without a descriptor.
| Property | What it changes |
|---|---|
Setting them directly is perfectly reasonable when you want one property rather than a whole handset:
The caveat that matters
A descriptor changes the engine only when nothing is open yet. Every
descriptor carries defaultBrowserType, and New Context honours it if there
is no browser running — so Get Device for iPhone 13 followed by New Context
does launch WebKit. Applied to an already running Chromium it changes nothing
but the emulation: a Chromium reporting an iPhone user agent at an iPhone
viewport. It is not Safari either way.
If iOS is the thing you care about, be explicit about the engine:
That is as close as automation gets: the same engine family Safari is built on, at the right size, with touch enabled. It is genuinely useful — WebKit catches CSS and JavaScript differences that Chromium never will — and it is still not Safari on an actual iPhone. Keep that last mile for real humans on real devices.
Orientation
There is no rotate keyword. Landscape is a viewport whose width exceeds its height, so either use the landscape descriptor:
or set the size yourself:
Read those numbers off the descriptor rather than transposing the portrait ones
— iPhone 13 is 390×664 and its landscape descriptor is 750×342, not a swap.
And the two routes are not equivalent: the descriptor also carries screen, the
user agent and isMobile, while Set Viewport Size leaves those alone and
overwrites window.screen with the new size, so the screen/viewport
distinction disappears.
If your app listens for orientationchange, the resize is what fires it.
A worked example
Putting the chapter together — one device, one context, a real check:
*** Settings ***Library Browser*** Variables ***${PHONE} iPhone 13${URL} https://www.imbus.de/en/academy/rfcp-robot-framework-certified-professional*** Test Cases ***The Certification Page On A Phone ${device} = Get Device ${PHONE} New Browser webkit headless=True New Context &{device} New Page ${URL} # A real cookie banner, in the way of everything until it is dealt with. Tap text="allow all cookies" Wait For Elements State button.cc-allow-all hidden Get Text h1 *= Certified Professional # …and the page never scrolls sideways. ${overflow} = Evaluate JavaScript ${None} ... () => document.documentElement.scrollWidth > document.documentElement.clientWidth Should Not Be True ${overflow}
Two details in there are worth more than the example itself.
hidden, not detached: that banner is still in the document after you accept
it, sized to nothing. Reaching for detached — the obvious guess — waits out
the full timeout and fails.
And it is a page you can actually run this against, which is the point. An example that only works against an app you do not have teaches the syntax and hides every problem the syntax exists for.
Tap rather than Click is deliberate, and it only works because the
descriptor set hasTouch. That is the subject of
the next page.