Эмуляция устройств, viewport, geolocation, touch events, mobile Chrome и Safari
Playwright позволяет эмулировать мобильные устройства с правильным viewport, user-agent, touch-событиями и геолокацией. Это не замена реальному устройству, но мощный инструмент для покрытия основных сценариев.
Playwright содержит профили 100+ устройств:
from playwright.sync_api import sync_playwright
with sync_playwright() as pw:
# Посмотреть все доступные устройства
print(list(pw.devices.keys())[:10])
# ['iPhone 13', 'iPhone 13 Pro', 'iPhone 14', 'iPad Pro 11',
# 'Pixel 7', 'Pixel 7 Pro', 'Galaxy S22', 'Galaxy Tab S8', ...]browser = pw.webkit.launch()
# Эмуляция iPhone 13
context = browser.new_context(**pw.devices['iPhone 13'])
page = context.new_page()
page.goto('https://example.com')
# Страница видит:
# - viewport 390x844
# - user-agent Safari на iPhone
# - touch-события включены
# - device scale factor: 3Важно: для мобильных устройств используйте WebKit — он ближе всего к реальному Safari на iOS. Chromium тоже работает, но рендеринг отличается.
Если нужного устройства нет в списке:
context = browser.new_context(
viewport={'width': 390, 'height': 844},
user_agent='Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)',
device_scale_factor=3,
is_mobile=True,
has_touch=True,
)Это эквивалентно профилю из devices, но с ручными значениями.
С has_touch=True метод tap() генерирует touch-события:
context = browser.new_context(**pw.devices['iPhone 13'], has_touch=True)
page = context.new_page()
page.goto('/mobile-app')
# Tap вместо click
await page.get_by_role('button', name='Меню').tap()
# Свайп — последовательность touch-действий
await page.touchscreen.tap(200, 500) # Начальная точка
await page.touchscreen.tap(200, 200) # Свайп вверхcontext = browser.new_context(
geolocation={'latitude': 55.7558, 'longitude': 37.6173}, # Москва
permissions=['geolocation'],
)
page = context.new_page()
page.goto('/map')
# Приложение получит координаты Москвы через navigator.geolocationИзменение геолокации на лету:
await context.set_geolocation({'latitude': 40.7128, 'longitude': -74.0060}) # Нью-Йорк
await page.reload()Замедление сети для тестирования лоадеров:
context = browser.new_context(
offline=False,
# Через route можно имитировать задержки
)
# Замедление через route (все запросы)
async def slow_route(route):
await route.continue_()
await page.route('**/*', lambda route: route.fulfill(
status=200,
delay=500, # 500мс задержка
))Offline-режим:
# Отключить сеть
await context.set_offline(True)
page.goto('/app')
# Приложение должно обработать offline
# Включить сеть обратно
await context.set_offline(False)# Портретная ориентация (по умолчанию для iPhone)
context = browser.new_context(**pw.devices['iPhone 13'])
# Альбомная ориентация — поменять viewport
context = browser.new_context(
**{**pw.devices['iPhone 13'], 'viewport': {'width': 844, 'height': 390}}
)Переключение viewport в одном тесте:
def test_responsive_layout(page):
# Мобильный
page.set_viewport_size({'width': 390, 'height': 844})
page.goto('/')
assert page.locator('.mobile-menu').is_visible()
assert page.locator('.desktop-nav').is_hidden()
# Десктоп
page.set_viewport_size({'width': 1920, 'height': 1080})
page.reload()
assert page.locator('.mobile-menu').is_hidden()
assert page.locator('.desktop-nav').is_visible()Важно:
set_viewport_size()меняет только размер. User-agent остаётся прежним. Для полной эмуляции создавайте новый контекст.
import pytest
@pytest.mark.parametrize('device_name', [
'iPhone 13',
'Pixel 7',
'iPad Pro 11',
])
def test_homepage_on_mobile(page, request, pw):
device_name = request.param
context = page.context().browser.new_context(**pw.devices[device_name])
mobile_page = context.new_page()
mobile_page.goto('/')
assert mobile_page.title()
context.close()| Что эмулируется | Что НЕ эмулируется |
|---|---|
| Viewport | Реальная производительность GPU |
| User-agent | Network latency (3G/4G) |
| Touch events | Аппаратное ускорение видео |
| Device scale factor | Реальный Safari/Chrome Mobile |
| Geolocation | Биометрическая аутентификация |
Для production-качества используйте реальные устройства или сервисы вроде BrowserStack.
❌ Использование click() вместо tap() на мобильных. click() генерирует mouse-события. С has_touch=True используйте tap().
❌ Ручной viewport без user-agent. Страница получит мобильный размер окна, но десктопный user-agent — некоторые сайты покажут десктопную версию.
❌ Эмуляция iPhone на Chromium. iPhone должен эмулироваться на WebKit. Chromium с мобильным viewport ≠ Safari на iOS.
✅ Правильный подход: pw.devices['iPhone 13'] с pw.webkit.launch(), tap() вместо click(), set_geolocation для тестирования карт.
Изучите Page Object Model, чтобы структурировать код тестов для поддержки.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.