Reputation: 2949
I'm using React-Testing-Library and I have my main.tsx
file like this with all the Redux-Toolkit and React-Router wrappers as follows:
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<Provider store={store}>
<Suspense fallback={<LoadingSpinner />}>
<BrowserRouter>
<Routes>
<Route path="/*" element={<App />} />
</Routes>
</BrowserRouter>
</Suspense>
</Provider>
</React.StrictMode>,
);
Now, when I try to test any component that uses any useDispatch
or useNavigate
the test fails unless I wrap that component with the providers like this:
test("Inputs should be initially empty", () => {
render(
<Provider store={store}>
<BrowserRouter>
<Login />
</BrowserRouter>
,
</Provider>,
);
const emailInput: HTMLInputElement = screen.getByRole("textbox", {
name: /email/i,
});
expect(emailInput.value).toBe("");
});
Now, the test has passed and everything is good.
My question is, is there a way to initially wrap all the components I'm going to test by default with these wrappers? So that I don't have to wrap all my test components with these every time?
Upvotes: 7
Views: 5759
Reputation: 1252
Here is an improved version of the https://testing-library.com/docs/react-testing-library/setup/#custom-render. The problem with the answer above (also found on the testing-library.com website), is that you can't use the "wrapper" property inside of the test-suite instance. This version allows you to use the "wrapper" property:
import { render, RenderOptions } from '@testing-library/react';
import { omit } from 'lodash';
import { ReactElement } from 'react';
import { ParentWrapper } from './parent-wrapper'
const customRender = (ui: ReactElement, renderOptions?: RenderOptions) => {
return render(ui, {
wrapper: props => {
const renderedComponent = renderOptions?.wrapper ? <renderOptions.wrapper {...props} /> : props.children;
return <ParentWrapper>{renderedComponent}</ParentWrapper>;
},
...(renderOptions ? omit(renderOptions, 'wrapper') : {}),
});
};
export * from '@testing-library/react';
export { customRender as render };
This way when you use the custom render you can add another wrapper:
import { Login, SubWrapper } from '.'
test("Inputs should be initially empty", () => {
render(<Login />, {
wrapper: props => <SubWrapper>{props.children}</SubWrapper>
}); // <-- custom sub wrapper
const emailInput: HTMLInputElement = screen.getByRole("textbox", {
name: /email/i,
});
expect(emailInput.value).toBe("");
});
The output result should be following:
<ParentWrapper>
<SubWrapper>
<Login />
</SubWrapper>
</ParentWrapper>
Upvotes: 1
Reputation: 203373
Create a wrapper component that renders any providers and wraps the children
prop.
const ProviderWrapper = ({ children }) => (
<Provider store={store}>
<BrowserRouter>
{children}
</BrowserRouter>
</Provider>
);
test("Inputs should be initially empty", () => {
render(<Login />, { wrapper: ProviderWrapper });
const emailInput: HTMLInputElement = screen.getByRole("textbox", {
name: /email/i,
});
expect(emailInput.value).toBe("");
});
If you just need to wrap every component you test with the same providers then you can also create a custom render
function.
Example:
import {render} from '@testing-library/react';
const ProviderWrapper = ({ children }) => (
<Provider store={store}>
<BrowserRouter>
{children}
</BrowserRouter>
</Provider>
);
const customRender = (ui, options) =>
render(ui, { wrapper: ProviderWrapper, ...options });
// re-export everything
export * from '@testing-library/react';
// override render method
export { customRender as render };
test("Inputs should be initially empty", () => {
render(<Login />); // <-- custom render already wraps with providers
const emailInput: HTMLInputElement = screen.getByRole("textbox", {
name: /email/i,
});
expect(emailInput.value).toBe("");
});
Upvotes: 16