Today I learned about mocking dates in Puppeteer

  • Written on: 03/05/2021
  • Last update: 03/05/2021

Mocking dates in Puppeteer

During my integration tests, I had a test that was picking a random date in a datepicker component. While this worked, it didn't feel scalable or reliable. Random dates could lead to flaky tests and made debugging difficult when issues occurred.

I needed a way to control the date during test execution to make my tests deterministic and predictable.

The Solution: Mocking Dates in Puppeteer

After some research, I discovered that Puppeteer's page.evaluate() method can be used to inject JavaScript code directly into the page context. This allows us to override the global Date constructor and Date.now() method.

Basic Approach with page.evaluate()

// Define the date you want to mock
const mockDate = new Date("2021-05-03T10:00:00Z");

// Inject the mock into the page context
await page.evaluate((date) => {
  // Store the original Date constructor
  const OriginalDate = Date;

  // Override the Date constructor
  Date = function (...args) {
    if (args.length === 0) {
      // When called without arguments, return our mock date
      return new OriginalDate(date);
    }
    // When called with arguments, use the original behavior
    return new OriginalDate(...args);
  };

  // Override Date.now() to return the mock timestamp
  Date.now = () => new OriginalDate(date).getTime();

  // Preserve other static methods
  Date.parse = OriginalDate.parse;
  Date.UTC = OriginalDate.UTC;
  Date.prototype = OriginalDate.prototype;
}, mockDate);

Alternative: Using page.addInitScript()

For cases where you want the mock to be available before the page loads, you can use page.addInitScript():

const mockDate = new Date("2021-05-03T10:00:00Z");

await page.addInitScript((date) => {
  const OriginalDate = Date;

  Date = function (...args) {
    if (args.length === 0) {
      return new OriginalDate(date);
    }
    return new OriginalDate(...args);
  };

  Date.now = () => new OriginalDate(date).getTime();
  Date.parse = OriginalDate.parse;
  Date.UTC = OriginalDate.UTC;
  Date.prototype = OriginalDate.prototype;
}, mockDate);

Cleaning Up After Tests

It's good practice to restore the original Date constructor after your tests:

await page.evaluate(() => {
  // Restore original Date constructor
  Date = window.OriginalDate || Date;
});