skip to content

In Playwright, what changes when you pass update: true to routeFromHAR()?

level: middleimportance: should knowfreq 44%

answer

  1. The same call, running backwards
  2. Requests go live, not to file
  3. File rewritten on context close
  4. Two companion options shape the output
  5. Never the default path in CI

basics

~20 s

The call stops serving from the file. Requests go to the real network, their traffic is recorded, and the HAR is rewritten when the context closes, so the same test that replays a recording can also refresh it.

solid answer

~40 s

`update: true` inverts the call: instead of answering from `har/weather.har`, Playwright lets the requests reach the live forecast API, records what comes back and overwrites the archive when the browser context closes. That means one code path both replays and re-records — you run the suite normally in CI and run it once with `update: true` when the recording needs refreshing, then commit the result. Two companion options only apply while updating: `updateContent` (`'attach'` by default, writing bodies as separate files beside the archive, or `'embed'` to inline them in the HAR JSON) and `updateMode` (`'minimal'` by default in Playwright 1.63, writing only what replay needs, or `'full'` for a complete HAR with timings, sizes and cookies). A refreshing run needs real network access and must never be the normal CI path.

code

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

// UPDATE_HAR=1 npx playwright test tests/forecast.spec.ts
test('forecast archive replays, and refreshes on demand', async ({ context, page }) => {
  await context.routeFromHAR('har/weather.har.zip', {
    url: '**/api/forecast**',
    update: !!process.env.UPDATE_HAR,
    updateMode: 'minimal',
  });

  await page.goto('/dashboard');
  await expect(page.getByTestId('forecast-panel')).toBeVisible();
});

go deeper

for a junior

Know that the same routeFromHAR call can refresh the recording instead of replaying it, and that the flag controlling this is update. Do not worry about the mode options yet.

for a middle

Explain the inversion: live requests, recording, and a rewrite at context close, plus what updateContent and updateMode change about the file that lands on disk.

for a senior

Show how you gate the flag so refreshing is deliberate, how you keep the resulting diff reviewable, and why an updating run in CI silently reintroduces the dependency you removed.

for a principal

Own who is allowed to refresh recordings and against which environment, given that an updating run needs live credentials and real network access from wherever it executes.

## What `update: true` actually does Without it, `routeFromHAR(har)` is a **reader**: requests are matched against the file and fulfilled from it. With `update: true`, the same call becomes a **writer**. Requests go out to the real server, Playwright records the exchanges, and the archive at `har` is overwritten with the new traffic when `context.close()` runs — the same close-time flush the `recordHar` context option uses. Three things follow immediately: - During an updating run the test is **not** hermetic. It hits the live forecast API, so it needs network access, credentials and a service that is actually up. - Assertions during that run are checked against **live** responses, not the recording. A refresh run is therefore also a smoke test against the real API, for better and worse. - The file only changes if the context closes normally. A crashed run can leave the old archive in place, which is the safer of the two failure modes. ## The two options that only matter while updating | option | values | default | effect on the rewritten archive | |---|---|---|---| | `updateContent` | `'attach'`, `'embed'` | `'attach'` | bodies as separate files beside the HAR, or inlined into the HAR JSON | | `updateMode` | `'minimal'`, `'full'` | `'minimal'` | only the fields replay needs, or a complete HAR record | `'attach'` keeps the JSON small and diff-readable, at the cost of the archive being several files rather than one — unless the `har` path ends in `.zip`, in which case the attachments are packed into that single zip. `'embed'` produces one self-contained file that is larger and much noisier in review. `updateMode: 'minimal'` writes method, URL, headers, status and body and drops the parts of the HAR spec that replay never reads — timings, sizes, cookies, security details, the page section. Choose `'full'` only when a human or another HAR tool will read the file. Note the asymmetry with recording: in Playwright 1.63, `recordHar.mode` defaults to `'full'`, while `routeFromHAR`'s `updateMode` defaults to `'minimal'`. ## A refresh workflow that works 1. Keep the `routeFromHAR` call in one helper or fixture so there is a single place to flip. 2. Read the flag from the environment, for example `update: !!process.env.UPDATE_HAR`, so the suite replays by default. 3. Run `UPDATE_HAR=1 npx playwright test tests/forecast.spec.ts` against the real API when you need a new recording. 4. Inspect the diff — with `minimal` mode and a narrow `url` it is small enough to actually read — then commit the archive and any attachment files together. ```ts await context.routeFromHAR('har/weather.har.zip', { url: '**/api/forecast**', update: !!process.env.UPDATE_HAR, }); ``` ## Why it must not be the CI path Leaving `update: true` on in CI defeats the whole point: - Every run calls the third-party forecast provider, so the suite is back to depending on a service you do not control, and on rate limits you may not own. - The archive is rewritten inside the CI workspace, where the change is thrown away — or worse, committed by an automation nobody reviews. - A test that passes against live data proves nothing about the fixture the other runs use. Gate the flag behind an environment variable, a dedicated project, or a tagged spec, and let the default be replay. ## Gotchas worth knowing - `update: true` rewrites the whole archive, it does not merge new entries into the old one. Anything the run did not exercise is gone from the file. - The scope of what gets recorded is still the `url` option: with `url: '**/api/forecast**'` set, an updating run rewrites only the forecast traffic and nothing else. - The refreshed archive reflects the browser and options of the run that produced it, so headers such as the user agent come from that project. - Because the write happens on context close, a `page.routeFromHAR(..., { update: true })` still depends on its parent context closing before the file appears.

  • Where do response bodies go when updateContent is left at its default?
    At `'attach'`, each body is written as a separate file next to the archive, or packed inside the archive when the path ends in `.zip`. The HAR JSON stays small and reviewable. `'embed'` instead inlines every body into the JSON, producing one self-contained but much larger file.
  • What does updateMode: 'minimal' leave out compared with 'full'?
    Minimal writes only what replay consults — method, URL, headers, status and body — and drops timings, sizes, cookies, security information and the page section the HAR spec allows. Full keeps them, which matters only when a person or another HAR tool reads the file.
  • How do you stop an updating run from being triggered accidentally?
    Make replay the default and put the flag behind something explicit: an environment variable read at the call site, a separate project in the config, or a tagged spec that CI does not run. The value should never be a literal true committed in the test.

saying these in an interview costs you the question

  • Thinks update: true still serves responses from the file
  • Expects the refreshed archive to appear mid-test
  • Believes leaving update on in CI is harmless
  • Assumes attach mode inlines bodies into the HAR JSON
  • Thinks refreshing needs a separate recording script
  • Expects update to merge new entries into the old archive