5

While I'm ultimately trying to write an Enzyme test for the flow in this react router example: https://reacttraining.com/react-router/web/example/auth-workflow

import React from 'react';
import ReactDOM from 'react-dom';
import Enzyme, { shallow, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import { MemoryRouter, Route, Switch, Link } from 'react-router-dom';

const Home = () => <div>Home</div>;
const MockComp = () => (
  <div className="protected">
    <nav>hi</nav>
    Protected
  </div>
);
const MockDenied = () => <div className="denied">Denied</div>;

test('Renders visited protected component if authorized', () => {
  const wrapper = mount(
    <MemoryRouter initialEntries={['/']}>
      <div>
        <Link to="/foo" />
        <Switch>
          <Route path="/" component={Home} />
          <Route path="/401" component={MockDenied} />
          <ProtectedRouteBasic
            path="/foo"
            auth={{ hasAuth: true }}
            component={MockComp}
          />
        </Switch>
      </div>
    </MemoryRouter>
  );
  wrapper.find('a').simulate('click', { button: 0 });
  expect(wrapper.find('.protected').length).toEqual(1);
  expect(wrapper.find('.denied').length).toEqual(0);
});

I've found a number of issues and have tried to peel away the complexity and then slowly reintroduce the elements that I've removed.

So I've landed on this test as what I will need to get working to proceed:

test('Clicking link will render component associated with path', () => {
  const wrapper = mount(
    <MemoryRouter>
      <div>
        <Link to="/foo" />
        <Switch>
          <Route path="/" component={Home} />
          <Route path="/foo" component={MockComp} />
        </Switch>
      </div>
    </MemoryRouter>
  );
  wrapper.find('a').simulate('click', { button: 0 });
  expect(wrapper.find('.protected')).toHaveLength(1);
});

However, this test isn't working as expected as I expect the test to pass in its current state. I've read this thread to update my simulate call to include the {button: 0} as well as this thread about wrapping the entire router in a functional component, however, that option's not available to me as far as I know, since the framework I'm working with doesn't seem to allow for it. Additionally, I believe that that piece is immaterial to the issue I'm having. That said, any help would be much appreciated.

reactor
  • 1,722
  • 1
  • 14
  • 34

1 Answers1

2

From the Switch docs:

Renders the first child <Route> or <Redirect> that matches the location.


In this case <Route path="/" component={Home} /> matches when the path is both / and /foo so Home is always rendered.

You can fix this by using either exact so it only matches if the path is exactly /, or moving it to the end of the Route list so other routes match first:

test('Clicking link will render component associated with path', () => {
  const wrapper = mount(
    <MemoryRouter>
      <div>
        <Link to="/foo" />
        <Switch>
          <Route path="/foo" component={MockComp} />
          <Route path="/" component={Home} />
        </Switch>
      </div>
    </MemoryRouter>
  );
  wrapper.find('a').simulate('click', { button: 0 });
  expect(wrapper.find('.protected')).toHaveLength(1);  // SUCCESS
});
Brian Adams
  • 43,011
  • 9
  • 113
  • 111