11
11
12
12
describe ( 'ReactDOMComponentTree' , ( ) => {
13
13
let React ;
14
- let ReactDOM ;
14
+ let ReactDOMClient ;
15
+ let act ;
15
16
let container ;
16
17
17
18
beforeEach ( ( ) => {
18
19
React = require ( 'react' ) ;
19
- ReactDOM = require ( 'react-dom' ) ;
20
+ ReactDOMClient = require ( 'react-dom/client' ) ;
21
+ act = require ( 'internal-test-utils' ) . act ;
22
+
20
23
container = document . createElement ( 'div' ) ;
21
24
document . body . appendChild ( container ) ;
22
25
} ) ;
@@ -26,25 +29,24 @@ describe('ReactDOMComponentTree', () => {
26
29
container = null ;
27
30
} ) ;
28
31
29
- it ( 'finds nodes for instances on events' , ( ) => {
32
+ it ( 'finds nodes for instances on events' , async ( ) => {
30
33
const mouseOverID = 'mouseOverID' ;
31
34
const clickID = 'clickID' ;
32
35
let currentTargetID = null ;
33
36
// the current target of an event is set to result of getNodeFromInstance
34
37
// when an event is dispatched so we can test behavior by invoking
35
38
// events on elements in the tree and confirming the expected node is
36
39
// set as the current target
37
- class Component extends React . Component {
38
- handler = e => {
40
+ function Component ( ) {
41
+ const handler = e => {
39
42
currentTargetID = e . currentTarget . id ;
40
43
} ;
41
- render ( ) {
42
- return (
43
- < div id = { mouseOverID } onMouseOver = { this . handler } >
44
- < div id = { clickID } onClick = { this . handler } />
45
- </ div >
46
- ) ;
47
- }
44
+
45
+ return (
46
+ < div id = { mouseOverID } onMouseOver = { handler } >
47
+ < div id = { clickID } onClick = { handler } />
48
+ </ div >
49
+ ) ;
48
50
}
49
51
50
52
function simulateMouseEvent ( elem , type ) {
@@ -54,34 +56,35 @@ describe('ReactDOMComponentTree', () => {
54
56
elem . dispatchEvent ( event ) ;
55
57
}
56
58
57
- const component = < Component /> ;
58
- ReactDOM . render ( component , container ) ;
59
+ const root = ReactDOMClient . createRoot ( container ) ;
60
+ await act ( ( ) => {
61
+ root . render ( < Component /> ) ;
62
+ } ) ;
59
63
expect ( currentTargetID ) . toBe ( null ) ;
60
64
simulateMouseEvent ( document . getElementById ( mouseOverID ) , 'mouseover' ) ;
61
65
expect ( currentTargetID ) . toBe ( mouseOverID ) ;
62
66
simulateMouseEvent ( document . getElementById ( clickID ) , 'click' ) ;
63
67
expect ( currentTargetID ) . toBe ( clickID ) ;
64
68
} ) ;
65
69
66
- it ( 'finds closest instance for node when an event happens' , ( ) => {
70
+ it ( 'finds closest instance for node when an event happens' , async ( ) => {
67
71
const nonReactElemID = 'aID' ;
68
72
const innerHTML = { __html : `<div id="${ nonReactElemID } "></div>` } ;
69
73
const closestInstanceID = 'closestInstance' ;
70
74
let currentTargetID = null ;
71
75
72
- class ClosestInstance extends React . Component {
73
- _onClick = e => {
76
+ function ClosestInstance ( ) {
77
+ const onClick = e => {
74
78
currentTargetID = e . currentTarget . id ;
75
79
} ;
76
- render ( ) {
77
- return (
78
- < div
79
- id = { closestInstanceID }
80
- onClick = { this . _onClick }
81
- dangerouslySetInnerHTML = { innerHTML }
82
- />
83
- ) ;
84
- }
80
+
81
+ return (
82
+ < div
83
+ id = { closestInstanceID }
84
+ onClick = { onClick }
85
+ dangerouslySetInnerHTML = { innerHTML }
86
+ />
87
+ ) ;
85
88
}
86
89
87
90
function simulateClick ( elem ) {
@@ -91,16 +94,22 @@ describe('ReactDOMComponentTree', () => {
91
94
elem . dispatchEvent ( event ) ;
92
95
}
93
96
94
- const component = < ClosestInstance /> ;
95
- ReactDOM . render ( < section > { component } </ section > , container ) ;
97
+ const root = ReactDOMClient . createRoot ( container ) ;
98
+ await act ( ( ) => {
99
+ root . render (
100
+ < section >
101
+ < ClosestInstance />
102
+ </ section > ,
103
+ ) ;
104
+ } ) ;
96
105
expect ( currentTargetID ) . toBe ( null ) ;
97
106
simulateClick ( document . getElementById ( nonReactElemID ) ) ;
98
107
expect ( currentTargetID ) . toBe ( closestInstanceID ) ;
99
108
} ) ;
100
109
101
- it ( 'updates event handlers from fiber props' , ( ) => {
110
+ it ( 'updates event handlers from fiber props' , async ( ) => {
102
111
let action = '' ;
103
- let instance ;
112
+ let flip ;
104
113
const handlerA = ( ) => ( action = 'A' ) ;
105
114
const handlerB = ( ) => ( action = 'B' ) ;
106
115
@@ -111,55 +120,56 @@ describe('ReactDOMComponentTree', () => {
111
120
target . dispatchEvent ( event ) ;
112
121
}
113
122
114
- class HandlerFlipper extends React . Component {
115
- state = { flip : false } ;
116
- flip ( ) {
117
- this . setState ( { flip : true } ) ;
118
- }
119
- render ( ) {
120
- return (
121
- < div
122
- id = "update"
123
- onMouseOver = { this . state . flip ? handlerB : handlerA }
124
- />
125
- ) ;
126
- }
123
+ function HandlerFlipper ( ) {
124
+ const [ flipVal , setFlipVal ] = React . useState ( false ) ;
125
+ flip = ( ) => setFlipVal ( true ) ;
126
+
127
+ return < div id = "update" onMouseOver = { flipVal ? handlerB : handlerA } /> ;
127
128
}
128
129
129
- ReactDOM . render (
130
- < HandlerFlipper key = "1" ref = { n => ( instance = n ) } /> ,
131
- container ,
132
- ) ;
130
+ const root = ReactDOMClient . createRoot ( container ) ;
131
+ await act ( ( ) => {
132
+ root . render ( < HandlerFlipper key = "1" /> ) ;
133
+ } ) ;
133
134
const node = container . firstChild ;
134
- simulateMouseOver ( node ) ;
135
+
136
+ await act ( ( ) => {
137
+ simulateMouseOver ( node ) ;
138
+ } ) ;
135
139
expect ( action ) . toEqual ( 'A' ) ;
136
140
action = '' ;
141
+
137
142
// Render with the other event handler.
138
- instance . flip ( ) ;
139
- simulateMouseOver ( node ) ;
143
+ await act ( ( ) => {
144
+ flip ( ) ;
145
+ } ) ;
146
+ await act ( ( ) => {
147
+ simulateMouseOver ( node ) ;
148
+ } ) ;
140
149
expect ( action ) . toEqual ( 'B' ) ;
141
150
} ) ;
142
151
143
- it ( 'finds a controlled instance from node and gets its current fiber props' , ( ) => {
152
+ it ( 'finds a controlled instance from node and gets its current fiber props' , async ( ) => {
153
+ let inputRef ;
144
154
const inputID = 'inputID' ;
145
155
const startValue = undefined ;
146
156
const finishValue = 'finish' ;
147
157
148
- class Controlled extends React . Component {
149
- state = { value : startValue } ;
150
- a = null ;
151
- _onChange = e => this . setState ( { value : e . currentTarget . value } ) ;
152
- render ( ) {
153
- return (
154
- < input
155
- id = { inputID }
156
- type = "text"
157
- ref = { n => ( this . a = n ) }
158
- value = { this . state . value }
159
- onChange = { this . _onChange }
160
- />
161
- ) ;
162
- }
158
+ function Controlled ( ) {
159
+ const [ state , setState ] = React . useState ( startValue ) ;
160
+ const ref = React . useRef ( ) ;
161
+ inputRef = ref ;
162
+ const onChange = e => setState ( e . currentTarget . value ) ;
163
+
164
+ return (
165
+ < input
166
+ id = { inputID }
167
+ type = "text"
168
+ ref = { ref }
169
+ value = { state }
170
+ onChange = { onChange }
171
+ />
172
+ ) ;
163
173
}
164
174
165
175
const setUntrackedInputValue = Object . getOwnPropertyDescriptor (
@@ -175,9 +185,17 @@ describe('ReactDOMComponentTree', () => {
175
185
elem . dispatchEvent ( inputEvent ) ;
176
186
}
177
187
178
- const component = < Controlled /> ;
179
- const instance = ReactDOM . render ( component , container ) ;
180
- expect ( ( ) => simulateInput ( instance . a , finishValue ) ) . toErrorDev (
188
+ const root = ReactDOMClient . createRoot ( container ) ;
189
+ await act ( ( ) => {
190
+ root . render ( < Controlled /> ) ;
191
+ } ) ;
192
+
193
+ await expect (
194
+ async ( ) =>
195
+ await act ( ( ) => {
196
+ simulateInput ( inputRef . current , finishValue ) ;
197
+ } ) ,
198
+ ) . toErrorDev (
181
199
'Warning: A component is changing an uncontrolled input to be controlled. ' +
182
200
'This is likely caused by the value changing from undefined to ' +
183
201
'a defined value, which should not happen. ' +
@@ -186,33 +204,4 @@ describe('ReactDOMComponentTree', () => {
186
204
'https://reactjs.org/link/controlled-components' ,
187
205
) ;
188
206
} ) ;
189
-
190
- it ( 'finds instance of node that is attempted to be unmounted' , ( ) => {
191
- const component = < div /> ;
192
- const node = ReactDOM . render ( < div > { component } </ div > , container ) ;
193
- expect ( ( ) => ReactDOM . unmountComponentAtNode ( node ) ) . toErrorDev (
194
- "unmountComponentAtNode(): The node you're attempting to unmount " +
195
- 'was rendered by React and is not a top-level container. You may ' +
196
- 'have accidentally passed in a React root node instead of its ' +
197
- 'container.' ,
198
- { withoutStack : true } ,
199
- ) ;
200
- } ) ;
201
-
202
- it ( 'finds instance from node to stop rendering over other react rendered components' , ( ) => {
203
- const component = (
204
- < div >
205
- < span > Hello</ span >
206
- </ div >
207
- ) ;
208
- const anotherComponent = < div /> ;
209
- const instance = ReactDOM . render ( component , container ) ;
210
- expect ( ( ) => ReactDOM . render ( anotherComponent , instance ) ) . toErrorDev (
211
- 'render(...): Replacing React-rendered children with a new root ' +
212
- 'component. If you intended to update the children of this node, ' +
213
- 'you should instead have the existing children update their state ' +
214
- 'and render the new components instead of calling ReactDOM.render.' ,
215
- { withoutStack : true } ,
216
- ) ;
217
- } ) ;
218
207
} ) ;
0 commit comments