skip to content

Browsers and Contexts

Launching the bundled engines and configuring the BrowserContext that owns a test's cookies, storage, permissions and emulated device. Asked because isolation is what keeps a suite fast.

on this pageshow

explore

questions

page 1 of 2

Why would a Playwright test call page.clock.install instead of page.clock.setFixedTime?

level: juniorimportance: must knowfreq 56%

answer

  1. One pins the date, one owns time
  2. Does the page render or compute with time?
  3. Timers keep running under a frozen date
  4. install fakes Date, timers and animation frames
  5. Only install unlocks runFor and pauseAt

basics

~10 s

page.clock.setFixedTime only pins what Date.now and new Date report; timers keep running on real time. page.clock.install replaces the whole timer stack, so the test can pause, tick or jump the page's clock.

solid answer

~40 s

Both live on `page.clock`, but they control different things. `setFixedTime(time)` makes `Date.now()` and `new Date()` return one fixed value while `setTimeout`, `setInterval` and `requestAnimationFrame` keep firing on real time — perfect when the page merely *renders* a date. `install({ time })` swaps out the page's entire time stack (`Date`, the timer functions, animation frames, idle callbacks and `performance`), which is what lets a test then call `runFor`, `fastForward`, `pauseAt` and `resume`. So you reach for `install` as soon as the page *computes* with time: a hotel checkout that counts down a ten-minute room hold, a relative "booked 3 minutes ago" label, or a poll you want to advance past. `page.clock` shipped in Playwright 1.45 and behaves this way in 1.63.

code

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

test('held-room countdown ticks down', async ({ page }) => {
  // A fixed date alone would freeze this countdown at its starting value.
  await page.clock.install({ time: new Date('2026-03-04T09:00:00') });
  await page.goto('/rooms/deluxe-king');
  await page.getByRole('button', { name: 'Hold this room' }).click();

  await page.clock.runFor('01:00');
  await expect(page.getByTestId('hold-remaining')).toHaveText('9:00');
});

go deeper

for a junior

Remember the split: setFixedTime pins what Date reports, install replaces the timers too. If the feature counts down or refreshes on an interval, you need install.

for a middle

Be able to name what install fakes — Date, setTimeout, setInterval, animation frames, idle callbacks and performance — and explain why a frozen Date.now with live timers leaves a countdown stuck.

for a senior

Show the ordering judgment: install before navigation, start slightly before the moment under test, let the page load, then pause. Explain why a page booted on a frozen clock can hang on a bootstrap timer.

for a principal

Own the blast radius. The clock is context-wide and survives navigation, so a fixture that installs it for every test changes the timing contract of the whole suite; scope it to the specs that need it.

