skip to content

In Playwright, what does browserContext.routeFromHAR() do to the requests a page makes?

level: juniorimportance: must knowfreq 62%

answer

  1. Real traffic saved to a file
  2. Recorded with a context option
  3. Replayed by a route handler
  4. Lookup by method and URL
  5. Missing entries abort by default

basics

~20 s

It installs a route that answers matching requests from a previously recorded HAR archive instead of the real server, so the test replays saved traffic. Requests the archive does not contain are aborted by default.

solid answer

~40 s

`browserContext.routeFromHAR(har)` points a whole context at a HAR archive: each request a page makes is looked up in that file by method and URL, and the recorded status, headers and body are served back without touching the network. `page.routeFromHAR()` does the same for one page only. You get the archive either from the `recordHar` context option (`{ path: 'har/weather.har' }`), which writes the file when the context closes, or from the CLI with `npx playwright open --save-har=har/weather.har`. Replay does not consume entries, so a weather dashboard polling the forecast endpoint repeatedly keeps getting the same recorded response. Two options shape the edges: `url` narrows which requests the archive may answer, and `notFound` decides what a request with no entry does — `'abort'` by default, or `'fallback'` to pass it on.

code

typescript · 11 lines
typescript
import { test, expect } from '@playwright/test';

test('forecast panel renders from a recorded session', async ({ page }) => {
  await page.routeFromHAR('har/weather.har', {
    url: '**/api/forecast**',
    notFound: 'abort',
  });

  await page.goto('/dashboard');
  await expect(page.getByTestId('forecast-high')).toHaveText('21');
});

go deeper

for a junior

Be able to say what a HAR file holds and that routeFromHAR replays it in place of the real server. Knowing that the recordHar context option produces the file is enough at this stage.

for a middle

Explain the mechanics: lookup by method and URL, entries that answer repeated requests, the file flushed on context close, and the difference between page and context scope.

for a senior

Show judgment about which endpoints an archive should carry and how a replaying suite behaves when the real service is unreachable, so CI never depends on a third party being up.

for a principal

Own the policy around archives as repository artefacts: where they live, who refreshes them, how large they may grow, and what must never be captured into one.

## What a HAR archive holds A **HAR** (HTTP Archive) file is a JSON log of HTTP traffic. Each entry records one exchange: the request method, URL, headers and post data, plus the response status, headers and body. Playwright can both write that log and read it back, which turns a single real session against the third-party forecast API behind a weather dashboard into a fixed backend that later runs replay offline. ## Producing the file There are two ways to record, and both flush the archive at the end of a session rather than as it goes: - **The `recordHar` context option** — `browser.newContext({ recordHar: { path: 'har/weather.har' } })`. Every request the context makes is logged, and the file appears when `context.close()` runs. Under Playwright Test you set it through `use: { contextOptions: { recordHar: { path: 'har/weather.har' } } }`, and the built-in `context` fixture closes at the end of each test. - **The CLI** — `npx playwright open --save-har=har/weather.har --save-har-glob='**/api/**' https://dashboard.example` drives a real browser by hand and saves the same format with no test code at all. `npx playwright codegen` accepts the same two flags. Besides `path`, `recordHar` takes `urlFilter` (a glob or `RegExp` limiting what is logged), `mode` (`'full'` or `'minimal'`) and `content` (`'omit'`, `'embed'` or `'attach'`), which decides whether response bodies are dropped, inlined into the JSON, or written as separate files beside the archive. ## Replaying the file `await context.routeFromHAR('har/weather.har')` installs a route across the whole browser context; `await page.routeFromHAR('har/weather.har')` scopes the same behaviour to one page. From then on Playwright looks each outgoing request up in the archive **by method and URL** and fulfils it from the recording — status, headers and body — without reaching the network. Two consequences follow: - Entries are **not consumed**. If the dashboard polls `/api/forecast?city=oslo` every thirty seconds, every poll is answered from that one recorded entry. - The lookup key is the URL as requested, so a recording made against one origin will not answer requests to another. `baseURL` differences and cache-busting query parameters both change the key. A relative `har` path is resolved against the **current working directory** of the run, not against the spec file, so build the path with `path.join(__dirname, ...)` if the suite might be started from another folder. ## The options at the edges | option | values | default | what it decides | |---|---|---|---| | `url` | glob or `RegExp` | every request | which requests the archive is allowed to answer | | `notFound` | `'abort'`, `'fallback'` | `'abort'` | what a request with no entry does | | `update` | boolean | `false` | re-record from the live network instead of serving the file | | `updateContent` | `'embed'`, `'attach'` | `'attach'` | where bodies go when re-recording | | `updateMode` | `'full'`, `'minimal'` | `'minimal'` | how much HAR detail is written back | `url` and `notFound` are the pair people confuse. `url` filters what the archive is even consulted about: a request outside the pattern is left alone and goes to the network or a later handler. `notFound` applies only to a request the archive **was** consulted about and could not answer. ## The usual workflow 1. Record once against the real forecast API, with `urlFilter` narrowed to the endpoints under test. 2. Commit the archive next to the spec — with its attachment files, or using a `.zip` path so the whole recording travels as a single file. 3. Replay it with `routeFromHAR` so CI never calls the third party, and re-record later by running the same call with `update: true`. ## Where it earns its keep - A dashboard that renders a dozen widgets from one large payload is tedious to stub by hand; a recording captures the response verbatim, including the fields nobody remembered. - The suite stops depending on a service you do not control, which removes a third party from your CI failure modes. - The scope is yours to pick: `page.routeFromHAR` lets one page replay while another page in the same context still talks to a local server. The cost is that an archive is a snapshot. It answers exactly what was recorded, and anything the app newly requests is a miss — which is why the `notFound` policy is a deliberate decision rather than a detail. `'abort'` makes an incomplete recording fail loudly; `'fallback'` quietly lets the request through to the real world.

  • How do you get a HAR file without writing any test code first?
    Drive the app by hand from the CLI: `npx playwright open --save-har=har/weather.har --save-har-glob='**/api/**' https://dashboard.example`. It launches a browser, records what you click through, keeps only the URLs matching the glob and writes the archive when you close it. `npx playwright codegen` takes the same two flags.
  • What is the difference between page.routeFromHAR() and browserContext.routeFromHAR()?
    Scope, and nothing else. The page form serves the archive to that one page, so a popup or a second page in the same context still reaches the network. The context form covers every page the context has or later opens. Options and matching behave identically in both.

A HAR archive is a tape of a conversation with the server: replay plays the same answers back on cue, and anything that was never said on tape has no answer at all.

saying these in an interview costs you the question

  • Thinks a HAR file replays automatically once it exists on disk
  • Believes each recorded entry can only be replayed once
  • Expects the archive to be written before the context closes
  • Assumes routeFromHAR proxies requests through to the real server
  • Thinks recording requires changing the application's network code