tomsah
tomsah

Reputation: 33

React testing library, dynamic import & React Router DOM always render the not-found component

I am having some trouble to test simple navigation,
I have a react app using react-router-dom and the App component is wrapped with suspense.
I also import my components dynamically to be able to code split.

For some reason, in the unit test when I trigger a click on the nav About link from the homepage.
it always renders the 404 NotFound components and never the about Component, which exists
What do I do wrong?

Main.js

const Main = () => <h1>Home page</h1>;

About.js

const About = () => <h1>About page</h1>;

NotFound.js

const NotFound = () => <div>404 this page does not exist</div>;

Nav.js

const Nav = () => (
  <div className="dummy-nav">
    <ul>
      <li>
        <Link to="/">Home</Link>
      </li>
      <li>
        <Link to="/about">About</Link>
      </li>
    </ul>
  </div>
);

App.js

import React, { Component, Suspense, lazy } from "react";
import { Switch, Route } from "react-router-dom";
import Nav from "./Nav";

 const Main = lazy(() => import(/* webpackChunkName: "Main" */ "./Main"));
 const About = lazy(() => import(/* webpackChunkName: "About" */ "./About"));
 const NotFound = lazy(() =>
  import(/* webpackChunkName: "About" */ "./NotFound")
 );

class App extends Component {
  render() {
    return (
      <Suspense fallback={<div>Loading...</div>}>
        <div>This is the App</div>
        <Nav />
        <Switch>
          <Route exact path="/" component={Main} />
          <Route path="/about" component={About} />
          <Route render={() => <NotFound />} />
        </Switch>
      </Suspense>
    );
  }
}

app.test.js

function renderWithRouter(
  ui,
  {
    route = "/",
    history = createMemoryHistory({ initialEntries: [route] }),
    ...renderOptions
  } = {}
) {
  // eslint-disable-next-line react/prop-types
  function Wrapper({ children }) {
    return <Router history={history}>{children}</Router>;
  }
  return {
    ...render(ui, { wrapper: Wrapper, ...renderOptions }),
    history
  };
}

test("renders homepage by default and can navigate to about", async () => {
  renderWithRouter(<App />);
  screen.getByText(/Home page/i);
  const link = screen.getByRole("link", { name: /About/i });
  userEvent.click(link);
  await waitFor(() => screen.getByText(/About page/i));
  screen.debug();
});

Here is a code sandbox with the broken test and the app structure that I am using, If you have any tips to use and test lazy and dynamic import, please share

https://codesandbox.io/s/react-router-suspense-lrnp7?file=/src/App.js

Upvotes: 1

Views: 1977

Answers (2)

Elli Zorro
Elli Zorro

Reputation: 501

You should use react-router-dom v6 if you want to use history v5.x.

https://www.npmjs.com/package/history

Upvotes: 0

tomsah
tomsah

Reputation: 33

I finally found what is going wrong but I don’t fully understand why.
The issue was the use of the latest version of the History package 5.0.0.
broken example => https://codesandbox.io/s/history-v50-always-render-404-after-usereventclick-h8osi?file=/src/tests/test.js

Downgrading to the previous version 4.10.1 fixes the issue. that is, any click on a Link will result to render the 404-page or nothing.

Working example with History 4.10.1 => https://codesandbox.io/s/history-v4101-fix-404-madness-4uitj?file=/src/tests/react-router.js:413-451

After looking at what changes in the latest History version,
I don’t see any breaking change or any obvious reason why that should happen.

Any idea of why that happens and how to use correctly the latest history??
If you get in this 404 madness, look at of your history version.

Upvotes: 0

Related Questions