Convert ReactTestUtils to createRoot (#28446)
it'll be removed soon anyway but until then let's unblock moving legacy APIs behind a flag.
Sebastian Silbermann committed
Feb 26, 2024 at 21:54 UTC
2314227de55b23e5819f7b96443693df16b5e911
1 file changed
+142
-59
packages/react-dom/src/__tests__/ReactTestUtils-test.js
+142
-59
@@ -9,8 +9,9 @@
9
10
'use strict';
11
12
+import {act} from 'internal-test-utils';
13
import * as React from 'react';
13
-import * as ReactDOM from 'react-dom';
14
+import * as ReactDOMClient from 'react-dom/client';
15
import * as ReactDOMServer from 'react-dom/server';
16
import * as ReactTestUtils from 'react-dom/test-utils';
17
@@ -30,7 +31,7 @@ describe('ReactTestUtils', () => {
31
expect(Object.keys(ReactTestUtils.Simulate).sort()).toMatchSnapshot();
32
});
33
33
- it('gives Jest mocks a passthrough implementation with mockComponent()', () => {
34
+ it('gives Jest mocks a passthrough implementation with mockComponent()', async () => {
35
class MockedComponent extends React.Component {
36
render() {
37
throw new Error('Should not get here.');
@@ -51,11 +52,15 @@ describe('ReactTestUtils', () => {
52
ReactTestUtils.mockComponent(MockedComponent);
53
54
const container = document.createElement('div');
54
- ReactDOM.render(<MockedComponent>Hello</MockedComponent>, container);
55
+ const root = ReactDOMClient.createRoot(container);
56
+ await act(() => {
57
+ root.render(<MockedComponent>Hello</MockedComponent>);
58
+ });
59
+
60
expect(container.textContent).toBe('Hello');
61
});
62
58
- it('can scryRenderedComponentsWithType', () => {
63
+ it('can scryRenderedComponentsWithType', async () => {
64
class Child extends React.Component {
65
render() {
66
return null;
@@ -70,7 +75,12 @@ describe('ReactTestUtils', () => {
75
);
76
}
77
}
73
- const renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
78
+ const container = document.createElement('div');
79
+ const root = ReactDOMClient.createRoot(container);
80
+ let renderedComponent;
81
+ await act(() => {
82
+ root.render(<Wrapper ref={current => (renderedComponent = current)} />);
83
+ });
84
const scryResults = ReactTestUtils.scryRenderedComponentsWithType(
85
renderedComponent,
86
Child,
@@ -78,7 +88,7 @@ describe('ReactTestUtils', () => {
88
expect(scryResults.length).toBe(1);
89
});
90
81
- it('can scryRenderedDOMComponentsWithClass with TextComponent', () => {
91
+ it('can scryRenderedDOMComponentsWithClass with TextComponent', async () => {
92
class Wrapper extends React.Component {
93
render() {
94
return (
@@ -89,7 +99,12 @@ describe('ReactTestUtils', () => {
99
}
100
}
101
92
- const renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
102
+ const container = document.createElement('div');
103
+ const root = ReactDOMClient.createRoot(container);
104
+ let renderedComponent;
105
+ await act(() => {
106
+ root.render(<Wrapper ref={current => (renderedComponent = current)} />);
107
+ });
108
const scryResults = ReactTestUtils.scryRenderedDOMComponentsWithClass(
109
renderedComponent,
110
'NonExistentClass',
@@ -97,7 +112,7 @@ describe('ReactTestUtils', () => {
112
expect(scryResults.length).toBe(0);
113
});
114
100
- it('can scryRenderedDOMComponentsWithClass with className contains \\n', () => {
115
+ it('can scryRenderedDOMComponentsWithClass with className contains \\n', async () => {
116
class Wrapper extends React.Component {
117
render() {
118
return (
@@ -108,7 +123,12 @@ describe('ReactTestUtils', () => {
123
}
124
}
125
111
- const renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
126
+ const container = document.createElement('div');
127
+ const root = ReactDOMClient.createRoot(container);
128
+ let renderedComponent;
129
+ await act(() => {
130
+ root.render(<Wrapper ref={current => (renderedComponent = current)} />);
131
+ });
132
const scryResults = ReactTestUtils.scryRenderedDOMComponentsWithClass(
133
renderedComponent,
134
'x',
@@ -116,7 +136,7 @@ describe('ReactTestUtils', () => {
136
expect(scryResults.length).toBe(1);
137
});
138
119
- it('can scryRenderedDOMComponentsWithClass with multiple classes', () => {
139
+ it('can scryRenderedDOMComponentsWithClass with multiple classes', async () => {
140
class Wrapper extends React.Component {
141
render() {
142
return (
@@ -127,7 +147,12 @@ describe('ReactTestUtils', () => {
147
}
148
}
149
130
- const renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
150
+ const container = document.createElement('div');
151
+ const root = ReactDOMClient.createRoot(container);
152
+ let renderedComponent;
153
+ await act(() => {
154
+ root.render(<Wrapper ref={current => (renderedComponent = current)} />);
155
+ });
156
const scryResults1 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
157
renderedComponent,
158
'x y',
@@ -162,7 +187,7 @@ describe('ReactTestUtils', () => {
187
expect(scryResults5.length).toBe(0);
188
});
189
165
- it('traverses children in the correct order', () => {
190
+ it('traverses children in the correct order', async () => {
191
class Wrapper extends React.Component {
192
render() {
193
return <div>{this.props.children}</div>;
@@ -170,25 +195,29 @@ describe('ReactTestUtils', () => {
195
}
196
197
const container = document.createElement('div');
173
- ReactDOM.render(
174
- <Wrapper>
175
- {null}
176
- <div>purple</div>
177
- </Wrapper>,
178
- container,
179
- );
180
- const tree = ReactDOM.render(
181
- <Wrapper>
182
- <div>orange</div>
183
- <div>purple</div>
184
- </Wrapper>,
185
- container,
186
- );
198
+ const root = ReactDOMClient.createRoot(container);
199
+ await act(() => {
200
+ root.render(
201
+ <Wrapper>
202
+ {null}
203
+ <div>purple</div>
204
+ </Wrapper>,
205
+ );
206
+ });
207
+ let tree;
208
+ await act(() => {
209
+ root.render(
210
+ <Wrapper ref={current => (tree = current)}>
211
+ <div>orange</div>
212
+ <div>purple</div>
213
+ </Wrapper>,
214
+ );
215
+ });
216
217
const log = [];
218
ReactTestUtils.findAllInRenderedTree(tree, function (child) {
219
if (ReactTestUtils.isDOMComponent(child)) {
191
- log.push(ReactDOM.findDOMNode(child).textContent);
220
+ log.push(child.textContent);
221
}
222
});
223
@@ -196,7 +225,7 @@ describe('ReactTestUtils', () => {
225
expect(log).toEqual(['orangepurple', 'orange', 'purple']);
226
});
227
199
- it('should support injected wrapper components as DOM components', () => {
228
+ it('should support injected wrapper components as DOM components', async () => {
229
const injectedDOMComponents = [
230
'button',
231
'form',
@@ -208,13 +237,22 @@ describe('ReactTestUtils', () => {
237
'textarea',
238
];
239
211
- injectedDOMComponents.forEach(function (type) {
212
- const testComponent = ReactTestUtils.renderIntoDocument(
213
- React.createElement(type),
214
- );
240
+ // eslint-disable-next-line no-for-of-loops/no-for-of-loops
241
+ for (const type of injectedDOMComponents) {
242
+ const container = document.createElement('div');
243
+ const root = ReactDOMClient.createRoot(container);
244
+ let testComponent;
245
+ await act(() => {
246
+ root.render(
247
+ React.createElement(type, {
248
+ ref: current => (testComponent = current),
249
+ }),
250
+ );
251
+ });
252
+
253
expect(testComponent.tagName).toBe(type.toUpperCase());
254
expect(ReactTestUtils.isDOMComponent(testComponent)).toBe(true);
217
- });
255
+ }
256
257
// Full-page components (html, head, body) can't be rendered into a div
258
// directly...
@@ -237,7 +275,13 @@ describe('ReactTestUtils', () => {
275
276
const markup = ReactDOMServer.renderToString(<Root />);
277
const testDocument = getTestDocument(markup);
240
- const component = ReactDOM.hydrate(<Root />, testDocument);
278
+ let component;
279
+ await act(() => {
280
+ ReactDOMClient.hydrateRoot(
281
+ testDocument,
282
+ <Root ref={current => (component = current)} />,
283
+ );
284
+ });
285
286
expect(component.htmlRef.current.tagName).toBe('HTML');
287
expect(component.headRef.current.tagName).toBe('HEAD');
@@ -247,7 +291,7 @@ describe('ReactTestUtils', () => {
291
expect(ReactTestUtils.isDOMComponent(component.bodyRef.current)).toBe(true);
292
});
293
250
- it('can scry with stateless components involved', () => {
294
+ it('can scry with stateless components involved', async () => {
295
const Function = () => (
296
<div>
297
<hr />
@@ -265,7 +309,13 @@ describe('ReactTestUtils', () => {
309
}
310
}
311
268
- const inst = ReactTestUtils.renderIntoDocument(<SomeComponent />);
312
+ const container = document.createElement('div');
313
+ const root = ReactDOMClient.createRoot(container);
314
+ let inst;
315
+ await act(() => {
316
+ root.render(<SomeComponent ref={current => (inst = current)} />);
317
+ });
318
+
319
const hrs = ReactTestUtils.scryRenderedDOMComponentsWithTag(inst, 'hr');
320
expect(hrs.length).toBe(2);
321
});
@@ -327,7 +377,7 @@ describe('ReactTestUtils', () => {
377
});
378
379
describe('Simulate', () => {
330
- it('should change the value of an input field', () => {
380
+ it('should change the value of an input field', async () => {
381
const obj = {
382
handler: function (e) {
383
e.persist();
@@ -335,10 +385,11 @@ describe('ReactTestUtils', () => {
385
};
386
spyOnDevAndProd(obj, 'handler');
387
const container = document.createElement('div');
338
- const node = ReactDOM.render(
339
- <input type="text" onChange={obj.handler} />,
340
- container,
341
- );
388
+ const root = ReactDOMClient.createRoot(container);
389
+ await act(() => {
390
+ root.render(<input type="text" onChange={obj.handler} />);
391
+ });
392
+ const node = container.firstChild;
393
394
node.value = 'giraffe';
395
ReactTestUtils.Simulate.change(node);
@@ -348,7 +399,7 @@ describe('ReactTestUtils', () => {
399
);
400
});
401
351
- it('should change the value of an input field in a component', () => {
402
+ it('should change the value of an input field in a component', async () => {
403
class SomeComponent extends React.Component {
404
inputRef = React.createRef();
405
render() {
@@ -371,10 +422,16 @@ describe('ReactTestUtils', () => {
422
};
423
spyOnDevAndProd(obj, 'handler');
424
const container = document.createElement('div');
374
- const instance = ReactDOM.render(
375
- <SomeComponent handleChange={obj.handler} />,
376
- container,
377
- );
425
+ const root = ReactDOMClient.createRoot(container);
426
+ let instance;
427
+ await act(() => {
428
+ root.render(
429
+ <SomeComponent
430
+ handleChange={obj.handler}
431
+ ref={current => (instance = current)}
432
+ />,
433
+ );
434
+ });
435
436
const node = instance.inputRef.current;
437
node.value = 'zebra';
@@ -385,27 +442,33 @@ describe('ReactTestUtils', () => {
442
);
443
});
444
388
- it('should not warn when used with extra properties', () => {
445
+ it('should not warn when used with extra properties', async () => {
446
const CLIENT_X = 100;
447
448
class Component extends React.Component {
449
+ childRef = React.createRef();
450
handleClick = e => {
451
expect(e.clientX).toBe(CLIENT_X);
452
};
453
454
render() {
397
- return <div onClick={this.handleClick} />;
455
+ return <div onClick={this.handleClick} ref={this.childRef} />;
456
}
457
}
458
459
const element = document.createElement('div');
402
- const instance = ReactDOM.render(<Component />, element);
403
- ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance), {
460
+ const root = ReactDOMClient.createRoot(element);
461
+ let instance;
462
+ await act(() => {
463
+ root.render(<Component ref={current => (instance = current)} />);
464
+ });
465
+
466
+ ReactTestUtils.Simulate.click(instance.childRef.current, {
467
clientX: CLIENT_X,
468
});
469
});
470
408
- it('should set the type of the event', () => {
471
+ it('should set the type of the event', async () => {
472
let event;
473
const stub = jest.fn().mockImplementation(e => {
474
e.persist();
@@ -413,8 +476,11 @@ describe('ReactTestUtils', () => {
476
});
477
478
const container = document.createElement('div');
416
- const instance = ReactDOM.render(<div onKeyDown={stub} />, container);
417
- const node = ReactDOM.findDOMNode(instance);
479
+ const root = ReactDOMClient.createRoot(container);
480
+ let node;
481
+ await act(() => {
482
+ root.render(<div onKeyDown={stub} ref={current => (node = current)} />);
483
+ });
484
485
ReactTestUtils.Simulate.keyDown(node);
486
@@ -422,7 +488,7 @@ describe('ReactTestUtils', () => {
488
expect(event.nativeEvent.type).toBe('keydown');
489
});
490
425
- it('should work with renderIntoDocument', () => {
491
+ it('should work with renderIntoDocument', async () => {
492
const onChange = jest.fn();
493
494
class MyComponent extends React.Component {
@@ -435,7 +501,13 @@ describe('ReactTestUtils', () => {
501
}
502
}
503
438
- const instance = ReactTestUtils.renderIntoDocument(<MyComponent />);
504
+ const container = document.createElement('div');
505
+ const root = ReactDOMClient.createRoot(container);
506
+ let instance;
507
+ await act(() => {
508
+ root.render(<MyComponent ref={current => (instance = current)} />);
509
+ });
510
+
511
const input = ReactTestUtils.findRenderedDOMComponentWithTag(
512
instance,
513
'input',
@@ -449,7 +521,7 @@ describe('ReactTestUtils', () => {
521
});
522
});
523
452
- it('should call setState callback with no arguments', () => {
524
+ it('should call setState callback with no arguments', async () => {
525
let mockArgs;
526
class Component extends React.Component {
527
componentDidMount() {
@@ -460,17 +532,28 @@ describe('ReactTestUtils', () => {
532
}
533
}
534
463
- ReactTestUtils.renderIntoDocument(<Component />);
535
+ const container = document.createElement('div');
536
+ const root = ReactDOMClient.createRoot(container);
537
+ await act(() => {
538
+ root.render(<Component />);
539
+ });
540
+
541
expect(mockArgs.length).toEqual(0);
542
});
466
- it('should find rendered component with type in document', () => {
543
+ it('should find rendered component with type in document', async () => {
544
class MyComponent extends React.Component {
545
render() {
546
return true;
547
}
548
}
549
473
- const instance = ReactTestUtils.renderIntoDocument(<MyComponent />);
550
+ const container = document.createElement('div');
551
+ const root = ReactDOMClient.createRoot(container);
552
+ let instance;
553
+ await act(() => {
554
+ root.render(<MyComponent ref={current => (instance = current)} />);
555
+ });
556
+
557
const renderedComponentType = ReactTestUtils.findRenderedComponentWithType(
558
instance,
559
MyComponent,