@@ -6,99 +6,116 @@ title: React Router
6
6
``` jsx
7
7
// app.js
8
8
import React from ' react'
9
- import { withRouter } from ' react-router'
10
9
import { Link , Route , Switch } from ' react-router-dom'
11
10
12
- const About = () => < h1> You are on the about page< / h1>
13
- const Home = () => < h1> You are home< / h1>
14
- const NoMatch = () => < h1> 404 Not Found< / h1>
15
-
16
- const LocationDisplay = withRouter (({ location }) => (
17
- < div data- testid= " location-display" > {location .pathname }< / div>
18
- ))
19
-
20
- function App () {
21
- return (
22
- < div>
23
- < Link to= " /" > Home< / Link>
24
- < Link to= " /about" > About< / Link>
25
- < Switch>
26
- < Route exact path= " /" component= {Home} / >
27
- < Route path= " /about" component= {About} / >
28
- < Route component= {NoMatch} / >
29
- < / Switch>
30
- < LocationDisplay / >
31
- < / div>
32
- )
11
+ const About = () => < div> You are on the about page< / div>
12
+ const Home = () => < div> You are home< / div>
13
+ const NoMatch = () => < div> No match< / div>
14
+
15
+ export const LocationDisplay = () => {
16
+ const location = useLocation ()
17
+
18
+ return < div data- testid= " location-display" > {location .pathname }< / div>
33
19
}
34
20
35
- export { LocationDisplay , App }
21
+ export const App = () => (
22
+ < div>
23
+ < Link to= " /" > Home< / Link>
24
+
25
+ < Link to= " /about" > About< / Link>
26
+
27
+ < Switch>
28
+ < Route exact path= " /" >
29
+ < Home / >
30
+ < / Route>
31
+
32
+ < Route path= " /about" >
33
+ < About / >
34
+ < / Route>
35
+
36
+ < Route>
37
+ < NoMatch / >
38
+ < / Route>
39
+ < / Switch>
40
+
41
+ < LocationDisplay / >
42
+ < / div>
43
+ )
36
44
```
37
45
38
46
``` jsx
39
47
// app.test.js
48
+ import { render , screen } from ' @testing-library/react'
49
+ import userEvent from ' @testing-library/user-event'
50
+ import { createMemoryHistory } from ' history'
40
51
import React from ' react'
41
52
import { Router } from ' react-router-dom'
42
- import { createMemoryHistory } from ' history'
43
- import { render , fireEvent } from ' @testing-library/react'
53
+
44
54
import ' @testing-library/jest-dom/extend-expect'
45
- import { LocationDisplay , App } from ' ./app'
55
+
56
+ import { App , LocationDisplay } from ' ./app'
46
57
47
58
test (' full app rendering/navigating' , () => {
48
59
const history = createMemoryHistory ()
49
- const { container , getByText } = render (
60
+ render (
50
61
< Router history= {history}>
51
62
< App / >
52
63
< / Router>
53
64
)
54
65
// verify page content for expected route
55
66
// often you'd use a data-testid or role query, but this is also possible
56
- expect (container . innerHTML ). toMatch ( ' You are home' )
67
+ expect (screen . getByText ( / you are home/ i )). toBeInTheDocument ( )
57
68
58
- fireEvent .click (getByText (/ about/ i ))
69
+ const leftClick = { button: 0 }
70
+ userEvent .click (screen .getByText (/ about/ i ), leftClick)
59
71
60
72
// check that the content changed to the new page
61
- expect (container . innerHTML ). toMatch ( ' You are on the about page' )
73
+ expect (screen . getByText ( / you are on the about page/ i )). toBeInTheDocument ( )
62
74
})
63
75
64
- test (' landing on a bad page shows 404 page ' , () => {
76
+ test (' landing on a bad page' , () => {
65
77
const history = createMemoryHistory ()
66
78
history .push (' /some/bad/route' )
67
- const { getByRole } = render (
79
+ render (
68
80
< Router history= {history}>
69
81
< App / >
70
82
< / Router>
71
83
)
72
- expect (getByRole (' heading' )).toHaveTextContent (' 404 Not Found' )
84
+
85
+ expect (screen .getByText (/ no match/ i )).toBeInTheDocument ()
73
86
})
74
87
75
- test (' rendering a component that uses withRouter ' , () => {
88
+ test (' rendering a component that uses useLocation ' , () => {
76
89
const history = createMemoryHistory ()
77
90
const route = ' /some-route'
78
91
history .push (route)
79
- const { getByTestId } = render (
92
+ render (
80
93
< Router history= {history}>
81
94
< LocationDisplay / >
82
95
< / Router>
83
96
)
84
- expect (getByTestId (' location-display' )).toHaveTextContent (route)
97
+
98
+ expect (screen .getByTestId (' location-display' )).toHaveTextContent (route)
85
99
})
86
100
```
87
101
88
102
## Reducing boilerplate
89
103
90
104
1 . You can use the ` wrapper ` option to wrap a ` MemoryRouter ` around the
91
- component you want to render (` MemoryRouter ` works when you don't need access
92
- to the history object itself in the test, but just need the components to be
93
- able to render and navigate).
105
+ component you want to render.
106
+ ` MemoryRouter ` works when you don't need access to the history object itself
107
+ in the test, but just need the components to be able to render and
108
+ navigate.
109
+ If you _ do_ need to change the history, you could use ` BrowserRouter ` .
94
110
95
111
``` jsx
96
112
import { MemoryRouter } from ' react-router-dom'
97
113
98
114
test (' full app rendering/navigating' , () => {
99
- const { container , getByText } = render (< App / > , { wrapper: MemoryRouter })
115
+ render (< App / > , { wrapper: MemoryRouter })
116
+
100
117
// verify page content for expected route
101
- expect (getByRole ( ' heading ' )).toMatch ( ' Home ' )
118
+ expect (screen . getByText ( / you are home / i )).toBeInTheDocument ( )
102
119
})
103
120
```
104
121
@@ -107,38 +124,35 @@ test('full app rendering/navigating', () => {
107
124
108
125
``` jsx
109
126
// test utils file
110
- function renderWithRouter (
111
- ui ,
112
- {
113
- route = ' /' ,
114
- history = createMemoryHistory ({ initialEntries: [route] }),
115
- } = {}
116
- ) {
117
- const Wrapper = ({ children }) => (
118
- < Router history= {history}> {children}< / Router>
119
- )
120
- return {
121
- ... render (ui, { wrapper: Wrapper }),
122
- // adding `history` to the returned utilities to allow us
123
- // to reference it in our tests (just try to avoid using
124
- // this to test implementation details).
125
- history,
126
- }
127
+ const renderWithRouter = (ui , { route = ' /' } = {}) => {
128
+ window .history .pushState ({}, ' Test page' , route)
129
+
130
+ return render (ui, { wrapper: BrowserRouter })
127
131
}
128
132
```
129
133
130
134
``` jsx
131
135
// app.test.js
136
+ test (' full app rendering/navigating' , () => {
137
+ renderWithRouter (< App / > )
138
+ expect (screen .getByText (/ you are home/ i )).toBeInTheDocument ()
139
+
140
+ const leftClick = { button: 0 }
141
+ userEvent .click (screen .getByText (/ about/ i ), leftClick)
142
+
143
+ expect (screen .getByText (/ you are on the about page/ i )).toBeInTheDocument ()
144
+ })
145
+
132
146
test (' landing on a bad page' , () => {
133
- const { container } = renderWithRouter (< App / > , {
134
- route: ' /something-that-does-not-match' ,
135
- })
136
- expect (container .innerHTML ).toMatch (' No match' )
147
+ renderWithRouter (< App / > , { route: ' /something-that-does-not-match' })
148
+
149
+ expect (screen .getByText (/ no match/ i )).toBeInTheDocument ()
137
150
})
138
151
139
- test (' rendering a component that uses withRouter ' , () => {
152
+ test (' rendering a component that uses useLocation ' , () => {
140
153
const route = ' /some-route'
141
- const { getByTestId } = renderWithRouter (< LocationDisplay / > , { route })
142
- expect (getByTestId (' location-display' )).toHaveTextContent (route)
154
+ renderWithRouter (< LocationDisplay / > , { route })
155
+
156
+ expect (screen .getByTestId (' location-display' )).toHaveTextContent (route)
143
157
})
144
158
```
0 commit comments