Skip to content

Commit 111b1c9

Browse files
fix: modern jest timers (#397)
* fix: modern jest timers * treat modern timers as default, but be explicit in tests * we're node-only * rename to Thenable * add clarifications; unify test names * Update src/__tests__/auto-cleanup-skip.test.js Co-authored-by: Maciej Jastrzebski <mdjastrzebski@gmail.com> Co-authored-by: Maciej Jastrzebski <mdjastrzebski@gmail.com>
1 parent 859cb82 commit 111b1c9

5 files changed

+72
-11
lines changed

src/__tests__/auto-cleanup-skip.js renamed to src/__tests__/auto-cleanup-skip.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import { View } from 'react-native';
44
let render;
55
beforeAll(() => {
66
process.env.RNTL_SKIP_AUTO_CLEANUP = 'true';
7-
const rtl = require('../');
8-
render = rtl.render;
7+
const rntl = require('..');
8+
render = rntl.render;
99
});
1010

1111
let isMounted = false;

src/__tests__/auto-cleanup.js renamed to src/__tests__/auto-cleanup.test.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,31 @@ class Test extends React.Component<*> {
2020
}
2121
}
2222

23+
afterEach(() => {
24+
jest.useRealTimers();
25+
});
26+
2327
// This just verifies that by importing RNTL in an environment which supports afterEach (like jest)
2428
// we'll get automatic cleanup between tests.
2529
test('component is mounted, but not umounted before test ends', () => {
2630
const fn = jest.fn();
2731
render(<Test onUnmount={fn} />);
32+
expect(isMounted).toEqual(true);
2833
expect(fn).not.toHaveBeenCalled();
2934
});
3035

3136
test('component is automatically umounted after first test ends', () => {
3237
expect(isMounted).toEqual(false);
3338
});
39+
40+
test('does not time out with legacy fake timers', () => {
41+
jest.useFakeTimers('legacy');
42+
render(<Test />);
43+
expect(isMounted).toEqual(true);
44+
});
45+
46+
test('does not time out with fake timers', () => {
47+
jest.useFakeTimers('modern');
48+
render(<Test />);
49+
expect(isMounted).toEqual(true);
50+
});

src/__tests__/waitFor.test.js

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ class BananaContainer extends React.Component<{}, any> {
3535
}
3636
}
3737

38+
afterEach(() => {
39+
jest.useRealTimers();
40+
});
41+
3842
test('waits for element until it stops throwing', async () => {
3943
const { getByText, queryByText } = render(<BananaContainer />);
4044

@@ -75,8 +79,8 @@ test('waits for element with custom interval', async () => {
7579
expect(mockFn).toHaveBeenCalledTimes(3);
7680
});
7781

78-
test('works with fake timers', async () => {
79-
jest.useFakeTimers();
82+
test('works with legacy fake timers', async () => {
83+
jest.useFakeTimers('legacy');
8084

8185
const mockFn = jest.fn(() => {
8286
throw Error('test');
@@ -87,9 +91,24 @@ test('works with fake timers', async () => {
8791
} catch (e) {
8892
// suppress
8993
}
90-
jest.runTimersToTime(400);
94+
jest.advanceTimersByTime(400);
9195

9296
expect(mockFn).toHaveBeenCalledTimes(3);
97+
});
9398

94-
jest.useRealTimers();
99+
test('works with fake timers', async () => {
100+
jest.useFakeTimers('modern');
101+
102+
const mockFn = jest.fn(() => {
103+
throw Error('test');
104+
});
105+
106+
try {
107+
waitFor(() => mockFn(), { timeout: 400, interval: 200 });
108+
} catch (e) {
109+
// suppress
110+
}
111+
jest.advanceTimersByTime(400);
112+
113+
expect(mockFn).toHaveBeenCalledTimes(3);
95114
});

src/__tests__/waitForElementToBeRemoved.test.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ const TestSetup = ({ shouldUseDelay = true }) => {
2525
);
2626
};
2727

28+
afterEach(() => {
29+
jest.useRealTimers();
30+
});
31+
2832
test('waits when using getBy query', async () => {
2933
const screen = render(<TestSetup />);
3034

@@ -126,8 +130,22 @@ test('waits with custom interval', async () => {
126130
expect(mockFn).toHaveBeenCalledTimes(4);
127131
});
128132

133+
test('works with legacy fake timers', async () => {
134+
jest.useFakeTimers('legacy');
135+
136+
const mockFn = jest.fn(() => <View />);
137+
138+
waitForElementToBeRemoved(() => mockFn(), {
139+
timeout: 400,
140+
interval: 200,
141+
});
142+
143+
jest.advanceTimersByTime(400);
144+
expect(mockFn).toHaveBeenCalledTimes(4);
145+
});
146+
129147
test('works with fake timers', async () => {
130-
jest.useFakeTimers();
148+
jest.useFakeTimers('modern');
131149

132150
const mockFn = jest.fn(() => <View />);
133151

src/flushMicroTasks.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
// @flow
2-
32
import { printDeprecationWarning } from './helpers/errors';
43

4+
type Thenable<T> = { then: (() => T) => mixed };
5+
56
/**
67
* Wait for microtasks queue to flush
78
*/
8-
export default function flushMicrotasksQueue(): Promise<any> {
9+
export default function flushMicrotasksQueue<T>(): Thenable<T> {
910
printDeprecationWarning('flushMicrotasksQueue');
1011
return flushMicroTasks();
1112
}
1213

13-
export function flushMicroTasks(): Promise<any> {
14-
return new Promise((resolve) => setImmediate(resolve));
14+
export function flushMicroTasks<T>(): Thenable<T> {
15+
return {
16+
// using "thenable" instead of a Promise, because otherwise it breaks when
17+
// using "modern" fake timers
18+
then(resolve) {
19+
setImmediate(resolve);
20+
},
21+
};
1522
}

0 commit comments

Comments
 (0)