Convert ReactDOMTextarea to createRoot (#28126)
Also removes usage of `ReactTestUtils`
Sebastian Silbermann committed
Jan 29, 2024 at 17:02 UTC
cdfaae73c4768ddcce724872b1aa45130b39c85f
1 file changed
+546
-278
packages/react-dom/src/__tests__/ReactDOMTextarea-test.js
+546
-278
@@ -13,9 +13,9 @@ function emptyFunction() {}
13
14
describe('ReactDOMTextarea', () => {
15
let React;
16
- let ReactDOM;
16
+ let ReactDOMClient;
17
let ReactDOMServer;
18
- let ReactTestUtils;
18
+ let act;
19
20
let renderTextarea;
21
@@ -25,15 +25,16 @@ describe('ReactDOMTextarea', () => {
25
jest.resetModules();
26
27
React = require('react');
28
- ReactDOM = require('react-dom');
28
+ ReactDOMClient = require('react-dom/client');
29
ReactDOMServer = require('react-dom/server');
30
- ReactTestUtils = require('react-dom/test-utils');
30
+ act = require('internal-test-utils').act;
31
32
- renderTextarea = function (component, container) {
33
- if (!container) {
34
- container = document.createElement('div');
35
- }
36
- const node = ReactDOM.render(component, container);
32
+ renderTextarea = async function (component, container, root) {
33
+ await act(() => {
34
+ root.render(component);
35
+ });
36
+
37
+ const node = container.firstChild;
38
39
// Fixing jsdom's quirky behavior -- in reality, the parser should strip
40
// off the leading newline but we need to do it by hand here.
@@ -46,93 +47,141 @@ describe('ReactDOMTextarea', () => {
47
jest.restoreAllMocks();
48
});
49
49
- it('should allow setting `defaultValue`', () => {
50
+ it('should allow setting `defaultValue`', async () => {
51
const container = document.createElement('div');
51
- const node = renderTextarea(<textarea defaultValue="giraffe" />, container);
52
+ const root = ReactDOMClient.createRoot(container);
53
+ const node = await renderTextarea(
54
+ <textarea defaultValue="giraffe" />,
55
+ container,
56
+ root,
57
+ );
58
59
expect(node.value).toBe('giraffe');
60
61
// Changing `defaultValue` should do nothing.
56
- renderTextarea(<textarea defaultValue="gorilla" />, container);
62
+ await renderTextarea(<textarea defaultValue="gorilla" />, container, root);
63
expect(node.value).toEqual('giraffe');
64
65
node.value = 'cat';
66
61
- renderTextarea(<textarea defaultValue="monkey" />, container);
67
+ await renderTextarea(<textarea defaultValue="monkey" />, container, root);
68
expect(node.value).toEqual('cat');
69
});
70
65
- it('should display `defaultValue` of number 0', () => {
66
- const stub = <textarea defaultValue={0} />;
67
- const node = renderTextarea(stub);
71
+ it('should display `defaultValue` of number 0', async () => {
72
+ const container = document.createElement('div');
73
+ const root = ReactDOMClient.createRoot(container);
74
+ const node = await renderTextarea(
75
+ <textarea defaultValue={0} />,
76
+ container,
77
+ root,
78
+ );
79
80
expect(node.value).toBe('0');
81
});
82
72
- it('should display "false" for `defaultValue` of `false`', () => {
73
- const stub = <textarea defaultValue={false} />;
74
- const node = renderTextarea(stub);
83
+ it('should display "false" for `defaultValue` of `false`', async () => {
84
+ const container = document.createElement('div');
85
+ const root = ReactDOMClient.createRoot(container);
86
+ const node = await renderTextarea(
87
+ <textarea defaultValue={false} />,
88
+ container,
89
+ root,
90
+ );
91
92
expect(node.value).toBe('false');
93
});
94
79
- it('should display "foobar" for `defaultValue` of `objToString`', () => {
95
+ it('should display "foobar" for `defaultValue` of `objToString`', async () => {
96
+ const container = document.createElement('div');
97
+ const root = ReactDOMClient.createRoot(container);
98
const objToString = {
99
toString: function () {
100
return 'foobar';
101
},
102
};
85
-
86
- const stub = <textarea defaultValue={objToString} />;
87
- const node = renderTextarea(stub);
103
+ const node = await renderTextarea(
104
+ <textarea defaultValue={objToString} />,
105
+ container,
106
+ root,
107
+ );
108
109
expect(node.value).toBe('foobar');
110
});
111
92
- it('should set defaultValue', () => {
112
+ it('should set defaultValue', async () => {
113
const container = document.createElement('div');
94
- ReactDOM.render(<textarea defaultValue="foo" />, container);
95
- ReactDOM.render(<textarea defaultValue="bar" />, container);
96
- ReactDOM.render(<textarea defaultValue="noise" />, container);
114
+ const root = ReactDOMClient.createRoot(container);
115
+ await act(() => {
116
+ root.render(<textarea defaultValue="foo" />);
117
+ });
118
+ await act(() => {
119
+ root.render(<textarea defaultValue="bar" />);
120
+ });
121
+ await act(() => {
122
+ root.render(<textarea defaultValue="noise" />);
123
+ });
124
+
125
expect(container.firstChild.defaultValue).toBe('noise');
126
});
127
100
- it('should not render value as an attribute', () => {
101
- const stub = <textarea value="giraffe" onChange={emptyFunction} />;
102
- const node = renderTextarea(stub);
128
+ it('should not render value as an attribute', async () => {
129
+ const container = document.createElement('div');
130
+ const root = ReactDOMClient.createRoot(container);
131
+ const node = await renderTextarea(
132
+ <textarea value="giraffe" onChange={emptyFunction} />,
133
+ container,
134
+ root,
135
+ );
136
137
expect(node.getAttribute('value')).toBe(null);
138
});
139
107
- it('should display `value` of number 0', () => {
108
- const stub = <textarea value={0} onChange={emptyFunction} />;
109
- const node = renderTextarea(stub);
140
+ it('should display `value` of number 0', async () => {
141
+ const container = document.createElement('div');
142
+ const root = ReactDOMClient.createRoot(container);
143
+ const node = await renderTextarea(
144
+ <textarea value={0} onChange={emptyFunction} />,
145
+ container,
146
+ root,
147
+ );
148
149
expect(node.value).toBe('0');
150
});
151
114
- it('should update defaultValue to empty string', () => {
152
+ it('should update defaultValue to empty string', async () => {
153
const container = document.createElement('div');
116
- ReactDOM.render(<textarea defaultValue={'foo'} />, container);
117
- ReactDOM.render(<textarea defaultValue={''} />, container);
154
+ const root = ReactDOMClient.createRoot(container);
155
+ await act(() => {
156
+ root.render(<textarea defaultValue={'foo'} />);
157
+ });
158
+
159
+ await act(() => {
160
+ root.render(<textarea defaultValue={''} />);
161
+ });
162
+
163
expect(container.firstChild.defaultValue).toBe('');
164
});
165
121
- it('should allow setting `value` to `giraffe`', () => {
166
+ it('should allow setting `value` to `giraffe`', async () => {
167
const container = document.createElement('div');
123
- let stub = <textarea value="giraffe" onChange={emptyFunction} />;
124
- const node = renderTextarea(stub, container);
168
+ const root = ReactDOMClient.createRoot(container);
169
+ const node = await renderTextarea(
170
+ <textarea value="giraffe" onChange={emptyFunction} />,
171
+ container,
172
+ root,
173
+ );
174
175
expect(node.value).toBe('giraffe');
176
128
- stub = ReactDOM.render(
129
- <textarea value="gorilla" onChange={emptyFunction} />,
130
- container,
131
- );
177
+ await act(() => {
178
+ root.render(<textarea value="gorilla" onChange={emptyFunction} />);
179
+ });
180
+
181
expect(node.value).toEqual('gorilla');
182
});
183
135
- it('will not initially assign an empty value (covers case where firefox throws a validation error when required attribute is set)', () => {
184
+ it('will not initially assign an empty value (covers case where firefox throws a validation error when required attribute is set)', async () => {
185
const container = document.createElement('div');
186
187
let counter = 0;
@@ -156,7 +205,10 @@ describe('ReactDOMTextarea', () => {
205
},
206
);
207
159
- ReactDOM.render(<textarea value="" readOnly={true} />, container);
208
+ const root = ReactDOMClient.createRoot(container);
209
+ await act(() => {
210
+ root.render(<textarea value="" readOnly={true} />);
211
+ });
212
213
expect(counter).toEqual(0);
214
});
@@ -178,38 +230,50 @@ describe('ReactDOMTextarea', () => {
230
expect(div.firstChild.getAttribute('defaultValue')).toBe(null);
231
});
232
181
- it('should allow setting `value` to `true`', () => {
233
+ it('should allow setting `value` to `true`', async () => {
234
const container = document.createElement('div');
183
- let stub = <textarea value="giraffe" onChange={emptyFunction} />;
184
- const node = renderTextarea(stub, container);
235
+ const root = ReactDOMClient.createRoot(container);
236
+ const node = await renderTextarea(
237
+ <textarea value="giraffe" onChange={emptyFunction} />,
238
+ container,
239
+ root,
240
+ );
241
242
expect(node.value).toBe('giraffe');
243
188
- stub = ReactDOM.render(
189
- <textarea value={true} onChange={emptyFunction} />,
190
- container,
191
- );
244
+ await act(() => {
245
+ root.render(<textarea value={true} onChange={emptyFunction} />);
246
+ });
247
+
248
expect(node.value).toEqual('true');
249
});
250
195
- it('should allow setting `value` to `false`', () => {
251
+ it('should allow setting `value` to `false`', async () => {
252
const container = document.createElement('div');
197
- let stub = <textarea value="giraffe" onChange={emptyFunction} />;
198
- const node = renderTextarea(stub, container);
253
+ const root = ReactDOMClient.createRoot(container);
254
+ const node = await renderTextarea(
255
+ <textarea value="giraffe" onChange={emptyFunction} />,
256
+ container,
257
+ root,
258
+ );
259
260
expect(node.value).toBe('giraffe');
261
202
- stub = ReactDOM.render(
203
- <textarea value={false} onChange={emptyFunction} />,
204
- container,
205
- );
262
+ await act(() => {
263
+ root.render(<textarea value={false} onChange={emptyFunction} />);
264
+ });
265
+
266
expect(node.value).toEqual('false');
267
});
268
209
- it('should allow setting `value` to `objToString`', () => {
269
+ it('should allow setting `value` to `objToString`', async () => {
270
const container = document.createElement('div');
211
- let stub = <textarea value="giraffe" onChange={emptyFunction} />;
212
- const node = renderTextarea(stub, container);
271
+ const root = ReactDOMClient.createRoot(container);
272
+ const node = await renderTextarea(
273
+ <textarea value="giraffe" onChange={emptyFunction} />,
274
+ container,
275
+ root,
276
+ );
277
278
expect(node.value).toBe('giraffe');
279
@@ -218,14 +282,15 @@ describe('ReactDOMTextarea', () => {
282
return 'foo';
283
},
284
};
221
- stub = ReactDOM.render(
222
- <textarea value={objToString} onChange={emptyFunction} />,
223
- container,
224
- );
285
+
286
+ await act(() => {
287
+ root.render(<textarea value={objToString} onChange={emptyFunction} />);
288
+ });
289
+
290
expect(node.value).toEqual('foo');
291
});
292
228
- it('should throw when value is set to a Temporal-like object', () => {
293
+ it('should throw when value is set to a Temporal-like object', async () => {
294
class TemporalLike {
295
valueOf() {
296
// Throwing here is the behavior of ECMAScript "Temporal" date/time API.
@@ -237,51 +302,72 @@ describe('ReactDOMTextarea', () => {
302
}
303
}
304
const container = document.createElement('div');
240
- const stub = <textarea value="giraffe" onChange={emptyFunction} />;
241
- const node = renderTextarea(stub, container);
305
+ const root = ReactDOMClient.createRoot(container);
306
+ const node = await renderTextarea(
307
+ <textarea value="giraffe" onChange={emptyFunction} />,
308
+ container,
309
+ root,
310
+ );
311
312
expect(node.value).toBe('giraffe');
313
245
- const test = () =>
246
- ReactDOM.render(
247
- <textarea value={new TemporalLike()} onChange={emptyFunction} />,
248
- container,
249
- );
250
- expect(() =>
251
- expect(test).toThrowError(new TypeError('prod message')),
314
+ const test = async () => {
315
+ await act(() => {
316
+ root.render(
317
+ <textarea value={new TemporalLike()} onChange={emptyFunction} />,
318
+ );
319
+ });
320
+ };
321
+ await expect(() =>
322
+ expect(test).rejects.toThrowError(new TypeError('prod message')),
323
).toErrorDev(
324
'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' +
325
'strings, not TemporalLike. This value must be coerced to a string before using it here.',
326
);
327
});
328
258
- it('should take updates to `defaultValue` for uncontrolled textarea', () => {
329
+ it('should take updates to `defaultValue` for uncontrolled textarea', async () => {
330
const container = document.createElement('div');
331
+ const root = ReactDOMClient.createRoot(container);
332
+ await act(() => {
333
+ root.render(<textarea defaultValue="0" />);
334
+ });
335
261
- const node = ReactDOM.render(<textarea defaultValue="0" />, container);
336
+ const node = container.firstChild;
337
338
expect(node.value).toBe('0');
339
265
- ReactDOM.render(<textarea defaultValue="1" />, container);
340
+ await act(() => {
341
+ root.render(<textarea defaultValue="1" />);
342
+ });
343
344
expect(node.value).toBe('0');
345
});
346
270
- it('should take updates to children in lieu of `defaultValue` for uncontrolled textarea', () => {
347
+ it('should take updates to children in lieu of `defaultValue` for uncontrolled textarea', async () => {
348
const container = document.createElement('div');
349
+ const root = ReactDOMClient.createRoot(container);
350
+ await act(() => {
351
+ root.render(<textarea defaultValue="0" />);
352
+ });
353
273
- const node = ReactDOM.render(<textarea defaultValue="0" />, container);
354
+ const node = container.firstChild;
355
356
expect(node.value).toBe('0');
357
277
- ReactDOM.render(<textarea>1</textarea>, container);
358
+ await act(() => {
359
+ root.render(<textarea>1</textarea>);
360
+ });
361
362
expect(node.value).toBe('0');
363
});
364
282
- it('should not incur unnecessary DOM mutations', () => {
365
+ it('should not incur unnecessary DOM mutations', async () => {
366
const container = document.createElement('div');
284
- ReactDOM.render(<textarea value="a" onChange={emptyFunction} />, container);
367
+ const root = ReactDOMClient.createRoot(container);
368
+ await act(() => {
369
+ root.render(<textarea value="a" onChange={emptyFunction} />);
370
+ });
371
372
const node = container.firstChild;
373
let nodeValue = 'a';
@@ -295,25 +381,35 @@ describe('ReactDOMTextarea', () => {
381
}),
382
});
383
298
- ReactDOM.render(<textarea value="a" onChange={emptyFunction} />, container);
384
+ await act(() => {
385
+ root.render(<textarea value="a" onChange={emptyFunction} />);
386
+ });
387
+
388
expect(nodeValueSetter).toHaveBeenCalledTimes(0);
389
301
- ReactDOM.render(<textarea value="b" onChange={emptyFunction} />, container);
390
+ await act(() => {
391
+ root.render(<textarea value="b" onChange={emptyFunction} />);
392
+ });
393
+
394
expect(nodeValueSetter).toHaveBeenCalledTimes(1);
395
});
396
305
- it('should properly control a value of number `0`', () => {
306
- const stub = <textarea value={0} onChange={emptyFunction} />;
397
+ it('should properly control a value of number `0`', async () => {
398
const setUntrackedValue = Object.getOwnPropertyDescriptor(
399
HTMLTextAreaElement.prototype,
400
'value',
401
).set;
402
403
const container = document.createElement('div');
404
+ const root = ReactDOMClient.createRoot(container);
405
document.body.appendChild(container);
406
407
try {
316
- const node = renderTextarea(stub, container);
408
+ const node = await renderTextarea(
409
+ <textarea value={0} onChange={emptyFunction} />,
410
+ container,
411
+ root,
412
+ );
413
414
setUntrackedValue.call(node, 'giraffe');
415
node.dispatchEvent(
@@ -326,30 +422,41 @@ describe('ReactDOMTextarea', () => {
422
});
423
424
if (ReactFeatureFlags.disableTextareaChildren) {
329
- it('should ignore children content', () => {
425
+ it('should ignore children content', async () => {
426
const container = document.createElement('div');
331
- let stub = <textarea>giraffe</textarea>;
427
+ const root = ReactDOMClient.createRoot(container);
428
let node;
429
334
- expect(() => {
335
- node = renderTextarea(stub, container);
430
+ await expect(async () => {
431
+ node = await renderTextarea(
432
+ <textarea>giraffe</textarea>,
433
+ container,
434
+ root,
435
+ );
436
}).toErrorDev(
437
'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
438
);
439
expect(node.value).toBe('');
340
- // Changing children should do nothing, it functions like `defaultValue`.
341
- stub = ReactDOM.render(<textarea>gorilla</textarea>, container);
440
+
441
+ await act(() => {
442
+ root.render(<textarea>gorilla</textarea>);
443
+ });
444
+
445
expect(node.value).toEqual('');
446
});
447
}
448
449
if (ReactFeatureFlags.disableTextareaChildren) {
347
- it('should receive defaultValue and still ignore children content', () => {
450
+ it('should receive defaultValue and still ignore children content', async () => {
451
+ const container = document.createElement('div');
452
+ const root = ReactDOMClient.createRoot(container);
453
let node;
454
350
- expect(() => {
351
- node = renderTextarea(
455
+ await expect(async () => {
456
+ node = await renderTextarea(
457
<textarea defaultValue="dragon">monkey</textarea>,
458
+ container,
459
+ root,
460
);
461
}).toErrorDev(
462
'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
@@ -359,67 +466,80 @@ describe('ReactDOMTextarea', () => {
466
}
467
468
if (!ReactFeatureFlags.disableTextareaChildren) {
362
- it('should treat children like `defaultValue`', () => {
469
+ it('should treat children like `defaultValue`', async () => {
470
const container = document.createElement('div');
364
- let stub = <textarea>giraffe</textarea>;
471
+ const root = ReactDOMClient.createRoot(container);
472
let node;
473
367
- expect(() => {
368
- node = renderTextarea(stub, container);
474
+ await expect(async () => {
475
+ node = await renderTextarea(
476
+ <textarea>giraffe</textarea>,
477
+ container,
478
+ root,
479
+ );
480
}).toErrorDev(
481
'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
482
);
483
484
expect(node.value).toBe('giraffe');
485
375
- // Changing children should do nothing, it functions like `defaultValue`.
376
- stub = ReactDOM.render(<textarea>gorilla</textarea>, container);
486
+ await act(() => {
487
+ root.render(<textarea>gorilla</textarea>);
488
+ });
489
+
490
expect(node.value).toEqual('giraffe');
491
});
492
}
493
381
- it('should keep value when switching to uncontrolled element if not changed', () => {
494
+ it('should keep value when switching to uncontrolled element if not changed', async () => {
495
const container = document.createElement('div');
383
-
384
- const node = renderTextarea(
496
+ const root = ReactDOMClient.createRoot(container);
497
+ const node = await renderTextarea(
498
<textarea value="kitten" onChange={emptyFunction} />,
499
container,
500
+ root,
501
);
502
503
expect(node.value).toBe('kitten');
504
391
- ReactDOM.render(<textarea defaultValue="gorilla" />, container);
505
+ await act(() => {
506
+ root.render(<textarea defaultValue="gorilla" />);
507
+ });
508
509
expect(node.value).toEqual('kitten');
510
});
511
396
- it('should keep value when switching to uncontrolled element if changed', () => {
512
+ it('should keep value when switching to uncontrolled element if changed', async () => {
513
const container = document.createElement('div');
398
-
399
- const node = renderTextarea(
514
+ const root = ReactDOMClient.createRoot(container);
515
+ const node = await renderTextarea(
516
<textarea value="kitten" onChange={emptyFunction} />,
517
container,
518
+ root,
519
);
520
521
expect(node.value).toBe('kitten');
522
406
- ReactDOM.render(
407
- <textarea value="puppies" onChange={emptyFunction} />,
408
- container,
409
- );
523
+ await act(() => {
524
+ root.render(<textarea value="puppies" onChange={emptyFunction} />);
525
+ });
526
527
expect(node.value).toBe('puppies');
528
413
- ReactDOM.render(<textarea defaultValue="gorilla" />, container);
529
+ await act(() => {
530
+ root.render(<textarea defaultValue="gorilla" />);
531
+ });
532
533
expect(node.value).toEqual('puppies');
534
});
535
536
if (ReactFeatureFlags.disableTextareaChildren) {
419
- it('should ignore numbers as children', () => {
537
+ it('should ignore numbers as children', async () => {
538
+ const container = document.createElement('div');
539
+ const root = ReactDOMClient.createRoot(container);
540
let node;
421
- expect(() => {
422
- node = renderTextarea(<textarea>{17}</textarea>);
541
+ await expect(async () => {
542
+ node = await renderTextarea(<textarea>{17}</textarea>, container, root);
543
}).toErrorDev(
544
'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
545
);
@@ -428,10 +548,12 @@ describe('ReactDOMTextarea', () => {
548
}
549
550
if (!ReactFeatureFlags.disableTextareaChildren) {
431
- it('should allow numbers as children', () => {
551
+ it('should allow numbers as children', async () => {
552
+ const container = document.createElement('div');
553
+ const root = ReactDOMClient.createRoot(container);
554
let node;
433
- expect(() => {
434
- node = renderTextarea(<textarea>{17}</textarea>);
555
+ await expect(async () => {
556
+ node = await renderTextarea(<textarea>{17}</textarea>, container, root);
557
}).toErrorDev(
558
'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
559
);
@@ -440,10 +562,16 @@ describe('ReactDOMTextarea', () => {
562
}
563
564
if (ReactFeatureFlags.disableTextareaChildren) {
443
- it('should ignore booleans as children', () => {
565
+ it('should ignore booleans as children', async () => {
566
+ const container = document.createElement('div');
567
+ const root = ReactDOMClient.createRoot(container);
568
let node;
445
- expect(() => {
446
- node = renderTextarea(<textarea>{false}</textarea>);
569
+ await expect(async () => {
570
+ node = await renderTextarea(
571
+ <textarea>{false}</textarea>,
572
+ container,
573
+ root,
574
+ );
575
}).toErrorDev(
576
'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
577
);
@@ -452,10 +580,16 @@ describe('ReactDOMTextarea', () => {
580
}
581
582
if (!ReactFeatureFlags.disableTextareaChildren) {
455
- it('should allow booleans as children', () => {
583
+ it('should allow booleans as children', async () => {
584
+ const container = document.createElement('div');
585
+ const root = ReactDOMClient.createRoot(container);
586
let node;
457
- expect(() => {
458
- node = renderTextarea(<textarea>{false}</textarea>);
587
+ await expect(async () => {
588
+ node = await renderTextarea(
589
+ <textarea>{false}</textarea>,
590
+ container,
591
+ root,
592
+ );
593
}).toErrorDev(
594
'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
595
);
@@ -464,15 +598,21 @@ describe('ReactDOMTextarea', () => {
598
}
599
600
if (ReactFeatureFlags.disableTextareaChildren) {
467
- it('should ignore objects as children', () => {
601
+ it('should ignore objects as children', async () => {
602
+ const container = document.createElement('div');
603
+ const root = ReactDOMClient.createRoot(container);
604
const obj = {
605
toString: function () {
606
return 'sharkswithlasers';
607
},
608
};
609
let node;
474
- expect(() => {
475
- node = renderTextarea(<textarea>{obj}</textarea>);
610
+ await expect(async () => {
611
+ node = await renderTextarea(
612
+ <textarea>{obj}</textarea>,
613
+ container,
614
+ root,
615
+ );
616
}).toErrorDev(
617
'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
618
);
@@ -481,15 +621,21 @@ describe('ReactDOMTextarea', () => {
621
}
622
623
if (!ReactFeatureFlags.disableTextareaChildren) {
484
- it('should allow objects as children', () => {
624
+ it('should allow objects as children', async () => {
625
+ const container = document.createElement('div');
626
+ const root = ReactDOMClient.createRoot(container);
627
const obj = {
628
toString: function () {
629
return 'sharkswithlasers';
630
},
631
};
632
let node;
491
- expect(() => {
492
- node = renderTextarea(<textarea>{obj}</textarea>);
633
+ await expect(async () => {
634
+ node = await renderTextarea(
635
+ <textarea>{obj}</textarea>,
636
+ container,
637
+ root,
638
+ );
639
}).toErrorDev(
640
'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
641
);
@@ -498,64 +644,86 @@ describe('ReactDOMTextarea', () => {
644
}
645
646
if (!ReactFeatureFlags.disableTextareaChildren) {
501
- it('should throw with multiple or invalid children', () => {
502
- expect(() => {
503
- expect(() =>
504
- ReactTestUtils.renderIntoDocument(
505
- <textarea>
506
- {'hello'}
507
- {'there'}
508
- </textarea>,
509
- ),
510
- ).toThrow('<textarea> can only have at most one child');
511
- }).toErrorDev(
647
+ it('should throw with multiple or invalid children', async () => {
648
+ const container = document.createElement('div');
649
+ const root = ReactDOMClient.createRoot(container);
650
+ await expect(async () => {
651
+ await expect(async () => {
652
+ await act(() => {
653
+ root.render(
654
+ <textarea>
655
+ {'hello'}
656
+ {'there'}
657
+ </textarea>,
658
+ );
659
+ });
660
+ }).rejects.toThrow('<textarea> can only have at most one child');
661
+ }).toErrorDev([
662
'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
513
- );
663
+ 'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
664
+ ]);
665
666
let node;
516
- expect(() => {
517
- expect(
518
- () =>
519
- (node = renderTextarea(
667
+ await expect(async () => {
668
+ await expect(
669
+ (async () =>
670
+ (node = await renderTextarea(
671
<textarea>
672
<strong />
673
</textarea>,
523
- )),
524
- ).not.toThrow();
525
- }).toErrorDev(
674
+ container,
675
+ root,
676
+ )))(),
677
+ ).resolves.not.toThrow();
678
+ }).toErrorDev([
679
'Use the `defaultValue` or `value` props instead of setting children on <textarea>.',
527
- );
680
+ ]);
681
682
expect(node.value).toBe('[object Object]');
683
});
684
}
685
533
- it('should unmount', () => {
686
+ it('should unmount', async () => {
687
const container = document.createElement('div');
535
- renderTextarea(<textarea />, container);
536
- ReactDOM.unmountComponentAtNode(container);
688
+ const root = ReactDOMClient.createRoot(container);
689
+ await act(() => {
690
+ root.render(<textarea />);
691
+ });
692
+
693
+ await act(() => {
694
+ root.unmount();
695
+ });
696
});
697
539
- it('should warn if value is null', () => {
540
- expect(() =>
541
- ReactTestUtils.renderIntoDocument(<textarea value={null} />),
542
- ).toErrorDev(
698
+ it('should warn if value is null', async () => {
699
+ const container = document.createElement('div');
700
+ const root = ReactDOMClient.createRoot(container);
701
+ await expect(async () => {
702
+ await act(() => {
703
+ root.render(<textarea value={null} />);
704
+ });
705
+ }).toErrorDev(
706
'`value` prop on `textarea` should not be null. ' +
707
'Consider using an empty string to clear the component or `undefined` ' +
708
'for uncontrolled components.',
709
);
710
548
- // No additional warnings are expected
549
- ReactTestUtils.renderIntoDocument(<textarea value={null} />);
711
+ await act(() => {
712
+ root.render(<textarea value={null} />);
713
+ });
714
});
715
552
- it('should warn if value and defaultValue are specified', () => {
716
+ it('should warn if value and defaultValue are specified', async () => {
717
const InvalidComponent = () => (
718
<textarea value="foo" defaultValue="bar" readOnly={true} />
719
);
556
- expect(() =>
557
- ReactTestUtils.renderIntoDocument(<InvalidComponent />),
558
- ).toErrorDev(
720
+ let container = document.createElement('div');
721
+ let root = ReactDOMClient.createRoot(container);
722
+ await expect(async () => {
723
+ await act(() => {
724
+ root.render(<InvalidComponent />);
725
+ });
726
+ }).toErrorDev(
727
'InvalidComponent contains a textarea with both value and defaultValue props. ' +
728
'Textarea elements must be either controlled or uncontrolled ' +
729
'(specify either the value prop, or the defaultValue prop, but not ' +
@@ -564,18 +732,33 @@ describe('ReactDOMTextarea', () => {
732
'https://reactjs.org/link/controlled-components',
733
);
734
567
- // No additional warnings are expected
568
- ReactTestUtils.renderIntoDocument(<InvalidComponent />);
735
+ container = document.createElement('div');
736
+ root = ReactDOMClient.createRoot(container);
737
+
738
+ await act(() => {
739
+ root.render(<InvalidComponent />);
740
+ });
741
});
742
571
- it('should not warn about missing onChange in uncontrolled textareas', () => {
743
+ it('should not warn about missing onChange in uncontrolled textareas', async () => {
744
const container = document.createElement('div');
573
- ReactDOM.render(<textarea />, container);
574
- ReactDOM.unmountComponentAtNode(container);
575
- ReactDOM.render(<textarea value={undefined} />, container);
745
+ let root = ReactDOMClient.createRoot(container);
746
+
747
+ await act(() => {
748
+ root.render(<textarea />);
749
+ });
750
+
751
+ await act(() => {
752
+ root.unmount();
753
+ });
754
+ root = ReactDOMClient.createRoot(container);
755
+
756
+ await act(() => {
757
+ root.render(<textarea value={undefined} />);
758
+ });
759
});
760
578
- it('does not set textContent if value is unchanged', () => {
761
+ it('does not set textContent if value is unchanged', async () => {
762
const container = document.createElement('div');
763
let node;
764
let instance;
@@ -609,7 +792,11 @@ describe('ReactDOMTextarea', () => {
792
);
793
}
794
}
612
- ReactDOM.render(<App />, container);
795
+ const root = ReactDOMClient.createRoot(container);
796
+ await act(() => {
797
+ root.render(<App />);
798
+ });
799
+
800
defaultValue = node.defaultValue;
801
Object.defineProperty(node, 'defaultValue', {get, set});
802
instance.setState({count: 1});
@@ -617,59 +804,77 @@ describe('ReactDOMTextarea', () => {
804
});
805
806
describe('When given a Symbol value', () => {
620
- it('treats initial Symbol value as an empty string', () => {
807
+ it('treats initial Symbol value as an empty string', async () => {
808
const container = document.createElement('div');
622
- expect(() =>
623
- ReactDOM.render(
624
- <textarea value={Symbol('foobar')} onChange={() => {}} />,
625
- container,
626
- ),
627
- ).toErrorDev('Invalid value for prop `value`');
809
+ const root = ReactDOMClient.createRoot(container);
810
+ await expect(async () => {
811
+ await act(() => {
812
+ root.render(
813
+ <textarea value={Symbol('foobar')} onChange={() => {}} />,
814
+ );
815
+ });
816
+ }).toErrorDev('Invalid value for prop `value`');
817
const node = container.firstChild;
818
819
expect(node.value).toBe('');
820
});
821
633
- it('treats initial Symbol children as an empty string', () => {
822
+ it('treats initial Symbol children as an empty string', async () => {
823
const container = document.createElement('div');
635
- expect(() =>
636
- ReactDOM.render(
637
- <textarea onChange={() => {}}>{Symbol('foo')}</textarea>,
638
- container,
639
- ),
640
- ).toErrorDev('Use the `defaultValue` or `value` props');
824
+ const root = ReactDOMClient.createRoot(container);
825
+ await expect(async () => {
826
+ await act(() => {
827
+ root.render(<textarea onChange={() => {}}>{Symbol('foo')}</textarea>);
828
+ });
829
+ }).toErrorDev('Use the `defaultValue` or `value` props');
830
const node = container.firstChild;
831
832
expect(node.value).toBe('');
833
});
834
646
- it('treats updated Symbol value as an empty string', () => {
835
+ it('treats updated Symbol value as an empty string', async () => {
836
const container = document.createElement('div');
648
- ReactDOM.render(<textarea value="foo" onChange={() => {}} />, container);
649
- expect(() =>
650
- ReactDOM.render(
651
- <textarea value={Symbol('foo')} onChange={() => {}} />,
652
- container,
653
- ),
654
- ).toErrorDev('Invalid value for prop `value`');
837
+ const root = ReactDOMClient.createRoot(container);
838
+
839
+ await act(() => {
840
+ root.render(<textarea value="foo" onChange={() => {}} />);
841
+ });
842
+
843
+ await expect(async () => {
844
+ await act(() => {
845
+ root.render(<textarea value={Symbol('foo')} onChange={() => {}} />);
846
+ });
847
+ }).toErrorDev('Invalid value for prop `value`');
848
const node = container.firstChild;
849
850
expect(node.value).toBe('');
851
});
852
660
- it('treats initial Symbol defaultValue as an empty string', () => {
853
+ it('treats initial Symbol defaultValue as an empty string', async () => {
854
const container = document.createElement('div');
662
- ReactDOM.render(<textarea defaultValue={Symbol('foobar')} />, container);
855
+ const root = ReactDOMClient.createRoot(container);
856
+
857
+ await act(() => {
858
+ root.render(<textarea defaultValue={Symbol('foobar')} />);
859
+ });
860
+
861
const node = container.firstChild;
862
863
// TODO: defaultValue is a reserved prop and is not validated. Check warnings when they are.
864
expect(node.value).toBe('');
865
});
866
669
- it('treats updated Symbol defaultValue as an empty string', () => {
867
+ it('treats updated Symbol defaultValue as an empty string', async () => {
868
const container = document.createElement('div');
671
- ReactDOM.render(<textarea defaultValue="foo" />, container);
672
- ReactDOM.render(<textarea defaultValue={Symbol('foobar')} />, container);
869
+ const root = ReactDOMClient.createRoot(container);
870
+ await act(() => {
871
+ root.render(<textarea defaultValue="foo" />);
872
+ });
873
+
874
+ await act(() => {
875
+ root.render(<textarea defaultValue={Symbol('foobar')} />);
876
+ });
877
+
878
const node = container.firstChild;
879
880
// TODO: defaultValue is a reserved prop and is not validated. Check warnings when they are.
@@ -678,59 +883,76 @@ describe('ReactDOMTextarea', () => {
883
});
884
885
describe('When given a function value', () => {
681
- it('treats initial function value as an empty string', () => {
886
+ it('treats initial function value as an empty string', async () => {
887
const container = document.createElement('div');
683
- expect(() =>
684
- ReactDOM.render(
685
- <textarea value={() => {}} onChange={() => {}} />,
686
- container,
687
- ),
688
- ).toErrorDev('Invalid value for prop `value`');
888
+ await expect(async () => {
889
+ const root = ReactDOMClient.createRoot(container);
890
+
891
+ await act(() => {
892
+ root.render(<textarea value={() => {}} onChange={() => {}} />);
893
+ });
894
+ }).toErrorDev('Invalid value for prop `value`');
895
const node = container.firstChild;
896
897
expect(node.value).toBe('');
898
});
899
694
- it('treats initial function children as an empty string', () => {
900
+ it('treats initial function children as an empty string', async () => {
901
const container = document.createElement('div');
696
- expect(() =>
697
- ReactDOM.render(
698
- <textarea onChange={() => {}}>{() => {}}</textarea>,
699
- container,
700
- ),
701
- ).toErrorDev('Use the `defaultValue` or `value` props');
902
+ await expect(async () => {
903
+ const root = ReactDOMClient.createRoot(container);
904
+
905
+ await act(() => {
906
+ root.render(<textarea onChange={() => {}}>{() => {}}</textarea>);
907
+ });
908
+ }).toErrorDev('Use the `defaultValue` or `value` props');
909
const node = container.firstChild;
910
911
expect(node.value).toBe('');
912
});
913
707
- it('treats updated function value as an empty string', () => {
914
+ it('treats updated function value as an empty string', async () => {
915
const container = document.createElement('div');
709
- ReactDOM.render(<textarea value="foo" onChange={() => {}} />, container);
710
- expect(() =>
711
- ReactDOM.render(
712
- <textarea value={() => {}} onChange={() => {}} />,
713
- container,
714
- ),
715
- ).toErrorDev('Invalid value for prop `value`');
916
+ const root = ReactDOMClient.createRoot(container);
917
+
918
+ await act(() => {
919
+ root.render(<textarea value="foo" onChange={() => {}} />);
920
+ });
921
+
922
+ await expect(async () => {
923
+ await act(() => {
924
+ root.render(<textarea value={() => {}} onChange={() => {}} />);
925
+ });
926
+ }).toErrorDev('Invalid value for prop `value`');
927
const node = container.firstChild;
928
929
expect(node.value).toBe('');
930
});
931
721
- it('treats initial function defaultValue as an empty string', () => {
932
+ it('treats initial function defaultValue as an empty string', async () => {
933
const container = document.createElement('div');
723
- ReactDOM.render(<textarea defaultValue={() => {}} />, container);
934
+ const root = ReactDOMClient.createRoot(container);
935
+ await act(() => {
936
+ root.render(<textarea defaultValue={() => {}} />);
937
+ });
938
+
939
const node = container.firstChild;
940
941
// TODO: defaultValue is a reserved prop and is not validated. Check warnings when they are.
942
expect(node.value).toBe('');
943
});
944
730
- it('treats updated function defaultValue as an empty string', () => {
945
+ it('treats updated function defaultValue as an empty string', async () => {
946
const container = document.createElement('div');
732
- ReactDOM.render(<textarea defaultValue="foo" />, container);
733
- ReactDOM.render(<textarea defaultValue={() => {}} />, container);
947
+ const root = ReactDOMClient.createRoot(container);
948
+ await act(() => {
949
+ root.render(<textarea defaultValue="foo" />);
950
+ });
951
+
952
+ await act(() => {
953
+ root.render(<textarea defaultValue={() => {}} />);
954
+ });
955
+
956
const node = container.firstChild;
957
958
// TODO: defaultValue is a reserved prop and is not validated. Check warnings when they are.
@@ -738,69 +960,103 @@ describe('ReactDOMTextarea', () => {
960
});
961
});
962
741
- it('should remove previous `defaultValue`', () => {
963
+ it('should remove previous `defaultValue`', async () => {
964
const container = document.createElement('div');
743
- const node = ReactDOM.render(<textarea defaultValue="0" />, container);
965
+ const root = ReactDOMClient.createRoot(container);
966
+ await act(() => {
967
+ root.render(<textarea defaultValue="0" />);
968
+ });
969
+
970
+ const node = container.firstChild;
971
972
expect(node.value).toBe('0');
973
expect(node.defaultValue).toBe('0');
974
748
- ReactDOM.render(<textarea />, container);
975
+ await act(() => {
976
+ root.render(<textarea />);
977
+ });
978
+
979
expect(node.defaultValue).toBe('');
980
});
981
752
- it('should treat `defaultValue={null}` as missing', () => {
982
+ it('should treat `defaultValue={null}` as missing', async () => {
983
const container = document.createElement('div');
754
- const node = ReactDOM.render(<textarea defaultValue="0" />, container);
984
+ const root = ReactDOMClient.createRoot(container);
985
+ await act(() => {
986
+ root.render(<textarea defaultValue="0" />);
987
+ });
988
+
989
+ const node = container.firstChild;
990
991
expect(node.value).toBe('0');
992
expect(node.defaultValue).toBe('0');
993
759
- ReactDOM.render(<textarea defaultValue={null} />, container);
994
+ await act(() => {
995
+ root.render(<textarea defaultValue={null} />);
996
+ });
997
+
998
expect(node.defaultValue).toBe('');
999
});
1000
763
- it('should not warn about missing onChange if value is not set', () => {
764
- expect(() => {
765
- ReactTestUtils.renderIntoDocument(<textarea />);
766
- }).not.toThrow();
1001
+ it('should not warn about missing onChange if value is not set', async () => {
1002
+ const container = document.createElement('div');
1003
+ const root = ReactDOMClient.createRoot(container);
1004
+ await expect(
1005
+ act(() => {
1006
+ root.render(<textarea />);
1007
+ }),
1008
+ ).resolves.not.toThrow();
1009
});
1010
769
- it('should not warn about missing onChange if value is undefined', () => {
770
- expect(() => {
771
- ReactTestUtils.renderIntoDocument(<textarea value={undefined} />);
772
- }).not.toThrow();
1011
+ it('should not warn about missing onChange if value is undefined', async () => {
1012
+ const container = document.createElement('div');
1013
+ const root = ReactDOMClient.createRoot(container);
1014
+ await expect(
1015
+ act(() => {
1016
+ root.render(<textarea value={undefined} />);
1017
+ }),
1018
+ ).resolves.not.toThrow();
1019
});
1020
775
- it('should not warn about missing onChange if onChange is set', () => {
776
- expect(() => {
777
- const change = jest.fn();
778
- ReactTestUtils.renderIntoDocument(
779
- <textarea value="something" onChange={change} />,
780
- );
781
- }).not.toThrow();
1021
+ it('should not warn about missing onChange if onChange is set', async () => {
1022
+ const change = jest.fn();
1023
+ const container = document.createElement('div');
1024
+ const root = ReactDOMClient.createRoot(container);
1025
+ await expect(
1026
+ act(() => {
1027
+ root.render(<textarea value="something" onChange={change} />);
1028
+ }),
1029
+ ).resolves.not.toThrow();
1030
});
1031
784
- it('should not warn about missing onChange if disabled is true', () => {
785
- expect(() => {
786
- ReactTestUtils.renderIntoDocument(
787
- <textarea value="something" disabled={true} />,
788
- );
789
- }).not.toThrow();
1032
+ it('should not warn about missing onChange if disabled is true', async () => {
1033
+ const container = document.createElement('div');
1034
+ const root = ReactDOMClient.createRoot(container);
1035
+ await expect(
1036
+ act(() => {
1037
+ root.render(<textarea value="something" disabled={true} />);
1038
+ }),
1039
+ ).resolves.not.toThrow();
1040
});
1041
792
- it('should not warn about missing onChange if value is not set', () => {
793
- expect(() => {
794
- ReactTestUtils.renderIntoDocument(
795
- <textarea value="something" readOnly={true} />,
796
- );
797
- }).not.toThrow();
1042
+ it('should not warn about missing onChange if value is not set', async () => {
1043
+ const container = document.createElement('div');
1044
+ const root = ReactDOMClient.createRoot(container);
1045
+ await expect(
1046
+ act(() => {
1047
+ root.render(<textarea value="something" readOnly={true} />);
1048
+ }),
1049
+ ).resolves.not.toThrow();
1050
});
1051
800
- it('should warn about missing onChange if value is false', () => {
801
- expect(() =>
802
- ReactTestUtils.renderIntoDocument(<textarea value={false} />),
803
- ).toErrorDev(
1052
+ it('should warn about missing onChange if value is false', async () => {
1053
+ const container = document.createElement('div');
1054
+ const root = ReactDOMClient.createRoot(container);
1055
+ await expect(async () => {
1056
+ await act(() => {
1057
+ root.render(<textarea value={false} />);
1058
+ });
1059
+ }).toErrorDev(
1060
'Warning: You provided a `value` prop to a form ' +
1061
'field without an `onChange` handler. This will render a read-only ' +
1062
'field. If the field should be mutable use `defaultValue`. ' +
@@ -808,10 +1064,14 @@ describe('ReactDOMTextarea', () => {
1064
);
1065
});
1066
811
- it('should warn about missing onChange if value is 0', () => {
812
- expect(() =>
813
- ReactTestUtils.renderIntoDocument(<textarea value={0} />),
814
- ).toErrorDev(
1067
+ it('should warn about missing onChange if value is 0', async () => {
1068
+ const container = document.createElement('div');
1069
+ const root = ReactDOMClient.createRoot(container);
1070
+ await expect(async () => {
1071
+ await act(() => {
1072
+ root.render(<textarea value={0} />);
1073
+ });
1074
+ }).toErrorDev(
1075
'Warning: You provided a `value` prop to a form ' +
1076
'field without an `onChange` handler. This will render a read-only ' +
1077
'field. If the field should be mutable use `defaultValue`. ' +
@@ -819,10 +1079,14 @@ describe('ReactDOMTextarea', () => {
1079
);
1080
});
1081
822
- it('should warn about missing onChange if value is "0"', () => {
823
- expect(() =>
824
- ReactTestUtils.renderIntoDocument(<textarea value="0" />),
825
- ).toErrorDev(
1082
+ it('should warn about missing onChange if value is "0"', async () => {
1083
+ const container = document.createElement('div');
1084
+ const root = ReactDOMClient.createRoot(container);
1085
+ await expect(async () => {
1086
+ await act(() => {
1087
+ root.render(<textarea value="0" />);
1088
+ });
1089
+ }).toErrorDev(
1090
'Warning: You provided a `value` prop to a form ' +
1091
'field without an `onChange` handler. This will render a read-only ' +
1092
'field. If the field should be mutable use `defaultValue`. ' +
@@ -830,10 +1094,14 @@ describe('ReactDOMTextarea', () => {
1094
);
1095
});
1096
833
- it('should warn about missing onChange if value is ""', () => {
834
- expect(() =>
835
- ReactTestUtils.renderIntoDocument(<textarea value="" />),
836
- ).toErrorDev(
1097
+ it('should warn about missing onChange if value is ""', async () => {
1098
+ const container = document.createElement('div');
1099
+ const root = ReactDOMClient.createRoot(container);
1100
+ await expect(async () => {
1101
+ await act(() => {
1102
+ root.render(<textarea value="" />);
1103
+ });
1104
+ }).toErrorDev(
1105
'Warning: You provided a `value` prop to a form ' +
1106
'field without an `onChange` handler. This will render a read-only ' +
1107
'field. If the field should be mutable use `defaultValue`. ' +