From f4595ef836abe413abbb059a5e112a2d1f367fd8 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Sat, 4 Mar 2023 15:20:32 -0500 Subject: [PATCH] Codemod tests to waitFor pattern (9/?) This converts some of our test suite to use the `waitFor` test pattern, instead of the `expect(Scheduler).toFlushAndYield` pattern. Most of these changes are automated with jscodeshift, with some slight manual cleanup in certain cases. See #26285 for full context. --- .../__tests__/ReactCacheOld-test.internal.js | 6 +- .../src/__tests__/ReactDOMFloat-test.js | 2 +- .../ReactDeprecationWarnings-test.js | 22 +++-- ...asedOnReactExpirationTime-test.internal.js | 6 +- .../src/__tests__/ReactFragment-test.js | 8 +- .../ReactHooksWithNoopRenderer-test.js | 90 +++++++++---------- .../src/__tests__/ReactIncremental-test.js | 19 ++-- ...tIncrementalErrorHandling-test.internal.js | 79 ++++++++-------- .../ReactIncrementalErrorLogging-test.js | 14 +-- .../ReactIncrementalErrorReplay-test.js | 8 +- .../ReactIncrementalSideEffects-test.js | 2 +- .../__tests__/ReactIncrementalUpdates-test.js | 41 ++++----- .../src/__tests__/ReactLazy-test.internal.js | 12 +-- .../src/__tests__/ReactMemo-test.js | 4 +- .../src/__tests__/ReactNewContext-test.js | 28 +++--- .../__tests__/ReactTransitionTracing-test.js | 13 +-- .../src/__tests__/useEffectEvent-test.js | 6 +- .../useMutableSource-test.internal.js | 18 ++-- .../__tests__/ReactProfiler-test.internal.js | 6 +- .../src/__tests__/SchedulerProfiling-test.js | 4 +- 20 files changed, 198 insertions(+), 190 deletions(-) diff --git a/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js b/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js index 82956027cca66..b2db6c5d34e55 100644 --- a/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js +++ b/packages/react-cache/src/__tests__/ReactCacheOld-test.internal.js @@ -20,6 +20,7 @@ let TextResource; let textResourceShouldFail; let waitForAll; let assertLog; +let waitForThrow; describe('ReactCache', () => { beforeEach(() => { @@ -38,6 +39,7 @@ describe('ReactCache', () => { const InternalTestUtils = require('internal-test-utils'); waitForAll = InternalTestUtils.waitForAll; assertLog = InternalTestUtils.assertLog; + waitForThrow = InternalTestUtils.waitForThrow; TextResource = createResource( ([text, ms = 0]) => { @@ -150,12 +152,12 @@ describe('ReactCache', () => { jest.advanceTimersByTime(100); assertLog(['Promise rejected [Hi]']); - expect(Scheduler).toFlushAndThrow('Failed to load: Hi'); + await waitForThrow('Failed to load: Hi'); assertLog(['Error! [Hi]', 'Error! [Hi]']); // Should throw again on a subsequent read root.update(); - expect(Scheduler).toFlushAndThrow('Failed to load: Hi'); + await waitForThrow('Failed to load: Hi'); assertLog(['Error! [Hi]', 'Error! [Hi]']); }); diff --git a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js index eebc2f078ead0..ca5222a9b56b4 100644 --- a/packages/react-dom/src/__tests__/ReactDOMFloat-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMFloat-test.js @@ -4398,7 +4398,7 @@ background-color: green; , ); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(getMeaningfulChildren(document)).toEqual( diff --git a/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js b/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js index 941c247bcb697..a263c18153644 100644 --- a/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js +++ b/packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js @@ -11,7 +11,6 @@ let React; let ReactNoop; -let Scheduler; let JSXDEVRuntime; let waitForAll; @@ -20,7 +19,6 @@ describe('ReactDeprecationWarnings', () => { jest.resetModules(); React = require('react'); ReactNoop = require('react-noop-renderer'); - Scheduler = require('scheduler'); const InternalTestUtils = require('internal-test-utils'); waitForAll = InternalTestUtils.waitForAll; if (__DEV__) { @@ -28,7 +26,7 @@ describe('ReactDeprecationWarnings', () => { } }); - it('should warn when given defaultProps', () => { + it('should warn when given defaultProps', async () => { function FunctionalComponent(props) { return null; } @@ -38,14 +36,14 @@ describe('ReactDeprecationWarnings', () => { }; ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( + await expect(async () => await waitForAll([])).toErrorDev( 'Warning: FunctionalComponent: Support for defaultProps ' + 'will be removed from function components in a future major ' + 'release. Use JavaScript default parameters instead.', ); }); - it('should warn when given defaultProps on a memoized function', () => { + it('should warn when given defaultProps on a memoized function', async () => { const MemoComponent = React.memo(function FunctionalComponent(props) { return null; }); @@ -59,14 +57,14 @@ describe('ReactDeprecationWarnings', () => { , ); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( + await expect(async () => await waitForAll([])).toErrorDev( 'Warning: FunctionalComponent: Support for defaultProps ' + 'will be removed from memo components in a future major ' + 'release. Use JavaScript default parameters instead.', ); }); - it('should warn when given string refs', () => { + it('should warn when given string refs', async () => { class RefComponent extends React.Component { render() { return null; @@ -79,7 +77,7 @@ describe('ReactDeprecationWarnings', () => { } ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( + await expect(async () => await waitForAll([])).toErrorDev( 'Warning: Component "Component" contains the string ref "refComponent". ' + 'Support for string refs will be removed in a future major release. ' + 'We recommend using useRef() or createRef() instead. ' + @@ -108,7 +106,7 @@ describe('ReactDeprecationWarnings', () => { await waitForAll([]); }); - it('should warn when owner and self are different for string refs', () => { + it('should warn when owner and self are different for string refs', async () => { class RefComponent extends React.Component { render() { return null; @@ -121,7 +119,7 @@ describe('ReactDeprecationWarnings', () => { } ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([ + await expect(async () => await waitForAll([])).toErrorDev([ 'Warning: Component "Component" contains the string ref "refComponent". ' + 'Support for string refs will be removed in a future major release. ' + 'This case cannot be automatically converted to an arrow function. ' + @@ -132,7 +130,7 @@ describe('ReactDeprecationWarnings', () => { }); if (__DEV__) { - it('should warn when owner and self are different for string refs', () => { + it('should warn when owner and self are different for string refs', async () => { class RefComponent extends React.Component { render() { return null; @@ -152,7 +150,7 @@ describe('ReactDeprecationWarnings', () => { } ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( + await expect(async () => await waitForAll([])).toErrorDev( 'Warning: Component "Component" contains the string ref "refComponent". ' + 'Support for string refs will be removed in a future major release. ' + 'This case cannot be automatically converted to an arrow function. ' + diff --git a/packages/react-reconciler/src/__tests__/ReactDisableSchedulerTimeoutBasedOnReactExpirationTime-test.internal.js b/packages/react-reconciler/src/__tests__/ReactDisableSchedulerTimeoutBasedOnReactExpirationTime-test.internal.js index 3af4d3738c441..0bf1bb1ee02b7 100644 --- a/packages/react-reconciler/src/__tests__/ReactDisableSchedulerTimeoutBasedOnReactExpirationTime-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactDisableSchedulerTimeoutBasedOnReactExpirationTime-test.internal.js @@ -6,6 +6,7 @@ let Suspense; let scheduleCallback; let NormalPriority; let waitForAll; +let waitFor; describe('ReactSuspenseList', () => { beforeEach(() => { @@ -24,6 +25,7 @@ describe('ReactSuspenseList', () => { const InternalTestUtils = require('internal-test-utils'); waitForAll = InternalTestUtils.waitForAll; + waitFor = InternalTestUtils.waitFor; }); function Text(props) { @@ -86,11 +88,11 @@ describe('ReactSuspenseList', () => { }); // This resolves A and schedules a task for React to retry. - await expect(Scheduler).toFlushAndYieldThrough(['Resolve A']); + await waitFor(['Resolve A']); // The next task that flushes should be the one that resolves B. The render // task should not jump the queue ahead of B. - await expect(Scheduler).toFlushAndYieldThrough(['Resolve B']); + await waitFor(['Resolve B']); await waitForAll(['A', 'B']); expect(root).toMatchRenderedOutput('AB'); diff --git a/packages/react-reconciler/src/__tests__/ReactFragment-test.js b/packages/react-reconciler/src/__tests__/ReactFragment-test.js index e6b9c185ca40e..3db7702f4cfef 100644 --- a/packages/react-reconciler/src/__tests__/ReactFragment-test.js +++ b/packages/react-reconciler/src/__tests__/ReactFragment-test.js @@ -11,7 +11,6 @@ let React; let ReactNoop; -let Scheduler; let waitForAll; describe('ReactFragment', () => { @@ -20,7 +19,6 @@ describe('ReactFragment', () => { React = require('react'); ReactNoop = require('react-noop-renderer'); - Scheduler = require('scheduler'); const InternalTestUtils = require('internal-test-utils'); waitForAll = InternalTestUtils.waitForAll; @@ -707,7 +705,7 @@ describe('ReactFragment', () => { ); }); - it('should not preserve state when switching to a keyed fragment to an array', async function () { + it('should not preserve state when switching to a keyed fragment to an array', async () => { const ops = []; class Stateful extends React.Component { @@ -742,7 +740,7 @@ describe('ReactFragment', () => { await waitForAll([]); ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( + await expect(async () => await waitForAll([])).toErrorDev( 'Each child in a list should have a unique "key" prop.', ); @@ -939,7 +937,7 @@ describe('ReactFragment', () => { } ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( + await expect(async () => await waitForAll([])).toErrorDev( 'Each child in a list should have a unique "key" prop.', ); diff --git a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js index 9a3bd8efdcd2e..b6686ec2bafcb 100644 --- a/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js +++ b/packages/react-reconciler/src/__tests__/ReactHooksWithNoopRenderer-test.js @@ -209,7 +209,7 @@ describe('ReactHooksWithNoopRenderer', () => { } ReactNoop.render(); - expect(Scheduler).toFlushAndThrow( + await waitForThrow( 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + ' one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + @@ -227,19 +227,20 @@ describe('ReactHooksWithNoopRenderer', () => { await waitForAll([10]); }); - if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) { - it('throws inside module-style components', async () => { - function Counter() { - return { - render() { - const [count] = useState(0); - return ; - }, - }; - } - ReactNoop.render(); - expect(() => - expect(Scheduler).toFlushAndThrow( + // @gate !disableModulePatternComponents + it('throws inside module-style components', async () => { + function Counter() { + return { + render() { + const [count] = useState(0); + return ; + }, + }; + } + ReactNoop.render(); + await expect( + async () => + await waitForThrow( 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen ' + 'for one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + @@ -247,23 +248,22 @@ describe('ReactHooksWithNoopRenderer', () => { '3. You might have more than one copy of React in the same app\n' + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.', ), - ).toErrorDev( - 'Warning: The component appears to be a function component that returns a class instance. ' + - 'Change Counter to a class that extends React.Component instead. ' + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - '`Counter.prototype = React.Component.prototype`. ' + - "Don't use an arrow function since it cannot be called with `new` by React.", - ); + ).toErrorDev( + 'Warning: The component appears to be a function component that returns a class instance. ' + + 'Change Counter to a class that extends React.Component instead. ' + + "If you can't use a class try assigning the prototype on the function as a workaround. " + + '`Counter.prototype = React.Component.prototype`. ' + + "Don't use an arrow function since it cannot be called with `new` by React.", + ); - // Confirm that a subsequent hook works properly. - function GoodCounter(props) { - const [count] = useState(props.initialCount); - return ; - } - ReactNoop.render(); - await waitForAll([10]); - }); - } + // Confirm that a subsequent hook works properly. + function GoodCounter(props) { + const [count] = useState(props.initialCount); + return ; + } + ReactNoop.render(); + await waitForAll([10]); + }); it('throws when called outside the render phase', async () => { expect(() => { @@ -487,20 +487,18 @@ describe('ReactHooksWithNoopRenderer', () => { assertLog(['Foo [0]', 'Bar']); // Bar will update Foo during its render phase. React should warn. - await act(async () => { - root.render( - <> - - - , - ); - expect(() => - expect(Scheduler).toFlushAndYield(['Foo [0]', 'Bar', 'Foo [1]']), - ).toErrorDev([ - 'Cannot update a component (`Foo`) while rendering a ' + - 'different component (`Bar`). To locate the bad setState() call inside `Bar`', - ]); - }); + root.render( + <> + + + , + ); + await expect( + async () => await waitForAll(['Foo [0]', 'Bar', 'Foo [1]']), + ).toErrorDev([ + 'Cannot update a component (`Foo`) while rendering a ' + + 'different component (`Bar`). To locate the bad setState() call inside `Bar`', + ]); // It should not warn again (deduplication). await act(async () => { @@ -562,7 +560,7 @@ describe('ReactHooksWithNoopRenderer', () => { return ; } ReactNoop.render(); - expect(Scheduler).toFlushAndThrow( + await waitForThrow( 'Too many re-renders. React limits the number of renders to prevent ' + 'an infinite loop.', ); @@ -3805,7 +3803,7 @@ describe('ReactHooksWithNoopRenderer', () => { assertLog(['A: 2, B: 3, C: 4']); expect(ReactNoop).toMatchRenderedOutput(); ReactNoop.render(); - expect(Scheduler).toFlushAndThrow( + await waitForThrow( 'Rendered fewer hooks than expected. This may be caused by an ' + 'accidental early return statement.', ); diff --git a/packages/react-reconciler/src/__tests__/ReactIncremental-test.js b/packages/react-reconciler/src/__tests__/ReactIncremental-test.js index abf56e32b73ad..460b751115fa2 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncremental-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncremental-test.js @@ -1896,7 +1896,7 @@ describe('ReactIncremental', () => { }); if (!require('shared/ReactFeatureFlags').disableModulePatternComponents) { - it('does not leak own context into context provider (factory components)', () => { + it('does not leak own context into context provider (factory components)', async () => { function Recurse(props, context) { return { getChildContext() { @@ -1919,13 +1919,14 @@ describe('ReactIncremental', () => { }; ReactNoop.render(); - expect(() => - expect(Scheduler).toFlushAndYield([ - 'Recurse {}', - 'Recurse {"n":2}', - 'Recurse {"n":1}', - 'Recurse {"n":0}', - ]), + await expect( + async () => + await waitForAll([ + 'Recurse {}', + 'Recurse {"n":2}', + 'Recurse {"n":1}', + 'Recurse {"n":0}', + ]), ).toErrorDev([ 'Warning: The component appears to be a function component that returns a class instance. ' + 'Change Recurse to a class that extends React.Component instead. ' + @@ -2281,7 +2282,7 @@ describe('ReactIncremental', () => { instance.setState({ throwError: true, }); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( + await expect(async () => await waitForAll([])).toErrorDev( 'Error boundaries should implement getDerivedStateFromError()', ); }); diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js index 5e72bfe95d011..219ce29b91103 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorHandling-test.internal.js @@ -1219,7 +1219,7 @@ describe('ReactIncrementalErrorHandling', () => { expect(ReactNoop).toMatchRenderedOutput(); }); - it('catches reconciler errors in a boundary during mounting', () => { + it('catches reconciler errors in a boundary during mounting', async () => { class ErrorBoundary extends React.Component { state = {error: null}; componentDidCatch(error) { @@ -1242,7 +1242,7 @@ describe('ReactIncrementalErrorHandling', () => { , ); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([ + await expect(async () => await waitForAll([])).toErrorDev([ 'Warning: React.createElement: type is invalid -- expected a string', // React retries once on error 'Warning: React.createElement: type is invalid -- expected a string', @@ -1293,7 +1293,7 @@ describe('ReactIncrementalErrorHandling', () => { , ); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([ + await expect(async () => await waitForAll([])).toErrorDev([ 'Warning: React.createElement: type is invalid -- expected a string', // React retries once on error 'Warning: React.createElement: type is invalid -- expected a string', @@ -1319,7 +1319,7 @@ describe('ReactIncrementalErrorHandling', () => { 'Warning: React.createElement: type is invalid -- expected a string', {withoutStack: true}, ); - expect(Scheduler).toFlushAndThrow( + await waitForThrow( 'Element type is invalid: expected a string (for built-in components) or ' + 'a class/function (for composite components) but got: undefined.' + (__DEV__ @@ -1425,7 +1425,7 @@ describe('ReactIncrementalErrorHandling', () => { // Unmount ReactNoop.render(); - expect(Scheduler).toFlushAndThrow('Detach error'); + await waitForThrow('Detach error'); assertLog([ 'barRef detach', // Bar should unmount even though its ref threw an error while detaching @@ -1435,16 +1435,16 @@ describe('ReactIncrementalErrorHandling', () => { expect(ReactNoop).toMatchRenderedOutput(null); }); - it('handles error thrown by host config while working on failed root', () => { + it('handles error thrown by host config while working on failed root', async () => { ReactNoop.render(); - expect(Scheduler).toFlushAndThrow('Error in host config.'); + await waitForThrow('Error in host config.'); }); - it('handles error thrown by top-level callback', () => { + it('handles error thrown by top-level callback', async () => { ReactNoop.render(
, () => { throw new Error('Error!'); }); - expect(Scheduler).toFlushAndThrow('Error!'); + await waitForThrow('Error!'); }); it('error boundaries capture non-errors', async () => { @@ -1758,37 +1758,36 @@ describe('ReactIncrementalErrorHandling', () => { ); }); - if (!ReactFeatureFlags.disableModulePatternComponents) { - it('handles error thrown inside getDerivedStateFromProps of a module-style context provider', () => { - function Provider() { - return { - getChildContext() { - return {foo: 'bar'}; - }, - render() { - return 'Hi'; - }, - }; - } - Provider.childContextTypes = { - x: () => {}, - }; - Provider.getDerivedStateFromProps = () => { - throw new Error('Oops!'); + // @gate !disableModulePatternComponents + it('handles error thrown inside getDerivedStateFromProps of a module-style context provider', async () => { + function Provider() { + return { + getChildContext() { + return {foo: 'bar'}; + }, + render() { + return 'Hi'; + }, }; + } + Provider.childContextTypes = { + x: () => {}, + }; + Provider.getDerivedStateFromProps = () => { + throw new Error('Oops!'); + }; - ReactNoop.render(); - expect(() => { - expect(Scheduler).toFlushAndThrow('Oops!'); - }).toErrorDev([ - 'Warning: The component appears to be a function component that returns a class instance. ' + - 'Change Provider to a class that extends React.Component instead. ' + - "If you can't use a class try assigning the prototype on the function as a workaround. " + - '`Provider.prototype = React.Component.prototype`. ' + - "Don't use an arrow function since it cannot be called with `new` by React.", - ]); - }); - } + ReactNoop.render(); + await expect(async () => { + await waitForThrow('Oops!'); + }).toErrorDev([ + 'Warning: The component appears to be a function component that returns a class instance. ' + + 'Change Provider to a class that extends React.Component instead. ' + + "If you can't use a class try assigning the prototype on the function as a workaround. " + + '`Provider.prototype = React.Component.prototype`. ' + + "Don't use an arrow function since it cannot be called with `new` by React.", + ]); + }); it('uncaught errors should be discarded if the render is aborted', async () => { const root = ReactNoop.createRoot(); @@ -1924,10 +1923,10 @@ describe('ReactIncrementalErrorHandling', () => { }); if (global.__PERSISTENT__) { - it('regression test: should fatal if error is thrown at the root', () => { + it('regression test: should fatal if error is thrown at the root', async () => { const root = ReactNoop.createRoot(); root.render('Error when completing root'); - expect(Scheduler).toFlushAndThrow('Error when completing root'); + await waitForThrow('Error when completing root'); }); } }); diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorLogging-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorLogging-test.js index 165edacd01d36..9519da162afc9 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorLogging-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorLogging-test.js @@ -14,6 +14,7 @@ let React; let ReactNoop; let Scheduler; let waitForAll; +let waitForThrow; describe('ReactIncrementalErrorLogging', () => { beforeEach(() => { @@ -24,6 +25,7 @@ describe('ReactIncrementalErrorLogging', () => { const InternalTestUtils = require('internal-test-utils'); waitForAll = InternalTestUtils.waitForAll; + waitForThrow = InternalTestUtils.waitForThrow; }); // Note: in this test file we won't be using toErrorDev() matchers @@ -39,7 +41,7 @@ describe('ReactIncrementalErrorLogging', () => { oldConsoleError = null; }); - it('should log errors that occur during the begin phase', () => { + it('should log errors that occur during the begin phase', async () => { class ErrorThrowingComponent extends React.Component { constructor(props) { super(props); @@ -56,7 +58,7 @@ describe('ReactIncrementalErrorLogging', () => {
, ); - expect(Scheduler).toFlushAndThrow('constructor error'); + await waitForThrow('constructor error'); expect(console.error).toHaveBeenCalledTimes(1); expect(console.error).toHaveBeenCalledWith( __DEV__ @@ -76,7 +78,7 @@ describe('ReactIncrementalErrorLogging', () => { ); }); - it('should log errors that occur during the commit phase', () => { + it('should log errors that occur during the commit phase', async () => { class ErrorThrowingComponent extends React.Component { componentDidMount() { throw new Error('componentDidMount error'); @@ -92,7 +94,7 @@ describe('ReactIncrementalErrorLogging', () => {
, ); - expect(Scheduler).toFlushAndThrow('componentDidMount error'); + await waitForThrow('componentDidMount error'); expect(console.error).toHaveBeenCalledTimes(1); expect(console.error).toHaveBeenCalledWith( __DEV__ @@ -112,7 +114,7 @@ describe('ReactIncrementalErrorLogging', () => { ); }); - it('should ignore errors thrown in log method to prevent cycle', () => { + it('should ignore errors thrown in log method to prevent cycle', async () => { const logCapturedErrorCalls = []; console.error.mockImplementation(error => { // Test what happens when logging itself is buggy. @@ -131,7 +133,7 @@ describe('ReactIncrementalErrorLogging', () => {
, ); - expect(Scheduler).toFlushAndThrow('render error'); + await waitForThrow('render error'); expect(logCapturedErrorCalls.length).toBe(1); expect(logCapturedErrorCalls[0]).toEqual( __DEV__ diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorReplay-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorReplay-test.js index 4f60e40efa23e..ed4317d95706e 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalErrorReplay-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalErrorReplay-test.js @@ -12,23 +12,23 @@ let React; let ReactNoop; -let Scheduler; let waitForAll; +let waitForThrow; describe('ReactIncrementalErrorReplay', () => { beforeEach(() => { jest.resetModules(); React = require('react'); ReactNoop = require('react-noop-renderer'); - Scheduler = require('scheduler'); const InternalTestUtils = require('internal-test-utils'); waitForAll = InternalTestUtils.waitForAll; + waitForThrow = InternalTestUtils.waitForThrow; }); - it('should fail gracefully on error in the host environment', () => { + it('should fail gracefully on error in the host environment', async () => { ReactNoop.render(); - expect(Scheduler).toFlushAndThrow('Error in host config.'); + await waitForThrow('Error in host config.'); }); it("should ignore error if it doesn't throw on retry", async () => { diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js index 4c20424e2e7e1..912680dfd14d1 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalSideEffects-test.js @@ -1296,7 +1296,7 @@ describe('ReactIncrementalSideEffects', () => { } ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( + await expect(async () => await waitForAll([])).toErrorDev( 'Warning: Function components cannot be given refs. ' + 'Attempts to access this ref will fail. ' + 'Did you mean to use React.forwardRef()?\n\n' + diff --git a/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js b/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js index 6f81c28ea9656..4fd471d7520f0 100644 --- a/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js +++ b/packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js @@ -421,27 +421,28 @@ describe('ReactIncrementalUpdates', () => { return {a: 'a'}; }); - expect(() => - expect(Scheduler).toFlushAndYield( - gate(flags => - flags.deferRenderPhaseUpdateToNextBatch - ? [ - 'setState updater', - // In the new reconciler, updates inside the render phase are - // treated as if they came from an event, so the update gets - // shifted to a subsequent render. - 'render', - 'render', - ] - : [ - 'setState updater', - // In the old reconciler, updates in the render phase receive - // the currently rendering expiration time, so the update - // flushes immediately in the same render. - 'render', - ], + await expect( + async () => + await waitForAll( + gate(flags => + flags.deferRenderPhaseUpdateToNextBatch + ? [ + 'setState updater', + // In the new reconciler, updates inside the render phase are + // treated as if they came from an event, so the update gets + // shifted to a subsequent render. + 'render', + 'render', + ] + : [ + 'setState updater', + // In the old reconciler, updates in the render phase receive + // the currently rendering expiration time, so the update + // flushes immediately in the same render. + 'render', + ], + ), ), - ), ).toErrorDev( 'An update (setState, replaceState, or forceUpdate) was scheduled ' + 'from inside an update function. Update functions should be pure, ' + diff --git a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js index 5ee887ee1081f..e289580d286a8 100644 --- a/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js +++ b/packages/react-reconciler/src/__tests__/ReactLazy-test.internal.js @@ -314,7 +314,7 @@ describe('ReactLazy', () => { await resolveFakeImport(T); - expect(() => expect(Scheduler).toFlushAndYield(['Hi'])).toErrorDev( + await expect(async () => await waitForAll(['Hi'])).toErrorDev( 'Warning: T: Support for defaultProps ' + 'will be removed from function components in a future major ' + 'release. Use JavaScript default parameters instead.', @@ -369,8 +369,8 @@ describe('ReactLazy', () => { await resolveFakeImport(LazyImpl); - expect(() => - expect(Scheduler).toFlushAndYield(['Lazy', 'Sibling', 'A']), + await expect( + async () => await waitForAll(['Lazy', 'Sibling', 'A']), ).toErrorDev( 'Warning: LazyImpl: Support for defaultProps ' + 'will be removed from function components in a future major ' + @@ -676,7 +676,7 @@ describe('ReactLazy', () => { expect(root).not.toMatchRenderedOutput('Hi Bye'); await resolveFakeImport(T); - expect(() => expect(Scheduler).toFlushAndYield(['Hi Bye'])).toErrorDev( + await expect(async () => await waitForAll(['Hi Bye'])).toErrorDev( 'Warning: T: Support for defaultProps ' + 'will be removed from function components in a future major ' + 'release. Use JavaScript default parameters instead.', @@ -721,7 +721,7 @@ describe('ReactLazy', () => { , ); - expect(Scheduler).toFlushAndThrow( + await waitForThrow( 'Element type is invalid. Received a promise that resolves to: 42. ' + 'Lazy element type must resolve to a class or function.', ); @@ -749,7 +749,7 @@ describe('ReactLazy', () => { , ); - expect(Scheduler).toFlushAndThrow( + await waitForThrow( 'Element type is invalid. Received a promise that resolves to: [object Object]. ' + 'Lazy element type must resolve to a class or function.' + (__DEV__ diff --git a/packages/react-reconciler/src/__tests__/ReactMemo-test.js b/packages/react-reconciler/src/__tests__/ReactMemo-test.js index 8b095e16339b7..9c3e59daed56d 100644 --- a/packages/react-reconciler/src/__tests__/ReactMemo-test.js +++ b/packages/react-reconciler/src/__tests__/ReactMemo-test.js @@ -58,7 +58,7 @@ describe('memo', () => { return {}} />; } ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([ + await expect(async () => await waitForAll([])).toErrorDev([ 'Warning: Function components cannot be given refs. Attempts to access ' + 'this ref will fail.', ]); @@ -76,7 +76,7 @@ describe('memo', () => { return {}} />; } ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([ + await expect(async () => await waitForAll([])).toErrorDev([ 'App: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.', 'Warning: Function components cannot be given refs. Attempts to access ' + 'this ref will fail.', diff --git a/packages/react-reconciler/src/__tests__/ReactNewContext-test.js b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js index 8bfbe2097463a..58dbb1c7abccc 100644 --- a/packages/react-reconciler/src/__tests__/ReactNewContext-test.js +++ b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js @@ -16,6 +16,7 @@ let Scheduler; let gen; let waitForAll; let waitFor; +let waitForThrow; describe('ReactNewContext', () => { beforeEach(() => { @@ -30,6 +31,7 @@ describe('ReactNewContext', () => { const InternalTestUtils = require('internal-test-utils'); waitForAll = InternalTestUtils.waitForAll; waitFor = InternalTestUtils.waitFor; + waitForThrow = InternalTestUtils.waitForThrow; }); afterEach(() => { @@ -850,14 +852,14 @@ describe('ReactNewContext', () => { } describe('Context.Provider', () => { - it('warns if no value prop provided', () => { + it('warns if no value prop provided', async () => { const Context = React.createContext(); ReactNoop.render( , ); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( + await expect(async () => await waitForAll([])).toErrorDev( 'The `value` prop is required for the ``. Did you misspell it or forget to pass it?', { withoutStack: true, @@ -1050,11 +1052,11 @@ describe('ReactNewContext', () => { }); describe('Context.Consumer', () => { - it('warns if child is not a function', () => { + it('warns if child is not a function', async () => { spyOnDev(console, 'error').mockImplementation(() => {}); const Context = React.createContext(0); ReactNoop.render(); - expect(Scheduler).toFlushAndThrow('is not a function'); + await waitForThrow('is not a function'); if (__DEV__) { expect(console.error.mock.calls[0][0]).toContain( 'A context consumer was rendered with multiple children, or a child ' + @@ -1298,7 +1300,7 @@ describe('ReactNewContext', () => { expect(ReactNoop).toMatchRenderedOutput(); }); - it('warns when reading context inside render phase class setState updater', () => { + it('warns when reading context inside render phase class setState updater', async () => { const ThemeContext = React.createContext('light'); class Cls extends React.Component { @@ -1312,7 +1314,7 @@ describe('ReactNewContext', () => { } ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev([ + await expect(async () => await waitForAll([])).toErrorDev([ 'Context can only be read while React is rendering', 'Cannot update during an existing state transition', ]); @@ -1320,7 +1322,7 @@ describe('ReactNewContext', () => { }); describe('useContext', () => { - it('throws when used in a class component', () => { + it('throws when used in a class component', async () => { const Context = React.createContext(0); class Foo extends React.Component { render() { @@ -1328,7 +1330,7 @@ describe('ReactNewContext', () => { } } ReactNoop.render(); - expect(Scheduler).toFlushAndThrow( + await waitForThrow( 'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen' + ' for one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + @@ -1338,27 +1340,27 @@ describe('ReactNewContext', () => { ); }); - it('warns when passed a consumer', () => { + it('warns when passed a consumer', async () => { const Context = React.createContext(0); function Foo() { return useContext(Context.Consumer); } ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( + await expect(async () => await waitForAll([])).toErrorDev( 'Calling useContext(Context.Consumer) is not supported, may cause bugs, ' + 'and will be removed in a future major release. ' + 'Did you mean to call useContext(Context) instead?', ); }); - it('warns when passed a provider', () => { + it('warns when passed a provider', async () => { const Context = React.createContext(0); function Foo() { useContext(Context.Provider); return null; } ReactNoop.render(); - expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev( + await expect(async () => await waitForAll([])).toErrorDev( 'Calling useContext(Context.Provider) is not supported. ' + 'Did you mean to call useContext(Context) instead?', ); @@ -1420,7 +1422,7 @@ describe('ReactNewContext', () => { , ); - expect(Scheduler).toFlushAndThrow('Error in host config.'); + await waitForThrow('Error in host config.'); ReactNoop.render( diff --git a/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js b/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js index 8ce36f55f9847..db1d34204d685 100644 --- a/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js +++ b/packages/react-reconciler/src/__tests__/ReactTransitionTracing-test.js @@ -1395,12 +1395,13 @@ describe('ReactInteractionTracing', () => { root.render(); ReactNoop.expire(1000); await advanceTimers(1000); - expect(() => - expect(Scheduler).toFlushAndYield([ - 'Suspend [Page Two]', - 'Loading...', - 'onMarkerIncomplete(transition one, marker one, 1000, [{endTime: 3000, name: marker one, newName: marker two, type: marker}])', - ]), + await expect( + async () => + await waitForAll([ + 'Suspend [Page Two]', + 'Loading...', + 'onMarkerIncomplete(transition one, marker one, 1000, [{endTime: 3000, name: marker one, newName: marker two, type: marker}])', + ]), ).toErrorDev(''); resolveText('Page Two'); diff --git a/packages/react-reconciler/src/__tests__/useEffectEvent-test.js b/packages/react-reconciler/src/__tests__/useEffectEvent-test.js index 3a470a7a465cc..6d83cf9d1a2b5 100644 --- a/packages/react-reconciler/src/__tests__/useEffectEvent-test.js +++ b/packages/react-reconciler/src/__tests__/useEffectEvent-test.js @@ -28,6 +28,7 @@ describe('useEffectEvent', () => { let useMemo; let waitForAll; let assertLog; + let waitForThrow; beforeEach(() => { React = require('react'); @@ -46,6 +47,7 @@ describe('useEffectEvent', () => { const InternalTestUtils = require('internal-test-utils'); waitForAll = InternalTestUtils.waitForAll; assertLog = InternalTestUtils.assertLog; + waitForThrow = InternalTestUtils.waitForThrow; }); function Text(props) { @@ -242,7 +244,7 @@ describe('useEffectEvent', () => { }); // @gate enableUseEffectEventHook - it('throws when called in render', () => { + it('throws when called in render', async () => { class IncrementButton extends React.PureComponent { increment = () => { this.props.onClick(); @@ -269,7 +271,7 @@ describe('useEffectEvent', () => { } ReactNoop.render(); - expect(Scheduler).toFlushAndThrow( + await waitForThrow( "A function wrapped in useEffectEvent can't be called during rendering.", ); diff --git a/packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js b/packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js index 67f54bd560a48..290ac1533bc8c 100644 --- a/packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js +++ b/packages/react-reconciler/src/__tests__/useMutableSource-test.internal.js @@ -1518,16 +1518,14 @@ describe('useMutableSource', () => { } const root = ReactNoop.createRoot(); - await act(async () => { - root.render( - <> - - , - ); - expect(() => expect(Scheduler).toFlushAndYield(['a'])).toErrorDev( - 'Mutable source should not return a function as the snapshot value.', - ); - }); + root.render( + <> + + , + ); + await expect(async () => await waitForAll(['a'])).toErrorDev( + 'Mutable source should not return a function as the snapshot value.', + ); expect(root).toMatchRenderedOutput('a'); }); diff --git a/packages/react/src/__tests__/ReactProfiler-test.internal.js b/packages/react/src/__tests__/ReactProfiler-test.internal.js index 5635f2d61bf31..b21df4da2eea1 100644 --- a/packages/react/src/__tests__/ReactProfiler-test.internal.js +++ b/packages/react/src/__tests__/ReactProfiler-test.internal.js @@ -20,6 +20,7 @@ let AdvanceTime; let assertLog; let waitFor; let waitForAll; +let waitForThrow; function loadModules({ enableProfilerTimer = true, @@ -56,6 +57,7 @@ function loadModules({ assertLog = InternalTestUtils.assertLog; waitFor = InternalTestUtils.waitFor; waitForAll = InternalTestUtils.waitForAll; + waitForThrow = InternalTestUtils.waitForThrow; AdvanceTime = class extends React.Component { static defaultProps = { @@ -1230,7 +1232,7 @@ describe(`onRender`, () => { hi , ); - expect(Scheduler).toFlushAndThrow('Error in host config.'); + await waitForThrow('Error in host config.'); // A similar case we've seen caused by an invariant in ReactDOM. // It didn't reproduce without a host component inside. @@ -1241,7 +1243,7 @@ describe(`onRender`, () => { , ); - expect(Scheduler).toFlushAndThrow('Error in host config.'); + await waitForThrow('Error in host config.'); // So long as the profiler timer's fiber stack is reset correctly, // Subsequent renders should not error. diff --git a/packages/scheduler/src/__tests__/SchedulerProfiling-test.js b/packages/scheduler/src/__tests__/SchedulerProfiling-test.js index 2a7dc3099d234..f6f89a4aa07a0 100644 --- a/packages/scheduler/src/__tests__/SchedulerProfiling-test.js +++ b/packages/scheduler/src/__tests__/SchedulerProfiling-test.js @@ -26,6 +26,7 @@ let cancelCallback; // let shouldYield; let waitForAll; let waitFor; +let waitForThrow; function priorityLevelToString(priorityLevel) { switch (priorityLevel) { @@ -75,6 +76,7 @@ describe('Scheduler', () => { const InternalTestUtils = require('internal-test-utils'); waitForAll = InternalTestUtils.waitForAll; waitFor = InternalTestUtils.waitFor; + waitForThrow = InternalTestUtils.waitForThrow; }); const TaskStartEvent = 1; @@ -335,7 +337,7 @@ Task 1 [Normal] │██████░░🡐 canceled throw Error('Oops'); }); - expect(Scheduler).toFlushAndThrow('Oops'); + await waitForThrow('Oops'); Scheduler.unstable_advanceTime(100); Scheduler.unstable_advanceTime(1000);