Q28 of 48 · Cypress

How do you mock the system clock in Cypress?

CypressMidcypressclocktimemid

Short answer

Short answer: Use `cy.clock()` to override `Date.now`, `setTimeout`, and `setInterval` in the application's window. Advance time with `cy.tick(ms)`. Restore with `cy.clock().then((c) => c.restore())`. Essential for testing scheduled UI, debounce, polling, and time-based business logic.

Detail

cy.clock() patches the browser's clock from the test, freezing time and giving you control over advancement. Without it, time-dependent UI (a "5 minutes ago" label, a debounced search input, a scheduled notification) can't be tested deterministically.

The core API:

cy.clock();                      // freezes Date.now() at current time, freezes timers
cy.clock(new Date('2026-01-01').getTime()); // freezes at a specific time
cy.tick(1000);                   // advances 1 second; runs any timers that fire

Important: cy.clock() must be called before the page sets up timers — typically before cy.visit. Set it, visit, then tick to advance.

Common scenarios:

  • Debounced inputs — type, cy.tick(300) past the debounce, assert the request fires.
  • Polling — assert the first request fires immediately, cy.tick(intervalMs) to verify subsequent ones.
  • Time-relative UI — set the clock to a specific moment, assert "2 hours ago" labels are correct.
  • Scheduled state — set up a future-fire scenario, tick past it, assert the side effect.

The functions to override are configurable: cy.clock(timestamp, ['Date', 'setTimeout']) to leave setInterval real, for instance.

Restoration is automatic at the end of each test under testIsolation: true. If you want to restore mid-test, cy.clock().then((c) => c.restore()).

// EXAMPLE

debounce.cy.ts

it('debounces search input by 300ms', () => {
  cy.intercept('GET', '/api/search*').as('search');
  cy.clock();
  cy.visit('/');

  cy.get('[data-test=search]').type('apple');

  // No request yet
  cy.tick(200);
  cy.get('@search.all').should('have.length', 0);

  // Past the 300ms debounce
  cy.tick(150);
  cy.wait('@search').its('request.url').should('include', 'q=apple');
});

it('shows "2 hours ago" for a 2h-old comment', () => {
  cy.clock(new Date('2026-05-10T15:00:00Z').getTime());
  cy.intercept('GET', '/api/comments', {
    body: [{ id: 1, createdAt: '2026-05-10T13:00:00Z', text: 'Nice' }],
  });
  cy.visit('/comments');
  cy.get('[data-test=comment-time]').should('have.text', '2 hours ago');
});

// WHAT INTERVIEWERS LOOK FOR

Knowing `cy.clock` must be set before timers register, the `cy.tick` API, and the typical use cases (debounce, polling, time-relative labels).

// COMMON PITFALL

Calling `cy.clock()` after `cy.visit` and being surprised that the page's setInterval is still running on the real clock.