Search code examples
reactjstypescriptreact-router-domreact-testing-library

React testing with `Link` or `NavLink` throws error


When I test home page which uses either Link or NavLink from react-router-dom test throws error as: TypeError: Cannot destructure property 'basename' of 'React__namespace.useContext(...)' as it is null.

I checked couple of answers here:

  1. Uncaught TypeError: Cannot destructure property 'basename' of 'React2.useContext(...)' as it is null

  2. React testing-library not testing component containing <Link/> react router dom V6 `

But nothing reproduces my issue.

My Home page component:

import { Link } from 'react-router-dom';
import PageNav from '../components/PageNav';
import styles from './Homepage.module.css';

export default function Homepage() {
  return (
    <main className={styles.homepage}>
      <PageNav />
      <section>
        <h1>
          You travel the world.
          <br />
          WorldWise keeps track of your adventures.
        </h1>
        <h2>
          A world map that tracks your footsteps into every city you can think
          of. Never forget your wonderful experiences, and show your friends how
          you have wandered the world.
        </h2>
        <Link to='app' className='cta'>
          Start tracking now
        </Link>
      </section>
    </main>
  );
}

Basic test:

import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { describe } from 'vitest';
import Homepage from './Homepage';

describe('home page component', () => {
  it('should renders home page ', () => {
    render(
      <MemoryRouter>
        <Homepage />
      </MemoryRouter>
    );
  });
  it('should renders home page ', () => {
    render(<Homepage />);
    const h1 = screen.getByRole('heading', { level: 1 });
    expect(h1).toBeInTheDocument();
    expect(h1).toHaveTextContent(/You travel the world./);

    const h2 = screen.getByRole('heading', { level: 2 });
    expect(h2).toBeInTheDocument();
    expect(h2).toHaveTextContent(/A world map that tracks your/);
  });
});

App.tsx:

import { Route, Routes } from 'react-router-dom';
import AppLayout from './pages/AppLayout';
import HomePage from './pages/Homepage';
import Login from './pages/Login';
import Pagenotfound from './pages/PageNotFound';
import Pricing from './pages/Pricing';
import Product from './pages/Product';

export default function App() {
  return (
    <Routes>
      <Route index element={<HomePage />} />
      <Route path='product' element={<Product />} />
      <Route path='pricing' element={<Pricing />} />
      <Route path='login' element={<Login />} />

      <Route path='app' element={<AppLayout />}>
        <Route index element={<p>List</p>} />
        <Route path='cities' element={<p>List of cities</p>} />
        <Route path='countries' element={<p>List of Countries</p>} />
        <Route path='form' element={<p>Form</p>} />
      </Route>

      <Route path='*' element={<Pagenotfound />} />
    </Routes>
  );
}

Main.tsx:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import App from './App.tsx';
import './index.css';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <BrowserRouter basename={import.meta.env.BASE_URL}>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

part of error:

The above error occurred in the <NavLink> component:

at NavLinkWithRef (/Users/mohamedarif/Documents/Tranings/React/Ultimate-React-23/node_modules/react-router-dom/dist/umd/react-router-dom.development.js:548:25)
at li
at ul
at nav
at PageNav
at main
at Homepage

Solution

  • Your test is wrong. Everywhere you are rendering Homepage, you need to wrap it with MemoryRouter/BrowserRouter. In your second test you have not done that:

    
    import { render, screen } from '@testing-library/react';
    import { MemoryRouter } from 'react-router-dom';
    import { describe } from 'vitest';
    import Homepage from './Homepage';
    
    describe('home page component', () => {
      it('should renders home page ', () => {
        render(
          <MemoryRouter>
            <Homepage />
          </MemoryRouter>
        );
      });
    
      it('should renders home page ', () => {
        render(
          <MemoryRouter>
            <Homepage />
          </MemoryRouter>
        );    
    
        const h1 = screen.getByRole('heading', { level: 1 });
        expect(h1).toBeInTheDocument();
        expect(h1).toHaveTextContent(/You travel the world./);
    
        const h2 = screen.getByRole('heading', { level: 2 });
        expect(h2).toBeInTheDocument();
        expect(h2).toHaveTextContent(/A world map that tracks your/);
      });
    });