Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

zurvan

tlewowski7.7kMIT0.8.1TypeScript support: included

A library for simulating time in Node.js

testing, timeouts, time, timers, fake time, timer mocks, timer stubs, timer management, faking timers, event loop management, fake timers, setImmediate, setInterval, setTimeout, process.uptime, Date, process.hrtime

readme

Zurvan

Build Status Code Climate codebeat badge Test Coverage Dependencies devDependencies GitHub version npm version

Zurvan is an asynchronous library for faking whole real-time dependency of node.js, mainly for test purposes.

Introduction

Zurvan includes fake implementations for setTimeout, clearTimeout, setInterval, clearInterval, Date.now, Date, process.uptime and process.hrtime. Also several other functions are taken into account, such as setImmediate, clearImmediate and process.nextTick, but they are not faked - library utilizes asynchronous execution heavily (under assumption that since time is asynchronous by default, it's better to leave it this way).

Zurvan is currently not tested in the browser, so if you want to use it there, you can either hack it yourself (see: Zurvan requirements) or a contact me.

Multiple testcases cannot be ran in parallel when using Zurvan, as there is only a single time stream for forwarding.

Zurvan will NOT work properly (at least in release 0.5.1) if test code uses real I/O (filesystem, sockets etc.). To be exact, waitForEmptyQueue will not be able to work, since there will be no scheduled tasks on the queue, despite the fact that I/O is not done. It is possible to use Zurvan in such cases, but additional Promises are required. It is generally preferred to use preloaded data and mock I/O via usual async actions (setImmediate/process.nextTick).

API overview

zurvan

This is the main module of the library. Typical forwarding of time is done step by step:

  1. Event queue is cleared (all immediates and ticks are executed)
  2. Nearest timer is inspected - if it is after due time (now + requested advance), time is set to requested due time and forwarding ends
  3. Otherwise a single nearest timer is expired, and the 1. is applied again, with smaller reqested advance time

zurvan.blockSystem is an exception, described below. If critical requirements are not met, zurvan will throw during first evaluation (at the point of require).

zurvan.interceptTimers([config])

Library setup. Causes timers to be intercepted, i.e. all required functions are overridden after this call. Returns a Promise that is resolved when timers are faked and event queue is empty and rejected if interception was not possible (e.g. timers were already intercepted) It takes an optional configuration object as parameter, which takes precedence over global configuration. Details of configuration options are described in configuration documentation.

Resolution value is undefined and rejection value is Error with proper message.

If the configuration is incompatible or required fields are not filled in, .interceptTimers() will throw an error with proper message. Invalid configuration has priority over timers already being intercepted, i.e., if timers are already intercepted and configuration is invalid, error about invalid configuration will be thrown. Errors due to invalid configuration are thrown synchronously, not via promise rejection.

zurvan.releaseTimers()

Library teardown. Causes timers to be restored, i.e. all original functions are set back. Returns a Promise that is resolved when timers are released and event queue is empty and rejected if release was not possible (e.g. timers weren't even intercepted) or failed (e.g. long enough setImmediate loop)

Resolution value is and object, defined as:

{
  timeouts: remainingTimeouts,
  intervals: remainingIntervals,
  immediates: remainingImmediates,
  date: zurvanEndDate,
  processTime: zurvanEndProcessTime,
  currentTime: zurvanEndTime
}

these fields are defined as:

  • remainingTimeouts is an array of Timer objects, representing timeouts that did not expire yet
  • remainingIntervals is an array of Timer objects, representing intervals that did not expire yet
  • zurvanEndDate is zurvan date in same format that is returned when timers are intercepted (not exactly same as usual Date - see [limitations]{#limitations})
  • zurvanEndProcessTime is zurvan process time in hrtime format ([seconds, nanoseconds]) at the release site
  • zurvanEndTime is TimeUnit that can be compared with dueTime of Timer. It represents amount of time that was forwarded.
  • remainingImmediates is an object of immediates that were not yet executed. In zurvan.releaseTimers() is should always be an object defined as: { size: 0 }. If you encounter other value, please report it. For other possible values, see zurvan.forcedReleaseTimers().

A Timer object consists of at least two fields: callback which is a 0-argument function executing what would be done if it expired and dueTime which is a TimeUnit, informing when would the timer be expired. In case of intervals it must also contain callDelay field, which is a TimeUnit representing delay between consecutive calls. It may also contain arbitrary other fields, but they shall not be relied upon. Order of elements in remainingTimeouts and remainingIntervals is undefined. If zurvan is executed with ignoreProcessTimers or ignoreDate configuration options, respective fields (processTime and date) will not be available in releaseTimers resolution.

If is rejected, rejection value is Error with proper message.

zurvan.forcedReleaseTimers()

Forced teardown, should be used with extreme care. Works roughly the same way as zurvan.releaseTimers(), but does not validate that queue is cleared AND resets internals totally. Should be used only in fatal situations, such as after zurvan.waitForEmptyQueue() failed due to infinite loop. Leaves global variables in unknown state (may cause dropping of some events). Return values are the the same as in zurvan.releaseTimers(), except remainingImmediates - here it will be an object containing key size with value equal to number of dropped immediates and size of other keys, each corresponding to one dropped immediate. Value of these keys are stacks pointing to location where setting the immediate was requested.

zurvan.withDefaultConfiguration([config])

Returns a new library object (new zurvan instance) with modified default configuration. This means that after calling .withDefaultConfiguration, there are two instances of zurvan. However, they should not be used in parallel, i.e. only one of them should intercept timers at the same time. If another one already does, promise returned by interceptTimers will be rejected. Chain of withDefaultConfiguration(config) causes all configs to be merged (newer configurations override their fields, rest is taken from previous).

Configuration options are described in configuration documentation.

If called without arguments, returned instance will be created with default parameters.

zurvan.advanceTime(timeToAdvance)

Returns a Promise that is resolved when time is forwarded by given time and all timers with this dueTime are expired, or rejected it time cannot be forwarded (e.g. timers were not intercepted yet). Resolution value is array of callbacks that exited with an exception (if everything went smoothly, it should be empty). If you want zurvan to reject the Promise in such case, turn rejectOnCallbackFailure configuration option to true. If you do, it'll be rejected with the array of failed callbacks. However, if the reason for rejection is different than failed callbacks, it will be rejected with Error with proper message.

Argument may be either a number (it is then interpreted as millisecond) or a TimeUnit object.

zurvan.blockSystem(blockingTime)

Simulates a blocking call - expires synchronously all timers up to due time at once, without actually executing them (during expiration). Argument may be either a number (it is then interpreted as millisecond) or a TimeUnit object.

To read about why is this function needed, and why does it require a synchronous API, see: blocking calls explaination.

Does not return anything. Throws if time cannot be forwarded (e.g. timers were not intercepted yet).

zurvan.setSystemTime(newSystemTime)

Sets values returned by new Date and Date.now at given point of time (returned values will be of course adjusted with advancing time). Argument is expected to be "castable" to Date - this means a Date object, string which is valid argument to Date.parse or number (which is then treated as timestamp).

zurvan.expireAllTimeouts()

Advances time up to the point when there is no timeout set any more. Intervals will remain.

Warning! Under certain circumstances this function may result in an infinite loop. Example:

function f() {
  setTimeout(f, 100);
}

Returns a Promise that is resolved when all timeouts are already called or rejected it time cannot be forwarded (e.g. timers were not intercepted yet). Resolution value is array of callbacks that exited with an exception (if everything went smoothly, it should be empty). If you want zurvan to reject the Promise in such case, turn rejectOnCallbackFailure configuration option to true. If you do, it'll be rejected with the array of failed callbacks. However, if the reason for rejection is different than failed callbacks, it will be rejected with Error with proper message.

zurvan.forwardTimeToNextTimer()

Forwards the time to the nearest timer and exipires all timers with same due time. Resolution value is array of callbacks that exited with an exception (if everything went smoothly, it should be empty). If you want zurvan to reject the Promise in such case, turn rejectOnCallbackFailure configuration option to true. If you do, it'll be rejected with the array of failed callbacks. However, if the reason for rejection is different than failed callbacks, it will be rejected with Error with proper message.

Returns a Promise that is resolved when all callbacks are executed and event queue is empty or rejected it time cannot be forwarded (e.g. timers were not intercepted yet)..

zurvan.waitForEmptyQueue()

Returns a Promise that is resolved when all immediates are already called or rejected it time cannot be forwarded (e.g. timers were not intercepted yet). Also timers with zero time will be expired.

Resolution value is array of callbacks that exited with an exception (if everything went smoothly, it should be empty). If you want zurvan to reject the Promise in such case, turn rejectOnCallbackFailure configuration option to true. If you do, it'll be rejected with the array of failed callbacks. However, if the reason for rejection is different than failed callbacks, it will be rejected with Error with proper message.

TimeUnit

A utility module providing time calculations that are - hopefully - more human-readable than operating on milliseconds everywhere. Provide factory functions for TimeUnit object, that represents time duration:

  • nanoseconds
  • microseconds
  • milliseconds
  • seconds
  • minutes
  • hours
  • days
  • weeks

TimeUnit has the following API methods:

  • unit.extended(unit2) - returns duration represented by sum of durations of unit and unit2
  • unit.shortened(unit2) - returns duration represented by difference of durations between unit and unit2
  • unit.add(unit2) - mutator. Equal to unit.setTo(unit.extended(unit2))
  • unit.subtract(unit2) - mutator. Equal to unit.setTo(unit.shortened(unit2))
  • unit.setTo(unit2) - sets unit duration to be equal to duration of unit2
  • unit.copy() - creates a deep copy of unit
  • unit.isShorterThan(unit2) - checks if unit represents shorter duration than unit2
  • unit.isLongerThan(unit2) - checks if unit represents longer duration than unit2
  • unit.isEqualTo(unit2) - checks if both unit and unit2 represent same duration, within a reasonable epsilon (current resolution is 10^-15 second)

All of them work only on TimeUnit objects, but work smoothly on cross-unit basis. They do not take into account phenomenons such as leap seconds. This is also the reason why units like month and year are not provided - because they would be ambigous and complicate the utility. To handle the calendar properly much bigger library would need to be used.

TimeUnit should be used as object with value semantics.

Other

There are no other API functions. All functions and modules in detail directory are library internal and are not guaranteed to expose a stable set of methods. Please do not use them directly. If you do - do it at your own risk. But if you do, and you find any of these functions useful (which I doubt - that's why they are in detail), contact me to make it part of stable API or extract to a separate library.

Limitations

After intercepting timers, Date object is overridden (if ignoreDate configuration option is set to false). As a result, some external calls that rely on types may fail. This is because for var d = new Date() call Object.prototype.toString(d) without zurvan will return [object Date], and after timer interception, [object Object]. Please file an issue if this poses a problem for you.

To use Zurvan with Node.js 0.10 (before global.Promise was introduced) promiseScheduler configuration option has to be set. It's value has to be a valid Promise library, fulfilling Promises/A+ requirements. An additional constraint is that .then cannot be scheduled via global setTimeout function, as it is overridden by zurvan, and this would lead to circular dependencies. It is theoretically possible to cache original setTimeout in the library and use it as a scheduler, but please do not do this. There are enough good Promise libraries delivering what you need (bluebird for example).

If there is no global.Promise variable in the environment, zurvan will attempt to require('bluebird'). If this fails, user must give his own scheduler via promiseScheduler configuration option. Warning: if there is no global.Promise and bluebird can be loaded, bluebird's scheduler will be permanently overridden with setImmediate. It will be possible to override it again, but zurvan will not keep track of the previous scheduler in such case. Note that this is mostly important in Node.js 0.10 and virtual environment (without context). Again, if this poses a problem, please file an issue.

If your code does not directly access faked functions (setTimeout, setImmediate etc.), but caches their original values instead, you need to first require zurvan, and later your module that caches the calls (if it's already included due to earlier requires, you can reload it by clearing cache). This is exactly why bluebird configuration option is needed if you use it (bluebird caches setImmediate). This might cause trouble when integrating with external libraries, like request-promise

If your code uses multiple versions of bluebird (for example your application uses one version, and one of external packages uses a different one), zurvan in version 0.3.2 will not work properly. This is because bluebird scheduler needs to be overridden, and current configuration allows only for a single bluebird. If this poses a problem, please file an issue on GitHub.

If you use time-based events that are scheduled by a mechanism different than setTimeout and setInterval (for example, an externally bound C++ module), events scheduled with it will not be subject to be managed by zurvan, i.e. .waitForEmptyQueue() will not take them into account, thus race conditions will appear.

Examples

For simple examples you can refer to examples directory. For more complex ones, please refer to tests directory.

All examples are executed at each CI loop, i.e., they have to pass in order for build to succeed. This is a guarantee that they are up-to-date with the actual code.

Requirements

Obviously, JavaScript environment is much bigger than just Node.js, and you might need to fake timers in other environments, such as the browser. If you do, your environment has to fulfill several requirements:

  • implement at least ECMAScript 5 (ES6 is better)
  • have a basic implementation of promises (ES6 promises are sufficient or any basically compatible library - be careful though. Promises shall be implemented as microqueue tasks, or at least scheduled by global setImmediate - otherwise, zurvan has to be started (not loaded - started. timers have to be intercepted) before promise library in every module they both occur. Additionally, promise library alone cannot be required by any file evaluated before first one requiring zurvan) For compatibility options with specific libraries (e.g. bluebird), see: compatibility options
  • implement setImmediate/clearImmediate - they cannot be implemented as wrappers over setTimeout/clearTimeout, at least for now.
  • implement process.uptime and process.hrtime - if it doesn't, Zurvan has to be ran with compatibility option: ignoreProcessTimers: true

See configuration documentation to check out possible compatibility options (e.g. evaluating strings in setTimeout) Of course, if you have trouble with running Zurvan on your custom target, feel free to contact me for support Be careful about scheduling - some asynchronous features used by browsers (such as MutationObserver, postMessage, requestAnimationFrame) are not faked by zurvan. Since this is only a time-faking library, it fakes only time-based async actions. Additionally, Promise.resolve and Promise.reject are both specified to be executed asynchronously, but engine implementation is free to use either microqueue or macroqueue. Additionally, if it doesn't use API functions (setImmediate) for scheduling macroqueue tasks, then there will be cases where Zurvan won't behave correctly. Currently there are no such known cases for Node.js - and if they will be found, they are a bug and shall be fixed.

If you're trying to run on Node.js older than 0.10 - you will have trouble, as in these Nodes setImmediate was not implemented and process.nextTick was used to handle the macroqueue. However, process.nextTick is not a function faked by zurvan. Again - contact me if you need support (possibly via GitHub issues).

Notes

As of version 0.5.1, Zurvan is tested on all main node versions starting from 4.0. As for versions below 4.0, either use Zurvan 0.3.2 or try the new one - I'm doing my best not to make breaking changes, but some new features (such as infinite immediate loop detection) may not work in 0.10. They may also not work depending on Promise library used - they are only tested with Zurvan in vanilla Promise mode, and definitely do not work if Zurvan uses bluebird as its scheduler (application may use bluebird, that's not a problem). It will generally not work with any internal scheduler that relies on setImmediate to schedule promises. It's best not to mess with internal scheduler - it was added to satisfy need of node 0.10 and possibly some web browsers, but should not be used in newer node versions.

TypeScript

Experimental types for usage with TypeScript are provided with the package. They are not thoroughly tested, so there may be problems. Please report any trouble you encounter with them as an issue on GitHub.

Other

Zurvan is available as package on NPM.

Name is taken after babilonian deity of infinite time, Zurvan. For more details see: https://en.wikipedia.org/wiki/Zurvanism

If you encouter a bug when using Zurvan, please report it as an issue on GitHub. Of course, if you are willing to issue a pull request, it is welcome.

changelog

0.8.0:

  • experimental TypeScript types
  • updated versions of dependency libraries
  • enabled coverage again

0.5.2:

0.5.1:

  • update release - updated versions of dependency libraries, added codebeat code analysis 0.5.0:
  • added a configuration parameter - timerExpirationPolicy (#17) - to allow non-FIFO expiration of timers with same due time. Default value remains FIFO
  • configuration sanity is now verified (#25)
  • minor documentation fixes

0.4.1:

  • BREAKING CHANGE implemented #36 - withDefaultConfiguration called without parameters will reset configuration for new zurvan instance
  • BREAKING CHANGE fixed bug reported in sinonjs/lolex as #83 - require('timers') was not faked during timer interception
  • BREAKING CHANGE promises returned by queue management function are resolved or rejected with an array of failed callbacks
  • added a configuration parameter - rejectOnCallbackFailure - if any of callbacks passed to setTimeout/setImmediate/setInterval exited with an exception, time forwarding promise will be rejected
  • added a configuration parameter - fakeNodeDedicatedTimers - not to break the flow for the browsers
  • added a configuration parameter - denyTimersLongerThanInt32 - to throw if timer delay is longer than allowed in Node (2^31 - 1). #67 in lolex

0.4.0:

  • BREAKING CHANGE implemented #42 - sufficiently long chain of setImmediates will cause a rejection (configurable via maxSetImmediateBatchSize) - global state will be unknown after this exception
  • BREAKING CHANGE dropped support for Node below 4 - new releases will be tested on node >= 4
  • added a configuration parameter - requestedCyclesAroundSetImmediateQueue - in case you encoutered a weird scenario which is not handled by zurvan by default, you can increment it as a workaround (should work). Please, report such scenarios
  • added forcedReleaseTimers (#37) - note that it should not be used except for special cases (like after detection of infinite loop) and may leave global components in unknown state
  • new field in resolution value of releaseTimers - immediates, contains an object of not-executed immediates (should be always empty in normal releaseTimers, will contain dropped ones with invocation stack in forcedReleaseTimers)
  • updated development packages
  • removed vulnerabilities badge, zurvan should be used for tests mostly, so it's irrelevant
  • minor README fixes

0.3.2:

  • BREAKING CHANGE added workaround for most typical cases of integration with request-promise. This increases number of cycles around event loop done before queue is assumed empty
  • Added a new badge to readme (vulnerabilities)
  • Removed grunt-jshint dependency (left only JSHint)
  • Upgraded error messages - in case current time is equal to target, added info that queue is not yet cleared
  • updated used mocha to 3.0.0

0.3.1:

  • BREAKING CHANGE fixed a bug where UIDs called by setImmediate leaked between test cases
  • BREAKING CHANGE throwOnInvalidClearTimer configuration option applies to setImmediate/clearImmediate as well now
  • removed dependencies on grunt-madge and grunt-mocha-test

0.3.0:

  • BREAKING CHANGE default value for TimeUnit are now milliseconds, i.e., TimeUnit called without unit will be converted from milliseconds now. TimeUnit module exports a function now instead of an object. All previous fields are still there.
  • added support for node 0.10 with compatibility mode -> new option to set internal Promise scheduler - this means that zurvan will no longer throw on require if there is no Promise in environment (it will throw at intercept instead), but will use bluebird if available
  • added configuration option to set internal Promise scheduler (useful also for other versions of node). Please use with care and only with thoroughly tested Promise libraries.
  • if there is no global.Promise in the environment, zurvan will attempt to use bluebird. User is able to override it at interception, but bluebird scheduler may be already changed.
  • enhanced error messages
  • added some examples that are executed as tests
  • minor refactoring changes

0.2.4:

  • added environment compatibility check - if required functions are not available in environment, zurvan will throw proper error at require
  • error messages shall be visible now (in Error object/reject) in case of using zurvan that was not initialized properly

0.2.3:

  • BREAKING CHANGE expireAllTimeouts() and forwardTimeToNextTimer clear the queue even if there are no timeouts (previously it was resolved immediately)
  • BREAKING CHANGE releaseTimers() is now resolved with leftovers from execution
  • added option to intercept timers while ignoring Date function - it may cause problems in setups that require high compatibility, so now it can be at least suppressed
  • fixed a bug - zurvan does not crash anymore when called as argument of util.format
  • zurvan is running in strict mode now

0.2.2:

  • added option to throw when argument to clearTimeout/clearInterval is not a valid timer issued by proper set function
  • fixed a bug - when clearTimeout/clearInterval is called with undefined, it no longer crashes

0.2.1:

  • BREAKING CHANGE removed hack for modifying original setImmediate fields for bluebird compatibility. Now bluebird.setScheduler is used
  • fixed a bug - waitForEmptyQueue() waits now longer (hopefully until queue is cleared) previously there was a problem in scenario setImmediate(process.nextTick(setImmediate)))

0.2.0:

  • BREAKING CHANGE blockSystem does no longer return a Promise, and is a synchronous call instead
  • added option to ignore faking process timers (process.hrtime and process.uptime)
  • added option to throw on values < 1 passed to setTimeout / setInterval
  • added option for adding per-module configuration
  • added compatibility option to run smoothly with bluebird
  • documentation fixes
  • fixed bug: microqueue tasks in last timeout were not executed before resolving advanceTime promises

0.1.1:

  • readme fixes

0.1.0:

  • fixed critical bugs