Skip to content

Commit

Permalink
feat(jest-util): use lolex as implementation for fake timers
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed Apr 18, 2019
1 parent 5a66bbb commit 61d7ac2
Show file tree
Hide file tree
Showing 23 changed files with 291 additions and 1,184 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ We skipped 24.2.0 because a draft was accidentally published. Please use `24.3.0
- `[jest-jasmine2]` Will now only execute at most 5 concurrent tests _within the same testsuite_ when using `test.concurrent` ([#7770](https://github.com/facebook/jest/pull/7770))
- `[jest-circus]` Same as `[jest-jasmine2]`, only 5 tests will run concurrently by default ([#7770](https://github.com/facebook/jest/pull/7770))
- `[jest-config]` A new `maxConcurrency` option allows to change the number of tests allowed to run concurrently ([#7770](https://github.com/facebook/jest/pull/7770))
- `[jest-util]`[**BREAKING**] Replace Jest's fake timers implementation with Lolex ([#5171](https://github.com/facebook/jest/pull/5171))

### Fixes

Expand Down
12 changes: 8 additions & 4 deletions docs/JestObjectAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -527,10 +527,6 @@ When this API is called, all pending macro-tasks and micro-tasks will be execute

This is often useful for synchronously executing setTimeouts during a test in order to synchronously assert about some behavior that would only happen after the `setTimeout()` or `setInterval()` callbacks executed. See the [Timer mocks](TimerMocks.md) doc for more information.

### `jest.runAllImmediates()`

Exhausts all tasks queued by `setImmediate()`.

### `jest.advanceTimersByTime(msToRun)`

##### renamed in Jest **22.0.0+**
Expand All @@ -557,6 +553,14 @@ This means, if any timers have been scheduled (but have not yet executed), they

Returns the number of fake timers still left to run.

### `.jest.setSystemTime()`

Set the current system time used by fake timers. Simulates a user changing the system clock while your program is running. It affects the current time but it does not in itself cause e.g. timers to fire; they will fire exactly as they would have done without the call to `jest.setSystemTime()`.

### `.jest.getRealSystemTime()`

When mocking time, `Date.now()` will also be mocked. If you for some reason need access to the real current time, you can invoke this function.

## Misc

### `jest.setTimeout(timeout)`
Expand Down
9 changes: 2 additions & 7 deletions e2e/__tests__/fakePromises.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,8 @@
import runJest from '../runJest';

describe('Fake promises', () => {
it('should be possible to resolve with fake timers using immediates', () => {
const result = runJest('fake-promises/immediate');
expect(result.status).toBe(0);
});

it('should be possible to resolve with fake timers using asap', () => {
const result = runJest('fake-promises/asap');
it('should be possible to resolve with fake timers', () => {
const result = runJest('fake-promises');
expect(result.status).toBe(0);
});
});
File renamed without changes.
19 changes: 0 additions & 19 deletions e2e/fake-promises/immediate/__tests__/generator.test.js

This file was deleted.

10 changes: 0 additions & 10 deletions e2e/fake-promises/immediate/fake-promises.js

This file was deleted.

9 changes: 0 additions & 9 deletions e2e/fake-promises/immediate/package.json

This file was deleted.

File renamed without changes.
12 changes: 6 additions & 6 deletions e2e/timer-reset-mocks/after-reset-all-mocks/timerAndMock.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ describe('timers', () => {
it('should work before calling resetAllMocks', () => {
jest.useFakeTimers();
const f = jest.fn();
setImmediate(() => f());
jest.runAllImmediates();
expect(f.mock.calls.length).toBe(1);
setTimeout(f, 0);
jest.runAllTimers();
expect(f).toHaveBeenCalledTimes(1);
});

it('should not break after calling resetAllMocks', () => {
jest.resetAllMocks();
jest.useFakeTimers();
const f = jest.fn();
setImmediate(() => f());
jest.runAllImmediates();
expect(f.mock.calls.length).toBe(1);
setTimeout(f, 0);
jest.runAllTimers();
expect(f).toHaveBeenCalledTimes(1);
});
});
6 changes: 3 additions & 3 deletions e2e/timer-reset-mocks/with-reset-mocks/timerWithMock.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ describe('timers', () => {
it('should work before calling resetAllMocks', () => {
const f = jest.fn();
jest.useFakeTimers();
setImmediate(() => f());
jest.runAllImmediates();
expect(f.mock.calls.length).toBe(1);
setTimeout(f, 0);
jest.runAllTimers();
expect(f).toHaveBeenCalledTimes(1);
});
});
9 changes: 5 additions & 4 deletions examples/timer/__tests__/infinite_timer_game.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@
jest.useFakeTimers();

it('schedules a 10-second timer after 1 second', () => {
jest.spyOn(global, 'setTimeout');
const infiniteTimerGame = require('../infiniteTimerGame');
const callback = jest.fn();

infiniteTimerGame(callback);

// At this point in time, there should have been a single call to
// setTimeout to schedule the end of the game in 1 second.
expect(setTimeout.mock.calls.length).toBe(1);
expect(setTimeout.mock.calls[0][1]).toBe(1000);
expect(setTimeout).toBeCalledTimes(1);
expect(setTimeout).toHaveBeenNthCalledWith(1, expect.any(Function), 1000);

// Fast forward and exhaust only currently pending timers
// (but not any new timers that get created during that process)
Expand All @@ -24,6 +25,6 @@ it('schedules a 10-second timer after 1 second', () => {

// And it should have created a new timer to start the game over in
// 10 seconds
expect(setTimeout.mock.calls.length).toBe(2);
expect(setTimeout.mock.calls[1][1]).toBe(10000);
expect(setTimeout).toBeCalledTimes(2);
expect(setTimeout).toHaveBeenNthCalledWith(2, expect.any(Function), 10000);
});
11 changes: 7 additions & 4 deletions examples/timer/__tests__/timer_game.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@
jest.useFakeTimers();

describe('timerGame', () => {
beforeEach(() => {
jest.spyOn(global, 'setTimeout');
});
it('waits 1 second before ending the game', () => {
const timerGame = require('../timerGame');
timerGame();

expect(setTimeout.mock.calls.length).toBe(1);
expect(setTimeout.mock.calls[0][1]).toBe(1000);
expect(setTimeout).toBeCalledTimes(1);
expect(setTimeout).toBeCalledWith(expect.any(Function), 1000);
});

it('calls the callback after 1 second via runAllTimers', () => {
Expand All @@ -27,7 +30,7 @@ describe('timerGame', () => {

// Now our callback should have been called!
expect(callback).toBeCalled();
expect(callback.mock.calls.length).toBe(1);
expect(callback).toBeCalledTimes(1);
});

it('calls the callback after 1 second via advanceTimersByTime', () => {
Expand All @@ -44,6 +47,6 @@ describe('timerGame', () => {

// Now our callback should have been called!
expect(callback).toBeCalled();
expect(callback.mock.calls.length).toBe(1);
expect(callback).toBeCalledTimes(1);
});
});
14 changes: 2 additions & 12 deletions packages/jest-environment-jsdom/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function isWin(globals: Win | Global.Global): globals is Win {

class JSDOMEnvironment implements JestEnvironment {
dom: JSDOM | null;
fakeTimers: FakeTimers<number> | null;
fakeTimers: FakeTimers | null;
// @ts-ignore
global: Global.Global | Win | null;
errorEventListener: ((event: Event & {error: Error}) => void) | null;
Expand Down Expand Up @@ -87,17 +87,7 @@ class JSDOMEnvironment implements JestEnvironment {

this.moduleMocker = new mock.ModuleMocker(global as any);

const timerConfig = {
idToRef: (id: number) => id,
refToId: (ref: number) => ref,
};

this.fakeTimers = new FakeTimers({
config,
global: global as any,
moduleMocker: this.moduleMocker,
timerConfig,
});
this.fakeTimers = new FakeTimers({config, global: global as any});
}

setup() {
Expand Down
33 changes: 2 additions & 31 deletions packages/jest-environment-node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,9 @@ import {installCommonGlobals} from 'jest-util';
import {JestFakeTimers as FakeTimers} from '@jest/fake-timers';
import {JestEnvironment} from '@jest/environment';

type Timer = {
id: number;
ref: () => Timer;
unref: () => Timer;
};

class NodeEnvironment implements JestEnvironment {
context: Context | null;
fakeTimers: FakeTimers<Timer> | null;
fakeTimers: FakeTimers | null;
global: Global.Global;
moduleMocker: ModuleMocker | null;

Expand Down Expand Up @@ -54,30 +48,7 @@ class NodeEnvironment implements JestEnvironment {
installCommonGlobals(global, config.globals);
this.moduleMocker = new ModuleMocker(global);

const timerIdToRef = (id: number) => ({
id,
ref() {
return this;
},
unref() {
return this;
},
});

const timerRefToId = (timer: Timer): number | undefined =>
(timer && timer.id) || undefined;

const timerConfig = {
idToRef: timerIdToRef,
refToId: timerRefToId,
};

this.fakeTimers = new FakeTimers({
config,
global,
moduleMocker: this.moduleMocker,
timerConfig,
});
this.fakeTimers = new FakeTimers({config, global});
}

setup() {
Expand Down
9 changes: 4 additions & 5 deletions packages/jest-environment/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type ModuleWrapper = (...args: Array<unknown>) => unknown;
export declare class JestEnvironment {
constructor(config: Config.ProjectConfig, context?: EnvironmentContext);
global: Global.Global;
fakeTimers: FakeTimers<unknown> | null;
fakeTimers: FakeTimers | null;
moduleMocker: ModuleMocker | null;
runScript(
script: Script,
Expand Down Expand Up @@ -164,10 +164,6 @@ export interface Jest {
* retries is exhausted. This only works with `jest-circus`!
*/
retryTimes(numRetries: number): Jest;
/**
* Exhausts tasks queued by setImmediate().
*/
runAllImmediates(): void;
/**
* Exhausts the micro-task queue (usually interfaced in node via
* process.nextTick).
Expand Down Expand Up @@ -248,4 +244,7 @@ export interface Jest {
* every test so that local module state doesn't conflict between tests.
*/
isolateModules(fn: () => void): Jest;

getRealSystemTime(): number;
setSystemTime(now?: number): void;
}
6 changes: 5 additions & 1 deletion packages/jest-fake-timers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
"dependencies": {
"@jest/types": "^24.7.0",
"jest-message-util": "^24.7.1",
"jest-mock": "^24.7.0"
"jest-mock": "^24.7.0",
"lolex": "^4.0.0"
},
"devDependencies": {
"@types/lolex": "^3.1.1"
},
"engines": {
"node": ">= 6"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`FakeTimers runAllTimers warns when trying to advance timers while real timers are used 1`] = `
"A function to advance timers was called but the timers API is not mocked with fake timers. Call \`jest.useFakeTimers()\` in this test or enable fake timers globally by setting \`\\"timers\\": \\"fake\\"\` in the configuration file. This warning is likely a result of a default configuration change in Jest 15.
Release Blog Post: https://jestjs.io/blog/2016/09/01/jest-15.html"
`;
exports[`FakeTimers runAllTimers warns when trying to advance timers while real timers are used 1`] = `"A function to advance timers was called but the timers API is not mocked with fake timers. Call \`jest.useFakeTimers()\` in this test or enable fake timers globally by setting \`\\"timers\\": \\"fake\\"\` in the configuration file"`;
Loading

0 comments on commit 61d7ac2

Please sign in to comment.