PRACTICAL GUIDE / playwright clock API

Deterministic Timer and Date Tests with the Playwright Clock API

Control dates, timers, intervals, and inactivity flows with Playwright Clock API examples that keep time-dependent browser tests deterministic.

By The Testing AcademyUpdated July 11, 20269 min read
All field guides
In this guide10 sections
  1. Choose the smallest clock model that proves the behavior
  2. Install controlled timers before the application starts
  3. Drive an inactivity policy across its exact boundary
  4. Select runFor or fastForward from the product scenario
  5. Separate browser time from server time
  6. Diagnose failures by identifying the competing clock
  7. Test dates as domain boundaries, not decorative strings
  8. Weigh determinism against realism
  9. Operational checklist for clock-based tests
  10. Build a deterministic time suite deliberately

What you will learn

  • Choose the smallest clock model that proves the behavior
  • Install controlled timers before the application starts
  • Drive an inactivity policy across its exact boundary
  • Select runFor or fastForward from the product scenario

A test that waits five real minutes to prove a session expires is not realistic automation. It is slow, exposed to scheduler noise, and still says little about the boundary immediately before expiry. The Playwright clock API lets the browser begin at a known instant and move under test control, so a five-minute policy can be checked at four minutes fifty-nine seconds and again one second later.

The important design choice is not simply to "mock Date." A browser application may combine Date.now(), intervals, timeouts, animation frames, and elapsed-time calculations. A reliable test must select the clock mode that matches that behavior and must install it before application code captures native timing functions.

Choose the smallest clock model that proves the behavior

The official Playwright clock guide separates a fixed date from fully controlled time. setFixedTime changes what Date.now() and new Date() return while ordinary timers continue to run. It is ideal for a tax-year label, scheduled banner, or date formatting rule that only reads the current date.

install replaces browser timing primitives and enables pauseAt, fastForward, runFor, and resume. Choose it for inactivity logout, debounced search, countdowns, polling, delayed notifications, or any implementation where timer progression and the current date must agree. setSystemTime is a narrower advanced operation: it shifts system time without firing timers, which can model a wall-clock correction but should not be mistaken for elapsed time.

Animated field map

Controlled browser time flow

Seed one clock, exercise the user event, and move only to the policy boundary under test.

  1. 01 / install clock

    Install clock

    Replace browser timing primitives before application startup.

  2. 02 / seed time

    Seed system time

    Start from an explicit ISO instant with a deliberate timezone.

  3. 03 / trigger behavior

    Trigger behavior

    Perform the action that schedules the timeout or interval.

  4. 04 / advance timers

    Advance timers

    Use runFor or fastForward according to the real event model.

  5. 05 / assert boundary

    Assert boundary

    Check state immediately before and after the deadline.

Install controlled timers before the application starts

Call page.clock.install() before page.goto() when the page can schedule work during startup. The clock applies to the entire browser context, including its pages and iframes. That shared scope is useful for a multi-page workflow, but it also means two independent time scenarios should use separate contexts rather than trying to maintain two clocks in one context.

Use an ISO timestamp with an explicit Z or numeric offset. A date such as 2032-03-14 can be interpreted differently by application formatting code and by the test environment. An explicit instant makes the test input reviewable.

TypeScript
import { expect, test } from '@playwright/test';

test('shows the renewal notice on the contract end date', async ({ page }) => {
  await page.clock.setFixedTime(new Date('2032-03-14T09:00:00Z'));
  await page.goto('/account/subscription');

  await expect(page.getByRole('status')).toContainText('Renews today');
});

This example deliberately uses setFixedTime: the assertion depends on a calendar reading, not on waiting for a callback. Replacing every timer would add control the test does not need.

Drive an inactivity policy across its exact boundary

An inactivity test should first prove that the user remains signed in before the deadline. Otherwise a passing logout assertion might hide an off-by-one error that expires sessions too early. Next, advance the final unit and assert the visible outcome plus one meaningful side effect, such as a request or cleared user state.

