Skip to content

Commit

Permalink
[Internal API only] Delete non-awaited form of act (#26339)
Browse files Browse the repository at this point in the history
**This commit only affects the internal version of `act` that we use in
this repo. The public `act` API is unaffected, for now.**

We should always await the result of an `act` call so that any work
queued in a microtask has a chance to flush. Neglecting to do this can
cause us to miss bugs when testing React behavior.

I codemodded all the existing `act` callers in previous PRs.
  • Loading branch information
acdlite authored Mar 8, 2023
1 parent 702fc98 commit d814473
Showing 1 changed file with 35 additions and 48 deletions.
83 changes: 35 additions & 48 deletions packages/jest-react/src/internalAct.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import enqueueTask from 'shared/enqueueTask';

let actingUpdatesScopeDepth = 0;

export function act<T>(scope: () => Thenable<T> | T): Thenable<T> {
export function act<T>(scope: () => Thenable<T>): Thenable<T> {
if (Scheduler.unstable_flushUntilNextPaint === undefined) {
throw Error(
'This version of `act` requires a special mock build of Scheduler.',
Expand Down Expand Up @@ -64,58 +64,45 @@ export function act<T>(scope: () => Thenable<T> | T): Thenable<T> {
}
};

// TODO: This would be way simpler if 1) we required a promise to be
// returned and 2) we could use async/await. Since it's only our used in
// our test suite, we should be able to.
// TODO: This would be way simpler if we could use async/await. Move this
// function to the internal-test-utils package.
try {
const result = scope();
if (
typeof result === 'object' &&
result !== null &&
// $FlowFixMe[method-unbinding]
typeof result.then === 'function'
typeof result !== 'object' ||
result === null ||
typeof (result: any).then !== 'function'
) {
const thenableResult: Thenable<T> = (result: any);
return {
then(resolve: T => mixed, reject: mixed => mixed) {
thenableResult.then(
returnValue => {
flushActWork(
() => {
unwind();
resolve(returnValue);
},
error => {
unwind();
reject(error);
},
);
},
error => {
unwind();
reject(error);
},
);
},
};
} else {
const returnValue: T = (result: any);
try {
// TODO: Let's not support non-async scopes at all in our tests. Need to
// migrate existing tests.
let didFlushWork;
do {
didFlushWork = Scheduler.unstable_flushAllWithoutAsserting();
} while (didFlushWork);
return {
then(resolve: T => mixed, reject: mixed => mixed) {
resolve(returnValue);
},
};
} finally {
unwind();
}
throw new Error(
'The internal version of `act` used in the React repo must be passed ' +
"an async function, even if doesn't await anything. This is a " +
'temporary limitation that will soon be fixed.',
);
}
const thenableResult: Thenable<T> = (result: any);

return {
then(resolve: T => mixed, reject: mixed => mixed) {
thenableResult.then(
returnValue => {
flushActWork(
() => {
unwind();
resolve(returnValue);
},
error => {
unwind();
reject(error);
},
);
},
error => {
unwind();
reject(error);
},
);
},
};
} catch (error) {
unwind();
throw error;
Expand Down

0 comments on commit d814473

Please sign in to comment.