-
Notifications
You must be signed in to change notification settings - Fork 82
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add error boundary component and test
- Loading branch information
1 parent
1f634b2
commit 180b8e0
Showing
2 changed files
with
117 additions
and
0 deletions.
There are no files selected for viewing
90 changes: 90 additions & 0 deletions
90
...est-react-components-with-jest-and-react-testing-library/__tests__/error-boundary.test.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,90 @@ | ||
import 'jest-dom/extend-expect'; | ||
import 'react-testing-library/cleanup-after-each'; | ||
|
||
import React from 'react'; | ||
import {render, fireEvent} from 'react-testing-library'; | ||
|
||
import {ErrorBoundary} from '../src/error-boundary'; | ||
import {reportError as mockReportError} from '../src/api'; | ||
|
||
jest.mock('../src/api', () => ({ | ||
reportError: jest.fn(() => Promise.resolve({success: true})), | ||
})); | ||
|
||
const Bomb = ({shouldThrow}) => { | ||
if (shouldThrow) throw new Error('boom'); | ||
|
||
return null; | ||
}; | ||
|
||
// we can mock out console.error that our Bomb component will throw, and do it | ||
// before each test runs | ||
// By doing this we run the risk of not getting information that would be | ||
// valuable to our tests, so we need to assert that console.error is being | ||
// called the correct number of times | ||
beforeEach(() => { | ||
jest.spyOn(console, 'error').mockImplementation(() => {}); | ||
}); | ||
// we need to clean that up, so we restore console.error its original | ||
// implementation after each test | ||
afterEach(() => { | ||
console.error.mockRestore(); | ||
}); | ||
|
||
describe('ErrorBoundary', () => { | ||
test('it reports errors and displays a message', () => { | ||
const {container, debug, getByText, rerender} = render( | ||
<ErrorBoundary> | ||
<Bomb /> | ||
</ErrorBoundary> | ||
); | ||
|
||
// rerender our component with an error, so we can assert that it's behaving | ||
// as it should | ||
rerender( | ||
<ErrorBoundary> | ||
<Bomb shouldThrow={true} /> | ||
</ErrorBoundary> | ||
); | ||
|
||
// assert that our container has the text indicating that there's a problem | ||
expect(container).toHaveTextContent('problem'); | ||
// console.error is called twice - once by React, and once by Jest | ||
expect(console.error).toHaveBeenCalledTimes(2); | ||
|
||
// expect.any and expect.stringContaining allow us to pass through | ||
// non-literal values to assertions | ||
// We need to assert that reportError is called with the correct parameters, | ||
// and instead of asserting against literal matches, we can specify to Jest | ||
// a more general constructor that we expect the parameters to match against | ||
const error = expect.any(Error); | ||
const info = {componentStack: expect.stringContaining('Bomb')}; | ||
expect(mockReportError).toHaveBeenCalledTimes(1); | ||
expect(mockReportError).toHaveBeenCalledWith(error, info); | ||
|
||
// we need to reset our mocks here so that we can evaluate them again | ||
// without having to take into account the previous times they were called, | ||
// and the parameters they received in those calls | ||
console.error.mockReset(); | ||
mockReportError.mockReset(); | ||
|
||
// we rerender our component without throwing an error. At this point the | ||
// component state is unchanged, so our component is still rendering the | ||
// button | ||
rerender( | ||
<ErrorBoundary> | ||
<Bomb /> | ||
</ErrorBoundary> | ||
); | ||
|
||
// fire an event on the try again button to trigger a rerender from within | ||
// the component | ||
fireEvent.click(getByText(/try again/i)); | ||
|
||
// we assert that the container doesn't contain the 'problem' text, and that | ||
// console.error and reportError were not called | ||
expect(container).not.toHaveTextContent('problem'); | ||
expect(console.error).not.toHaveBeenCalled(); | ||
expect(mockReportError).not.toHaveBeenCalled(); | ||
}); | ||
}); |
27 changes: 27 additions & 0 deletions
27
06-test-react-components-with-jest-and-react-testing-library/src/error-boundary.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,27 @@ | ||
import React from 'react'; | ||
import {reportError} from './api'; | ||
|
||
class ErrorBoundary extends React.Component { | ||
state = {hasError: false}; | ||
|
||
componentDidCatch(error, info) { | ||
this.setState({hasError: true}); | ||
|
||
reportError(error, info); | ||
} | ||
|
||
tryAgain = () => this.setState({hasError: false}); | ||
|
||
render() { | ||
return this.state.hasError ? ( | ||
<div> | ||
<div>There was a problem.</div> | ||
<button onClick={this.tryAgain}>try again?</button> | ||
</div> | ||
) : ( | ||
this.props.children | ||
); | ||
} | ||
} | ||
|
||
export {ErrorBoundary}; |