-
Notifications
You must be signed in to change notification settings - Fork 47.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixed render-phase error boundary reconciliation for concurrent mode …
…(and added tests)
- Loading branch information
Brian Vaughn
committed
Sep 27, 2018
1 parent
89fb6e1
commit d8700c1
Showing
2 changed files
with
119 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
111 changes: 111 additions & 0 deletions
111
packages/react-reconciler/src/__tests__/ErrorBoundaryReconciliation-test.internal.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
const jestDiff = require('jest-diff'); | ||
|
||
describe('ErrorBoundaryReconciliation', () => { | ||
let BrokenRender; | ||
let DidCatchErrorBoundary; | ||
let GetDerivedErrorBoundary; | ||
let React; | ||
let ReactFeatureFlags; | ||
let ReactTestRenderer; | ||
let span; | ||
|
||
beforeEach(() => { | ||
jest.resetModules(); | ||
|
||
ReactFeatureFlags = require('shared/ReactFeatureFlags'); | ||
ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false; | ||
ReactTestRenderer = require('react-test-renderer'); | ||
React = require('react'); | ||
|
||
DidCatchErrorBoundary = class extends React.Component { | ||
state = {error: null}; | ||
componentDidCatch(error) { | ||
this.setState({error}); | ||
} | ||
render() { | ||
return this.state.error | ||
? React.createElement(this.props.fallbackTagName, { | ||
prop: 'ErrorBoundary', | ||
}) | ||
: this.props.children; | ||
} | ||
}; | ||
|
||
GetDerivedErrorBoundary = class extends React.Component { | ||
state = {error: null}; | ||
static getDerivedStateFromCatch(error) { | ||
return {error}; | ||
} | ||
render() { | ||
return this.state.error | ||
? React.createElement(this.props.fallbackTagName, { | ||
prop: 'ErrorBoundary', | ||
}) | ||
: this.props.children; | ||
} | ||
}; | ||
|
||
const InvalidType = undefined; | ||
BrokenRender = ({fail}) => | ||
fail ? <InvalidType /> : <span prop="BrokenRender" />; | ||
|
||
function toHaveRenderedChildren(renderer, children) { | ||
let actual, expected; | ||
try { | ||
actual = renderer.toJSON(); | ||
expected = ReactTestRenderer.create(children).toJSON(); | ||
expect(actual).toEqual(expected); | ||
} catch (error) { | ||
return { | ||
message: () => jestDiff(expected, actual), | ||
pass: false, | ||
}; | ||
} | ||
return {pass: true}; | ||
} | ||
expect.extend({toHaveRenderedChildren}); | ||
}); | ||
|
||
[true, false].forEach(isConcurrent => { | ||
function sharedTest(ErrorBoundary, fallbackTagName) { | ||
const renderer = ReactTestRenderer.create( | ||
<ErrorBoundary fallbackTagName={fallbackTagName}> | ||
<BrokenRender fail={false} /> | ||
</ErrorBoundary>, | ||
{unstable_isConcurrent: isConcurrent}, | ||
); | ||
if (isConcurrent) { | ||
renderer.unstable_flushAll(); | ||
} | ||
expect(renderer).toHaveRenderedChildren(<span prop="BrokenRender" />); | ||
|
||
expect(() => { | ||
renderer.update( | ||
<ErrorBoundary fallbackTagName={fallbackTagName}> | ||
<BrokenRender fail={true} /> | ||
</ErrorBoundary>, | ||
); | ||
if (isConcurrent) { | ||
renderer.unstable_flushAll(); | ||
} | ||
}).toWarnDev(isConcurrent ? ['invalid', 'invalid'] : ['invalid']); | ||
expect(renderer).toHaveRenderedChildren( | ||
React.createElement(fallbackTagName, {prop: 'ErrorBoundary'}), | ||
); | ||
} | ||
|
||
describe(isConcurrent ? 'concurrent' : 'sync', () => { | ||
it('componentDidCatch can recover by rendering an element of the same type', () => | ||
sharedTest(DidCatchErrorBoundary, 'span')); | ||
|
||
it('componentDidCatch can recover by rendering an element of a different type', () => | ||
sharedTest(DidCatchErrorBoundary, 'div')); | ||
|
||
it('getDerivedStateFromCatch can recover by rendering an element of the same type', () => | ||
sharedTest(GetDerivedErrorBoundary, 'span')); | ||
|
||
it('getDerivedStateFromCatch can recover by rendering an element of a different type', () => | ||
sharedTest(GetDerivedErrorBoundary, 'div')); | ||
}); | ||
}); | ||
}); |