From 6fce9f205c32f5071d0aa453f4bad90a5127f36c Mon Sep 17 00:00:00 2001 From: Sunil Pai Date: Tue, 13 Aug 2019 15:34:47 +0100 Subject: [PATCH] manually flush microtasks in afterEach This PR replaces calling async act() in afterEach, with a version that still flushes microtasks, but doesn't wrap with act(). Explanation: If there are any hanging microtasks, that means a test ran without asserting on a valid state. Any async portion that causes updates should be asserted on, or atleast resolved within the scope of a test. This PR should still fire missing act warnings for a test, but won't let it leak to the next one. --- src/flush-microtasks.js | 47 +++++++++++++++++++++++++++++++++++++++++ src/index.js | 4 ++-- 2 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/flush-microtasks.js diff --git a/src/flush-microtasks.js b/src/flush-microtasks.js new file mode 100644 index 00000000..a34b5e85 --- /dev/null +++ b/src/flush-microtasks.js @@ -0,0 +1,47 @@ +/* istanbul ignore file */ +// the part of this file that we need tested is definitely being run +// and the part that is not cannot easily have useful tests written +// anyway. So we're just going to ignore coverage for this file +/** + * copied from React's enqueueTask.js + */ + +let didWarnAboutMessageChannel = false +let enqueueTask +try { + // read require off the module object to get around the bundlers. + // we don't want them to detect a require and bundle a Node polyfill. + const requireString = `require${Math.random()}`.slice(0, 7) + const nodeRequire = module && module[requireString] + // assuming we're in node, let's try to get node's + // version of setImmediate, bypassing fake timers if any. + enqueueTask = nodeRequire('timers').setImmediate +} catch (_err) { + // we're in a browser + // we can't use regular timers because they may still be faked + // so we try MessageChannel+postMessage instead + enqueueTask = callback => { + if (didWarnAboutMessageChannel === false) { + didWarnAboutMessageChannel = true + // eslint-disable-next-line no-console + console.error( + typeof MessageChannel !== 'undefined', + 'This browser does not have a MessageChannel implementation, ' + + 'so enqueuing tasks via await act(async () => ...) will fail. ' + + 'Please file an issue at https://github.com/facebook/react/issues ' + + 'if you encounter this warning.', + ) + } + const channel = new MessageChannel() + channel.port1.onmessage = callback + channel.port2.postMessage(undefined) + } +} + +export default function flushMicroTasks() { + return { + then(resolve) { + enqueueTask(resolve) + }, + } +} diff --git a/src/index.js b/src/index.js index 5e05b725..5e942670 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -import {asyncAct} from './act-compat' +import flush from './flush-microtasks' import {cleanup} from './pure' // if we're running in a test runner that supports afterEach @@ -8,7 +8,7 @@ import {cleanup} from './pure' // or set the RTL_SKIP_AUTO_CLEANUP env variable to 'true'. if (typeof afterEach === 'function' && !process.env.RTL_SKIP_AUTO_CLEANUP) { afterEach(async () => { - await asyncAct(async () => {}) + await flush() cleanup() }) }