mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Add tests for recovery from errors thrown in the reconciler (#8462)
Test that errors in the reconciler can be caught by error boundaries, and that we can still schedule updates if they are uncaught.
This commit is contained in:
@@ -1087,6 +1087,9 @@ src/renderers/shared/fiber/__tests__/ReactIncrementalErrorHandling-test.js
|
||||
* can schedule updates after uncaught error during umounting
|
||||
* continues work on other roots despite caught errors
|
||||
* continues work on other roots despite uncaught errors
|
||||
* catches reconciler errors in a boundary during mounting
|
||||
* catches reconciler errors in a boundary during update
|
||||
* recovers from uncaught reconciler errors
|
||||
|
||||
src/renderers/shared/fiber/__tests__/ReactIncrementalReflection-test.js
|
||||
* handles isMounted even when the initial render is deferred
|
||||
|
||||
@@ -275,4 +275,97 @@ describe('ReactIncrementalErrorHandling', () => {
|
||||
expect(ReactNoop.getChildren('e')).toEqual(null);
|
||||
expect(ReactNoop.getChildren('f')).toEqual(null);
|
||||
});
|
||||
|
||||
it('catches reconciler errors in a boundary during mounting', () => {
|
||||
spyOn(console, 'error');
|
||||
|
||||
class ErrorBoundary extends React.Component {
|
||||
state = {error: null};
|
||||
unstable_handleError(error) {
|
||||
this.setState({error});
|
||||
}
|
||||
render() {
|
||||
if (this.state.error) {
|
||||
return <span prop={this.state.error.message} />;
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
const InvalidType = undefined;
|
||||
const brokenElement = <InvalidType />;
|
||||
function BrokenRender(props) {
|
||||
return brokenElement;
|
||||
}
|
||||
|
||||
ReactNoop.render(
|
||||
<ErrorBoundary>
|
||||
<BrokenRender />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
ReactNoop.flush();
|
||||
expect(ReactNoop.getChildren()).toEqual([span(
|
||||
'Element type is invalid: expected a string (for built-in components) or ' +
|
||||
'a class/function (for composite components) but got: undefined.'
|
||||
)]);
|
||||
expect(console.error.calls.count()).toBe(1);
|
||||
});
|
||||
|
||||
it('catches reconciler errors in a boundary during update', () => {
|
||||
spyOn(console, 'error');
|
||||
|
||||
class ErrorBoundary extends React.Component {
|
||||
state = {error: null};
|
||||
unstable_handleError(error) {
|
||||
this.setState({error});
|
||||
}
|
||||
render() {
|
||||
if (this.state.error) {
|
||||
return <span prop={this.state.error.message} />;
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
const InvalidType = undefined;
|
||||
const brokenElement = <InvalidType />;
|
||||
function BrokenRender(props) {
|
||||
return props.fail ? brokenElement : <span />;
|
||||
}
|
||||
|
||||
ReactNoop.render(
|
||||
<ErrorBoundary>
|
||||
<BrokenRender fail={false} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
ReactNoop.flush();
|
||||
|
||||
ReactNoop.render(
|
||||
<ErrorBoundary>
|
||||
<BrokenRender fail={true} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
ReactNoop.flush();
|
||||
expect(ReactNoop.getChildren()).toEqual([span(
|
||||
'Element type is invalid: expected a string (for built-in components) or ' +
|
||||
'a class/function (for composite components) but got: undefined.'
|
||||
)]);
|
||||
expect(console.error.calls.count()).toBe(1);
|
||||
});
|
||||
|
||||
it('recovers from uncaught reconciler errors', () => {
|
||||
spyOn(console, 'error');
|
||||
const InvalidType = undefined;
|
||||
ReactNoop.render(<InvalidType />);
|
||||
expect(() => {
|
||||
ReactNoop.flush();
|
||||
}).toThrowError(
|
||||
'Element type is invalid: expected a string (for built-in components) or ' +
|
||||
'a class/function (for composite components) but got: undefined.'
|
||||
);
|
||||
|
||||
ReactNoop.render(<span prop="hi" />);
|
||||
ReactNoop.flush();
|
||||
expect(ReactNoop.getChildren()).toEqual([span('hi')]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user