IKivan-kozenko -aqa
Try yourself as a QA tester
All topics·Intermediate·14 / 27

Network

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')
  await expect(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')
  await expect(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.

ts
test('save button triggers API and shows success', async ({ page }) => {
  await page.goto('/settings/profile')

  // Встановлюємо очікування ДО кліку (інакше запит може завершитися раніше)
  const responsePromise = page.waitForResponse('**/api/profile')

  await page.getByRole('button', { name: 'Зберегти' }).click()

  const response = await responsePromise
  expect(response.status()).toBe(200)
  await expect(page.getByText('Зміни збережено')).toBeVisible()
})

Block requests

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')
  await expect(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',
    },
  })
})

// Отримуємо реальну відповідь і підправляємо JSON
await 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)
})