React Router - render function - React Testing Library - reactjs

I'm new to React Testing library and facing some challenges in writing the test case for render function in React router.
I'm unsure to get the code coverage for the render function of Route. Can some one guide me in that.
<Route path="/employee" render={() => <div><h4>Employee Page</h4></div>} />
App.test.js
it('should render Employee Page', () => {
render(
<MemoryRouter initialEntries={['/employee']}>
<App/>
</MemoryRouter>
);
expect(screen.getByText('Employee Page')).toBeInTheDocument();
});
App.js
<Router>
<div>
<ul>
<li><NavLink to="/home">Home</NavLink></li>
<li><NavLink to="/article">Article</NavLink></li>
<li><NavLink to="/employee">Employee</NavLink></li>
</ul>
<Switch>
<Route path="/home" render={() => <div><h4>Home
Page</h4></div>} />
<Route path="/article" render={() => <div><h4>Article
Page</h4></div>} />
<Route path="/employee" render={() => <div><h4>Employee
Page</h4></div>} />
</Switch>
</div>
</Router>

Finally I successfully completed the unit testing of React Router and got the code coverage of render function of Route.
App.test.js
import {createMemoryHistory} from 'history';
it('should render Employee', () => {
const history = createMemoryHistory(["/", "/employee"])
history.push('/employee');
const component = <Router history={history}>
<NavLink to="/employee">Employee</NavLink>
<App/>
</Router>
render(component);
});

Related

React Router V5 How to have links to nested and parent router? Adding Redux messes with Routing