TypeScript
import { expect, test } from '@playwright/test';

test('logs out only after five idle minutes', async ({ page }) => {
  await page.clock.install({ time: new Date('2032-06-01T12:00:00Z') });
  await page.goto('/workspace');

  await page.getByRole('button', { name: 'Open profile' }).click();
  await expect(page.getByText('Signed in as Maya')).toBeVisible();

  await page.clock.runFor('04:59');
  await expect(page.getByText('Signed in as Maya')).toBeVisible();

  const logoutRequest = page.waitForRequest(
    request => request.url().endsWith('/api/logout') && request.method() === 'POST',
  );
  await page.clock.runFor('01');

  await logoutRequest;
  await expect(page.getByRole('heading', { name: 'Session expired' })).toBeVisible();
});

The web assertion still auto-retries while the page renders the result. Clock control removes the long wait; it does not justify replacing locator assertions with immediate DOM reads.

Select runFor or fastForward from the product scenario

runFor advances through time and fires all time-related callbacks that become due. Use it when every interval matters: a progress indicator increments each second, a polling loop makes repeated requests, or a debounce schedules follow-up work. It models continuous active execution under a manually controlled clock.

fastForward jumps ahead and fires due timers at most once. That resembles a laptop sleeping and later waking. It is appropriate when the requirement concerns the final state after suspension, such as an expired session or a stale dashboard refresh. It is not a shortcut for verifying that a callback ran 300 times. If callback count is the behavior, use runFor and assert the externally visible consequence rather than private timer implementation.

pauseAt jumps to an instant and leaves timers paused. resume returns the browser to ordinary progression. These operations are useful for inspecting an exact deadline, but repeated pauses can make a test harder to understand. Prefer a linear chronology with one seed and a small number of explicit advances.

Separate browser time from server time

The clock controls browser globals. It does not change a backend's operating-system clock, an API gateway token validator, or database-generated timestamps. A browser can believe it is noon while the server still believes it is morning. Sometimes that mismatch is the very case you need to test, but it must be intentional.

For end-to-end expiry involving signed tokens, provide a backend test hook, inject a server-side clock, or route the relevant API to deterministic responses. Keep ownership visible: the Playwright clock drives client chronology, while the server fixture drives server chronology. Avoid forging production authentication internals in the browser merely to make the test pass.

Diagnose failures by identifying the competing clock

When a fake-time test hangs, first look for installation order. Application code may have created a native interval before install, leaving the test with both native and controlled timers. Move installation before navigation and before any page.evaluate() that touches time.

If a countdown does not update after fastForward, determine whether the UI expects every interval tick. Switching to runFor is justified when intermediate callbacks produce the state. If the date is correct but the displayed day is wrong, inspect timezone and locale configuration rather than adding another hour until the assertion turns green.

A request that never arrives may be scheduled by a service worker, backend job, or WebSocket peer outside the page's controlled context. Trace the ownership of the timer. Fake browser time cannot advance code running in a separate server process.

Finally, check for hidden real sleeps in test helpers. page.waitForTimeout() uses test wall time as a debugging crutch and defeats the purpose of controlled chronology. Advance the product clock, then wait on an observable condition with an assertion or event promise.

Test dates as domain boundaries, not decorative strings

Calendar behavior deserves cases around midnight, leap days, daylight-saving transitions, and billing cutoffs, but only when the product supports those conditions. Pin the browser context's timezone where display rules depend on local time, and state the business zone in the test title.

Keep date formatting assertions resilient. If the requirement is "renewal is due today," assert that semantic status rather than duplicating every comma in a localized date. If formatting itself is the requirement, configure locale and timezone explicitly so the expected string has a stable contract.

