-
Notifications
You must be signed in to change notification settings - Fork 273
Flush all pending microtasks and updates before returning from waitFor #1366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
70edb16
1c11074
70fc3cd
c229766
ac9597b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,7 +2,7 @@ import { setImmediate } from './helpers/timers'; | |
|
||
type Thenable<T> = { then: (callback: () => T) => unknown }; | ||
|
||
export function flushMicroTasks<T>(): Thenable<T> { | ||
export function flushMicroTasks(): Thenable<void> { | ||
return { | ||
// using "thenable" instead of a Promise, because otherwise it breaks when | ||
// using "modern" fake timers | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a weird thenable because the flushMicroTasks().then( flushMicroTasks ).then( flushMicroTasks ) should run there The way how My intuition is that the really correct version will look differently 🙂 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My bad, I've missed that comment. So when used this in instead of inline There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jsnajdr Should the implementation be just:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add #1374 so that we have a proper place for discussion |
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
/* globals jest */ | ||
import act, { setReactActEnvironment, getIsReactActEnvironment } from './act'; | ||
import { getConfig } from './config'; | ||
import { flushMicroTasks } from './flushMicroTasks'; | ||
import { ErrorWithStack, copyStackTrace } from './helpers/errors'; | ||
import { | ||
setTimeout, | ||
|
@@ -196,7 +197,10 @@ export default async function waitFor<T>( | |
setReactActEnvironment(false); | ||
|
||
try { | ||
return await waitForInternal(expectation, optionsWithStackTrace); | ||
const result = await waitForInternal(expectation, optionsWithStackTrace); | ||
// Flush the microtask queue before restoring the `act` environment | ||
await flushMicroTasks(); | ||
return result; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can see in the reference PR that there is if (jestFakeTimersAreEnabled()) {
jest.advanceTimersByTime(0)
} Do you know why this is not needed here? Did your added test failed with both fake and real timers? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes.
Yes and it's an interesting story 🙂 The We could use await new Promise((resolve) => {
global.setImmediate(resolve);
if (jestFakeTimersAreEnabled()) {
jest.advanceTimersByTime(0);
}
}); But, if you really do this, the test will start failing with fake timers! The scheduled effects won't be executed. That's because there is a bug in React: facebook/react#25889. When the With fake To conclude, the " There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, that's a really good catch, this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I had a closer look at the question whether we should call Our issue is that when a component like this is rerendered: function Comp({ text }) {
useEffect(() => {
sideEffectWith(text);
}, [text]);
return <div>{ text }</div>;
} then React, when committing the rerender (the A await waitFor(() => getByText(text)); looks at the DOM, and can be satisfied and return immediately after the DOM update. But at that time, the effects are not yet flushed, the The effects are scheduled with the
React Native tests usually run in Because of facebook/react#25889, the callback is always scheduled using real timers. We don't need to advance any fake timers. We're waiting solely for things scheduled in React internals, never for any user-space callback. The extra |
||
} finally { | ||
setReactActEnvironment(previousActEnvironment); | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.