From e98695db918721a08eae0a2dceeb5719c1aa7261 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Sat, 4 Mar 2023 11:45:26 -0500 Subject: [PATCH] Codemod tests to waitFor pattern (6/?) (#26305) 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. --- .../src/__tests__/ReactMemo-test.js | 92 ++-- .../src/__tests__/ReactNewContext-test.js | 188 ++++---- .../__tests__/ReactNoopRendererAct-test.js | 7 +- .../src/__tests__/ReactOffscreen-test.js | 215 +++++---- .../__tests__/ReactOffscreenSuspense-test.js | 46 +- .../src/__tests__/ReactPersistent-test.js | 31 +- .../ReactSchedulerIntegration-test.js | 42 +- .../ReactSubtreeFlagsWarning-test.js | 8 +- .../__tests__/ReactSuspense-test.internal.js | 420 ++++++++---------- .../__tests__/ReactSuspenseCallback-test.js | 24 +- 10 files changed, 517 insertions(+), 556 deletions(-) diff --git a/packages/react-reconciler/src/__tests__/ReactMemo-test.js b/packages/react-reconciler/src/__tests__/ReactMemo-test.js index 1ca0cf2e99dde..8b095e16339b7 100644 --- a/packages/react-reconciler/src/__tests__/ReactMemo-test.js +++ b/packages/react-reconciler/src/__tests__/ReactMemo-test.js @@ -18,6 +18,8 @@ let ReactNoop; let Suspense; let Scheduler; let act; +let waitForAll; +let assertLog; describe('memo', () => { beforeEach(() => { @@ -29,6 +31,10 @@ describe('memo', () => { Scheduler = require('scheduler'); act = require('jest-react').act; ({Suspense} = React); + + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + assertLog = InternalTestUtils.assertLog; }); function Text(props) { @@ -105,9 +111,7 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushAndYield(['Loading...']); - await Promise.resolve(); - expect(Scheduler).toFlushAndYield([0]); + await waitForAll(['Loading...', 0]); expect(ReactNoop).toMatchRenderedOutput(); // Should bail out because props have not changed @@ -116,7 +120,7 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput(); // Should update because count prop changed @@ -125,7 +129,7 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushAndYield([1]); + await waitForAll([1]); expect(ReactNoop).toMatchRenderedOutput(); }); @@ -160,19 +164,17 @@ describe('memo', () => { const parent = React.createRef(null); ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Loading...']); - await Promise.resolve(); - expect(Scheduler).toFlushAndYield(['Count: 0']); + await waitForAll(['Loading...', 'Count: 0']); expect(ReactNoop).toMatchRenderedOutput(); // Should bail out because props have not changed ReactNoop.render(); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput(); // Should update because there was a context change parent.current.setState({count: 1}); - expect(Scheduler).toFlushAndYield(['Count: 1']); + await waitForAll(['Count: 1']); expect(ReactNoop).toMatchRenderedOutput(); }); @@ -273,7 +275,7 @@ describe('memo', () => { await act(async () => { root.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'SimpleMemo [A0]', 'ComplexMemo [A0]', 'MemoWithIndirection [A0]', @@ -283,7 +285,7 @@ describe('memo', () => { await act(async () => { root.render(); }); - expect(Scheduler).toHaveYielded([ + assertLog([ 'SimpleMemo [B0]', 'ComplexMemo [B0]', 'MemoWithIndirection [B0]', @@ -298,7 +300,7 @@ describe('memo', () => { root.render(); }); // Nothing re-renders - expect(Scheduler).toHaveYielded([]); + assertLog([]); // Demonstrate what happens when the prop object changes, it bails out // because all the props are the same, but we still render the @@ -309,7 +311,7 @@ describe('memo', () => { }); // The components should re-render with the new local state, but none // of the props objects should have changed - expect(Scheduler).toHaveYielded([ + assertLog([ 'SimpleMemo [B1]', 'ComplexMemo [B1]', 'MemoWithIndirection [B1]', @@ -322,7 +324,7 @@ describe('memo', () => { }); // The components should re-render with the new local state, but none // of the props objects should have changed - expect(Scheduler).toHaveYielded([ + assertLog([ 'SimpleMemo [B2]', 'ComplexMemo [B2]', 'MemoWithIndirection [B2]', @@ -345,9 +347,7 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushAndYield(['Loading...']); - await Promise.resolve(); - expect(Scheduler).toFlushAndYield([0]); + await waitForAll(['Loading...', 0]); expect(ReactNoop).toMatchRenderedOutput(); // Should bail out because props have not changed @@ -356,7 +356,7 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushAndYield(['Old count: 0, New count: 0']); + await waitForAll(['Old count: 0, New count: 0']); expect(ReactNoop).toMatchRenderedOutput(); // Should update because count prop changed @@ -365,7 +365,7 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushAndYield(['Old count: 0, New count: 1', 1]); + await waitForAll(['Old count: 0, New count: 1', 1]); expect(ReactNoop).toMatchRenderedOutput(); }); @@ -383,9 +383,7 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushAndYield(['Loading...']); - await Promise.resolve(); - expect(Scheduler).toFlushAndYield(['0!']); + await waitForAll(['Loading...', '0!']); expect(ReactNoop).toMatchRenderedOutput(); // Should bail out because props have not changed @@ -394,7 +392,7 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput(); // Should update because count prop changed @@ -403,7 +401,7 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushAndYield(['1!']); + await waitForAll(['1!']); expect(ReactNoop).toMatchRenderedOutput(); }); @@ -436,10 +434,8 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushAndYield(['Loading...']); - await Promise.resolve(); - expect(() => { - expect(Scheduler).toFlushAndYield([15]); + await expect(async () => { + await waitForAll(['Loading...', 15]); }).toErrorDev([ 'Counter: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.', ]); @@ -451,7 +447,7 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushAndYield([]); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput(); // Should update because count prop changed @@ -460,7 +456,7 @@ describe('memo', () => { , ); - expect(Scheduler).toFlushAndYield([20]); + await waitForAll([20]); expect(ReactNoop).toMatchRenderedOutput(); }); @@ -480,7 +476,7 @@ describe('memo', () => { ); }); - it('validates propTypes declared on the inner component', () => { + it('validates propTypes declared on the inner component', async () => { function FnInner(props) { return props.inner; } @@ -488,23 +484,23 @@ describe('memo', () => { const Fn = React.memo(FnInner); // Mount - expect(() => { + await expect(async () => { ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); }).toErrorDev( 'Invalid prop `inner` of type `string` supplied to `FnInner`, expected `number`.', ); // Update - expect(() => { + await expect(async () => { ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); }).toErrorDev( 'Invalid prop `inner` of type `boolean` supplied to `FnInner`, expected `number`.', ); }); - it('validates propTypes declared on the outer component', () => { + it('validates propTypes declared on the outer component', async () => { function FnInner(props) { return props.outer; } @@ -512,25 +508,25 @@ describe('memo', () => { Fn.propTypes = {outer: PropTypes.number.isRequired}; // Mount - expect(() => { + await expect(async () => { ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); }).toErrorDev( // Outer props are checked in createElement 'Invalid prop `outer` of type `string` supplied to `FnInner`, expected `number`.', ); // Update - expect(() => { + await expect(async () => { ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); }).toErrorDev( // Outer props are checked in createElement 'Invalid prop `outer` of type `boolean` supplied to `FnInner`, expected `number`.', ); }); - it('validates nested propTypes declarations', () => { + it('validates nested propTypes declarations', async () => { function Inner(props) { return props.inner + props.middle + props.outer; } @@ -549,20 +545,20 @@ describe('memo', () => { , ); - expect(() => { - expect(Scheduler).toFlushWithoutYielding(); + await expect(async () => { + await waitForAll([]); }).toErrorDev([ 'Inner: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.', ]); // Mount - expect(() => { + await expect(async () => { ReactNoop.render(
, ); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); }).toErrorDev([ 'Invalid prop `outer` of type `string` supplied to `Inner`, expected `number`.', 'Invalid prop `middle` of type `string` supplied to `Inner`, expected `number`.', @@ -570,13 +566,13 @@ describe('memo', () => { ]); // Update - expect(() => { + await expect(async () => { ReactNoop.render(
, ); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); }).toErrorDev([ 'Invalid prop `outer` of type `boolean` supplied to `Inner`, expected `number`.', 'Invalid prop `middle` of type `boolean` supplied to `Inner`, expected `number`.', diff --git a/packages/react-reconciler/src/__tests__/ReactNewContext-test.js b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js index bc9d4283894d3..8bfbe2097463a 100644 --- a/packages/react-reconciler/src/__tests__/ReactNewContext-test.js +++ b/packages/react-reconciler/src/__tests__/ReactNewContext-test.js @@ -14,6 +14,8 @@ let useContext; let ReactNoop; let Scheduler; let gen; +let waitForAll; +let waitFor; describe('ReactNewContext', () => { beforeEach(() => { @@ -24,6 +26,10 @@ describe('ReactNewContext', () => { ReactNoop = require('react-noop-renderer'); Scheduler = require('scheduler'); gen = require('random-seed'); + + const InternalTestUtils = require('internal-test-utils'); + waitForAll = InternalTestUtils.waitForAll; + waitFor = InternalTestUtils.waitFor; }); afterEach(() => { @@ -110,7 +116,7 @@ describe('ReactNewContext', () => { function sharedContextTests(label, getConsumer) { describe(`reading context with ${label}`, () => { - it('simple mount and update', () => { + it('simple mount and update', async () => { const Context = React.createContext(1); const Consumer = getConsumer(Context); @@ -131,16 +137,16 @@ describe('ReactNewContext', () => { } ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput(); // Update ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput(); }); - it('propagates through shouldComponentUpdate false', () => { + it('propagates through shouldComponentUpdate false', async () => { const Context = React.createContext(1); const ContextConsumer = getConsumer(Context); @@ -189,7 +195,7 @@ describe('ReactNewContext', () => { } ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'App', 'Provider', 'Indirection', @@ -201,15 +207,11 @@ describe('ReactNewContext', () => { // Update ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ - 'App', - 'Provider', - 'Consumer render prop', - ]); + await waitForAll(['App', 'Provider', 'Consumer render prop']); expect(ReactNoop).toMatchRenderedOutput(); }); - it('consumers bail out if context value is the same', () => { + it('consumers bail out if context value is the same', async () => { const Context = React.createContext(1); const ContextConsumer = getConsumer(Context); @@ -258,7 +260,7 @@ describe('ReactNewContext', () => { } ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'App', 'Provider', 'Indirection', @@ -270,7 +272,7 @@ describe('ReactNewContext', () => { // Update with the same context value ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'App', 'Provider', // Don't call render prop again @@ -278,7 +280,7 @@ describe('ReactNewContext', () => { expect(ReactNoop).toMatchRenderedOutput(); }); - it('nested providers', () => { + it('nested providers', async () => { const Context = React.createContext(1); const Consumer = getConsumer(Context); @@ -325,16 +327,16 @@ describe('ReactNewContext', () => { } ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput(); // Update ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput(); }); - it('should provide the correct (default) values to consumers outside of a provider', () => { + it('should provide the correct (default) values to consumers outside of a provider', async () => { const FooContext = React.createContext({value: 'foo-initial'}); const BarContext = React.createContext({value: 'bar-initial'}); const FooConsumer = getConsumer(FooContext); @@ -369,10 +371,10 @@ describe('ReactNewContext', () => { , ); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); }); - it('multiple consumers in different branches', () => { + it('multiple consumers in different branches', async () => { const Context = React.createContext(1); const Consumer = getConsumer(Context); @@ -420,7 +422,7 @@ describe('ReactNewContext', () => { } ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput( <> @@ -430,7 +432,7 @@ describe('ReactNewContext', () => { // Update ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput( <> @@ -440,7 +442,7 @@ describe('ReactNewContext', () => { // Another update ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput( <> @@ -449,7 +451,7 @@ describe('ReactNewContext', () => { ); }); - it('compares context values with Object.is semantics', () => { + it('compares context values with Object.is semantics', async () => { const Context = React.createContext(1); const ContextConsumer = getConsumer(Context); @@ -498,7 +500,7 @@ describe('ReactNewContext', () => { } ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'App', 'Provider', 'Indirection', @@ -510,7 +512,7 @@ describe('ReactNewContext', () => { // Update ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'App', 'Provider', // Consumer should not re-render again @@ -519,7 +521,7 @@ describe('ReactNewContext', () => { expect(ReactNoop).toMatchRenderedOutput(); }); - it('context unwinds when interrupted', () => { + it('context unwinds when interrupted', async () => { const Context = React.createContext('Default'); const ContextConsumer = getConsumer(Context); @@ -564,14 +566,14 @@ describe('ReactNewContext', () => { } ReactNoop.render(); - expect(Scheduler).toFlushWithoutYielding(); + await waitForAll([]); expect(ReactNoop).toMatchRenderedOutput( // The second provider should use the default value. , ); }); - it("does not re-render if there's an update in a child", () => { + it("does not re-render if there's an update in a child", async () => { const Context = React.createContext(0); const Consumer = getConsumer(Context); @@ -603,19 +605,19 @@ describe('ReactNewContext', () => { // Initial mount ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['Consumer render prop', 'Child']); + await waitForAll(['Consumer render prop', 'Child']); expect(ReactNoop).toMatchRenderedOutput( , ); child.setState({step: 1}); - expect(Scheduler).toFlushAndYield(['Child']); + await waitForAll(['Child']); expect(ReactNoop).toMatchRenderedOutput( , ); }); - it('consumer bails out if value is unchanged and something above bailed out', () => { + it('consumer bails out if value is unchanged and something above bailed out', async () => { const Context = React.createContext(0); const Consumer = getConsumer(Context); @@ -660,7 +662,7 @@ describe('ReactNewContext', () => { // Initial mount ReactNoop.render(); - expect(Scheduler).toFlushAndYield([ + await waitForAll([ 'App', 'PureIndirection', 'ChildWithInlineRenderCallback', @@ -677,7 +679,7 @@ describe('ReactNewContext', () => { // Update (bailout) ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['App']); + await waitForAll(['App']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -687,7 +689,7 @@ describe('ReactNewContext', () => { // Update (no bailout) ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['App', 'Consumer', 'Consumer']); + await waitForAll(['App', 'Consumer', 'Consumer']); expect(ReactNoop).toMatchRenderedOutput( <> @@ -697,7 +699,7 @@ describe('ReactNewContext', () => { }); // @gate www - it("context consumer doesn't bail out inside hidden subtree", () => { + it("context consumer doesn't bail out inside hidden subtree", async () => { const Context = React.createContext('dark'); const Consumer = getConsumer(Context); @@ -712,7 +714,7 @@ describe('ReactNewContext', () => { } ReactNoop.render(); - expect(Scheduler).toFlushAndYield(['dark']); + await waitForAll(['dark']); expect(ReactNoop.getChildrenAsJSX()).toEqual(