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;
});