React Router v4 Redirect unit test

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.


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);
  });
});