I have a page which uses React Router V5. There is a general section and there is a section specifically for user profiles. As I have a different page structure there, I used nested routes for the account section. The SideBar is what the name implies and contains buttons which can be used to navigate the account pages /account/profile, /account/settings, as well as navigate to pages outside the nested switch - namely /help.
The App used to be structured roughly like this:
// in index.js
const appDiv = document.getElementById("app")
render(<App />, appDiv)
// in App.js
const App = () => {
return (
<Router>
<div className={styles.pageContainer}>
<Header />
<Switch>
<Route exact path='/' component={() => <HomePage link='about' />} />
<Route path='/about' component={AboutPage} />
<Route path='/help' component={HelpPage} />
<Route path='/log-in' component={LoginPage} />
<Route path='/sign-up/:signupType' component={SignUpPage} />
<Route path='/account' component={AccountRouter} />
</Switch>
<Footer />
</div>
</Router>
}
// in AccountRouter.js
const AccountRouter = () => {
return (
<div className={styles.container}>
<SideBar />
<Switch>
<Route path='/account/settings' component={AccountSettingsPage} />
<Route path='/account/profile' component={ProfileSettingsPage} />
<Route exact path='/account' component={ProfileSettingsPage} />
</Switch>
</div>
);
};
// in SideBar.js
const SideBar = () => {
const history = useHistory();
return (
<div>
<button onClick={() => history.push('/account/profile')}>Go to Account Profile</button>
<button onClick={() => history.push('/account/settings')}>Go to Account Settings</button>
<button onClick={() => history.push('/help')}>Go to Help Page</button>
</div>
)
}
Now it is structured like this:
// in index.js
const appDiv = document.getElementById("app")
render(
<Provider store={store}>
<App />
</Provider>
, appDiv)
// in App.js
// This looks the same
// in AccountRouter.js
// This now has Route protection
const AccountRouter = () => {
const accountData = useSelector((state) => state.account);
return (
<div className={styles.container}>
{!accountData.isLoggedIn ? <Redirect to='/log-in' /> : null}
<SideBar />
<Switch>
<Route path='/account/settings' component={AccountSettingsPage} />
<Route path='/account/profile' component={ProfileSettingsPage} />
<Route exact path='/account' component={ProfileSettingsPage} />
</Switch>
</div>
);
};
// in SideBar.js
// This looks the same.
Before I added Redux, the Sidebar was properly redirecting.
After Redux, I have the following behaviour:
When the SideBar is outside of the Switch, you can properly navigate to the Help page, but the components don't render, when I try to navigate to the pages inside the AccountRouter Switch. When I move the SideBar into the Switch, the links to the pages inside this switch start working again, but the /help link stops working.
Is there a way of having links to both inside and outside of this Switch in the same SideBar? How could Redux have affected the Router?

Routing pages into components: React

I have 3 components: Header.js, Main.js, and Footer.js, and App.js is like
const App = () => {
<Header/>
<Main/>
<Footer/>
}
In the Header.js I have links like About and Projects. I would like to be able when I click About in the header for example to display the page About.js in Main.js, and when I click Projects to display the page Projects.js in the Main.js component. I tried to use Routing in the Main.js component like
const Main = () => {
<Router>
<Switch>
<Route exact path='/' component={About.js} />
<Route exact path='/projects' component={Projects.js} />
</Switch>
</Router>
}
but it wouldn't allow me, saying that I cannot use Link outside a router, where I use Link in the Header.js. How can I achieve this?
The Header.js is the following
const Header = () => {
return (
<div>
<ul>
<li>
<Link to="/">
About
</Link>
</li>
<li>
<Link to="/projects">
Projects
</Link>
</li>
</ul>
</div>
)
}
You simply need to make sure your Router component surrounds any components doing routing. For simplicity, here’s the router surrounding your whole app at the App level.
const App = () => {
<Router>
<Header/>
<Main/>
<Footer/>
</Router>
}
Edit: make sure you’re passing your components correctly to the Routes:
const Main = () => {
<Switch>
<Route exact path='/' component={About} />
<Route exact path='/projects' component={Projects} />
</Switch>
}

testing click behavior on React Router Links with Enzyme

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

Testing react-router v4 with jest and enzyme always got .404 component

For some reason, I can't reach router in my tests.
My render in main container:
render() {
return (
<div className="AppEntry-scope">
<BrowserRouter>
<Switch>
<Route exact path="/" component={Dashboard} />
// ...Other routes
<Route component={FourOhFourNothinFound} />
</Switch>
</BrowserRouter>
</div>
);
}
My test:
describe('<Dashboard />', () => {
it('Should see dashboard', () => {
const wrapper = mount(
<MemoryRouter initialEntries={['/']}>
<AppEntry store={store} />
</MemoryRouter>,
);
expect(wrapper.find(Dashboard)).toBePresent();
});
});
Also, I've tried:
expect(wrapper.find(Dashboard).length).toBe(1);
And my test doesn't see Dashboard component.
In my app this route is works, but I want to set up test for routes.
BTW, when I tried to test my notfound component FourOhFourNothinFound - it works and seems my tests always see notfound component.
What is wrong and how I can set up my react-router tests correctly?

React Router 4 Navbar props not updating

I am building a small project to test the React Router 4. So far so good, my url updates and my props.locations shows up with withRouter. But I can't seem to change my navBar base on the props.location.
This is what my Routes look like:
<Provider store={ store }>
<BrowserRouter onUpdate={() => window.scrollTo(0, 0)}>
<div className="root">
<App/>
<Switch>
<Route exact path="/" component={HomePageContainer}/>
<Route eact path="/signin" component={SignInContainer}/>
<Route eact path="/reviews" component={Reviews}/>
<Route path="/favorites" component={Favorites}/>
<Route render={() => (
<p>Page Not Found</p>
)}/>
</Switch>
</div>
</BrowserRouter>
</Provider>
My component basically contains my HeaderBar and navBar, I have messages thats in navBar that I want to change so I would have title of the page, My App looks like this:
const App = (props) => {
let toRender = null;
if(props.location.pathname !== '/signin'){
toRender = (
<div>
<HeaderContainer />
<NavBarContainer />
</div>
);
} else {
toRender = null;
}
return(
<div className="App">
{ toRender }
</div>
);
}
I can import my navBar container into each of the routes i have for '/', '/reviews', and '/favorites'. But I don't think that would be a modular way to do it. I also have a shouldComponentUpdate lifecycle method inside NavBar, and I tested with a console.log to print something when it does update when I switch url, but it doesn't. Does anyone have any suggestions on a clean solution to pass in the props to my NavBar without importing it into every single one of the components? I also tried putting App component in the place of Route so I would have:
<App exact path="/" component={HomePageContainer}/>
<Route eact path="/signin" component={SignInContainer}/>
<App eact path="/reviews" component={Reviews}/>
<App path="/favorites" component={Favorites}/>
But then my Components aren't rendering besides the App. I'm not sure what's happening or why it's not rendering the components. Any suggestions would be much appreciate it. Thank you.

Resources