Gathering detailed insights and metrics for @render-with/react-router
Gathering detailed insights and metrics for @render-with/react-router
Gathering detailed insights and metrics for @render-with/react-router
Gathering detailed insights and metrics for @render-with/react-router
npm install @render-with/react-router
Typescript
Module System
Min. Node Version
Node Version
NPM Version
55.1
Supply Chain
89.9
Quality
79.5
Maintenance
100
Vulnerability
100
License
JavaScript (100%)
Total Downloads
5,630
Last Day
1
Last Week
27
Last Month
118
Last Year
1,589
2 Stars
308 Commits
1 Watching
6 Branches
4 Contributors
Minified
Minified + Gzipped
Latest Version
5.0.0
Package Id
@render-with/react-router@5.0.0
Unpacked Size
24.66 kB
Size
7.01 kB
File Count
5
NPM Version
10.2.1
Node Version
18.20.2
Publised On
08 May 2024
Cumulative downloads
Total Downloads
Last day
0%
1
Compared to previous day
Last week
-25%
27
Compared to previous week
Last month
-22.9%
118
Compared to previous month
Last year
-56.7%
1,589
Compared to previous year
3
22
Use one of these decorators if your component under test requires a React Router:
withRouter()
withLocation(..)
withRouting(..)
Example:
1import { render, screen, withLocation } from './test-utils' 2 3it('shows login page', () => { 4 render(<App />, withLocation('/login')) 5 expect(screen.getByRole('heading', { name: /login/i })).toBeInTheDocument() 6})
Note: Refer to the core library to learn more about how decorators can simplify writing tests for React components with React Testing Library.
This library is distributed via npm, which is bundled with node and should be installed as one of your project's devDependencies
.
First, install the core library with a render function that supports decorators:
1npm install --save-dev @render-with/decorators
Next, install the React Router decorators provided by this library:
1npm install --save-dev @render-with/react-router
or
for installation via yarn:
1yarn add --dev @render-with/decorators 2yarn add --dev @render-with/react-router
This library has the following peerDependencies
:
and supports the following node
versions:
In your test-utils file, re-export the render function that supports decorators and the React Router decorators:
1// test-utils.js 2// ... 3export * from '@testing-library/react' // makes all React Testing Library's exports available 4export * from '@render-with/decorators' // overrides React Testing Library's render function 5export * from '@render-with/react-router' // makes decorators like withLocation(..) available
And finally, use the React Router decorators in your tests:
1import { render, screen, withLocation } from './test-utils' 2 3it('shows login page', () => { 4 render(<App />, withLocation('/login')) 5 expect(screen.getByRole('heading', { name: /login/i })).toBeInTheDocument() 6})
React Router 6 made it very hard to inspect the current location in tests. It takes some time to get used to, but it turns out to be a good change as it supports Testing-Library's guiding principle:
The more your tests resemble the way your software is used, the more confidence they can give you.
An actual end-user does not look at the URL while using a web app. They look at what the browser presents to them.
A bad workaround has been spreading in the wild that involves mocking React Router hooks like useNavigate
.
We do not recommend mocking hooks. It ties the tests to implementation details and, besides other downsides, makes refactoring harder.
Instead, we recommend using the decorators in this library.
If your test does not care about the current location, history, or navigation, you can use withRouter()
. The decorator will create and use a MemoryRouter
for you and initialize the current location with /
:
1import { useNavigate } from 'react-router' 2 3export const Page = () => { 4 const navigate = useNavigate() 5 return ( 6 <> 7 <h1>Page</h1> 8 <button onClick={() => navigate(-1)}>Go back</button> 9 </> 10 ) 11}
1import { render, screen, withRouter } from './test-utils' 2 3it('uses given name as heading', () => { 4 render(<Page />, withRouter()) 5 expect(screen.getByRole('heading', { name: /page/i })).toBeInTheDocument() 6})
If your component cares about a certain path or your test cares about the handling of certain query params, you can use the withLocation(..)
decorator:
1import { Routes, Route } from 'react-router' 2 3export const App = () => ( 4 <Routes> 5 <Route path='/' element={<h1>Homepage</h1>} /> 6 <Route path='/product' element={<h1>Products</h1>} /> 7 </Routes> 8)
1import { render, screen, withLocation } from './test-utils' 2 3it('shows product page', () => { 4 render(<App />, withLocation('/product')) 5 expect(screen.getByRole('heading', { name: /products/i })).toBeInTheDocument() 6})
The withLocation
decorator also supports path params in case your project uses routes with variable parts:
1import { Routes, Route } from 'react-router' 2 3export const App = () => ( 4 <Routes> 5 <Route path='/' element={<h1>Homepage</h1>} /> 6 <Route path='/users/:userId' element={<h1>User</h1>} /> 7 </Routes> 8)
1import { render, screen, withLocation } from './test-utils' 2 3it('shows user page', () => { 4 render(<App />, withLocation('/users/:userId', { userId: '42' })) 5 expect(screen.getByRole('heading', { name: /user/i })).toBeInTheDocument() 6})
If your test cares about navigation and location, you can use the versatile withRouting(..)
decorator.
The component under test is not just rendered within MemoryRouter
but also within a small page wrapper component that identifies the current page by name and the current location by rendering a simplified breadcrumb:
1import { render, screen, withRouting } from './test-utils' 2 3it('shows current page and location', async () => { 4 render(<div>Test</div>, withRouting()) 5 expect(screen.getByRole('main', { name: /current page/i })).toBeInTheDocument() 6 expect(screen.getByRole('link')).toHaveAttribute('href', '/current') 7})
The decorator provides a catch-all route that renders an "Other Page" in case the target route does not exist.
1import { render, screen, withRouting } from './test-utils' 2 3it('shows other page and location', async () => { 4 render(<Link to='/users'>Users</Link>, withRouting()) 5 await userEvent.click(screen.getByRole('link', { name: /users/i })) 6 expect(screen.getByRole('main', { name: /other page/i })).toBeInTheDocument() 7 expect(screen.getByRole('link')).toHaveAttribute('href', '/users') 8})
The decorator provides a "Previous Page" and sets up the history accordingly:
1import { render, screen, withRouting } from './test-utils' 2 3it('shows previous page and location', async () => { 4 render(<Link to={-1}>Back</Link>, withRouting()) 5 await userEvent.click(screen.getByRole('link', { name: /back/i })) 6 expect(screen.getByRole('main', { name: /previous page/i })).toBeInTheDocument() 7 expect(screen.getByRole('link')).toHaveAttribute('href', '/previous') 8})
The decorator provides a "Next Page" and sets up the history accordingly:
1import { render, screen, withRouting } from './test-utils' 2 3it('shows next page and location', async () => { 4 render(<Link to={+1}>Next</Link>, withRouting()) 5 await userEvent.click(screen.getByRole('link', { name: /next/i })) 6 expect(screen.getByRole('main', { name: /next page/i })).toBeInTheDocument() 7 expect(screen.getByRole('link')).toHaveAttribute('href', '/next') 8})
You can configure the decorator with one or more routes. This can be useful if user interaction with the component under test results in navigational changes away from the current location to a different route:
1import { render, screen, withRouting } from './test-utils' 2 3it('shows next page and location', async () => { 4 render(<Link to='/users'>Customers</Link>, withRouting({ routes: { users: 'Customers' } })) 5 await userEvent.click(screen.getByRole('link', { name: /customers/i })) 6 expect(screen.getByRole('main', { name: /customers/i })).toBeInTheDocument() 7 expect(screen.getByRole('link')).toHaveAttribute('href', '/users') 8})
You can configure the decorator with one or more subroutes. This can be useful if user interaction with the component under test results in navigational changes away from the current location to a subroute:
1import { render, screen, withRouting } from './test-utils' 2 3it('shows next page and location', async () => { 4 render(<Link to='/current/details'>More</Link>, withRouting({ subroutes: { details: 'More' } })) 5 await userEvent.click(screen.getByRole('link', { name: /more/i })) 6 expect(screen.getByRole('main', { name: /more/i })).toBeInTheDocument() 7 expect(screen.getByRole('link')).toHaveAttribute('href', '/current/details') 8})
You can also configure the current path and current page name:
1import { render, screen, withRouting } from './test-utils' 2 3it('shows current page and location', async () => { 4 render(<div>Test</div>, withRouting({ path: '/users', name: 'Users' })) 5 expect(screen.getByRole('main', { name: /users/i })).toBeInTheDocument() 6 expect(screen.getByRole('link')).toHaveAttribute('href', '/users') 7})
The decorator supports path params in the current path in case your project uses routes with variable parts:
1import { render, screen, withRouting } from './test-utils' 2 3it('shows current page and location', async () => { 4 render(<div>Test</div>, withRouting({ path: '/users/:userId', params: { userId: '42' } })) 5 expect(screen.getByRole('main', { name: /current/i })).toBeInTheDocument() 6 expect(screen.getByRole('link')).toHaveAttribute('href', '/users/42') 7})
Routes and subroutes can also have path params that React Router will resolve:
1import { render, screen, withRouting } from './test-utils' 2 3it('shows current page and location', async () => { 4 render(<Link to='/users/42'>John</Link>, withRouting({ routes: { ['/users/:userId']: 'User' } })) 5 await userEvent.click(screen.getByRole('link', { name: /john/i })) 6 expect(screen.getByRole('main', { name: /user/i })).toBeInTheDocument() 7 expect(screen.getByRole('link')).toHaveAttribute('href', '/users/42') 8})
Note: This API reference uses simplified types. You can find the full type specification here.
function withRouter(): Decorator
Wraps component under test in a MemoryRouter
and initializes location with /
.
function withLocation(path?: string, params?: Params): Decorator
Wraps component under test in a MemoryRouter
and initializes location with the given path. Resolves path parameters with the given values.
function withRouting(options?: {
name?: string,
path?: string,
params?: Params,
routes?: Routes,
subroutes?: Routes
}): Decorator
Wraps component under test in a MemoryRouter
and a simples Routes
infrastructure with routes history for the Current page, a Previous page, and a Next page. Supports a custom path and name for the current page and additional routes and subroutes.
type Params = { [param: string]: string }
Maps path parameter names to parameter values.
type Routes = { [route: string]: string }
Maps routes to page names.
Looking to contribute? PRs are welcome. Checkout this project's Issues on GitHub for existing issues.
Please file an issue for bugs, missing documentation, or unexpected behavior.
Please file an issue to suggest new features. Vote on feature requests by adding a 👍. This helps maintainers prioritize what to work on.
Please file an issue on the core project to suggest additional libraries that would benefit from decorators. Vote on library support adding a 👍. This helps maintainers prioritize what to work on.
For questions related to using the library, file an issue on GitHub.
Every release is documented on the GitHub Releases page.
Thanks goes to these people:
cultivate(software) 💼 💵 | David Bieder 💻 ⚠️ 📖 👀 🚇 🚧 🤔 | Jerome Weiß 📖 🚇 🚧 | Maurice Reichelt 📖 🚇 🚧 |
This project follows the all-contributors specification. Contributions of any kind welcome!
No vulnerabilities found.
No security vulnerabilities found.
@sequencemedia/react-router-render
Sync/async isomorphic render of React apps with React Router
@sequencemedia/react-router-redux-render
Sync/async isomorphic render of React apps with React Router + Redux
react-isomorphic-render
Isomorphic rendering with React, Redux, React-router and Redux-router. Includes support for Webpack
@cultivateit/decorate-with-react-router
Render decorators for components under test that require a React Router.