Mobile Device Emulation in Playwright: iPhone, Android and Responsive Testing

Most of your users are on phones, and Playwright can test your site as an iPhone, Android phone or tablet without any real device. In this lesson you will add mobile projects, write mobile-aware tests, check responsive breakpoints and emulate location, language and offline mode.

Advertisement

📖 What You’ll Learn: what device emulation changes, device descriptors and mobile projects, isMobile, test.use for a single file, tap vs click, responsive breakpoint tests, geolocation, locale, time zone, dark mode and offline emulation, and a mobile testing checklist.


📱 What Is Mobile Emulation?

More than half of web traffic comes from phones. Mobile emulation lets Playwright open your website the way a phone does: small screen, touch input, mobile user agent and high pixel density. It runs in the normal desktop browser engines, so it is fast and needs no real device or Android emulator.

Emulation changes:

PropertyExample (iPhone 13)
viewport390 x 664 CSS pixels
deviceScaleFactor3 (retina)
isMobiletrue (mobile meta viewport is respected)
hasTouchtrue (touch events, tap())
userAgentAn iPhone Safari user agent
defaultBrowserTypewebkit

📝 Note: Emulation tests mobile websites, not native apps. For Android and iOS apps from the app stores, use Appium. For final sign-off on real hardware, use a real-device cloud.


🧭 Add Mobile Projects to the Config

Playwright ships ready-made device descriptors for popular phones and tablets. Add them as projects in playwright.config.ts:

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

export default defineConfig({
  use: { baseURL: 'https://www.saucedemo.com' },
  projects: [
    { name: 'Desktop Chrome', use: { ...devices['Desktop Chrome'] } },
    { name: 'Mobile Chrome',  use: { ...devices['Pixel 5'] } },
    { name: 'Mobile Safari',  use: { ...devices['iPhone 13'] } },
    { name: 'iPad',           use: { ...devices['iPad (gen 7)'] } },
    { name: 'iPhone landscape', use: { ...devices['iPhone 13 landscape'] } },
  ],
});
npx playwright test --project="Mobile Safari"

The full list of device names is in Playwright’s source file deviceDescriptorsSource.json; your editor also autocompletes the names inside devices['...'].


✍️ A Mobile Test

The same test code runs on desktop and mobile. Where behaviour differs, check isMobile:

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

test('logged-in user can open the side menu', async ({ page, isMobile }) => {
  await page.goto('/');
  await page.getByPlaceholder('Username').fill('standard_user');
  await page.getByPlaceholder('Password').fill('secret_sauce');
  await page.getByRole('button', { name: 'Login' }).click();

  await page.locator('#react-burger-menu-btn').click();
  await expect(page.locator('#logout_sidebar_link')).toBeVisible();

  if (isMobile) {
    console.log('Viewport on this device:', page.viewportSize());
  }
});

🎯 Emulate a Device in One File: test.use

To make one file always run as a phone, regardless of the project:

import { test, expect, devices } from '@playwright/test';

test.use({ ...devices['Pixel 5'] });   // must be at the top level of the file

test('mobile login page', async ({ page }) => {
  await page.goto('https://www.saucedemo.com/');
  await expect(page.getByPlaceholder('Username')).toBeVisible();
});

⚠️ Common mistake: Device descriptors include defaultBrowserType, which cannot be changed inside a test.describe block. If you see “Cannot use({ defaultBrowserType }) in a describe group”, move test.use to the top of the file or remove defaultBrowserType from the object.


👆 Touch: tap() vs click()

With hasTouch: true, you can send real touch events:

await page.getByRole('button', { name: 'Login' }).tap();

click() also works on mobile emulation; use tap() when the page listens specifically for touch events (swipe menus, mobile-only gestures).


📐 Test Responsive Breakpoints with Viewports

You do not always need a full device. To check how a layout responds at different widths, set the viewport directly and generate tests with a loop (parameterized tests):

const breakpoints = [
  { name: 'mobile',  width: 375,  height: 812 },
  { name: 'tablet',  width: 768,  height: 1024 },
  { name: 'desktop', width: 1440, height: 900 },
];

for (const bp of breakpoints) {
  test('login form is visible on ' + bp.name, async ({ page }) => {
    await page.setViewportSize({ width: bp.width, height: bp.height });
    await page.goto('https://www.saucedemo.com/');
    await expect(page.locator('.login-box')).toBeVisible();
    await expect(page).toHaveScreenshot('login-' + bp.name + '.png');
  });
}

Combine this with visual regression testing to catch broken responsive layouts.


🌍 Emulate Location, Language, Time Zone, Theme and Offline

Mobile scenarios often depend on context, all of which Playwright can emulate:

test.use({
  geolocation: { latitude: 19.0760, longitude: 72.8777 },  // Mumbai
  permissions: ['geolocation'],
  locale: 'hi-IN',
  timezoneId: 'Asia/Kolkata',
  colorScheme: 'dark',
});

test('shows the nearest store', async ({ page, context }) => {
  await page.goto('https://example.com/store-locator');
  // ...

  await context.setOffline(true);    // simulate losing the network
  await page.reload().catch(() => {}); // the reload fails while offline
  await context.setOffline(false);
});
OptionUse case
geolocation + permissionsStore locators, delivery areas, maps
localeTranslated UI, number and date formats
timezoneIdDates and deadlines across time zones
colorSchemeDark mode styles
context.setOffline(true)Offline banners, retry logic, PWAs

✅ Mobile Testing Checklist

  • Key user journeys pass on at least one Android (Pixel) and one iOS (iPhone) descriptor.
  • Navigation works through the mobile menu, not only desktop links.
  • Forms are usable with the on-screen keyboard layout (no hidden buttons).
  • Tap targets are reachable; no horizontal scrolling at 375 px.
  • Visual baselines exist for the main mobile pages.

📝 Assignment

  1. Add Mobile Chrome (Pixel 5) and Mobile Safari (iPhone 13) projects and run your Sauce Demo login tests on them.
  2. Write a test that logs in and opens the side menu, and log isMobile and the viewport size.
  3. Create a file that always runs as iPad (gen 7) using test.use at the top level.
  4. Use tap() instead of click() for the login button on a touch device.
  5. Generate breakpoint tests for 375, 768 and 1440 px widths with a visual snapshot each.
  6. Bonus: emulate Mumbai geolocation, the hi-IN locale and dark mode, then log navigator.language with page.evaluate.

❓ FAQ

🤔 How do I test a mobile website with Playwright?

Add a project using a device descriptor, for example use: { ...devices['iPhone 13'] }, and run your tests with that project. Playwright emulates the screen, touch, user agent and pixel density.

🤔 Can Playwright test native Android or iOS apps?

No. Playwright tests web apps, including mobile websites through emulation. Use Appium for native apps.

🤔 What is the difference between setViewportSize and a device descriptor?

setViewportSize only changes the window size. A device descriptor also sets touch support, mobile mode, user agent and device scale factor, which affects how the site behaves.

🤔 How do I see the list of Playwright devices?

Type devices[' in your editor to get autocomplete, or look at deviceDescriptorsSource.json in the Playwright repository.

🤔 Why does test.use with a device fail inside describe?

Device descriptors contain defaultBrowserType, which can only be set at the top level of a file or in the config, not inside a describe block.


🎯 Next Steps

🎬 Watch the full Playwright tutorial series on YouTube!

Subscribe to SqaToolsByDY • Practice on sqatools.in

1 thought on “Mobile Device Emulation in Playwright: iPhone, Android and Responsive Testing”

Leave a Comment