Сеть
Введение
Playwright предоставляет API для мониторинга и модификации сетевого трафика браузера, как HTTP, так и HTTPS. Любые запросы, которые делает страница, включая XHR и fetch запросы, могут быть отслежены, изменены и обработаны.
Мокирование API
Ознакомьтесь с нашим руководством по мокированию API, чтобы узнать больше о том, как
- мокировать API-запросы и никогда не обращаться к API
- выполнять API-запрос и изменять ответ
- использовать HAR-файлы для мокирования сетевых запросов.
Мокирование сети
Вам не нужно ничего настраивать для мокирования сетевых запросов. Просто определите пользовательский Route, который мокирует сеть для контекста браузера.
import { test, expect } from '@playwright/test';
test.beforeEach(async ({ context }) => {
// Блокировать любые css-запросы для каждого теста в этом файле.
await context.route(/.css$/, route => route.abort());
});
test('загружает страницу без css', async ({ page }) => {
await page.goto('https://playwright.dev');
// ... тест здесь
});
Кроме того, вы можете использовать page.route() для мокирования сети на одной странице.
import { test, expect } from '@playwright/test';
test('загружает страницу без изображений', async ({ page }) => {
// Блокировать png и jpeg изображения.
await page.route(/(png|jpeg)$/, route => route.abort());
await page.goto('https://playwright.dev');
// ... тест здесь
});
HTTP-аутентификация
Выполнение HTTP-аутентификации.
- Test
- Library
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
httpCredentials: {
username: 'bill',
password: 'pa55w0rd',
}
}
});
const context = await browser.newContext({
httpCredentials: {
username: 'bill',
password: 'pa55w0rd',
},
});
const page = await context.newPage();
await page.goto('https://example.com');
HTTP-прокси
Вы можете настроить страницы для загрузки через HTTP(S) прокси или SOCKSv5. Прокси может быть установлен глобально для всего браузера или для каждого контекста браузера отдельно.
Вы можете дополнительно указать имя пользователя и пароль для HTTP(S) прокси, а также указать хосты, которые нужно обойти прокси.
Вот пример глобального прокси:
- Test
- Library
import { defineConfig } from '@playwright/test';
export default defineConfig({
use: {
proxy: {
server: 'http://myproxy.com:3128',
username: 'usr',
password: 'pwd'
}
}
});
const browser = await chromium.launch({
proxy: {
server: 'http://myproxy.com:3128',
username: 'usr',
password: 'pwd'
}
});
Также возможно указать его для каждого контекста:
- Test
- Library
import { test, expect } from '@playwright/test';
test('должен использовать пользовательский прокси в новом контексте', async ({ browser }) => {
const context = await browser.newContext({
proxy: {
server: 'http://myproxy.com:3128',
}
});
const page = await context.newPage();
await context.close();
});
const browser = await chromium.launch();
const context = await browser.newContext({
proxy: { server: 'http://myproxy.com:3128' }
});
Сетевые события
Вы можете отслеживать все Request и Response:
// Подписка на события 'request' и 'response'.
page.on('request', request => console.log('>>', request.method(), request.url()));
page.on('response', response => console.log('<<', response.status(), response.url()));
await page.goto('https://example.com');
Или ожидать сетевого ответа после нажатия кнопки с помощью page.waitForResponse():
// Используйте шаблон URL с глобами. Обратите внимание, что нет await.
const responsePromise = page.waitForResponse('**/api/fetch_data');
await page.getByText('Update').click();
const response = await responsePromise;
Вариации
Ожидание Response с page.waitForResponse()
// Используйте RegExp. Обратите внимание, что нет await.
const responsePromise = page.waitForResponse(/\.jpeg$/);
await page.getByText('Update').click();
const response = await responsePromise;
// Используйте предикат, принимающий объект Response. Обратите внимание, что нет await.
const responsePromise = page.waitForResponse(response => response.url().includes(token));
await page.getByText('Update').click();
const response = await responsePromise;
Обработка запросов
await page.route('**/api/fetch_data', route => route.fulfill({
status: 200,
body: testData,
}));
await page.goto('https://example.com');
Вы можете мокировать API-эндпоинты, обрабатывая сетевые запросы в вашем скрипте Playwright.
Вариации
Настройте маршрут для всего контекста браузера с помощью browserContext.route() или страницы с помощью page.route(). Это будет применяться к всплывающим окнам и открытым ссылкам.
await browserContext.route('**/api/login', route => route.fulfill({
status: 200,
body: 'accept',
}));
await page.goto('https://example.com');
Изменение запросов
// Удаление заголовка
await page.route('**/*', async route => {
const headers = route.request().headers();
delete headers['X-Secret'];
await route.continue({ headers });
});
// Продолжение запросов как POST.
await page.route('**/*', route => route.continue({ method: 'POST' }));
Вы можете продолжать запросы с модификациями. Пример выше удаляет HTTP-заголовок из исходящих запросов.
Прерывание запросов
Вы можете прерывать запросы, используя page.route() и route.abort().
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
// Прерывание на основе типа запроса
await page.route('**/*', route => {
return route.request().resourceType() === 'image' ? route.abort() : route.continue();
});
Изменение ответов
Чтобы изменить ответ, используйте APIRequestContext для получения оригинального ответа, а затем передайте ответ в route.fulfill(). Вы можете переопределить отдельные поля в ответе через опции:
await page.route('**/title.html', async route => {
// Получение оригинального ответа.
const response = await route.fetch();
// Добавление префикса к заголовку.
let body = await response.text();
body = body.replace('<title>', '<title>My prefix:');
await route.fulfill({
// Передача всех полей из ответа.
response,
// Переопределение тела ответа.
body,
// Принудительное указание типа контента как html.
headers: {
...response.headers(),
'content-type': 'text/html'
}
});
});
Шаблоны URL с глобами
Playwright использует упрощённые glob-шаблоны для сопоставления URL в методах перехвата сети, таких как page.route() или page.waitForResponse(). Эти шаблоны поддерживают базовые подстановочные символы:
- Звёздочки:
- Одна
*сопоставляет любые символы, кроме/ - Две
**сопоставляют любые символы, включая/
- Одна
- Знак вопроса
?сопоставляет только сам символ?. Если нужно сопоставить любой символ, используйте*. - Фигурные скобки
{}можно использовать, чтобы сопоставлять список вариантов, разделённых запятыми, - Обратный слэш
\можно использовать для экранирования любых специальных символов (учтите, что сам обратный слэш нужно экранировать как\\)
Примеры:
https://example.com/*.jsсопоставляетhttps://example.com/file.js, но неhttps://example.com/path/file.jshttps://example.com/?page=1сопоставляетhttps://example.com/?page=1, но неhttps://example.com**/*.jsсопоставляет иhttps://example.com/file.js, иhttps://example.com/path/file.js**/*.{png,jpg,jpeg}сопоставляет все запросы изображений
Важные замечания:
- Шаблон с глобами должен соответствовать всему URL, а не только его части.
- При использовании глобов для сопоставления URL учитывайте полную структуру URL, включая протокол и разделители пути.
- Для более сложных требований к сопоставлению рассмотрите возможность использования RegExp вместо шаблонов с глобами.
WebSockets
Playwright поддерживает инспекцию, мокирование и модификацию WebSockets из коробки. См. наше руководство по мокированию API, чтобы узнать, как мокировать WebSockets.
Каждый раз, когда создается WebSocket, срабатывает событие page.on('websocket'). Это событие содержит экземпляр WebSocket для дальнейшей инспекции фреймов веб-сокета:
page.on('websocket', ws => {
console.log(`WebSocket открыт: ${ws.url()}>`);
ws.on('framesent', event => console.log(event.payload));
ws.on('framereceived', event => console.log(event.payload));
ws.on('close', () => console.log('WebSocket закрыт'));
});
Отсутствующие сетевые события и Service Workers
Встроенные методы Playwright — browserContext.route() и page.route() — позволяют тестам нативно маршрутизировать запросы, а также выполнять мокирование и перехват.
Если вы используете нативные browserContext.route() и page.route(), и вам кажется, что сетевые события пропадают, отключите Service Worker’ы, установив serviceWorkers в 'block'.
Возможно, вы используете инструмент для моков, например Mock Service Worker (MSW). Хотя он отлично работает для мокирования ответов, он добавляет собственный Service Worker, который перехватывает сетевые запросы — из‑за этого они становятся невидимыми для browserContext.route() и page.route(). Если вам нужно и тестировать сеть, и делать моки, рассмотрите использование встроенных browserContext.route() и page.route() для мокирования ответов.
Если вам важно не только использовать Service Worker’ы для тестирования и сетевого мокирования, но и маршрутизировать/слушать запросы, которые выполняют сами Service Worker’ы, см. это руководство.