Skip to content

Commit ea22d73

Browse files
authored
fix: use realTimers when using fakeTimers (#652)
A recent change to `runWithRealTimers` added a check for the `clock` attribute on `setTimeout` so that we can determine whether timers were faked with Jest's modern fake timers (which are based on `@sinonjs/fake-timers`). Unfortunately, this attribute can be present when users are not using Jest's fake timers and are using sinonjs. Also, when `useFakeTimers` are invoked without parameters in Jest 26, the legacy timers are used by default. This PR addresses two issues: 1. Don't use realTimers if the timers were not faked by Jest. 2. If we're using realTimers, make sure we're explicit when calling `useFakeTimers` so that the fakes are restored to their original state. Closes #612
1 parent 2c19f4f commit ea22d73

File tree

2 files changed

+46
-7
lines changed

2 files changed

+46
-7
lines changed

src/__tests__/helpers.js

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,18 +53,42 @@ describe('query container validation throws when validation fails', () => {
5353
})
5454
})
5555

56-
test('should always use realTimers before using callback', () => {
56+
test('should always use realTimers before using callback when timers are faked with useFakeTimers', () => {
5757
const originalSetTimeout = globalObj.setTimeout
5858

59+
// legacy timers use mocks and do not rely on a clock instance
5960
jest.useFakeTimers('legacy')
6061
runWithRealTimers(() => {
6162
expect(originalSetTimeout).toEqual(globalObj.setTimeout)
6263
})
64+
expect(globalObj.setTimeout._isMockFunction).toBe(true);
65+
expect(globalObj.setTimeout.clock).toBeUndefined();
6366

6467
jest.useRealTimers()
6568

69+
// modern timers use a clock instance instead of a mock
6670
jest.useFakeTimers('modern')
6771
runWithRealTimers(() => {
6872
expect(originalSetTimeout).toEqual(globalObj.setTimeout)
6973
})
74+
expect(globalObj.setTimeout._isMockFunction).toBeUndefined();
75+
expect(globalObj.setTimeout.clock).toBeDefined();
7076
})
77+
78+
test('should not use realTimers when timers are not faked with useFakeTimers', () => {
79+
const originalSetTimeout = globalObj.setTimeout;
80+
81+
// useFakeTimers is not used, timers are faked in some other way
82+
const fakedSetTimeout = (callback) => {
83+
callback();
84+
};
85+
fakedSetTimeout.clock = jest.fn();
86+
87+
globalObj.setTimeout = fakedSetTimeout;
88+
89+
runWithRealTimers(() => {
90+
expect(fakedSetTimeout).toEqual(globalObj.setTimeout)
91+
})
92+
93+
globalObj.setTimeout = originalSetTimeout;
94+
});

src/helpers.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,26 @@ const globalObj = typeof window === 'undefined' ? global : window
22

33
// Currently this fn only supports jest timers, but it could support other test runners in the future.
44
function runWithRealTimers(callback) {
5+
const usingJestAndTimers = typeof jest !== 'undefined' && typeof globalObj.setTimeout !== 'undefined';
6+
const usingLegacyJestFakeTimers = usingJestAndTimers && typeof globalObj.setTimeout._isMockFunction !== 'undefined' && globalObj.setTimeout._isMockFunction;
7+
8+
let usingModernJestFakeTimers = false;
9+
if (
10+
usingJestAndTimers &&
11+
typeof globalObj.setTimeout.clock !== "undefined" &&
12+
typeof jest.getRealSystemTime !== "undefined"
13+
) {
14+
try {
15+
// jest.getRealSystemTime is only supported for Jest's `modern` fake timers and otherwise throws
16+
jest.getRealSystemTime();
17+
usingModernJestFakeTimers = true;
18+
} catch {
19+
// not using Jest's modern fake timers
20+
}
21+
}
22+
523
const usingJestFakeTimers =
6-
globalObj.setTimeout &&
7-
(globalObj.setTimeout._isMockFunction ||
8-
typeof globalObj.setTimeout.clock !== 'undefined') &&
9-
typeof jest !== 'undefined'
24+
usingLegacyJestFakeTimers || usingModernJestFakeTimers;
1025

1126
if (usingJestFakeTimers) {
1227
jest.useRealTimers()
@@ -15,7 +30,7 @@ function runWithRealTimers(callback) {
1530
const callbackReturnValue = callback()
1631

1732
if (usingJestFakeTimers) {
18-
jest.useFakeTimers()
33+
jest.useFakeTimers(usingModernJestFakeTimers ? 'modern' : 'legacy')
1934
}
2035

2136
return callbackReturnValue
@@ -36,7 +51,7 @@ function getTimeFunctions() {
3651
}
3752
}
3853

39-
const {clearTimeoutFn, setImmediateFn, setTimeoutFn} = runWithRealTimers(
54+
const { clearTimeoutFn, setImmediateFn, setTimeoutFn } = runWithRealTimers(
4055
getTimeFunctions,
4156
)
4257

0 commit comments

Comments
 (0)