Skip to content

Commit

Permalink
Codemod tests to waitFor pattern (9/?)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
acdlite committed Mar 4, 2023
1 parent d8f9a72 commit f4595ef
Show file tree
Hide file tree
Showing 20 changed files with 198 additions and 190 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ let TextResource;
let textResourceShouldFail;
let waitForAll;
let assertLog;
let waitForThrow;

describe('ReactCache', () => {
beforeEach(() => {
Expand All @@ -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]) => {
Expand Down Expand Up @@ -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(<App />);
expect(Scheduler).toFlushAndThrow('Failed to load: Hi');
await waitForThrow('Failed to load: Hi');
assertLog(['Error! [Hi]', 'Error! [Hi]']);
});

Expand Down
2 changes: 1 addition & 1 deletion packages/react-dom/src/__tests__/ReactDOMFloat-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4398,7 +4398,7 @@ background-color: green;
</body>
</html>,
);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
expect(getMeaningfulChildren(document)).toEqual(
<html>
<head>
Expand Down
22 changes: 10 additions & 12 deletions packages/react-dom/src/__tests__/ReactDeprecationWarnings-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

let React;
let ReactNoop;
let Scheduler;
let JSXDEVRuntime;
let waitForAll;

Expand All @@ -20,15 +19,14 @@ 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__) {
JSXDEVRuntime = require('react/jsx-dev-runtime');
}
});

it('should warn when given defaultProps', () => {
it('should warn when given defaultProps', async () => {
function FunctionalComponent(props) {
return null;
}
Expand All @@ -38,14 +36,14 @@ describe('ReactDeprecationWarnings', () => {
};

ReactNoop.render(<FunctionalComponent />);
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;
});
Expand All @@ -59,14 +57,14 @@ describe('ReactDeprecationWarnings', () => {
<MemoComponent />
</div>,
);
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;
Expand All @@ -79,7 +77,7 @@ describe('ReactDeprecationWarnings', () => {
}

ReactNoop.render(<Component />);
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. ' +
Expand Down Expand Up @@ -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;
Expand All @@ -121,7 +119,7 @@ describe('ReactDeprecationWarnings', () => {
}

ReactNoop.render(<Component />);
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. ' +
Expand All @@ -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;
Expand All @@ -152,7 +150,7 @@ describe('ReactDeprecationWarnings', () => {
}

ReactNoop.render(<Component />);
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. ' +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ let Suspense;
let scheduleCallback;
let NormalPriority;
let waitForAll;
let waitFor;

describe('ReactSuspenseList', () => {
beforeEach(() => {
Expand All @@ -24,6 +25,7 @@ describe('ReactSuspenseList', () => {

const InternalTestUtils = require('internal-test-utils');
waitForAll = InternalTestUtils.waitForAll;
waitFor = InternalTestUtils.waitFor;
});

function Text(props) {
Expand Down Expand Up @@ -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');
Expand Down
8 changes: 3 additions & 5 deletions packages/react-reconciler/src/__tests__/ReactFragment-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

let React;
let ReactNoop;
let Scheduler;
let waitForAll;

describe('ReactFragment', () => {
Expand All @@ -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;
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -742,7 +740,7 @@ describe('ReactFragment', () => {
await waitForAll([]);

ReactNoop.render(<Foo condition={false} />);
expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
await expect(async () => await waitForAll([])).toErrorDev(
'Each child in a list should have a unique "key" prop.',
);

Expand Down Expand Up @@ -939,7 +937,7 @@ describe('ReactFragment', () => {
}

ReactNoop.render(<Foo condition={true} />);
expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
await expect(async () => await waitForAll([])).toErrorDev(
'Each child in a list should have a unique "key" prop.',
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ describe('ReactHooksWithNoopRenderer', () => {
}
ReactNoop.render(<BadCounter />);

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' +
Expand All @@ -227,43 +227,43 @@ 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 <Text text={this.props.label + ': ' + count} />;
},
};
}
ReactNoop.render(<Counter />);
expect(() =>
expect(Scheduler).toFlushAndThrow(
// @gate !disableModulePatternComponents
it('throws inside module-style components', async () => {
function Counter() {
return {
render() {
const [count] = useState(0);
return <Text text={this.props.label + ': ' + count} />;
},
};
}
ReactNoop.render(<Counter />);
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' +
'2. You might be breaking the Rules of Hooks\n' +
'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 <Counter /> 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 <Counter /> 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 <Text text={count} />;
}
ReactNoop.render(<GoodCounter initialCount={10} />);
await waitForAll([10]);
});
}
// Confirm that a subsequent hook works properly.
function GoodCounter(props) {
const [count] = useState(props.initialCount);
return <Text text={count} />;
}
ReactNoop.render(<GoodCounter initialCount={10} />);
await waitForAll([10]);
});

it('throws when called outside the render phase', async () => {
expect(() => {
Expand Down Expand Up @@ -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(
<>
<Foo />
<Bar triggerUpdate={true} />
</>,
);
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(
<>
<Foo />
<Bar triggerUpdate={true} />
</>,
);
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 () => {
Expand Down Expand Up @@ -562,7 +560,7 @@ describe('ReactHooksWithNoopRenderer', () => {
return <Text text={count} />;
}
ReactNoop.render(<Counter />);
expect(Scheduler).toFlushAndThrow(
await waitForThrow(
'Too many re-renders. React limits the number of renders to prevent ' +
'an infinite loop.',
);
Expand Down Expand Up @@ -3805,7 +3803,7 @@ describe('ReactHooksWithNoopRenderer', () => {
assertLog(['A: 2, B: 3, C: 4']);
expect(ReactNoop).toMatchRenderedOutput(<span prop="A: 2, B: 3, C: 4" />);
ReactNoop.render(<App loadC={false} />);
expect(Scheduler).toFlushAndThrow(
await waitForThrow(
'Rendered fewer hooks than expected. This may be caused by an ' +
'accidental early return statement.',
);
Expand Down
19 changes: 10 additions & 9 deletions packages/react-reconciler/src/__tests__/ReactIncremental-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -1919,13 +1919,14 @@ describe('ReactIncremental', () => {
};

ReactNoop.render(<Recurse />);
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 <Recurse /> component appears to be a function component that returns a class instance. ' +
'Change Recurse to a class that extends React.Component instead. ' +
Expand Down Expand Up @@ -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()',
);
});
Expand Down
Loading

0 comments on commit f4595ef

Please sign in to comment.