UNPKG

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
"use strict"; 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