1
1
import * as React from 'react'
2
2
import ReactDOM from 'react-dom'
3
3
import ReactDOMServer from 'react-dom/server'
4
- import { fireEvent , render , screen } from '../'
4
+ import { fireEvent , render , screen , configure } from '../'
5
+
6
+ describe ( 'render API' , ( ) => {
7
+ let originalConfig
8
+ beforeEach ( ( ) => {
9
+ // Grab the existing configuration so we can restore
10
+ // it at the end of the test
11
+ configure ( existingConfig => {
12
+ originalConfig = existingConfig
13
+ // Don't change the existing config
14
+ return { }
15
+ } )
16
+ } )
5
17
6
- test ( 'renders div into document' , ( ) => {
7
- const ref = React . createRef ( )
8
- const { container} = render ( < div ref = { ref } /> )
9
- expect ( container . firstChild ) . toBe ( ref . current )
10
- } )
18
+ afterEach ( ( ) => {
19
+ configure ( originalConfig )
20
+ } )
11
21
12
- test ( 'works great with react portals' , ( ) => {
13
- class MyPortal extends React . Component {
14
- constructor ( ...args ) {
15
- super ( ...args )
16
- this . portalNode = document . createElement ( 'div' )
17
- this . portalNode . dataset . testid = 'my-portal'
18
- }
19
- componentDidMount ( ) {
20
- document . body . appendChild ( this . portalNode )
21
- }
22
- componentWillUnmount ( ) {
23
- this . portalNode . parentNode . removeChild ( this . portalNode )
24
- }
25
- render ( ) {
26
- return ReactDOM . createPortal (
27
- < Greet greeting = "Hello" subject = "World" /> ,
28
- this . portalNode ,
29
- )
30
- }
31
- }
32
-
33
- function Greet ( { greeting, subject} ) {
34
- return (
35
- < div >
36
- < strong >
37
- { greeting } { subject }
38
- </ strong >
39
- </ div >
40
- )
41
- }
42
-
43
- const { unmount} = render ( < MyPortal /> )
44
- expect ( screen . getByText ( 'Hello World' ) ) . toBeInTheDocument ( )
45
- const portalNode = screen . getByTestId ( 'my-portal' )
46
- expect ( portalNode ) . toBeInTheDocument ( )
47
- unmount ( )
48
- expect ( portalNode ) . not . toBeInTheDocument ( )
49
- } )
22
+ test ( 'renders div into document' , ( ) => {
23
+ const ref = React . createRef ( )
24
+ const { container} = render ( < div ref = { ref } /> )
25
+ expect ( container . firstChild ) . toBe ( ref . current )
26
+ } )
50
27
51
- test ( 'returns baseElement which defaults to document.body' , ( ) => {
52
- const { baseElement} = render ( < div /> )
53
- expect ( baseElement ) . toBe ( document . body )
54
- } )
28
+ test ( 'works great with react portals' , ( ) => {
29
+ class MyPortal extends React . Component {
30
+ constructor ( ...args ) {
31
+ super ( ...args )
32
+ this . portalNode = document . createElement ( 'div' )
33
+ this . portalNode . dataset . testid = 'my-portal'
34
+ }
35
+ componentDidMount ( ) {
36
+ document . body . appendChild ( this . portalNode )
37
+ }
38
+ componentWillUnmount ( ) {
39
+ this . portalNode . parentNode . removeChild ( this . portalNode )
40
+ }
41
+ render ( ) {
42
+ return ReactDOM . createPortal (
43
+ < Greet greeting = "Hello" subject = "World" /> ,
44
+ this . portalNode ,
45
+ )
46
+ }
47
+ }
55
48
56
- test ( 'supports fragments' , ( ) => {
57
- class Test extends React . Component {
58
- render ( ) {
49
+ function Greet ( { greeting, subject} ) {
59
50
return (
60
51
< div >
61
- < code > DocumentFragment</ code > is pretty cool!
52
+ < strong >
53
+ { greeting } { subject }
54
+ </ strong >
62
55
</ div >
63
56
)
64
57
}
65
- }
66
58
67
- const { asFragment} = render ( < Test /> )
68
- expect ( asFragment ( ) ) . toMatchSnapshot ( )
69
- } )
59
+ const { unmount} = render ( < MyPortal /> )
60
+ expect ( screen . getByText ( 'Hello World' ) ) . toBeInTheDocument ( )
61
+ const portalNode = screen . getByTestId ( 'my-portal' )
62
+ expect ( portalNode ) . toBeInTheDocument ( )
63
+ unmount ( )
64
+ expect ( portalNode ) . not . toBeInTheDocument ( )
65
+ } )
70
66
71
- test ( 'renders options.wrapper around node' , ( ) => {
72
- const WrapperComponent = ( { children} ) => (
73
- < div data-testid = "wrapper" > { children } </ div >
74
- )
67
+ test ( 'returns baseElement which defaults to document.body' , ( ) => {
68
+ const { baseElement} = render ( < div /> )
69
+ expect ( baseElement ) . toBe ( document . body )
70
+ } )
71
+
72
+ test ( 'supports fragments' , ( ) => {
73
+ class Test extends React . Component {
74
+ render ( ) {
75
+ return (
76
+ < div >
77
+ < code > DocumentFragment</ code > is pretty cool!
78
+ </ div >
79
+ )
80
+ }
81
+ }
75
82
76
- const { container } = render ( < div data-testid = "inner" /> , {
77
- wrapper : WrapperComponent ,
83
+ const { asFragment } = render ( < Test /> )
84
+ expect ( asFragment ( ) ) . toMatchSnapshot ( )
78
85
} )
79
86
80
- expect ( screen . getByTestId ( 'wrapper' ) ) . toBeInTheDocument ( )
81
- expect ( container . firstChild ) . toMatchInlineSnapshot ( `
87
+ test ( 'renders options.wrapper around node' , ( ) => {
88
+ const WrapperComponent = ( { children} ) => (
89
+ < div data-testid = "wrapper" > { children } </ div >
90
+ )
91
+
92
+ const { container} = render ( < div data-testid = "inner" /> , {
93
+ wrapper : WrapperComponent ,
94
+ } )
95
+
96
+ expect ( screen . getByTestId ( 'wrapper' ) ) . toBeInTheDocument ( )
97
+ expect ( container . firstChild ) . toMatchInlineSnapshot ( `
82
98
<div
83
99
data-testid=wrapper
84
100
>
@@ -87,102 +103,138 @@ test('renders options.wrapper around node', () => {
87
103
/>
88
104
</div>
89
105
` )
90
- } )
106
+ } )
91
107
92
- test ( 'flushes useEffect cleanup functions sync on unmount()' , ( ) => {
93
- const spy = jest . fn ( )
94
- function Component ( ) {
95
- React . useEffect ( ( ) => spy , [ ] )
96
- return null
97
- }
98
- const { unmount} = render ( < Component /> )
99
- expect ( spy ) . toHaveBeenCalledTimes ( 0 )
108
+ test ( 'renders options.wrapper around node when reactStrictMode is true' , ( ) => {
109
+ configure ( { reactStrictMode : true } )
100
110
101
- unmount ( )
111
+ const WrapperComponent = ( { children} ) => (
112
+ < div data-testid = "wrapper" > { children } </ div >
113
+ )
114
+ const { container} = render ( < div data-testid = "inner" /> , {
115
+ wrapper : WrapperComponent ,
116
+ } )
102
117
103
- expect ( spy ) . toHaveBeenCalledTimes ( 1 )
104
- } )
118
+ expect ( screen . getByTestId ( 'wrapper' ) ) . toBeInTheDocument ( )
119
+ expect ( container . firstChild ) . toMatchInlineSnapshot ( `
120
+ <div
121
+ data-testid=wrapper
122
+ >
123
+ <div
124
+ data-testid=inner
125
+ />
126
+ </div>
127
+ ` )
128
+ } )
129
+
130
+ test ( 'renders twice when reactStrictMode is true' , ( ) => {
131
+ configure ( { reactStrictMode : true } )
105
132
106
- test ( 'can be called multiple times on the same container' , ( ) => {
107
- const container = document . createElement ( 'div' )
133
+ const spy = jest . fn ( )
134
+ function Component ( ) {
135
+ spy ( )
136
+ return null
137
+ }
108
138
109
- const { unmount} = render ( < strong /> , { container} )
139
+ render ( < Component /> )
140
+ expect ( spy ) . toHaveBeenCalledTimes ( 2 )
141
+ } )
110
142
111
- expect ( container ) . toContainHTML ( '<strong></strong>' )
143
+ test ( 'flushes useEffect cleanup functions sync on unmount()' , ( ) => {
144
+ const spy = jest . fn ( )
145
+ function Component ( ) {
146
+ React . useEffect ( ( ) => spy , [ ] )
147
+ return null
148
+ }
149
+ const { unmount} = render ( < Component /> )
150
+ expect ( spy ) . toHaveBeenCalledTimes ( 0 )
112
151
113
- render ( < em /> , { container } )
152
+ unmount ( )
114
153
115
- expect ( container ) . toContainHTML ( '<em></em>' )
154
+ expect ( spy ) . toHaveBeenCalledTimes ( 1 )
155
+ } )
116
156
117
- unmount ( )
157
+ test ( 'can be called multiple times on the same container' , ( ) => {
158
+ const container = document . createElement ( 'div' )
118
159
119
- expect ( container ) . toBeEmptyDOMElement ( )
120
- } )
160
+ const { unmount} = render ( < strong /> , { container} )
121
161
122
- test ( 'hydrate will make the UI interactive' , ( ) => {
123
- function App ( ) {
124
- const [ clicked , handleClick ] = React . useReducer ( n => n + 1 , 0 )
162
+ expect ( container ) . toContainHTML ( '<strong></strong>' )
125
163
126
- return (
127
- < button type = "button" onClick = { handleClick } >
128
- clicked:{ clicked }
129
- </ button >
130
- )
131
- }
132
- const ui = < App />
133
- const container = document . createElement ( 'div' )
134
- document . body . appendChild ( container )
135
- container . innerHTML = ReactDOMServer . renderToString ( ui )
164
+ render ( < em /> , { container} )
136
165
137
- expect ( container ) . toHaveTextContent ( 'clicked:0 ')
166
+ expect ( container ) . toContainHTML ( '<em></em> ')
138
167
139
- render ( ui , { container , hydrate : true } )
168
+ unmount ( )
140
169
141
- fireEvent . click ( container . querySelector ( 'button' ) )
170
+ expect ( container ) . toBeEmptyDOMElement ( )
171
+ } )
142
172
143
- expect ( container ) . toHaveTextContent ( 'clicked:1' )
144
- } )
173
+ test ( 'hydrate will make the UI interactive' , ( ) => {
174
+ function App ( ) {
175
+ const [ clicked , handleClick ] = React . useReducer ( n => n + 1 , 0 )
145
176
146
- test ( 'hydrate can have a wrapper' , ( ) => {
147
- const wrapperComponentMountEffect = jest . fn ( )
148
- function WrapperComponent ( { children} ) {
149
- React . useEffect ( ( ) => {
150
- wrapperComponentMountEffect ( )
151
- } )
177
+ return (
178
+ < button type = "button" onClick = { handleClick } >
179
+ clicked:{ clicked }
180
+ </ button >
181
+ )
182
+ }
183
+ const ui = < App />
184
+ const container = document . createElement ( 'div' )
185
+ document . body . appendChild ( container )
186
+ container . innerHTML = ReactDOMServer . renderToString ( ui )
152
187
153
- return children
154
- }
155
- const ui = < div />
156
- const container = document . createElement ( 'div' )
157
- document . body . appendChild ( container )
158
- container . innerHTML = ReactDOMServer . renderToString ( ui )
188
+ expect ( container ) . toHaveTextContent ( 'clicked:0' )
159
189
160
- render ( ui , { container, hydrate : true , wrapper : WrapperComponent } )
190
+ render ( ui , { container, hydrate : true } )
161
191
162
- expect ( wrapperComponentMountEffect ) . toHaveBeenCalledTimes ( 1 )
163
- } )
192
+ fireEvent . click ( container . querySelector ( 'button' ) )
164
193
165
- test ( 'legacyRoot uses legacy ReactDOM.render' , ( ) => {
166
- expect ( ( ) => {
167
- render ( < div /> , { legacyRoot : true } )
168
- } ) . toErrorDev (
169
- [
170
- "Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot" ,
171
- ] ,
172
- { withoutStack : true } ,
173
- )
174
- } )
194
+ expect ( container ) . toHaveTextContent ( 'clicked:1' )
195
+ } )
196
+
197
+ test ( 'hydrate can have a wrapper' , ( ) => {
198
+ const wrapperComponentMountEffect = jest . fn ( )
199
+ function WrapperComponent ( { children} ) {
200
+ React . useEffect ( ( ) => {
201
+ wrapperComponentMountEffect ( )
202
+ } )
203
+
204
+ return children
205
+ }
206
+ const ui = < div />
207
+ const container = document . createElement ( 'div' )
208
+ document . body . appendChild ( container )
209
+ container . innerHTML = ReactDOMServer . renderToString ( ui )
175
210
176
- test ( 'legacyRoot uses legacy ReactDOM.hydrate' , ( ) => {
177
- const ui = < div />
178
- const container = document . createElement ( 'div' )
179
- container . innerHTML = ReactDOMServer . renderToString ( ui )
180
- expect ( ( ) => {
181
- render ( ui , { container, hydrate : true , legacyRoot : true } )
182
- } ) . toErrorDev (
183
- [
184
- "Warning: ReactDOM.hydrate is no longer supported in React 18. Use hydrateRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot" ,
185
- ] ,
186
- { withoutStack : true } ,
187
- )
211
+ render ( ui , { container, hydrate : true , wrapper : WrapperComponent } )
212
+
213
+ expect ( wrapperComponentMountEffect ) . toHaveBeenCalledTimes ( 1 )
214
+ } )
215
+
216
+ test ( 'legacyRoot uses legacy ReactDOM.render' , ( ) => {
217
+ expect ( ( ) => {
218
+ render ( < div /> , { legacyRoot : true } )
219
+ } ) . toErrorDev (
220
+ [
221
+ "Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot" ,
222
+ ] ,
223
+ { withoutStack : true } ,
224
+ )
225
+ } )
226
+
227
+ test ( 'legacyRoot uses legacy ReactDOM.hydrate' , ( ) => {
228
+ const ui = < div />
229
+ const container = document . createElement ( 'div' )
230
+ container . innerHTML = ReactDOMServer . renderToString ( ui )
231
+ expect ( ( ) => {
232
+ render ( ui , { container, hydrate : true , legacyRoot : true } )
233
+ } ) . toErrorDev (
234
+ [
235
+ "Warning: ReactDOM.hydrate is no longer supported in React 18. Use hydrateRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot" ,
236
+ ] ,
237
+ { withoutStack : true } ,
238
+ )
239
+ } )
188
240
} )
0 commit comments