Навигация, вкладки и перехват сети
Реальные сценарии живут на нескольких страницах и зависят от сети — учимся управлять навигацией и подменять ответы сервера.
Playwright легко управляет навигацией (переходы, «назад», новые вкладки) и позволяет перехватывать сетевые запросы, подменяя ответы сервера.
Базовая навигация
Переход по URL — это goto. Кнопки браузера — goBack и goForward. Навигация ожидается автоматически: после goto страница уже загружена.
await page.goto('/catalog');
await page.getByRole('link', { name: 'Ноутбуки' }).click();
await expect(page).toHaveURL(/\/catalog\/laptops/);
await page.goBack(); // назад
await expect(page).toHaveURL('/catalog');Новая вкладка (popup)
Если ссылка открывается в новой вкладке (target="_blank"), Playwright создаёт отдельный объект page. Перехватить его нужно через событие popup, и важно начать ждать до клика — та же ловушка, что с waitForResponse.
// начинаем ждать новую вкладку ДО клика
const popupPromise = page.waitForEvent('popup');
await page.getByRole('link', { name: 'Открыть в новой вкладке' }).click();
const popup = await popupPromise;
// работаем с новой вкладкой как с обычной page
await expect(popup).toHaveURL(/\/details/);
await expect(popup.getByRole('heading')).toBeVisible();Несколько независимых пользователей
Когда нужно смоделировать двух разных пользователей одновременно (продавец и покупатель), создают два контекста — каждый со своей сессией.
test('продавец видит новый заказ покупателя', async ({ browser }) => {
const buyerCtx = await browser.newContext();
const sellerCtx = await browser.newContext();
const buyer = await buyerCtx.newPage();
const seller = await sellerCtx.newPage();
await seller.goto('/seller/orders');
await buyer.goto('/product/42');
await buyer.getByRole('button', { name: 'Купить' }).click();
// продавец видит заказ (страница сама обновляется)
await expect(seller.getByText('Новый заказ #')).toBeVisible();
});Перехват сети: mock API
page.route — механизм перехвата сетевых запросов: можно вернуть свой ответ (mock), изменить или заблокировать запрос.
E2E-тесты обычно ходят на настоящий бэкенд — в этом их сила. Но иногда трудно воспроизвести редкий ответ (ошибка 500, пустой список), бэкенд нестабилен, или нужно проверить поведение фронтенда при конкретных данных. Тогда перехватывают ответ.
test('отображает список товаров из API', async ({ page }) => {
// подменяем ответ на /api/products
await page.route('**/api/products', async (route) => {
await route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{ id: 1, name: 'Ноутбук', price: 50000 },
{ id: 2, name: 'Мышь', price: 1500 },
]),
});
});
await page.goto('/catalog');
await expect(page.getByText('Ноутбук')).toBeVisible();
await expect(page.getByText('Мышь')).toBeVisible();
});Эмуляция ошибки и блокировка
Самое ценное применение — проверить, как UI ведёт себя при ошибке. В реальности добиться 500-й трудно, а с route — одна строка. Можно и вовсе заблокировать запрос (например, аналитику), чтобы не замедлял тест.
// ошибка сервера
await page.route('**/api/products', (route) => route.fulfill({ status: 500 }));
await page.goto('/catalog');
await expect(page.getByText('Не удалось загрузить товары')).toBeVisible();
// блокировка ненужных запросов
await page.route('**/analytics/**', (route) => route.abort());| Метод | Что делает |
route.fulfill(...) | вернуть свой ответ (mock) |
route.abort() | заблокировать запрос |
route.continue() | пропустить как есть (можно изменив) |
Не злоупотребляйте моками
Чрезмерный мок превращает E2E в «фронтенд в вакууме» и теряет главное преимущество — проверку реальной интеграции. Используйте route точечно: для трудновоспроизводимых ошибок, нестабильных внешних сервисов и краевых случаев. Основные сценарии лучше гонять на настоящем бэкенде.
Итог
goto,goBack,goForwardуправляют навигацией; новая вкладка приходит через событиеpopup(слушатель ставят до клика).- Несколько контекстов моделируют разных пользователей с раздельными сессиями.
page.routeперехватывает запросы:fulfill— мок,abort— блок,continue— пропуск.- Мок ценен для эмуляции ошибок; не злоупотребляйте — основные сценарии гоняйте на реальном бэкенде.