skip to content

Emulated Environment

The options that make a context look like a different device, language, colour scheme or moment in time, all inside the engine you launched. Probed because the limits of emulation matter.

on this pageshow

explore

questions

15

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

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

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

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 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, 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

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

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

What can a Playwright device descriptor not reproduce about a real phone, and what does a green mobile project prove?

level: principalimportance: should knowfreq 36%

basics

~20 s

A descriptor overrides metrics, user agent and input flags inside a desktop engine. It reproduces no phone hardware, no shipping mobile browser and no mobile OS, so a green run proves your responsive layout works, not that iOS does.

open as a page

In Playwright, what still works on a context created with javaScriptEnabled: false?

level: middleimportance: nice to knowfreq 33%

basics

~20 s

Almost everything Playwright drives. The page's own scripts never run, but navigation, clicks, filling fields, locators, assertions and Playwright's evaluate call all still work, because the driver talks to the browser over the protocol rather than through page script.

open as a page

Your team treats Playwright's emulated locale, colour scheme and offline mode as proof the booking site works there. Where does that emulation stop?

level: principalimportance: nice to knowfreq 26%

basics

~20 s

It stops at what the page can observe. Playwright's context options substitute the values and media-query state the browser reports, so the right code branch runs; they do not reproduce a real device, font stack or network.

open as a page