jest-time-helpers
Version:
Helpers you can use in tests that relate to the passage of time (i.e. code that involves setTimeout, setInterval, new Date(), Date.now(), etc)
170 lines • 6.93 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.WEEK = exports.DAY = exports.HOUR = exports.MINUTE = exports.SECOND = exports.sleep = void 0;
exports.sleepUntil = sleepUntil;
exports.setupFakeTimers = setupFakeTimers;
const assert = __importStar(require("assert"));
const util = __importStar(require("util"));
// Grab the setTimeout from global before jest overwrites it with useFakeTimers
const setTimeoutBypassingFakes = global.setTimeout;
/**
* Wait a number of milliseconds of _real time_ (not mocked time); useful for
* allowing the runloop or external systems to advance.
*
* @param ts - how long to sleep for.
*/
const sleep = (ms, unref = false) => {
let timeout;
const promise = new Promise((resolve) => {
timeout = setTimeoutBypassingFakes(resolve, ms);
});
if (unref) {
timeout.unref();
}
return Object.assign(promise, { timeout });
};
exports.sleep = sleep;
/**
* Polls until the condition passes.
*
* Polls the `condition` callback every `pollInterval` ms of real time. If/when
* it returns a truthy value, resolves successfully. If `maxDuration` is
* exceeded before `condition()` returns true, rejects with an error.
*
* @param condition - a callback function that should return true when no more sleep is required
* @param maxDuration - an optional maximum duration to sleep
* @param pollInterval - an optional period of how long we should wait between checks; lower values increase load on the server but may make tests pass faster, larger values are more efficient but increase test latency.
*/
async function sleepUntil(condition, maxDuration = 2000, pollInterval = 2) {
assert.ok(pollInterval >= 1, "pollInterval must be >= 1 millisecond");
if (condition()) {
// Already fine, no need to sleep
return;
}
// Wait for condition to pass
const start = Date.now();
while (Date.now() - start < maxDuration) {
await (0, exports.sleep)(pollInterval);
if (condition()) {
// Success
return;
}
}
// maxDuration exceeded
throw new Error(`Slept for ${Date.now() - start}ms but condition never passed`);
}
/**
* This is for letting the Node.js event loop advance, e.g. when `setTimeout`
* has `await` in the chain.
*
* @internal
*/
async function aFewRunLoops(count = 5) {
for (let i = 0; i < count; i++) {
await (0, exports.sleep)(0);
}
}
const OriginalDate = global.Date;
/**
* Returns the real-world current timestamp (unaffected by fake timers).
*/
function realNow() {
return OriginalDate.now();
}
/**
* Sets up fake timers and Date implementation within your Jest test file. You
* should call this at the top of the file (**not** within a test or an
* after/before); tests that need fake timers should go into their own test
* file.
*
* Returns an object containing a `setTime` function which you can use to set
* the current (fake) date within your test to a particular JavaScript
* timestamp (number of milliseconds since 1970-01-01T00:00:00Z).
*
* Enables the `jest.useFakeTimers()` integration, and overwrites `global.Date`
* with a custom function that automatically applies your test file's given
* offset.
*/
function setupFakeTimers() {
beforeEach(() => void jest.useFakeTimers());
afterEach(() => void jest.useRealTimers());
/**
* Sets the fake time such that a call to `Date.now()` (or `new Date()`)
* would return this timestamp if called immediately (but time continues to
* progress as expected after this). Also advances the timers by the
* difference from the previous time, if positive. Setting time backwards is
* allowed (like setting back the system clock on a computer), but will not
* advance (or undo the advancement of) any timers.
*
* Since advancing the time a few hours might not run all the intermediary
* code quite right, we actually step it up by a configurable increment
* (defaults to one minute) at a time. Setting time backwards (no matter how
* far back) is done all at once.
*
* @param targetTimestamp - the target timestamp
* @param increment - the maximum we should advance time by at once in order to step towards `timestamp`
*/
async function setTime(targetTimestamp, increment = exports.MINUTE) {
assert.strictEqual(typeof targetTimestamp, "number", `Expected \`setTime\` to be passed a number of milliseconds, instead received '${util.inspect(targetTimestamp)}'`);
if (targetTimestamp < Date.now()) {
jest.setSystemTime(targetTimestamp);
}
else {
while (Date.now() + increment < targetTimestamp) {
jest.advanceTimersByTime(increment);
await aFewRunLoops();
}
if (Date.now() < targetTimestamp) {
jest.advanceTimersByTime(targetTimestamp - Date.now());
await aFewRunLoops();
}
}
}
// In future we may add other methods such as `setTimeWithoutAdvancingTimers`
// to emulate the system clock changing without real time elapsing.
return { setTime, realNow };
}
/** One second in milliseconds */
exports.SECOND = 1000;
/** One minute in milliseconds */
exports.MINUTE = 60 * exports.SECOND;
/** One hour in milliseconds */
exports.HOUR = 60 * exports.MINUTE;
/** One day in milliseconds */
exports.DAY = 24 * exports.HOUR;
/** One week in milliseconds */
exports.WEEK = 7 * exports.DAY;
//# sourceMappingURL=index.js.map