Codemod tests to waitFor pattern (9/?) (#26309)

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.
This commit is contained in:
Andrew Clark
2023-03-04 18:06:20 -05:00
committed by GitHub
parent 64dde70827
commit 25685d8a90
22 changed files with 233 additions and 217 deletions
@@ -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(<App />);
expect(Scheduler).toFlushAndThrow('Failed to load: Hi');
await waitForThrow('Failed to load: Hi');
assertLog(['Error! [Hi]', 'Error! [Hi]']);
});
+1 -1
View File
@@ -4398,7 +4398,7 @@ background-color: green;
</body>
</html>,
);
expect(Scheduler).toFlushWithoutYielding();
await waitForAll([]);
expect(getMeaningfulChildren(document)).toEqual(
<html>
<head>
@@ -1092,8 +1092,8 @@ describe('ReactDOMServerSelectiveHydration', () => {
const innerHTML = ReactDOMServer.renderToString(<InnerApp />);
innerContainer.innerHTML = innerHTML;
expect(OuterScheduler).toHaveYielded(['Outer']);
expect(InnerScheduler).toHaveYielded(['Inner']);
expect(OuterScheduler.unstable_clearYields()).toEqual(['Outer']);
expect(InnerScheduler.unstable_clearYields()).toEqual(['Inner']);
suspendOuter = true;
suspendInner = true;
@@ -1101,8 +1101,10 @@ describe('ReactDOMServerSelectiveHydration', () => {
OuterReactDOMClient.hydrateRoot(outerContainer, <OuterApp />);
InnerReactDOMClient.hydrateRoot(innerContainer, <InnerApp />);
expect(OuterScheduler).toFlushAndYield(['Suspend Outer']);
expect(InnerScheduler).toFlushAndYield(['Suspend Inner']);
OuterScheduler.unstable_flushAllWithoutAsserting();
InnerScheduler.unstable_flushAllWithoutAsserting();
expect(OuterScheduler.unstable_clearYields()).toEqual(['Suspend Outer']);
expect(InnerScheduler.unstable_clearYields()).toEqual(['Suspend Inner']);
innerDiv = document.querySelector('#inner');
@@ -1115,7 +1117,7 @@ describe('ReactDOMServerSelectiveHydration', () => {
InnerScheduler.unstable_flushAllWithoutAsserting();
});
expect(OuterScheduler).toHaveYielded(['Suspend Outer']);
expect(OuterScheduler.unstable_clearYields()).toEqual(['Suspend Outer']);
if (
gate(
flags =>
@@ -1124,10 +1126,12 @@ describe('ReactDOMServerSelectiveHydration', () => {
) {
// InnerApp doesn't see the event because OuterApp calls stopPropagation in
// capture phase since the event is blocked on suspended component
expect(InnerScheduler).toHaveYielded([]);
expect(InnerScheduler.unstable_clearYields()).toEqual([]);
} else {
// no stopPropagation
expect(InnerScheduler).toHaveYielded(['Suspend Inner']);
expect(InnerScheduler.unstable_clearYields()).toEqual([
'Suspend Inner',
]);
}
assertLog([]);
@@ -1149,15 +1153,15 @@ describe('ReactDOMServerSelectiveHydration', () => {
InnerScheduler.unstable_flushAllWithoutAsserting();
});
expect(OuterScheduler).toHaveYielded(['Suspend Outer']);
expect(OuterScheduler.unstable_clearYields()).toEqual(['Suspend Outer']);
// Inner App renders because it is unblocked
expect(InnerScheduler).toHaveYielded(['Inner']);
expect(InnerScheduler.unstable_clearYields()).toEqual(['Inner']);
// No event is replayed yet
assertLog([]);
dispatchMouseHoverEvent(innerDiv);
expect(OuterScheduler).toHaveYielded([]);
expect(InnerScheduler).toHaveYielded([]);
expect(OuterScheduler.unstable_clearYields()).toEqual([]);
expect(InnerScheduler.unstable_clearYields()).toEqual([]);
// No event is replayed yet
assertLog([]);
@@ -1172,9 +1176,9 @@ describe('ReactDOMServerSelectiveHydration', () => {
// Nothing happens to inner app yet.
// Its blocked on the outer app replaying the event
expect(InnerScheduler).toHaveYielded([]);
expect(InnerScheduler.unstable_clearYields()).toEqual([]);
// Outer hydrates and schedules Replay
expect(OuterScheduler).toHaveYielded(['Outer']);
expect(OuterScheduler.unstable_clearYields()).toEqual(['Outer']);
// No event is replayed yet
assertLog([]);
@@ -1203,9 +1207,9 @@ describe('ReactDOMServerSelectiveHydration', () => {
});
// Outer resolves and scheduled replay
expect(OuterScheduler).toHaveYielded(['Outer']);
expect(OuterScheduler.unstable_clearYields()).toEqual(['Outer']);
// Inner App is still blocked
expect(InnerScheduler).toHaveYielded([]);
expect(InnerScheduler.unstable_clearYields()).toEqual([]);
// Replay outer event
await act(async () => {
@@ -1217,12 +1221,12 @@ describe('ReactDOMServerSelectiveHydration', () => {
// Inner is still blocked so when Outer replays the event in capture phase
// inner ends up caling stopPropagation
assertLog([]);
expect(OuterScheduler).toHaveYielded([]);
expect(InnerScheduler).toHaveYielded(['Suspend Inner']);
expect(OuterScheduler.unstable_clearYields()).toEqual([]);
expect(InnerScheduler.unstable_clearYields()).toEqual(['Suspend Inner']);
dispatchMouseHoverEvent(innerDiv);
expect(OuterScheduler).toHaveYielded([]);
expect(InnerScheduler).toHaveYielded([]);
expect(OuterScheduler.unstable_clearYields()).toEqual([]);
expect(InnerScheduler.unstable_clearYields()).toEqual([]);
assertLog([]);
await act(async () => {
@@ -1234,9 +1238,9 @@ describe('ReactDOMServerSelectiveHydration', () => {
});
// Inner hydrates
expect(InnerScheduler).toHaveYielded(['Inner']);
expect(InnerScheduler.unstable_clearYields()).toEqual(['Inner']);
// Outer was hydrated earlier
expect(OuterScheduler).toHaveYielded([]);
expect(OuterScheduler.unstable_clearYields()).toEqual([]);
await act(async () => {
Scheduler.unstable_flushAllWithoutAsserting();
@@ -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(<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;
});
@@ -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;
@@ -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. ' +
@@ -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(<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. ' +
@@ -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(<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. ' +
@@ -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');
@@ -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(<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.',
);
@@ -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.',
);
@@ -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' +
@@ -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 <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' +
@@ -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 <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(() => {
@@ -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 () => {
@@ -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.',
);
@@ -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.',
);
@@ -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(<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. ' +
@@ -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()',
);
});
@@ -1219,7 +1219,7 @@ describe('ReactIncrementalErrorHandling', () => {
expect(ReactNoop).toMatchRenderedOutput(<span prop="a" />);
});
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', () => {
<BrokenRender />
</ErrorBoundary>,
);
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', () => {
<BrokenRender fail={true} />
</ErrorBoundary>,
);
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(<Foo hide={true} />);
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(<errorInBeginPhase />);
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(<div />, () => {
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(<Provider />);
expect(() => {
expect(Scheduler).toFlushAndThrow('Oops!');
}).toErrorDev([
'Warning: The <Provider /> 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(<Provider />);
await expect(async () => {
await waitForThrow('Oops!');
}).toErrorDev([
'Warning: The <Provider /> 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');
});
}
});
@@ -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', () => {
</span>
</div>,
);
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', () => {
</span>
</div>,
);
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', () => {
</span>
</div>,
);
expect(Scheduler).toFlushAndThrow('render error');
await waitForThrow('render error');
expect(logCapturedErrorCalls.length).toBe(1);
expect(logCapturedErrorCalls[0]).toEqual(
__DEV__
@@ -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(<errorInBeginPhase />);
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 () => {
@@ -1296,7 +1296,7 @@ describe('ReactIncrementalSideEffects', () => {
}
ReactNoop.render(<Foo show={true} />);
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' +
@@ -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, ' +
@@ -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', () => {
<BadLazy />
</Suspense>,
);
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', () => {
<Lazy2 text="Hello" />
</Suspense>,
);
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__
+2 -2
View File
@@ -58,7 +58,7 @@ describe('memo', () => {
return <App ref={() => {}} />;
}
ReactNoop.render(<Outer />);
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 <App ref={() => {}} />;
}
ReactNoop.render(<Outer />);
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.',
@@ -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(
<Context.Provider anyPropNameOtherThanValue="value could be anything" />,
);
expect(() => expect(Scheduler).toFlushWithoutYielding()).toErrorDev(
await expect(async () => await waitForAll([])).toErrorDev(
'The `value` prop is required for the `<Context.Provider>`. 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(<Context.Consumer />);
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(<span prop="goodbye" />);
});
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(<Cls />);
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(<Foo />);
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(<Foo />);
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(<Foo />);
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', () => {
<Context.Provider value={null} />
</errorInCompletePhase>,
);
expect(Scheduler).toFlushAndThrow('Error in host config.');
await waitForThrow('Error in host config.');
ReactNoop.render(
<Context.Provider value={10}>
@@ -1395,12 +1395,13 @@ describe('ReactInteractionTracing', () => {
root.render(<App navigate={true} markerName="marker two" />);
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');
@@ -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(<Counter incrementBy={1} />);
expect(Scheduler).toFlushAndThrow(
await waitForThrow(
"A function wrapped in useEffectEvent can't be called during rendering.",
);
@@ -1518,16 +1518,14 @@ describe('useMutableSource', () => {
}
const root = ReactNoop.createRoot();
await act(async () => {
root.render(
<>
<Read />
</>,
);
expect(() => expect(Scheduler).toFlushAndYield(['a'])).toErrorDev(
'Mutable source should not return a function as the snapshot value.',
);
});
root.render(
<>
<Read />
</>,
);
await expect(async () => await waitForAll(['a'])).toErrorDev(
'Mutable source should not return a function as the snapshot value.',
);
expect(root).toMatchRenderedOutput('a');
});
@@ -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`, () => {
<errorInCompletePhase>hi</errorInCompletePhase>
</React.Profiler>,
);
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`, () => {
</errorInCompletePhase>
</React.Profiler>,
);
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.
@@ -144,7 +144,8 @@ describe('Scheduler', () => {
assertLog([]);
Scheduler.unstable_advanceTime(1);
expect(Scheduler).toFlushExpired(['A']);
Scheduler.unstable_flushExpired();
assertLog(['A']);
});
it('continues working on same task after yielding', async () => {
@@ -221,7 +222,8 @@ describe('Scheduler', () => {
// Advance time by just a bit more. This should expire all the remaining work.
Scheduler.unstable_advanceTime(1);
expect(Scheduler).toFlushExpired(['C', 'D']);
Scheduler.unstable_flushExpired();
assertLog(['C', 'D']);
});
it('continuations are interrupted by higher priority work', async () => {
@@ -326,7 +328,8 @@ describe('Scheduler', () => {
// Immediate callback hasn't fired, yet.
assertLog([]);
// They all flush immediately within the subsequent task.
expect(Scheduler).toFlushExpired(['A', 'B', 'C', 'D']);
Scheduler.unstable_flushExpired();
assertLog(['A', 'B', 'C', 'D']);
});
it('nested immediate callbacks are added to the queue of immediate callbacks', () => {
@@ -345,7 +348,8 @@ describe('Scheduler', () => {
);
assertLog([]);
// C should flush at the end
expect(Scheduler).toFlushExpired(['A', 'B', 'D', 'C']);
Scheduler.unstable_flushExpired();
assertLog(['A', 'B', 'D', 'C']);
});
it('wrapped callbacks have same signature as original callback', () => {
@@ -410,12 +414,12 @@ describe('Scheduler', () => {
throw new Error('Oops C');
});
expect(() => expect(Scheduler).toFlushExpired()).toThrow('Oops A');
expect(() => Scheduler.unstable_flushExpired()).toThrow('Oops A');
assertLog(['A']);
// B and C flush in a subsequent event. That way, the second error is not
// swallowed.
expect(() => expect(Scheduler).toFlushExpired()).toThrow('Oops C');
expect(() => Scheduler.unstable_flushExpired()).toThrow('Oops C');
assertLog(['B', 'C']);
});
@@ -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);