Skip to content
This repository was archived by the owner on Mar 5, 2022. It is now read-only.

Commit 5733f5d

Browse files
Add next.js router example (#456)
* Add next.js router example * Update tests * Add new covered file to the check-coverage script * Add pages/router.js to only-covered script * fix name Co-authored-by: Gleb Bahmutov <[email protected]>
1 parent 938b7ab commit 5733f5d

File tree

5 files changed

+171
-17
lines changed

5 files changed

+171
-17
lines changed

examples/nextjs/README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,45 @@ cy.contains(
5555

5656
Find more examples in [Page.spec.jsx](./cypress/components/Page.spec.jsx).
5757

58+
## Router mocking
59+
60+
If your components depends on the next.js router (using `useRouter` or `withRouter`) **we recommend to cover this component with integration tests**, because this component is likely not reusable and depends on application flow. But if you still need to write component test for it you will likely meet the problem like `cannot read property 'pathname' of undefined`.
61+
62+
It happens because we render the component just like any other react component – without any specific next.js features. In order to make router work it is required to provide specific context that would be picked up by `useRouter`:
63+
64+
```js
65+
import { mount } from 'cypress-react-unit-test'
66+
import { RouterContext } from 'next/dist/next-server/lib/router-context'
67+
68+
// Create a router value you need for the components. Here are all available values as for next v9.5
69+
// P.S. using cy.spy() here is not required, it is possible to pass simple () => {} function
70+
const router = {
71+
pathname: '/testPath',
72+
route: '/testPath',
73+
query: {},
74+
asPath: '/testPath',
75+
components: {},
76+
isFallback: false,
77+
basePath: '',
78+
events: { emit: cy.spy(), off: cy.spy(), on: cy.spy() },
79+
push: cy.spy(),
80+
replace: cy.spy(),
81+
reload: cy.spy(),
82+
back: cy.spy(),
83+
prefetch: cy.spy(),
84+
beforePopState: cy.spy(),
85+
}
86+
87+
// And wrap your component with special provider
88+
mount(
89+
<RouterContext.Provider value={router}>
90+
<YourComponent />
91+
</RouterContext.Provider>,
92+
)
93+
```
94+
95+
Find more examples in [Router.spec.jsx](./cypress/components/Router.spec.jsx)
96+
5897
## Mocking imports
5998

6099
Mocking imports is not working yet, seems the plugin we are inserting for loose mode causes problems, see issue [439](https://github.com/bahmutov/cypress-react-unit-test/issues/439).
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// @ts-check
2+
/// <reference types="cypress" />
3+
import * as React from 'react'
4+
import RouterPage from '../../pages/router'
5+
import { createRouter } from 'next/router'
6+
import { RouterContext } from 'next/dist/next-server/lib/router-context'
7+
import { mount } from 'cypress-react-unit-test'
8+
9+
describe('Component with router usage', () => {
10+
it('renders the component that uses next.js router context', () => {
11+
const router = {
12+
pathname: '/testPath',
13+
route: '/testPath',
14+
query: {},
15+
asPath: '/testPath',
16+
components: {},
17+
isFallback: false,
18+
basePath: '',
19+
events: { emit: cy.spy(), off: cy.spy(), on: cy.spy() },
20+
push: cy.spy(),
21+
replace: cy.spy(),
22+
reload: cy.spy(),
23+
back: cy.spy(),
24+
prefetch: cy.spy(),
25+
beforePopState: cy.spy(),
26+
}
27+
28+
mount(
29+
<RouterContext.Provider value={router}>
30+
<RouterPage />
31+
</RouterContext.Provider>,
32+
)
33+
34+
cy.contains('Next.js route /testPath')
35+
})
36+
37+
it('renders the component that uses next.js with parsed query', () => {
38+
// alternatively you can use next's internal `createRouter` function to create a real instance of NextRouter
39+
const router = createRouter(
40+
'/testPath',
41+
{ param1: 'param1' },
42+
'/asTestPath',
43+
{
44+
subscription: cy.spy(),
45+
initialProps: {},
46+
App: cy.spy(),
47+
Component: cy.spy(),
48+
pageLoader: cy.spy(),
49+
initialStyleSheets: [],
50+
wrapApp: cy.spy(),
51+
isFallback: false,
52+
},
53+
)
54+
55+
mount(
56+
<RouterContext.Provider value={router}>
57+
<RouterPage />
58+
</RouterContext.Provider>,
59+
)
60+
61+
cy.contains('My query: {"param1":"param1"}')
62+
})
63+
64+
it('pushes the new route', () => {
65+
const router = {
66+
pathname: '/router',
67+
route: '/router',
68+
query: {},
69+
asPath: '/router',
70+
components: {},
71+
isFallback: false,
72+
basePath: '',
73+
events: { emit: cy.spy(), off: cy.spy(), on: cy.spy() },
74+
push: cy.spy(),
75+
replace: cy.spy(),
76+
reload: cy.spy(),
77+
back: cy.spy(),
78+
prefetch: cy.spy(),
79+
beforePopState: cy.spy(),
80+
}
81+
82+
mount(
83+
<RouterContext.Provider value={router}>
84+
<RouterPage />
85+
</RouterContext.Provider>,
86+
)
87+
88+
cy.get('button')
89+
.click()
90+
.then(() => {
91+
// Make sure that `.then` here is required to make an assertion after component mount and retrying button
92+
expect(router.push).to.be.called
93+
})
94+
})
95+
})

examples/nextjs/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
"dev": "next",
88
"build": "next build",
99
"start": "next start",
10-
"test": "../../node_modules/.bin/../../node_modules/.bin/cypress-expect run --min-passing 7",
10+
"test": "../../node_modules/.bin/../../node_modules/.bin/cypress-expect run --min-passing 10",
1111
"cy:open": "../../node_modules/.bin/cypress open",
1212
"build:static": "next build && next out",
13-
"check-coverage": "../../node_modules/.bin/check-coverage components/Search.jsx && ../../node_modules/.bin/check-coverage pages/index.js",
14-
"only-covered": "../../node_modules/.bin/only-covered components/Search.jsx pages/index.js"
13+
"check-coverage": "../../node_modules/.bin/check-coverage components/Search.jsx && ../../node_modules/.bin/check-coverage pages/index.js && ../../node_modules/.bin/check-coverage pages/router.js",
14+
"only-covered": "../../node_modules/.bin/only-covered components/Search.jsx pages/index.js pages/router.js"
1515
},
1616
"devDependencies": {
1717
"cypress-react-unit-test": "file:../.."

examples/nextjs/pages/router.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// @ts-check
2+
import { useRouter } from 'next/router'
3+
4+
function RouterPage(props) {
5+
const router = useRouter()
6+
console.log(router.push)
7+
return (
8+
<main>
9+
<h1> Next.js route </h1>
10+
{router.pathname}
11+
<br />
12+
As path: {router.asPath}
13+
<br />
14+
My query: {JSON.stringify(router.query)}
15+
<button onClick={() => router.push('/')}>push</button>
16+
</main>
17+
)
18+
}
19+
20+
export default RouterPage

package-lock.json

Lines changed: 14 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)