-1

I have the React/Typescript component below. It works as expected, but I'm trying to prove that with a test (using testing-library). I want to ensure that it correctly receives the level parameter and renders it to the page:

import React from "react";
import { useParams } from "react-router-dom";

type QuestionGridParams = {
  level: string;
};

export const QuestionGrid = () => {
  const { level } = useParams<QuestionGridParams>();

  return (
    <>
      <header>
        <h1>Level {level}</h1>
      </header>
    </>
  );
};

I'd expected to do this by stubbing the context, or the function that returns the context, so that it always returns a specified value for the parameter. None of my searching has given a solution that I can get to work. Closest I have come is the test below, which only proves that the component works if no parameter is passed in. I want to pass in a level of 4, for example, and assert that there is an element with the text Level 4.

I don't think that this is the approach I need, it just happens to be the only way for now I can get the test not to error when useParams() is invoked..

 test("page renders in the absence of a 'level' parameter", () => {
    const layout = render(
      <Router history={createMemoryHistory()}>
        <QuestionGrid/>
      </Router>
    );
    const header: any = layout.getByText("Level");
    expect(header).toBeInTheDocument();
  });

Any help getting past this mindblock would be greatly appreciated.

elgaz
  • 193
  • 1
  • 1
  • 13
  • You don't need to and shouldn't mock React Router here. Where do you think you're providing the params in the test's memory router? – jonrsharpe Jul 10 '21 at 14:41
  • Well, i guess wrapping the component inside a Router gives it a RouterContext, and I'm assuming that has an empty set of parameters to return. I just don't see a way to pass in the parameter values I want – elgaz Jul 10 '21 at 14:52
  • 1
    Does this answer your question? [Recommended approach for route-based tests within routes of react-router](https://stackoverflow.com/questions/65270992/recommended-approach-for-route-based-tests-within-routes-of-react-router) – jonrsharpe Jul 10 '21 at 15:02
  • @elgaz what testing library are you using? you don't specify. i would recommend using `@testing-library/react` aka, RTL. to prove it renders the correct route, you can query the dom for a piece of text from that page. for route based tests i typically use the page header text, eg `const {getByText} = render(); const heading = await waitFor(()=>getByText('Some Page Heading Text')); expect(heading).toBeInTheDocument();` pretty easy test. – r3wt Jul 10 '21 at 18:09
  • 1
    oh, also to set the route for the test, you can do `initialEntries={['/the/path/you/want']}` prop on `MemoryRouter`. this works really well for example if you want test correct route rendered for entire app on given path. i just use a for loop over route paths and it works well, 100% coverage for `App` component. – r3wt Jul 10 '21 at 18:13
  • @jonrsharpe Got it! I see what I think you were suggesting. Thanks for the help. – elgaz Jul 10 '21 at 18:27
  • @r3wt. Thanks, I've got a solution with your advice too! I am using testing-library – elgaz Jul 10 '21 at 18:36

1 Answers1

0

OK, so this worked:

test("page reads and renders the 'level' path parameter ", () => {
    const layout = render(
      <Router history={createMemoryHistory()}>
        <Route path={"/:level"}>
            <QuestionGrid/>
        </Route>
      </Router>
    );
    history.push("/4");
    const header: any = layout.getByText("Level 4");
    expect(header).toBeInTheDocument();
  });

or more elegantly

test("page reads and renders the 'level' path parameter ", () => {
    const layout = render(
      <MemoryRouter initialEntries={["/4"]}>
        <Route path={"/:level"}>
            <QuestionGrid/>
        </Route>
      </MemoryRouter>
    );
    const header: any = layout.getByText("Level 4");
    expect(header).toBeInTheDocument();
  });

No stubbing/mocking, just dynamically creating a real route. Welcome to React, me!

Thanks @jonrsharpe and @r3wt

elgaz
  • 193
  • 1
  • 1
  • 13