Playwright lets you intercept, mock, modify and block any HTTP request the browser makes — without a proxy or third-party tool.
How network routing works
route() intercepts the request before it reaches the server
page.route(pattern, handler) registers a function that Playwright calls every time the browser makes a request matching the pattern. Inside the handler you decide what happens: fulfill with fake data, forward to the real server, modify headers, or abort entirely.
The pattern can be a glob string ('**/api/orders'), a regex (/\.png$/), or a full URL. Use context.route() if you want the handler to apply to all pages in the test, or page.route() for one page only.
Mock an API endpoint
The most common use case: return fake JSON from an API call instead of hitting the real backend. Useful for testing edge cases (empty list, error state, paginated results) that are hard to reproduce with real data.
ts
test('shows empty state when no orders', async ({ page }) => {
// Перехоплюємо запит до API замовленьawait page.route('**/api/orders', route =>
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify({ orders: [], total: 0 }),
})
)
await page.goto('/orders')
awaitexpect(page.getByText('Замовлень ще немає')).toBeVisible()
})
test('shows error state when API fails', async ({ page }) => {
await page.route('**/api/orders', route =>
route.fulfill({ status: 500, body: 'Internal Server Error' })
)
await page.goto('/orders')
awaitexpect(page.getByRole('alert')).toContainText('Не вдалося завантажити')
})
Wait for a specific API response
When a user action triggers an API call, you often want to assert after the call completes — not after an arbitrary timeout. page.waitForResponse() waits for a response matching a pattern before your assertion. The key detail: set up the promise BEFORE the action that triggers the request.
Blocking requests speeds up tests that don't need images, fonts or analytics. Block what's irrelevant and the page loads faster — especially useful on slow CI machines.
ts
// Блокуємо всі зображення для тестів що перевіряють текстtest.beforeEach(async ({ page }) => {
await page.route(/\.(png|jpg|jpeg|gif|webp|svg)$/, route => route.abort())
})
// Або блокуємо за типом ресурсуtest('page works without stylesheets', async ({ page }) => {
await page.route('**/*', route => {
if (route.request().resourceType() === 'stylesheet') {
return route.abort()
}
return route.continue()
})
await page.goto('/orders')
awaitexpect(page.getByRole('table')).toBeVisible()
})
Modify requests and responses
Sometimes you need to add a header, change the method, or tweak the response body without replacing it entirely. route.continue() forwards the request with modifications. route.fetch() + route.fulfill() lets you get the real response and modify it before the browser sees it.
ts
// Додаємо заголовок авторизації до всіх запитівawait page.route('**/api/**', async route => {
await route.continue({
headers: {
...route.request().headers(),
'X-Test-Auth': 'internal-token',
},
})
})
// Отримуємо реальну відповідь і підправляємо JSONawait page.route('**/api/orders', async route => {
const response = await route.fetch()
const json = await response.json()
// Додаємо тестовий запис на початок списку
json.orders.unshift({ id: 'TEST-001', status: 'pending' })
await route.fulfill({ response, json })
})
Listen to network events
If you need to log or inspect requests without intercepting them, subscribe to page.on('request') and page.on('response'). This doesn't affect the requests — they proceed normally. Useful for debugging or building assertions based on what the page fetches.
ts
test('track API calls during page load', async ({ page }) => {
const apiCalls: string[] = []
page.on('request', req => {
if (req.url().includes('/api/')) {
apiCalls.push(`${req.method()} ${req.url()}`)
}
})
page.on('response', res => {
if (!res.ok() && res.url().includes('/api/')) {
console.warn('Failed API call:', res.status(), res.url())
}
})
await page.goto('/dashboard')
console.log('API calls made:', apiCalls)
})