Навигация, вкладки и перехват сети

Реальные сценарии живут на нескольких страницах и зависят от сети — учимся управлять навигацией и подменять ответы сервера.

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 — пропуск.
  • Мок ценен для эмуляции ошибок; не злоупотребляйте — основные сценарии гоняйте на реальном бэкенде.
Проверьте себя
1. Как перехватить вкладку, открытую ссылкой с target="_blank"?
AЧерез page.goto на новый URL
BЧерез событие popup, начав ждать его до клика
CЧерез waitForTimeout
DНовые вкладки нельзя тестировать
2. Что делает метод route.fulfill?
AБлокирует запрос полностью
BВозвращает заранее заданный ответ (mock) вместо реального
CЗамедляет загрузку страницы
DОчищает cookie
3. В чём риск чрезмерного использования моков в E2E?
AТесты начинают падать чаще
BТеряется главное преимущество E2E — проверка реальной интеграции
CБраузер перестаёт запускаться
DМоки запрещены спецификацией Playwright
Поддержать проект