A emulação de dispositivo do Playwright define viewport, pixel ratio, user agent e eventos de toque juntos, mas não testa motores reais de navegador mobile. Você ainda está rodando Chromium independente de ter selecionado devices['iPhone 14']. O atalho setViewportSize muda só o viewport, não o user agent nem o comportamento de toque. É suficiente para testes de layout responsivo, mas errado para qualquer coisa que verifique CSS específico de mobile ou sniffing de user agent.
O que a emulação mobile faz (e não faz)
A emulação do Playwright simula:
- Dimensões do viewport
- Device pixel ratio (telas retina)
- String de user agent
- Eventos de toque (substitui eventos de mouse)
- Geolocalização (opcional)
- Locale e timezone (opcional)
Ela não testa:
- Performance real de dispositivo
- Motores de navegador mobile reais (você ainda está no Chromium, Firefox ou WebKit)
- Renderização específica de hardware
- Apps mobile nativos
Para testes em dispositivos reais você precisa de serviços como BrowserStack ou Sauce Labs. A emulação cobre a necessidade mais comum: verificar que seu app se comporta corretamente em tamanhos de viewport mobile e responde ao toque.
Usando presets de dispositivo integrados
O Playwright inclui uma biblioteca de presets de dispositivo. Use no config:
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
projects: [
{
name: 'chromium-desktop',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'mobile-chrome',
use: { ...devices['Pixel 7'] },
},
{
name: 'mobile-safari',
use: { ...devices['iPhone 14'] },
},
{
name: 'tablet',
use: { ...devices['iPad Pro 11'] },
},
],
});Para rodar só testes mobile:
npx playwright test --project=mobile-chromePara ver todos os dispositivos disponíveis:
npx playwright --list-devicesA lista inclui aproximadamente 80 presets: dispositivos Pixel, iPhones, telefones Galaxy, iPads, tablets Surface.
Emulação de dispositivo por teste
Sobrescreva a emulação para um único teste sem alterar o config:
import { test, expect, devices } from '@playwright/test';
test('navegação mobile funciona', async ({ browser }) => {
const context = await browser.newContext({
...devices['iPhone 14'],
});
const page = await context.newPage();
await page.goto('/');
// Menu hambúrguer deve estar visível no mobile
await expect(page.getByRole('button', { name: 'Menu' })).toBeVisible();
await page.getByRole('button', { name: 'Menu' }).click();
await expect(page.getByRole('navigation')).toBeVisible();
await context.close();
});Eventos de toque
Em dispositivos mobile, o Playwright usa eventos de toque em vez de eventos de mouse. A maioria das interações funciona de forma idêntica: click(), fill(), type() traduzem corretamente. Onde o toque importa:
// Tap (mesmo que click no mobile)
await page.getByRole('button').tap();
// Gesto de swipe
await page.touchscreen.tap(100, 200);
// Drag na tela sensível ao toque
await page.touchscreen.tap(100, 400);
// depois simule o swipe tocando uma série de pontosPara carrosséis com swipe, pull-to-refresh ou gestos complexos, pode ser necessário usar page.evaluate() para disparar eventos de toque customizados. A maioria das interações mobile simples funciona com métodos de locator regulares.
Testando comportamento responsivo
O uso mais prático: verificar o layout em diferentes breakpoints.
test.describe('layout responsivo', () => {
test('desktop mostra nav completa', async ({ page }) => {
await page.setViewportSize({ width: 1280, height: 720 });
await page.goto('/');
await expect(page.getByRole('navigation')).toBeVisible();
await expect(page.getByRole('button', { name: 'Menu' })).not.toBeVisible();
});
test('mobile mostra hambúrguer', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 812 });
await page.goto('/');
await expect(page.getByRole('button', { name: 'Menu' })).toBeVisible();
await expect(page.getByRole('navigation')).not.toBeVisible();
});
});setViewportSize muda só o viewport: não muda o user agent. Para simulação completa de dispositivo, use presets de devices ou um contexto com viewport, userAgent e deviceScaleFactor definidos juntos.
Viewport vs. configuração via use
// Só viewport — não muda user agent nem eventos de toque
await page.setViewportSize({ width: 375, height: 812 });
// Emulação completa de dispositivo — viewport + user agent + toque + pixel ratio
const context = await browser.newContext({
viewport: { width: 390, height: 844 },
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)...',
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
});Para testes responsivos que verificam só layout, setViewportSize é suficiente. Para testes que verificam comportamento específico de mobile (eventos de toque, CSS específico de mobile, sniffing de user agent), use emulação completa via presets de devices.
Comparação de screenshots para testes responsivos
A regressão visual é especialmente valiosa para mobile: bugs de layout frequentemente são invisíveis em assertions de teste, mas óbvios em um screenshot.
test('homepage mobile corresponde ao baseline', async ({ page }) => {
await page.setViewportSize({ width: 375, height: 812 });
await page.goto('/');
await expect(page).toHaveScreenshot('mobile-homepage.png', {
fullPage: true,
});
});Rode uma vez para gerar o baseline. Execuções seguintes comparam com ele. Um shift de layout de um único pixel falha o teste.
O que testar especificamente no mobile
Nem todo teste precisa de uma versão mobile. Priorize:
- Navegação (menus hambúrguer, drawers mobile)
- Formulários (teclado virtual não cobre o botão de submit, alvos de toque são grandes o suficiente)
- Tabelas (scroll horizontal vs. layout empilhado)
- Imagens (
srcsetresponsivo, sem overflow) - Fluxos de pagamento (caminho crítico, botões amigáveis ao toque)
- Modais (conteúdo rolável, botão de fechar acessível)
Pule testes mobile para:
- Dashboards de admin que explicitamente não suportam mobile
- Testes que verificam comportamento idêntico independente do viewport
- Testes de API e testes unitários