Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Router v4 Redirect unit test

How do I unit test the component in react router v4? I am unsuccessfully trying to unit test a simple component with a redirect using jest and enzyme.

My component:

 const AppContainer = ({ location }) =>
  (isUserAuthenticated()
    ? <AppWithData />
    : <Redirect
        to={{
          pathname: "/login",
          state: { from: location }
        }}
      />);

My attempt to test it:

  function setup() {
      const enzymeWrapper = mount(
        <MemoryRouter initialEntries={["/"]}>
          <AppContainer />
        </MemoryRouter>
      );

      return {
        enzymeWrapper
      };
    }

    jest.mock("lib/authAPI", () => ({
      isUserAuthenticated: jest.fn(() => false)
    }));

    describe("AppContainer component", () => {
      it("renders redirect", () => {
        const { enzymeWrapper } = setup();

        expect(enzymeWrapper.find("<Redirect></Redirect>")).toBe(true);
      });
    });
like image 499
Luca Marangon Avatar asked Jun 01 '17 14:06

Luca Marangon


People also ask

How to check that a React component successfully redirects to another page?

You’ve set up react-testing-library with Jest and react-router. You want to check that a component successfully redirects to another page. But how? Your React application comes with a protected route. If a user is not authenticated, the app should redirect the user to the login screen.

What is the use of memoryrouter in react?

MemoryRouter works when you don't need access to the history object itself in the test, but just need the components to be able to render and navigate. If you do need to change the history, you could use BrowserRouter. import { MemoryRouter } from 'react-router-dom' test('full app rendering/navigating', () => {

What happens if a user is not authenticated in react?

Your React application comes with a protected route. If a user is not authenticated, the app should redirect the user to the login screen. You’ve managed to set up react-router-dom for your component. For example, here’s an excerpt from a UserStatus.jsx component that only an authenticated user should be able to access:


2 Answers

Answering my own question. Basically I'm making a shallow render of my component and verifying that if authenticated is rendering the redirect component otherwise the App one. Here the code:

function setup() {
  const enzymeWrapper = shallow(<AuthenticatedApp />);

  return {
    enzymeWrapper
  };
}

describe("AuthenticatedApp component", () => {
  it("renders Redirect when user NOT autheticated", () => {
    authApi.isUserAuthenticated = jest.fn(() => false);
    const { enzymeWrapper } = setup();

    expect(enzymeWrapper.find(Redirect)).toHaveLength(1);
  });

  it("renders AppWithData when user autheticated", () => {
    authApi.isUserAuthenticated = jest.fn(() => true);
    const { enzymeWrapper } = setup();

    expect(enzymeWrapper.find(AppWithData)).toHaveLength(1);
  });
});
like image 77
Luca Marangon Avatar answered Oct 10 '22 23:10

Luca Marangon


Neither of these answers worked for me and took a fair bit of digging so I thought I'd chip in my experience here.

PrivateRoute.js

export const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route {...rest} render={(props) => (
    auth.isAuthenticated
      ? <Component {...props} />
      : <Redirect to={{
        pathname: '/',
        state: { from: props.location }
      }} />
  )} />
)

PrivateRoute.spec.js

This test worked for me with no problems whatsoever, it rendered the PrivateComponent when auth.isAuthenticated evaluated to true.

it('renders the component when the user is authorised', () => {
  auth.login()
  expect(auth.isAuthenticated).toBe(true)
  const privateRoute = mount(
    <MemoryRouter initialEntries={['/privateComponent']}>
      <PrivateRoute path='/privateComponent' component={PrivateComponent} />
    </MemoryRouter>
  )
  expect(privateRoute.find('PrivateComponent').length).toEqual(1)
})

This was the test that gave me a lot of issues. At first I was checking for the Redirect component.

I tried to just do something like

expect(privateRoute.find('Redirect').length).toEqual(1)

But that just wouldn't work, no matter what I did, it just couldn't find the Redirect component. In the end, I ended up checking the history but couldn't find any reliable documentation online and ended up looking at the React Router codebase.

In MemoryRouter.js (line 30) I saw that it rendered a Router component. I noticed that it was also passing it's history as a prop to Router so I figured I would be able to grab it from there.

I ended up grabbing the history prop from Router using privateRoute.find('Router').prop('history') which then finally gave me evidence that a redirect had actually happened, to the correct location, no less.

it('renders a redirect when the user is not authorised', () => {
  auth.logout()
  expect(auth.isAuthenticated).toBe(false)
  const privateRoute = mount(
    <MemoryRouter initialEntries={['/privateComponent']}>
      <PrivateRoute path='/privateComponent' component={PrivateComponent} />
    </MemoryRouter>
  )
  expect(privateRoute.find('PrivateComponent').length).toEqual(0)
  expect(
    privateRoute.find('Router').prop('history').location.pathname
  ).toEqual('/')
})

With this test, you're testing the actual functionality of the PrivateRoute component and ensuring that it goes where it's saying it's going.

The documentation leaves a lot to be desired. For example, it took a fair bit of digging for me to find out about initialEntries as a prop for MemoryRouter, you need this so it actually hits the route and executes the conditional, I spent too long trying to cover both branches only to realise this was what was needed.

Hope this helps someone.

like image 18
Michael Curry Avatar answered Oct 10 '22 22:10

Michael Curry