A useful date matrix is small and risk-based: one ordinary date, each relevant boundary, and any incident regression. Do not multiply every clock test across all browsers unless browser timing behavior is part of the risk. Many business-date rules belong in unit tests, with a focused browser test proving that the UI consumes the rule correctly.

Weigh determinism against realism

Clock control improves speed and precision, but it can hide defects caused by real scheduling pressure, background throttling, or distributed clock skew. Keep a narrow set of controlled tests for business boundaries and use separate integration or exploratory coverage for behaviors that depend on actual suspension, multiple devices, or server time.

Tests coupled to an implementation's exact number of interval callbacks become brittle during harmless refactoring. Assert the state users observe, the request contract, or the transition at a deadline. The fake clock should make the scenario executable, not expose private timer mechanics as a public API.

Operational checklist for clock-based tests

  • Install the clock before navigation when startup code touches time.
  • Seed an ISO instant with an explicit timezone or offset.
  • Use setFixedTime when only Date must be stable.
  • Use runFor when intermediate callbacks matter.
  • Use fastForward for resume-after-gap behavior.
  • Assert both sides of expiry and scheduling boundaries.
  • Control backend time separately when server chronology affects the result.
  • Replace wall-clock sleeps with events or auto-retrying assertions.
  • Give independent scenarios separate browser contexts.
  • Record the product behavior being modeled in the test name.

Build a deterministic time suite deliberately

Start with one expensive or flaky time-dependent journey and map every clock that participates. Move the browser to a fixed instant, install timer control only if the behavior requires it, and assert the state immediately before and after the business boundary. Then review the test for accidental dependence on locale, server time, or private callback counts. This sequence turns time from an uncontrolled environment condition into explicit test data while preserving realism where it actually matters.

// LIVE COURSE / THE TESTING ACADEMY

Playwright Automation Mastery

Go beyond Selenium. Master Playwright with JS/TS in 90 days.

From the instructor behind this guide.

Playwright jobs are growing 8x faster than Selenium. 90 days / 75+ live hrs / Tue-Thu-Sat 7 AM IST.

Code PROMODE / 10% offJoin the batch

The Testing Academy editorial desk

Practical QA guidance built around test evidence, production tradeoffs, and interview-ready explanations.

Published July 11, 2026 / Reviewed July 11, 2026

PRIMARY REFERENCES

Verify the details at the source

QABattle guides are practical explanations. Product behavior, standards, and APIs can change, so use these primary references for the canonical details.

  1. 01
    Playwright documentation

    Microsoft

    Canonical API, locator, fixture, browser, and test-runner behavior.

  2. 02
    Playwright best practices

    Microsoft

    Official guidance for resilient tests, isolation, and user-facing locators.

  3. 03
    HTTP Semantics

    IETF

    The normative semantics for methods, status codes, fields, and HTTP behavior.

  4. 04
    OWASP API Security Top 10

    OWASP Foundation

    Primary API-specific risk taxonomy and defensive guidance.

FAQ / QUICK ANSWERS

Questions testers ask

When should a test use setFixedTime instead of installing the Playwright clock?

Use setFixedTime when the application only reads Date and timers should continue normally. Install the clock when the test must control timeout, interval, animation frame, idle callback, or performance timing behavior.

Why must page.clock.install run before navigation?

Application startup can create timers immediately. Installing first ensures those callbacks use the controlled implementations from their creation, avoiding a mixture of native and fake timing behavior.

What is the difference between fastForward and runFor?

fastForward jumps to the destination and fires each due timer at most once, which models a suspended device resuming later. runFor advances through the interval and executes all time-related callbacks that become due.

Does the Playwright clock affect every page in a browser context?

Yes. The clock is installed for the BrowserContext, so pages and iframes in that context share the same controlled time. Use separate contexts when scenarios require independent clocks.

Can clock control replace server-side time injection?

No. It controls browser globals, not a backend clock, database timestamp, or token issuer. Inject or stub server time separately when an outcome depends on both browser and server chronology.