Playwright gives a test control over the page's sense of time through `page.clock`. It is the same object as `context.clock`: the fake clock is installed for the whole browser context, so every page and every iframe inside that context shares one clock. Two entry points look interchangeable and are not. ## What each call actually replaces `page.clock.setFixedTime(time)` pins what the page **reads**. `Date.now()` and `new Date()` return the value you passed, on every call, and nothing else changes. `setTimeout`, `setInterval`, `requestAnimationFrame` and `performance` all keep running against real wall time, at real speed. You can call it again later to move the reported date to a new value. `page.clock.install(options)` replaces the page's whole time stack with one the test drives. The globals it fakes are: - `Date` - `setTimeout` and `clearTimeout` - `setInterval` and `clearInterval` - `requestAnimationFrame` and `cancelAnimationFrame` - `requestIdleCallback` and `cancelIdleCallback` - `performance`, including `Event.timeStamp` Only after `install` do the advance verbs mean anything: `runFor(ticks)` ticks through time firing every callback on the way, `fastForward(ticks)` jumps ahead firing each due timer at most once, `pauseAt(time)` jumps to a moment and stops time dead, and `resume()` lets it flow again. `install` takes an optional `time` (a number of epoch milliseconds, a string, or a `Date`) and defaults to the current system time. ## The deciding question: does the page render the date or compute with it? A room-detail page that prints `new Date().toLocaleDateString()` into a "check-in" field only *renders* time. `setFixedTime` is enough, it is one line, and it is what the Playwright docs recommend starting with. A checkout page that holds a room for ten minutes computes with time. Its code looks like `const remaining = Math.round((endTime - Date.now()) / 1000)`, re-rendered by a one-second interval. Under `setFixedTime` the interval still fires every real second, but `Date.now()` never moves, so the banner is frozen on "held for 10:00" forever and the expiry branch is unreachable. The page is not broken — the test froze half of time and left the other half running. `install` is the fix, because it moves the timers and the date together. ## Side by side | Behaviour | `setFixedTime` | `install` | |---|---|---| | `Date.now()` advances on its own | no | yes, until paused | | `setTimeout` / `setInterval` still fire | yes, on real time | yes, on the fake clock | | Test can jump ahead | no | yes, via `fastForward` or `pauseAt` | | Test can tick callback by callback | no | yes, via `runFor` | | Test can stop timers entirely | no | yes, via `pauseAt` | | Setup cost | one call, any time | must precede the page's own timers | ## Ordering, scope and lifetime The fake clock is delivered as an init script, so it applies to the document the page loads next and survives navigation inside the context. Two consequences matter in practice: 1. Call `install` **before** `page.goto`, so the page boots on the fake clock rather than registering timers against the real one. The documented rule is that if you use `install` at all, it must come before any other clock-related call. 2. Prefer starting the clock slightly *before* the moment under test and letting the page load naturally, then pausing. Loading a page with time already frozen can leave a spinner or a bootstrap timer that never resolves. Because the clock belongs to the context, you do not install it per page; a second tab opened in the same context is already on the same clock. ## Choosing in practice 1. Start with `setFixedTime` — the cheapest thing that could work. 2. If a countdown, a relative timestamp or a poll stalls, switch to `install({ time })` before navigation. 3. Then pick the advance verb by intent: `runFor` when every intermediate tick must fire, `fastForward` when you only care about the state on the far side of the jump, `pauseAt` when the assertion needs the clock held still. The cost of `install` is that the page is now entirely on your clock: anything the app expects to happen "eventually" happens only when the test says so. That is the point, and it is also why an over-installed clock can make a test hang while it waits for a timer nobody advanced.

  • Can you still call page.clock.setFixedTime after page.clock.install?
    Yes. `setFixedTime` pins `Date.now()` and `new Date()` to a value while the installed timers keep running, so you can install for timer control and then freeze the visible date. Calling it again moves the pinned value; the timers are unaffected either way.
  • Which pages does page.clock.install affect in a Playwright test?
    All of them in that browser context. `page.clock` and `context.clock` are the same clock, so every page and every iframe in the context runs on it, and it is delivered as an init script, so it applies across navigations too. You do not install it per page.

setFixedTime is painting a fixed time on the wall clock while the kitchen timers keep ticking; install replaces every timepiece in the building with ones you hold the winder for.

saying these in an interview costs you the question

  • Thinks setFixedTime also pauses setTimeout and setInterval callbacks
  • Calls install after the page has already scheduled its timers
  • Believes a frozen Date.now still lets a countdown advance
  • Reaches for install when the page only renders a static date
  • Assumes the fake clock covers one page but not its iframes
open as a page

In Playwright, what does spreading `devices['iPhone 15']` into a project's `use` block actually configure?

level: juniorimportance: must knowfreq 74%

basics

~20 s

A device descriptor is a bundle of browser context options under one name. Spreading it sets viewport, screen, userAgent, deviceScaleFactor, isMobile and hasTouch together, plus defaultBrowserType, which tells the test runner which engine to launch.

open as a page

Why does Playwright download its own Chromium, Firefox and WebKit builds instead of driving your installed browsers?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Playwright pins a patched build of each engine to its own release, so every machine runs an identical browser. Its WebKit is built from WebKit sources rather than Safari, and its Chromium is not the Chrome you have installed.

open as a page

In Playwright, what does the headless option on browserType.launch() control, and what is its default?

level: juniorimportance: must knowfreq 76%

basics

~20 s

Playwright's headless launch option decides whether the browser draws a visible window. It defaults to true, so launch() starts an invisible browser; passing headless: false opens a real window you can watch a failing flow in.

open as a page

In Playwright, which browser context options set the page's language and time zone?

level: juniorimportance: must knowfreq 66%

basics

~10 s

Playwright's locale and timezoneId context options. locale sets navigator.language, the Accept-Language header and Intl formatting; timezoneId sets the IANA zone that Date and Intl.DateTimeFormat resolve to.

