I am trying to test a component which uses both react redux and react router. so when you click a card in the main page it routes to this particular component. here is the card component
here is the page I am routing to
const MovieDetail = () => {
const { id } = useParams();
const dispatch = useDispatch();
const movie = useSelector((state) => state.movies.selectedItem);
const crews = useSelector((state) => state.movies.detailList);
useEffect(() => {
dispatch(fetchDetail(id, "movies", movieActions));
dispatch(fetchDetailList(id, "movies", "crews", movieActions));
}, [dispatch, id]);
return (
<Box>
<Card
sx={{
display: "flex",
justifyContent: "space-between",
gap: "80px",
margin: "80px 10px 10px 10px",
}}
>
<Box sx={{ margin: "10px" }}>
<Typography sx={{ fontSize: "25px" }}>{movie.title}</Typography>
<Box
sx={{
paddingLeft: "3px",
marginTop: "20px",
fontSize: "24px",
display: "flex",
justifyContent: "space-between",
}}
>
<span>
<img
src="https://img.icons8.com/tiny-color/16/000000/star.png"
alt="star icon"
/>
IMDB Rating <i></i>: {movie.imdbRating}
</span>
<span>
IMDB Votes <i></i>: {movie.imdbVotes}
</span>
<span>
Runtime <i></i> : {movie.runtime}
</span>
<span>
Year <i></i> : {movie.year}
</span>
</Box>
<Typography sx={{ margin: "20px" }}>{movie.plot}</Typography>
</Box>
</Card>
</Box>
);
};
export default MovieDetail;
this is my handler for jest
rest.get("http://localhost:8080/api/movies/tt0389790", (req, res, ctx) => {
return res(
ctx.json({
id: "tt0389790",
title: "Bee Movie",
year: "2007",
country: "United States",
poster:
"https://m.media-amazon.com/images/M/MV5BMjE1MDYxOTA4MF5BMl5BanBnXkFtZTcwMDE0MDUzMw@@._V1_SX300.jpg",
})
);
}),
this is my custom wrapper
export function renderWithProviders(
ui,
{
route = '/',
history = createMemoryHistory({initialEntries: [route]}),
preloadedState = {},
storeUtils = store(preloadedState),
...renderOptions
} = {}
) {
function Wrapper({ ui}) {
return (
<Provider store={storeUtils}>
<Router history={history}>{ui}</Router>
</Provider>
);
}
return { store, ...render(ui, { wrapper: Wrapper, history, ...renderOptions }) };
}
and finally this is my test
test("data is shown properly", async () => {
renderWithProviders(
<Route path="/api/movies/tt0389790">
{props =><MovieDetail {...props} />}
</Route>,
{
route: '/api/movies/tt0389790',
},
);
const moviePoster = await screen.findByAltText("Bee Movie");
expect(moviePoster).toBeInTheDocument();
const movieTitle = await screen.findByText("Bee Movie");
expect(movieTitle).toBeInTheDocument();
const movieYear = await screen.findByText("2007");
expect(movieYear).toBeInTheDocument();
});
but nothing works and I am getting errors like
Uncaught [TypeError: Cannot read properties of undefined (reading 'pathname')]
this is my route
<Route path="/movies/:id" element={<MovieDetail />} />
after trying multiple ways I decided to not wrapp Router around all of my test components. the handlers mock server for requests with ids should look like this:
"http://localhost:8080/api/movies/:id"
which I made a mistake by writting it like this at first:
"http://localhost:8080/api/movies/tt0389790"
as for the test I used:
createMemoryHistory()
this is how I used it in the test:
const history = createMemoryHistory();
renderWithProviders(
<Router location={history.location} navigator={history}>
<MovieDetail />
</Router>
);
and changed my custom render to this:
export function renderWithProviders(
ui,
{
preloadedState = {},
storeUtils = store(preloadedState),
...renderOptions
} = {}
) {
function Wrapper({ children }) {
return <Provider store={storeUtils}>{children}</Provider>;
}
return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) };
}
after all these the test passed perfectly.