Pretty much what the title says.
I have a a test case that looks like this:
it("recovers from error boundry", async () => {
const user = userEvent.setup();
const ThrowError = () => {
throw new Error("Test");
};
render(
<ErrorBoundary>
<ThrowError />
</ErrorBoundary>
);
expect(screen.getByTestId("alert")).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: "Try again?" }));
expect(screen.queryByRole("alert")).not.toBeInTheDocument();
});
And an ErrorBoundry which looks like this:
class ErrorBoundary extends Component<ErrorBoundryProps, ErrorBoundryState> {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: Error, info: ErrorInfo) {
// IRL would log to an error reporting serive
//errorService.log({ error, info });
}
render() {
if (this.state.hasError) {
return (
<div className="app-error-boundry" role="alert">
<div className="app-error-boundry__content">
<h1 className="app-error-boundry__title">Something went wrong.</h1>
<button
type="button"
className="app-error-boundry__btn btn"
onClick={() => this.setState({ hasError: false })}
>
Try again?
</button>
</div>
</div>
);
}
return this.props.children;
}
}
However, in the test the ErrorBoundry is still being found after the button to reset error state is being clicked.
CodePudding user response:
When you reset the error state, the ErrorBoundary
component will render this.props.children
which is the ThrowError
component. The problem is this component will always throw an error when it renders. So the ErrorBoundary
will always catch the error, and the error state hasError
will always be true. There is no chance of recovering from the error.
You should use some conditions to throw the error from your test component.
E.g.
index.tsx
:
import React from "react";
export class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch() { }
render() {
if (this.state.hasError) {
return (
<div role="alert">
<div>
<h1>Something went wrong.</h1>
<button
type="button"
onClick={() => this.setState({ hasError: false })}
>
Try again?
</button>
</div>
</div>
);
}
return this.props.children;
}
}
index.test.tsx
:
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import React, { useState } from 'react';
import { ErrorBoundary } from '.';
describe('74115177', () => {
test('should pass', () => {
const ThrowError = () => {
const [count, setCount] = useState(0);
if (count > 1) {
throw new Error("Test");
}
return <div>
<p>count: {count}</p>
<button onClick={() => setCount(count 1)}>increment</button>
</div>
};
render(
<ErrorBoundary>
<ThrowError />
</ErrorBoundary>
);
fireEvent.click(screen.getByRole('button', { name: 'increment' }))
fireEvent.click(screen.getByRole('button', { name: 'increment' }))
expect(screen.queryByRole("alert")).toBeInTheDocument();
fireEvent.click(screen.getByRole("button", { name: "Try again?" }));
expect(screen.queryByRole("alert")).not.toBeInTheDocument();
})
})