open as a page

In Playwright, how do you open a second tab in the same browser context?

level: juniorimportance: must knowfreq 68%

basics

~10 s

Calling context.newPage() creates another tab inside the same BrowserContext. Both tabs share that context's cookies, storage and emulation, and context.pages() lists every page currently open in it.

open as a page

In Playwright, what state does a fresh browser.newContext() isolate from every other context?

level: juniorimportance: must knowfreq 80%

basics

~20 s

A new browser context is an incognito-like profile: its own cookies, localStorage, sessionStorage, IndexedDB, cache, service workers and permission grants. Contexts share only the running browser, so one test cannot leak client state into another.

open as a page

What happens to a 30-second polling timer when a Playwright test calls page.clock.fastForward('05:00')?

level: middleimportance: must knowfreq 64%

basics

~20 s

The clock jumps five minutes ahead and the poll fires once, not ten times. fastForward models closing a laptop lid: due timers fire at most once on landing. Use page.clock.runFor to fire every intermediate tick.

open as a page

How does a Playwright device descriptor's `defaultBrowserType` decide which engine a project runs?

level: middleimportance: must knowfreq 58%

basics

~10 s

The test runner declares browserName as an option that falls back to defaultBrowserType, which itself defaults to chromium. A descriptor carrying defaultBrowserType 'webkit' therefore selects WebKit, unless the same use block sets browserName explicitly.

open as a page

What does the --with-deps flag add to npx playwright install on a fresh Linux machine?

level: middleimportance: must knowfreq 61%

basics

~10 s

It installs the operating-system shared libraries the engine binaries link against, on top of downloading the engines themselves. On Linux it runs the system package manager as root; on macOS it is unnecessary.

open as a page

In Playwright, how do you reset one page.emulateMedia override without clearing the others?

level: middleimportance: must knowfreq 48%

basics

~20 s

Pass null for that one key. In Playwright, page.emulateMedia treats null as reset to default and leaves every key you omit at its current value, so emulateMedia with colorScheme null clears only the colour scheme.

open as a page

In Playwright, why must page.waitForEvent('popup') be created before the click that opens the tab?

level: middleimportance: must knowfreq 76%

basics

~20 s

Playwright emits the popup event the moment the new tab appears and never replays it. Awaiting the click first lets the event fire with nobody listening, so the later wait blocks on a second popup that never comes.

open as a page

In Playwright, why does context.addCookies() reject a cookie that has only a name and a value?

level: middleimportance: must knowfreq 58%

basics

~10 s

Because Playwright cannot place a cookie without knowing where it belongs. Every entry needs a url, or both domain and path, and passing neither throws: Cookie should have a url or a domain/path pair.

open as a page

Your Playwright suite suddenly fails with "Executable doesn't exist at .../chromium-1244/..." after a dependency update. What happened?

level: seniorimportance: must knowfreq 53%

basics

~20 s

The Playwright package moved to a version that expects a newer engine revision, and nobody re-ran the download. Engine builds live in revision-stamped folders, so the runtime looked for a build that was never installed.

open as a page

In Playwright, why does page.clock.setSystemTime move the clock without firing any timers?

level: middleimportance: should knowfreq 33%

basics

~20 s

It exists to test how a page reacts to a time shift, not to drive timers. setSystemTime changes what Date reports; pending setTimeout and setInterval callbacks keep their original schedules, so nothing fires and nothing is rescheduled.

open as a page

In a Playwright `use` block, why is a `viewport` override ignored when written before the `...devices` spread?

level: middleimportance: should knowfreq 46%

basics

~10 s

Because the use block is one object literal and JavaScript applies its keys in source order. The spread comes later, so the descriptor's own viewport overwrites the earlier key and only one value survives.

open as a page

In Playwright, what does setting PLAYWRIGHT_BROWSERS_PATH change about where engine builds live?

level: middleimportance: should knowfreq 44%

basics

~20 s

It moves the directory Playwright installs engine builds into and reads them from. An absolute path selects that directory; the value 0 puts the builds inside the installed package itself, giving the project its own private copy.

open as a page

What does Playwright's chromium.launchPersistentContext(userDataDir) buy you, and what does it cost?

level: middleimportance: should knowfreq 37%

