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:
Dan Abramov
2016-11-30 17:00:14 +00:00
committed by GitHub
parent 623f608aab
commit b1c988d0de
2 changed files with 96 additions and 0 deletions
+3
View File
@@ -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')]);
});
});