From 3dae1ec455be144045f60ba909a7b892d5647922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Mon, 22 Jun 2020 10:50:22 +0200 Subject: [PATCH 1/6] fix: modern jest timers --- src/__tests__/auto-cleanup.js | 13 ++++++++++ src/__tests__/waitFor.test.js | 23 +++++++++++++++-- .../waitForElementToBeRemoved.test.js | 18 +++++++++++++ src/flushMicroTasks.js | 25 ++++++++++++++++--- 4 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/__tests__/auto-cleanup.js b/src/__tests__/auto-cleanup.js index 5a26d7c8f..405c588ae 100644 --- a/src/__tests__/auto-cleanup.js +++ b/src/__tests__/auto-cleanup.js @@ -25,9 +25,22 @@ class Test extends React.Component<*> { test('component is mounted, but not umounted before test ends', () => { const fn = jest.fn(); render(); + expect(isMounted).toEqual(true); expect(fn).not.toHaveBeenCalled(); }); test('component is automatically umounted after first test ends', () => { expect(isMounted).toEqual(false); }); + +test('does not time out with fake timers', () => { + jest.useFakeTimers(); + render(); + expect(isMounted).toEqual(true); +}); + +test('does not time out with modern fake timers', () => { + jest.useFakeTimers('modern'); + render(); + expect(isMounted).toEqual(true); +}); diff --git a/src/__tests__/waitFor.test.js b/src/__tests__/waitFor.test.js index e38f05c44..5f6496e28 100644 --- a/src/__tests__/waitFor.test.js +++ b/src/__tests__/waitFor.test.js @@ -35,6 +35,10 @@ class BananaContainer extends React.Component<{}, any> { } } +afterEach(() => { + jest.useRealTimers(); +}); + test('waits for element until it stops throwing', async () => { const { getByText, queryByText } = render(); @@ -87,9 +91,24 @@ test('works with fake timers', async () => { } catch (e) { // suppress } - jest.runTimersToTime(400); + jest.advanceTimersByTime(400); expect(mockFn).toHaveBeenCalledTimes(3); +}); - jest.useRealTimers(); +test('works with modern fake timers', async () => { + jest.useFakeTimers('modern'); + + const mockFn = jest.fn(() => { + throw Error('test'); + }); + + try { + waitFor(() => mockFn(), { timeout: 400, interval: 200 }); + } catch (e) { + // suppress + } + jest.advanceTimersByTime(400); + + expect(mockFn).toHaveBeenCalledTimes(3); }); diff --git a/src/__tests__/waitForElementToBeRemoved.test.js b/src/__tests__/waitForElementToBeRemoved.test.js index d2fc12091..7ea08b69a 100644 --- a/src/__tests__/waitForElementToBeRemoved.test.js +++ b/src/__tests__/waitForElementToBeRemoved.test.js @@ -25,6 +25,10 @@ const TestSetup = ({ shouldUseDelay = true }) => { ); }; +afterEach(() => { + jest.useRealTimers(); +}); + test('waits when using getBy query', async () => { const screen = render(); @@ -139,3 +143,17 @@ test('works with fake timers', async () => { jest.advanceTimersByTime(400); expect(mockFn).toHaveBeenCalledTimes(4); }); + +test('works with modern fake timers', async () => { + jest.useFakeTimers('modern'); + + const mockFn = jest.fn(() => ); + + waitForElementToBeRemoved(() => mockFn(), { + timeout: 400, + interval: 200, + }); + + jest.advanceTimersByTime(400); + expect(mockFn).toHaveBeenCalledTimes(4); +}); diff --git a/src/flushMicroTasks.js b/src/flushMicroTasks.js index c4bb67903..e6359d808 100644 --- a/src/flushMicroTasks.js +++ b/src/flushMicroTasks.js @@ -2,14 +2,33 @@ import { printDeprecationWarning } from './helpers/errors'; +type SimpleThenable = { then: (() => mixed) => mixed }; + /** * Wait for microtasks queue to flush */ -export default function flushMicrotasksQueue(): Promise { +export default function flushMicrotasksQueue(): SimpleThenable { printDeprecationWarning('flushMicrotasksQueue'); return flushMicroTasks(); } -export function flushMicroTasks(): Promise { - return new Promise((resolve) => setImmediate(resolve)); +let enqueueTask; +try { + // assuming we're in node, let's try to get node's + // version of setImmediate, bypassing fake timers if any. + // $FlowFixMe - timers is internal Node module + enqueueTask = require('timers').setImmediate; +} catch (_err) { + // we're in a browser, do nothing + enqueueTask = (resolve) => { + resolve(); + }; +} + +export function flushMicroTasks(): SimpleThenable { + return { + then(resolve) { + enqueueTask(resolve); + }, + }; } From 360f71fbb92fb3d4e57a7e64d5efe4e859841596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Fri, 10 Jul 2020 15:05:12 +0200 Subject: [PATCH 2/6] treat modern timers as default, but be explicit in tests --- src/__tests__/auto-cleanup.js | 6 +++--- src/__tests__/waitFor.test.js | 6 +++--- src/__tests__/waitForElementToBeRemoved.test.js | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/__tests__/auto-cleanup.js b/src/__tests__/auto-cleanup.js index 405c588ae..8b6d79162 100644 --- a/src/__tests__/auto-cleanup.js +++ b/src/__tests__/auto-cleanup.js @@ -33,13 +33,13 @@ test('component is automatically umounted after first test ends', () => { expect(isMounted).toEqual(false); }); -test('does not time out with fake timers', () => { - jest.useFakeTimers(); +test('does not time out with legacy fake timers', () => { + jest.useFakeTimers('legacy'); render(); expect(isMounted).toEqual(true); }); -test('does not time out with modern fake timers', () => { +test('does not time out with fake timers', () => { jest.useFakeTimers('modern'); render(); expect(isMounted).toEqual(true); diff --git a/src/__tests__/waitFor.test.js b/src/__tests__/waitFor.test.js index 5f6496e28..84ee17266 100644 --- a/src/__tests__/waitFor.test.js +++ b/src/__tests__/waitFor.test.js @@ -79,8 +79,8 @@ test('waits for element with custom interval', async () => { expect(mockFn).toHaveBeenCalledTimes(3); }); -test('works with fake timers', async () => { - jest.useFakeTimers(); +test('works with legacy fake timers', async () => { + jest.useFakeTimers('legacy'); const mockFn = jest.fn(() => { throw Error('test'); @@ -96,7 +96,7 @@ test('works with fake timers', async () => { expect(mockFn).toHaveBeenCalledTimes(3); }); -test('works with modern fake timers', async () => { +test('works with fake timers', async () => { jest.useFakeTimers('modern'); const mockFn = jest.fn(() => { diff --git a/src/__tests__/waitForElementToBeRemoved.test.js b/src/__tests__/waitForElementToBeRemoved.test.js index 7ea08b69a..ad5add433 100644 --- a/src/__tests__/waitForElementToBeRemoved.test.js +++ b/src/__tests__/waitForElementToBeRemoved.test.js @@ -130,8 +130,8 @@ test('waits with custom interval', async () => { expect(mockFn).toHaveBeenCalledTimes(4); }); -test('works with fake timers', async () => { - jest.useFakeTimers(); +test('works with legacy fake timers', async () => { + jest.useFakeTimers('legacy'); const mockFn = jest.fn(() => ); @@ -144,7 +144,7 @@ test('works with fake timers', async () => { expect(mockFn).toHaveBeenCalledTimes(4); }); -test('works with modern fake timers', async () => { +test('works with fake timers', async () => { jest.useFakeTimers('modern'); const mockFn = jest.fn(() => ); From 1f1f7f447cfb6c4dae2eb49d52ce05551e777fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Fri, 10 Jul 2020 15:06:41 +0200 Subject: [PATCH 3/6] we're node-only --- src/flushMicroTasks.js | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/flushMicroTasks.js b/src/flushMicroTasks.js index e6359d808..2d726d553 100644 --- a/src/flushMicroTasks.js +++ b/src/flushMicroTasks.js @@ -12,18 +12,9 @@ export default function flushMicrotasksQueue(): SimpleThenable { return flushMicroTasks(); } -let enqueueTask; -try { - // assuming we're in node, let's try to get node's - // version of setImmediate, bypassing fake timers if any. - // $FlowFixMe - timers is internal Node module - enqueueTask = require('timers').setImmediate; -} catch (_err) { - // we're in a browser, do nothing - enqueueTask = (resolve) => { - resolve(); - }; -} +// let's try to get node's version of setImmediate, bypassing fake timers if +// any. $FlowFixMe - timers is internal Node module +const enqueueTask = require('timers').setImmediate; export function flushMicroTasks(): SimpleThenable { return { From 6bbcc5eea6aaa5444e1544a9a0b4c3c6a0262b90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Fri, 10 Jul 2020 15:07:05 +0200 Subject: [PATCH 4/6] rename to Thenable --- src/flushMicroTasks.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/flushMicroTasks.js b/src/flushMicroTasks.js index 2d726d553..c8e161300 100644 --- a/src/flushMicroTasks.js +++ b/src/flushMicroTasks.js @@ -2,12 +2,12 @@ import { printDeprecationWarning } from './helpers/errors'; -type SimpleThenable = { then: (() => mixed) => mixed }; +type Thenable = { then: (() => mixed) => mixed }; /** * Wait for microtasks queue to flush */ -export default function flushMicrotasksQueue(): SimpleThenable { +export default function flushMicrotasksQueue(): Thenable { printDeprecationWarning('flushMicrotasksQueue'); return flushMicroTasks(); } @@ -16,7 +16,7 @@ export default function flushMicrotasksQueue(): SimpleThenable { // any. $FlowFixMe - timers is internal Node module const enqueueTask = require('timers').setImmediate; -export function flushMicroTasks(): SimpleThenable { +export function flushMicroTasks(): Thenable { return { then(resolve) { enqueueTask(resolve); From ad7cc9c306fec1dc1ab6611a98a85e609f9bc701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Fri, 10 Jul 2020 16:02:43 +0200 Subject: [PATCH 5/6] add clarifications; unify test names --- ...-cleanup-skip.js => auto-cleanup-skip.test.js} | 2 +- .../{auto-cleanup.js => auto-cleanup.test.js} | 4 ++++ src/flushMicroTasks.js | 15 ++++++--------- 3 files changed, 11 insertions(+), 10 deletions(-) rename src/__tests__/{auto-cleanup-skip.js => auto-cleanup-skip.test.js} (96%) rename src/__tests__/{auto-cleanup.js => auto-cleanup.test.js} (96%) diff --git a/src/__tests__/auto-cleanup-skip.js b/src/__tests__/auto-cleanup-skip.test.js similarity index 96% rename from src/__tests__/auto-cleanup-skip.js rename to src/__tests__/auto-cleanup-skip.test.js index eece886ab..8d1112ebe 100644 --- a/src/__tests__/auto-cleanup-skip.js +++ b/src/__tests__/auto-cleanup-skip.test.js @@ -4,7 +4,7 @@ import { View } from 'react-native'; let render; beforeAll(() => { process.env.RNTL_SKIP_AUTO_CLEANUP = 'true'; - const rtl = require('../'); + const rtl = require('..'); render = rtl.render; }); diff --git a/src/__tests__/auto-cleanup.js b/src/__tests__/auto-cleanup.test.js similarity index 96% rename from src/__tests__/auto-cleanup.js rename to src/__tests__/auto-cleanup.test.js index 8b6d79162..04c7eb5d5 100644 --- a/src/__tests__/auto-cleanup.js +++ b/src/__tests__/auto-cleanup.test.js @@ -20,6 +20,10 @@ class Test extends React.Component<*> { } } +afterEach(() => { + jest.useRealTimers(); +}); + // This just verifies that by importing RNTL in an environment which supports afterEach (like jest) // we'll get automatic cleanup between tests. test('component is mounted, but not umounted before test ends', () => { diff --git a/src/flushMicroTasks.js b/src/flushMicroTasks.js index c8e161300..5a5a0153d 100644 --- a/src/flushMicroTasks.js +++ b/src/flushMicroTasks.js @@ -1,25 +1,22 @@ // @flow - import { printDeprecationWarning } from './helpers/errors'; -type Thenable = { then: (() => mixed) => mixed }; +type Thenable = { then: (() => T) => mixed }; /** * Wait for microtasks queue to flush */ -export default function flushMicrotasksQueue(): Thenable { +export default function flushMicrotasksQueue(): Thenable { printDeprecationWarning('flushMicrotasksQueue'); return flushMicroTasks(); } -// let's try to get node's version of setImmediate, bypassing fake timers if -// any. $FlowFixMe - timers is internal Node module -const enqueueTask = require('timers').setImmediate; - -export function flushMicroTasks(): Thenable { +export function flushMicroTasks(): Thenable { return { + // using "thenable" instead of a Promise, because otherwise it breaks when + // using "modern" fake timers then(resolve) { - enqueueTask(resolve); + setImmediate(resolve); }, }; } From 174000677dc527fde21b0f6308fc771513b0287d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Fri, 10 Jul 2020 16:16:11 +0200 Subject: [PATCH 6/6] Update src/__tests__/auto-cleanup-skip.test.js Co-authored-by: Maciej Jastrzebski --- src/__tests__/auto-cleanup-skip.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/__tests__/auto-cleanup-skip.test.js b/src/__tests__/auto-cleanup-skip.test.js index 8d1112ebe..168ce2814 100644 --- a/src/__tests__/auto-cleanup-skip.test.js +++ b/src/__tests__/auto-cleanup-skip.test.js @@ -4,8 +4,8 @@ import { View } from 'react-native'; let render; beforeAll(() => { process.env.RNTL_SKIP_AUTO_CLEANUP = 'true'; - const rtl = require('..'); - render = rtl.render; + const rntl = require('..'); + render = rntl.render; }); let isMounted = false;