basics

~10 s

It launches a browser against a profile directory on disk and returns the single BrowserContext that owns it. You gain state that survives runs plus Chromium extensions; you lose fresh isolation and independent contexts.

open as a page

What does Playwright's slowMo launch option do, and why does it not make a flaky test reliable?

level: middleimportance: should knowfreq 44%

basics

~10 s

slowMo is a launch option that pauses Playwright by the given number of milliseconds between operations so a human can follow the run. It hides races behind extra delay; it never removes them.

open as a page

In Playwright, why does navigator.geolocation still fail after you set the context's geolocation option?

level: middleimportance: should knowfreq 41%

basics

~10 s

Because coordinates and permission are two separate context settings. Playwright's geolocation option only supplies a position; the context must also hold the geolocation permission before the page is allowed to read it.

open as a page

In Playwright, how does the BrowserContext 'page' event differ from the Page 'popup' event?

level: middleimportance: should knowfreq 54%

basics

~20 s

The context's page event fires for every page created in that context, including ones your script opens. The page's popup event fires only for popups that particular page opened, and it fires in addition to the context event.

open as a page

In Playwright, how do you let a test use a permission-gated feature such as clipboard read?

level: middleimportance: should knowfreq 40%

basics

~10 s

Grant it on the context before the feature is used: context.grantPermissions(['clipboard-read']), or the permissions option on browser.newContext. Add an origin option to limit the grant, and clearPermissions to drop every override again.

open as a page

Why does a Playwright test that writes localStorage after page.goto() fail to change how the app boots?

level: middleimportance: should knowfreq 46%

basics

~10 s

Because the app has already started and read storage by the time the navigation resolves. Seeded values must exist before the document's own scripts run, which is what context.addInitScript() and page.addInitScript() are for.

open as a page

A Playwright test calls page.clock.pauseAt before page.goto and the room page never finishes loading — how should the clock calls be ordered?

level: seniorimportance: should knowfreq 41%

basics

~20 s

Install the clock before navigating, at a moment slightly before the time under test, let the page load with timers running, and only then pause. Pausing before navigation stops the bootstrap timers the page needs to finish loading.

open as a page

A Playwright test fails with 'The page does not support tap' on a mobile flow — what is missing?

level: seniorimportance: should knowfreq 51%

basics

~20 s

The context was built without hasTouch, which defaults to false. Playwright refuses locator.tap when there is no touchscreen. A hand-written viewport and user agent do not enable touch; a device descriptor sets hasTouch and isMobile together.

open as a page

When would you attach with browserType.connect rather than browserType.connectOverCDP?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Use browserType.connect whenever the far side is a Playwright browser server: it speaks Playwright's own protocol, works for all three engines, and keeps full fidelity. connectOverCDP is the Chromium-only, lower-fidelity way to attach to a browser someone else started.

open as a page

Your Playwright suite must reach a staging hotel-booking site only through an authenticated HTTP proxy. How do you configure that?

level: seniorimportance: should knowfreq 31%

basics

~20 s

Pass a proxy object to browserType.launch(): server, plus optional username, password and a comma-separated bypass list. Set at launch it covers every context that browser creates, and the credentials should come from environment variables, not source.

open as a page

A Playwright test calls context.setOffline(true), yet the booking page still renders room data. What would you check?

level: seniorimportance: should knowfreq 44%

basics

~20 s

Check what is actually serving the data. Playwright's context.setOffline only stops new browser network traffic in that context; already-rendered markup, cached responses, a service worker and the Node-side request client all keep working, and nothing forces the page to reload.

open as a page

In a Playwright teardown, when do you call page.close(), context.close() and browser.close()?

level: seniorimportance: should knowfreq 47%

basics

~10 s

page.close() ends one tab and leaves its context running. context.close() closes the context and all its pages, flushing artifacts. browser.close() ends everything at once, like force-quitting, so close your contexts before it.

open as a page

Your Playwright suite tests a staging site behind HTTP Basic auth that also calls a partner API — how do you authenticate without leaking credentials?

level: seniorimportance: should knowfreq 36%

basics

~20 s

Put httpCredentials on the context and pin it with the origin field, so Basic auth is answered only for the staging host. Without an origin, the same username and password answer any 401, including the partner's.

open as a page

showing 1–30 of 36