Skip to content

Commit

Permalink
fix: fake all supported timers by default (#323)
Browse files Browse the repository at this point in the history
* fix: fake all supported timers by default

* re-add test

* re-write test

* call out promises

* doc: added note about ignoreMissingTimers from #491

---------

Co-authored-by: Carl-Erik Kopseng <[email protected]>
  • Loading branch information
SimenB and fatso83 authored Aug 25, 2024
1 parent 01c2cb1 commit 5a4532a
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 43 deletions.
81 changes: 57 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,27 @@
[![codecov](https://codecov.io/gh/sinonjs/fake-timers/branch/main/graph/badge.svg)](https://codecov.io/gh/sinonjs/fake-timers)
<a href="CODE_OF_CONDUCT.md"><img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Contributor Covenant" /></a>

JavaScript implementation of the timer APIs; `setTimeout`, `clearTimeout`, `setImmediate`, `clearImmediate`, `setInterval`, `clearInterval`, `requestAnimationFrame`, `cancelAnimationFrame`, `requestIdleCallback`, and `cancelIdleCallback`, along with a clock instance that controls the flow of time. FakeTimers also provides a `Date` implementation that gets its time from the clock.
JavaScript implementation of the timer
APIs; `setTimeout`, `clearTimeout`, `setImmediate`, `clearImmediate`, `setInterval`, `clearInterval`, `requestAnimationFrame`, `cancelAnimationFrame`, `requestIdleCallback`,
and `cancelIdleCallback`, along with a clock instance that controls the flow of time. FakeTimers also provides a `Date`
implementation that gets its time from the clock.

In addition in browser environment `@sinonjs/fake-timers` provides a `performance` implementation that gets its time from the clock. In Node environments FakeTimers provides a `nextTick` implementation that is synchronized with the clock - and a `process.hrtime` shim that works with the clock.
In addition in browser environment `@sinonjs/fake-timers` provides a `performance` implementation that gets its time
from the clock. In Node environments FakeTimers provides a `nextTick` implementation that is synchronized with the
clock - and a `process.hrtime` shim that works with the clock.

`@sinonjs/fake-timers` can be used to simulate passing time in automated tests and other
situations where you want the scheduling semantics, but don't want to actually
wait.

`@sinonjs/fake-timers` is extracted from [Sinon.JS](https://github.com/sinonjs/sinon.js) and targets the [same runtimes](https://sinonjs.org/releases/latest/#supported-runtimes).
`@sinonjs/fake-timers` is extracted from [Sinon.JS](https://github.com/sinonjs/sinon.js) and targets
the [same runtimes](https://sinonjs.org/releases/latest/#supported-runtimes).

## Autocomplete, IntelliSense and TypeScript definitions

Version 7 introduced JSDoc to the codebase. This should provide autocomplete and type suggestions in supporting IDEs. If you need more elaborate type support, TypeScript definitions for the Sinon projects are independently maintained by the Definitely Types community:
Version 7 introduced JSDoc to the codebase. This should provide autocomplete and type suggestions in supporting IDEs. If
you need more elaborate type support, TypeScript definitions for the Sinon projects are independently maintained by the
Definitely Types community:

```
npm install -D @types/sinonjs__fake-timers
Expand All @@ -29,7 +37,8 @@ npm install -D @types/sinonjs__fake-timers
npm install @sinonjs/fake-timers
```

If you want to use `@sinonjs/fake-timers` in a browser you can either build your own bundle or use [Skypack](https://www.skypack.dev).
If you want to use `@sinonjs/fake-timers` in a browser you can either build your own bundle or
use [Skypack](https://www.skypack.dev).

## Usage

Expand All @@ -54,7 +63,8 @@ clock.tick(15);

Upon executing the last line, an interesting fact about the
[Poblano](https://en.wikipedia.org/wiki/Poblano) will be printed synchronously to
the screen. If you want to simulate asynchronous behavior, please see the `async` function variants (eg `clock.tick(time)` vs `await clock.tickAsync(time)`).
the screen. If you want to simulate asynchronous behavior, please see the `async` function variants (
eg `clock.tick(time)` vs `await clock.tickAsync(time)`).

The `next`, `runAll`, `runToFrame`, and `runToLast` methods are available to advance the clock. See the
API Reference for more details.
Expand All @@ -67,7 +77,9 @@ clock instance, not the browser's internals.

Calling `install` with no arguments achieves this. You can call `uninstall`
later to restore things as they were again.
Note that in NodeJS the [timers](https://nodejs.org/api/timers.html) and [timers/promises](https://nodejs.org/api/timers.html#timers-promises-api) modules will also receive fake timers when using global scope.
Note that in NodeJS the [timers](https://nodejs.org/api/timers.html)
and [timers/promises](https://nodejs.org/api/timers.html#timers-promises-api) modules will also receive fake timers when
using global scope.

```js
// In the browser distribution, a global `FakeTimers` is already available
Expand Down Expand Up @@ -143,22 +155,26 @@ Creates a clock. The default

The `now` argument may be a number (in milliseconds) or a Date object.

The `loopLimit` argument sets the maximum number of timers that will be run when calling `runAll()` before assuming that we have an infinite loop and throwing an error. The default is `1000`.
The `loopLimit` argument sets the maximum number of timers that will be run when calling `runAll()` before assuming that
we have an infinite loop and throwing an error. The default is `1000`.

### `var clock = FakeTimers.install([config])`

Installs FakeTimers using the specified config (otherwise with epoch `0` on the global scope).
Note that in NodeJS the [timers](https://nodejs.org/api/timers.html) and [timers/promises](https://nodejs.org/api/timers.html#timers-promises-api) modules will also receive fake timers when using global scope.
Note that in NodeJS the [timers](https://nodejs.org/api/timers.html)
and [timers/promises](https://nodejs.org/api/timers.html#timers-promises-api) modules will also receive fake timers when
using global scope.
The following configuration options are available

| Parameter | Type | Default | Description |
| -------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `config.now` | Number/Date | 0 | installs FakeTimers with the specified unix epoch |
| `config.toFake` | String[] | ["setTimeout", "clearTimeout", "setImmediate", "clearImmediate","setInterval", "clearInterval", "Date", "requestAnimationFrame", "cancelAnimationFrame", "requestIdleCallback", "cancelIdleCallback", "hrtime", "performance"] | an array with explicit function names (or objects, in the case of "performance") to hijack. _When not set, FakeTimers will automatically fake all methods **except** `nextTick`_ e.g., `FakeTimers.install({ toFake: ["setTimeout","nextTick"]})` will fake only `setTimeout` and `nextTick` |
| `config.loopLimit` | Number | 1000 | the maximum number of timers that will be run when calling runAll() |
| `config.shouldAdvanceTime` | Boolean | false | tells FakeTimers to increment mocked time automatically based on the real system time shift (e.g. the mocked time will be incremented by 20ms for every 20ms change in the real system time) |
| `config.advanceTimeDelta` | Number | 20 | relevant only when using with `shouldAdvanceTime: true`. increment mocked time by `advanceTimeDelta` ms every `advanceTimeDelta` ms change in the real system time. |
| `config.shouldClearNativeTimers` | Boolean | false | tells FakeTimers to clear 'native' (i.e. not fake) timers by delegating to their respective handlers. These are not cleared by default, leading to potentially unexpected behavior if timers existed prior to installing FakeTimers. |
| Parameter | Type | Default | Description |
| -------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `config.now` | Number/Date | 0 | installs FakeTimers with the specified unix epoch |
| `config.toFake` | String[] | ["setTimeout", "clearTimeout", "setImmediate", "clearImmediate","setInterval", "clearInterval", "Date", "requestAnimationFrame", "cancelAnimationFrame", "requestIdleCallback", "cancelIdleCallback", "hrtime", "performance"] | an array with explicit function names (or objects, in the case of "performance") to hijack. \_When not set, FakeTimers will automatically fake all methods e.g., `FakeTimers.install({ toFake: ["setTimeout","nextTick"]})` will fake only `setTimeout` and `nextTick` |
| `config.loopLimit` | Number | 1000 | the maximum number of timers that will be run when calling runAll() |
| `config.shouldAdvanceTime` | Boolean | false | tells FakeTimers to increment mocked time automatically based on the real system time shift (e.g. the mocked time will be incremented by 20ms for every 20ms change in the real system time) |
| `config.advanceTimeDelta` | Number | 20 | relevant only when using with `shouldAdvanceTime: true`. increment mocked time by `advanceTimeDelta` ms every `advanceTimeDelta` ms change in the real system time. |
| `config.shouldClearNativeTimers` | Boolean | false | tells FakeTimers to clear 'native' (i.e. not fake) timers by delegating to their respective handlers. These are not cleared by default, leading to potentially unexpected behavior if timers existed prior to installing FakeTimers. |
| `config.ignoreMissingTimers` | Boolean | false | tells FakeTimers to ignore missing timers that might not exist in the given environment |

### `var id = clock.setTimeout(callback, timeout)`

Expand Down Expand Up @@ -218,7 +234,9 @@ Cancels the callback scheduled by the provided id.

### `clock.requestIdleCallback(callback[, timeout])`

Queued the callback to be fired during idle periods to perform background and low priority work on the main event loop. Callbacks which have a timeout option will be fired no later than time in milliseconds. Returns an `id` which can be used to cancel the callback.
Queued the callback to be fired during idle periods to perform background and low priority work on the main event loop.
Callbacks which have a timeout option will be fired no later than time in milliseconds. Returns an `id` which can be
used to cancel the callback.

### `clock.cancelIdleCallback(id)`

Expand Down Expand Up @@ -263,7 +281,8 @@ callbacks to execute _before_ running the timers.
Advance the clock by jumping forward in time, firing callbacks at most once.
`time` takes the same formats as [`clock.tick`](#clockticktime--await-clocktickasynctime).

This can be used to simulate the JS engine (such as a browser) being put to sleep and resumed later, skipping intermediary timers.
This can be used to simulate the JS engine (such as a browser) being put to sleep and resumed later, skipping
intermediary timers.

### `clock.reset()`

Expand All @@ -273,9 +292,11 @@ Useful to reset the state of the clock without having to `uninstall` and `instal

### `clock.runAll()` / `await clock.runAllAsync()`

This runs all pending timers until there are none remaining. If new timers are added while it is executing they will be run as well.
This runs all pending timers until there are none remaining. If new timers are added while it is executing they will be
run as well.

This makes it easier to run asynchronous tests to completion without worrying about the number of timers they use, or the delays in those timers.
This makes it easier to run asynchronous tests to completion without worrying about the number of timers they use, or
the delays in those timers.

It runs a maximum of `loopLimit` times after which it assumes there is an infinite loop of timers and throws an error.

Expand All @@ -284,7 +305,8 @@ callbacks to execute _before_ running the timers.

### `clock.runMicrotasks()`

This runs all pending microtasks scheduled with `nextTick` but none of the timers and is mostly useful for libraries using FakeTimers underneath and for running `nextTick` items without any timers.
This runs all pending microtasks scheduled with `nextTick` but none of the timers and is mostly useful for libraries
using FakeTimers underneath and for running `nextTick` items without any timers.

### `clock.runToFrame()`

Expand Down Expand Up @@ -323,11 +345,22 @@ Implements the `Date` object but using the clock to provide the correct time.

### `Performance`

Implements the `now` method of the [`Performance`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now) object but using the clock to provide the correct time. Only available in environments that support the Performance object (browsers mostly).
Implements the `now` method of the [`Performance`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/now)
object but using the clock to provide the correct time. Only available in environments that support the Performance
object (browsers mostly).

### `FakeTimers.withGlobal`

In order to support creating clocks based on separate or sandboxed environments (such as JSDOM), FakeTimers exports a factory method which takes single argument `global`, which it inspects to figure out what to mock and what features to support. When invoking this function with a global, you will get back an object with `timers`, `createClock` and `install` - same as the regular FakeTimers exports only based on the passed in global instead of the global environment.
In order to support creating clocks based on separate or sandboxed environments (such as JSDOM), FakeTimers exports a
factory method which takes single argument `global`, which it inspects to figure out what to mock and what features to
support. When invoking this function with a global, you will get back an object with `timers`, `createClock`
and `install` - same as the regular FakeTimers exports only based on the passed in global instead of the global
environment.

## Promises and fake time

If you use a Promise library like Bluebird, note that you should either call `clock.runMicrotasks()` or make sure to
_not_ mock `nextTick`.

## Running tests

Expand Down
5 changes: 1 addition & 4 deletions src/fake-timers-src.js
Original file line number Diff line number Diff line change
Expand Up @@ -1776,10 +1776,7 @@ function withGlobal(_global) {
clock.methods = config.toFake || [];

if (clock.methods.length === 0) {
// do not fake nextTick by default - GitHub#126
clock.methods = Object.keys(timers).filter(function (key) {
return key !== "nextTick" && key !== "queueMicrotask";
});
clock.methods = Object.keys(timers);
}

if (config.shouldAdvanceTime === true) {
Expand Down
Loading

0 comments on commit 5a4532a

Please sign in to comment.