@samitouri / QOS-React-2 / commits / a7fa58d8b6

Convert ReactDOMInput-test to createRoot (#28084)

Convert ReactDOMInput-test to createRoot

Jan Kassens committed Jan 25, 2024 at 14:22 UTC a7fa58d8b6e8306f9c4f4135e702f1bc7a099a49
1 file changed +998 -680
packages/react-dom/src/__tests__/ReactDOMInput-test.js
+998 -680
@@ -25,6 +25,7 @@ describe('ReactDOMInput', () => {
25 let setUntrackedValue;
26 let setUntrackedChecked;
27 let container;
28 + let root;
29
30 function dispatchEventOnNode(node, type) {
31 node.dispatchEvent(new Event(type, {bubbles: true, cancelable: true}));
@@ -99,6 +100,7 @@ describe('ReactDOMInput', () => {
100
101 container = document.createElement('div');
102 document.body.appendChild(container);
103 + root = ReactDOMClient.createRoot(container);
104 });
105
106 afterEach(() => {
@@ -106,9 +108,11 @@ describe('ReactDOMInput', () => {
108 jest.restoreAllMocks();
109 });
110
109 - it('should warn for controlled value of 0 with missing onChange', () => {
110 - expect(() => {
111 - ReactDOM.render(<input type="text" value={0} />, container);
111 + it('should warn for controlled value of 0 with missing onChange', async () => {
112 + await expect(async () => {
113 + await act(() => {
114 + root.render(<input type="text" value={0} />);
115 + });
116 }).toErrorDev(
117 'Warning: You provided a `value` prop to a form ' +
118 'field without an `onChange` handler. This will render a read-only ' +
@@ -117,9 +121,11 @@ describe('ReactDOMInput', () => {
121 );
122 });
123
120 - it('should warn for controlled value of "" with missing onChange', () => {
121 - expect(() => {
122 - ReactDOM.render(<input type="text" value="" />, container);
124 + it('should warn for controlled value of "" with missing onChange', async () => {
125 + await expect(async () => {
126 + await act(() => {
127 + root.render(<input type="text" value="" />);
128 + });
129 }).toErrorDev(
130 'Warning: You provided a `value` prop to a form ' +
131 'field without an `onChange` handler. This will render a read-only ' +
@@ -128,9 +134,11 @@ describe('ReactDOMInput', () => {
134 );
135 });
136
131 - it('should warn for controlled value of "0" with missing onChange', () => {
132 - expect(() => {
133 - ReactDOM.render(<input type="text" value="0" />, container);
137 + it('should warn for controlled value of "0" with missing onChange', async () => {
138 + await expect(async () => {
139 + await act(() => {
140 + root.render(<input type="text" value="0" />);
141 + });
142 }).toErrorDev(
143 'Warning: You provided a `value` prop to a form ' +
144 'field without an `onChange` handler. This will render a read-only ' +
@@ -139,72 +147,95 @@ describe('ReactDOMInput', () => {
147 );
148 });
149
142 - it('should warn for controlled value of false with missing onChange', () => {
143 - expect(() =>
144 - ReactDOM.render(<input type="checkbox" checked={false} />, container),
145 - ).toErrorDev(
150 + it('should warn for controlled value of false with missing onChange', async () => {
151 + await expect(async () => {
152 + await act(() => {
153 + root.render(<input type="checkbox" checked={false} />);
154 + });
155 + }).toErrorDev(
156 'Warning: You provided a `checked` prop to a form field without an `onChange` handler.',
157 );
158 });
159
150 - it('should warn with checked and no onChange handler with readOnly specified', () => {
151 - ReactDOM.render(
152 - <input type="checkbox" checked={false} readOnly={true} />,
153 - container,
154 - );
155 - ReactDOM.unmountComponentAtNode(container);
160 + it('should warn with checked and no onChange handler with readOnly specified', async () => {
161 + await act(() => {
162 + root.render(<input type="checkbox" checked={false} readOnly={true} />);
163 + });
164 + root.unmount();
165 + root = ReactDOMClient.createRoot(container);
166
157 - expect(() =>
158 - ReactDOM.render(
159 - <input type="checkbox" checked={false} readOnly={false} />,
160 - container,
161 - ),
162 - ).toErrorDev(
167 + await expect(async () => {
168 + await act(() => {
169 + root.render(<input type="checkbox" checked={false} readOnly={false} />);
170 + });
171 + }).toErrorDev(
172 'Warning: You provided a `checked` prop to a form field without an `onChange` handler. ' +
173 'This will render a read-only field. If the field should be mutable use `defaultChecked`. ' +
174 'Otherwise, set either `onChange` or `readOnly`.',
175 );
176 });
177
169 - it('should not warn about missing onChange in uncontrolled inputs', () => {
170 - ReactDOM.render(<input />, container);
171 - ReactDOM.unmountComponentAtNode(container);
172 - ReactDOM.render(<input value={undefined} />, container);
173 - ReactDOM.unmountComponentAtNode(container);
174 - ReactDOM.render(<input type="text" />, container);
175 - ReactDOM.unmountComponentAtNode(container);
176 - ReactDOM.render(<input type="text" value={undefined} />, container);
177 - ReactDOM.unmountComponentAtNode(container);
178 - ReactDOM.render(<input type="checkbox" />, container);
179 - ReactDOM.unmountComponentAtNode(container);
180 - ReactDOM.render(<input type="checkbox" checked={undefined} />, container);
178 + it('should not warn about missing onChange in uncontrolled inputs', async () => {
179 + await act(() => {
180 + root.render(<input />);
181 + });
182 + root.unmount();
183 + root = ReactDOMClient.createRoot(container);
184 + await act(() => {
185 + root.render(<input value={undefined} />);
186 + });
187 + root.unmount();
188 + root = ReactDOMClient.createRoot(container);
189 + await act(() => {
190 + root.render(<input type="text" />);
191 + });
192 + root.unmount();
193 + root = ReactDOMClient.createRoot(container);
194 + await act(() => {
195 + root.render(<input type="text" value={undefined} />);
196 + });
197 + root.unmount();
198 + root = ReactDOMClient.createRoot(container);
199 + await act(() => {
200 + root.render(<input type="checkbox" />);
201 + });
202 + root.unmount();
203 + root = ReactDOMClient.createRoot(container);
204 + await act(() => {
205 + root.render(<input type="checkbox" checked={undefined} />);
206 + });
207 });
208
183 - it('should not warn with value and onInput handler', () => {
184 - ReactDOM.render(<input value="..." onInput={() => {}} />, container);
209 + it('should not warn with value and onInput handler', async () => {
210 + await act(() => {
211 + root.render(<input value="..." onInput={() => {}} />);
212 + });
213 });
214
187 - it('should properly control a value even if no event listener exists', () => {
188 - let node;
189 -
190 - expect(() => {
191 - node = ReactDOM.render(<input type="text" value="lion" />, container);
215 + it('should properly control a value even if no event listener exists', async () => {
216 + await expect(async () => {
217 + await act(() => {
218 + root.render(<input type="text" value="lion" />);
219 + });
220 }).toErrorDev(
221 'Warning: You provided a `value` prop to a form field without an `onChange` handler.',
222 );
223 + const node = container.firstChild;
224 expect(isValueDirty(node)).toBe(true);
225
226 setUntrackedValue.call(node, 'giraffe');
227
228 // This must use the native event dispatching. If we simulate, we will
229 // bypass the lazy event attachment system so we won't actually test this.
201 - dispatchEventOnNode(node, 'input');
230 + await act(() => {
231 + dispatchEventOnNode(node, 'input');
232 + });
233
234 expect(node.value).toBe('lion');
235 expect(isValueDirty(node)).toBe(true);
236 });
237
207 - it('should control a value in reentrant events', () => {
238 + it('should control a value in reentrant events', async () => {
239 class ControlledInputs extends React.Component {
240 state = {value: 'lion'};
241 a = null;
@@ -240,23 +271,35 @@ describe('ReactDOMInput', () => {
271 }
272 }
273
243 - const instance = ReactDOM.render(<ControlledInputs />, container);
274 + const ref = React.createRef();
275 + await act(() => {
276 + root.render(<ControlledInputs ref={ref} />);
277 + });
278 + const instance = ref.current;
279
280 // Focus the field so we can later blur it.
281 // Don't remove unless you've verified the fix in #8240 is still covered.
247 - instance.a.focus();
282 + await act(() => {
283 + instance.a.focus();
284 + });
285 setUntrackedValue.call(instance.a, 'giraffe');
286 // This must use the native event dispatching. If we simulate, we will
287 // bypass the lazy event attachment system so we won't actually test this.
251 - dispatchEventOnNode(instance.a, 'input');
252 - dispatchEventOnNode(instance.a, 'blur');
253 - dispatchEventOnNode(instance.a, 'focusout');
288 + await act(() => {
289 + dispatchEventOnNode(instance.a, 'input');
290 + });
291 + await act(() => {
292 + dispatchEventOnNode(instance.a, 'blur');
293 + });
294 + await act(() => {
295 + dispatchEventOnNode(instance.a, 'focusout');
296 + });
297
298 expect(instance.a.value).toBe('giraffe');
299 expect(instance.switchedFocus).toBe(true);
300 });
301
259 - it('should control values in reentrant events with different targets', () => {
302 + it('should control values in reentrant events with different targets', async () => {
303 class ControlledInputs extends React.Component {
304 state = {value: 'lion'};
305 a = null;
@@ -288,21 +331,29 @@ describe('ReactDOMInput', () => {
331 }
332 }
333
291 - const instance = ReactDOM.render(<ControlledInputs />, container);
334 + const ref = React.createRef();
335 + await act(() => {
336 + root.render(<ControlledInputs ref={ref} />);
337 + });
338 + const instance = ref.current;
339
340 setUntrackedValue.call(instance.a, 'giraffe');
341 // This must use the native event dispatching. If we simulate, we will
342 // bypass the lazy event attachment system so we won't actually test this.
296 - dispatchEventOnNode(instance.a, 'input');
343 + await act(() => {
344 + dispatchEventOnNode(instance.a, 'input');
345 + });
346
347 expect(instance.a.value).toBe('lion');
348 expect(instance.b.checked).toBe(true);
349 });
350
351 describe('switching text inputs between numeric and string numbers', () => {
303 - it('does change the number 2 to "2.0" with no change handler', () => {
304 - const stub = <input type="text" value={2} onChange={jest.fn()} />;
305 - const node = ReactDOM.render(stub, container);
352 + it('does change the number 2 to "2.0" with no change handler', async () => {
353 + await act(() => {
354 + root.render(<input type="text" value={2} onChange={jest.fn()} />);
355 + });
356 + const node = container.firstChild;
357
358 setUntrackedValue.call(node, '2.0');
359 dispatchEventOnNode(node, 'input');
@@ -315,9 +366,11 @@ describe('ReactDOMInput', () => {
366 }
367 });
368
318 - it('does change the string "2" to "2.0" with no change handler', () => {
319 - const stub = <input type="text" value={'2'} onChange={jest.fn()} />;
320 - const node = ReactDOM.render(stub, container);
369 + it('does change the string "2" to "2.0" with no change handler', async () => {
370 + await act(() => {
371 + root.render(<input type="text" value={'2'} onChange={jest.fn()} />);
372 + });
373 + const node = container.firstChild;
374
375 setUntrackedValue.call(node, '2.0');
376 dispatchEventOnNode(node, 'input');
@@ -330,7 +383,7 @@ describe('ReactDOMInput', () => {
383 }
384 });
385
333 - it('changes the number 2 to "2.0" using a change handler', () => {
386 + it('changes the number 2 to "2.0" using a change handler', async () => {
387 class Stub extends React.Component {
388 state = {
389 value: 2,
@@ -345,8 +398,10 @@ describe('ReactDOMInput', () => {
398 }
399 }
400
348 - const stub = ReactDOM.render(<Stub />, container);
349 - const node = ReactDOM.findDOMNode(stub);
401 + await act(() => {
402 + root.render(<Stub />);
403 + });
404 + const node = container.firstChild;
405
406 setUntrackedValue.call(node, '2.0');
407 dispatchEventOnNode(node, 'input');
@@ -360,7 +415,7 @@ describe('ReactDOMInput', () => {
415 });
416 });
417
363 - it('does change the string ".98" to "0.98" with no change handler', () => {
418 + it('does change the string ".98" to "0.98" with no change handler', async () => {
419 class Stub extends React.Component {
420 state = {
421 value: '.98',
@@ -370,20 +425,24 @@ describe('ReactDOMInput', () => {
425 }
426 }
427
373 - let stub;
374 - expect(() => {
375 - stub = ReactDOM.render(<Stub />, container);
428 + const ref = React.createRef();
429 + await expect(async () => {
430 + await act(() => {
431 + root.render(<Stub ref={ref} />);
432 + });
433 }).toErrorDev(
434 'You provided a `value` prop to a form field ' +
435 'without an `onChange` handler.',
436 );
380 - const node = ReactDOM.findDOMNode(stub);
381 - stub.setState({value: '0.98'});
437 + const node = container.firstChild;
438 + await act(() => {
439 + ref.current.setState({value: '0.98'});
440 + });
441
442 expect(node.value).toEqual('0.98');
443 });
444
386 - it('performs a state change from "" to 0', () => {
445 + it('performs a state change from "" to 0', async () => {
446 class Stub extends React.Component {
447 state = {
448 value: '',
@@ -393,40 +452,43 @@ describe('ReactDOMInput', () => {
452 }
453 }
454
396 - const stub = ReactDOM.render(<Stub />, container);
397 - const node = ReactDOM.findDOMNode(stub);
398 - stub.setState({value: 0});
455 + const ref = React.createRef();
456 + await act(() => {
457 + root.render(<Stub ref={ref} />);
458 + });
459 + const node = container.firstChild;
460 + await act(() => {
461 + ref.current.setState({value: 0});
462 + });
463
464 expect(node.value).toEqual('0');
465 });
466
403 - it('updates the value on radio buttons from "" to 0', function () {
404 - ReactDOM.render(
405 - <input type="radio" value="" onChange={function () {}} />,
406 - container,
407 - );
408 - ReactDOM.render(
409 - <input type="radio" value={0} onChange={function () {}} />,
410 - container,
411 - );
467 + it('updates the value on radio buttons from "" to 0', async () => {
468 + await act(() => {
469 + root.render(<input type="radio" value="" onChange={function () {}} />);
470 + });
471 + await act(() => {
472 + root.render(<input type="radio" value={0} onChange={function () {}} />);
473 + });
474 expect(container.firstChild.value).toBe('0');
475 expect(container.firstChild.getAttribute('value')).toBe('0');
476 });
477
416 - it('updates the value on checkboxes from "" to 0', function () {
417 - ReactDOM.render(
418 - <input type="checkbox" value="" onChange={function () {}} />,
419 - container,
420 - );
421 - ReactDOM.render(
422 - <input type="checkbox" value={0} onChange={function () {}} />,
423 - container,
424 - );
478 + it('updates the value on checkboxes from "" to 0', async () => {
479 + await act(() => {
480 + root.render(<input type="checkbox" value="" onChange={function () {}} />);
481 + });
482 + await act(() => {
483 + root.render(
484 + <input type="checkbox" value={0} onChange={function () {}} />,
485 + );
486 + });
487 expect(container.firstChild.value).toBe('0');
488 expect(container.firstChild.getAttribute('value')).toBe('0');
489 });
490
429 - it('distinguishes precision for extra zeroes in string number values', () => {
491 + it('distinguishes precision for extra zeroes in string number values', async () => {
492 class Stub extends React.Component {
493 state = {
494 value: '3.0000',
@@ -436,37 +498,45 @@ describe('ReactDOMInput', () => {
498 }
499 }
500
439 - let stub;
440 -
441 - expect(() => {
442 - stub = ReactDOM.render(<Stub />, container);
501 + const ref = React.createRef();
502 + await expect(async () => {
503 + await act(() => {
504 + root.render(<Stub ref={ref} />);
505 + });
506 }).toErrorDev(
507 'You provided a `value` prop to a form field ' +
508 'without an `onChange` handler.',
509 );
447 - const node = ReactDOM.findDOMNode(stub);
448 - stub.setState({value: '3'});
510 + const node = container.firstChild;
511 + await act(() => {
512 + ref.current.setState({value: '3'});
513 + });
514
515 expect(node.value).toEqual('3');
516 });
517
453 - it('should display `defaultValue` of number 0', () => {
454 - const stub = <input type="text" defaultValue={0} />;
455 - const node = ReactDOM.render(stub, container);
518 + it('should display `defaultValue` of number 0', async () => {
519 + await act(() => {
520 + root.render(<input type="text" defaultValue={0} />);
521 + });
522 + const node = container.firstChild;
523
524 expect(node.getAttribute('value')).toBe('0');
525 expect(node.value).toBe('0');
526 });
527
461 - it('only assigns defaultValue if it changes', () => {
528 + it('only assigns defaultValue if it changes', async () => {
529 class Test extends React.Component {
530 render() {
531 return <input defaultValue="0" />;
532 }
533 }
534
468 - const component = ReactDOM.render(<Test />, container);
469 - const node = ReactDOM.findDOMNode(component);
535 + const ref = React.createRef();
536 + await act(() => {
537 + root.render(<Test ref={ref} />);
538 + });
539 + const node = container.firstChild;
540
541 Object.defineProperty(node, 'defaultValue', {
542 get() {
@@ -479,28 +549,36 @@ describe('ReactDOMInput', () => {
549 },
550 });
551
482 - component.forceUpdate();
552 + await act(() => {
553 + ref.current.forceUpdate();
554 + });
555 });
556
485 - it('should display "true" for `defaultValue` of `true`', () => {
557 + it('should display "true" for `defaultValue` of `true`', async () => {
558 const stub = <input type="text" defaultValue={true} />;
487 - const node = ReactDOM.render(stub, container);
559 + await act(() => {
560 + root.render(stub);
561 + });
562 + const node = container.firstChild;
563
564 expect(node.value).toBe('true');
565 });
566
492 - it('should display "false" for `defaultValue` of `false`', () => {
567 + it('should display "false" for `defaultValue` of `false`', async () => {
568 const stub = <input type="text" defaultValue={false} />;
494 - const node = ReactDOM.render(stub, container);
569 + await act(() => {
570 + root.render(stub);
571 + });
572 + const node = container.firstChild;
573
574 expect(node.value).toBe('false');
575 });
576
499 - it('should update `defaultValue` for uncontrolled input', () => {
500 - const node = ReactDOM.render(
501 - <input type="text" defaultValue="0" />,
502 - container,
503 - );
577 + it('should update `defaultValue` for uncontrolled input', async () => {
578 + await act(() => {
579 + root.render(<input type="text" defaultValue="0" />);
580 + });
581 + const node = container.firstChild;
582
583 expect(node.value).toBe('0');
584 expect(node.defaultValue).toBe('0');
@@ -510,7 +588,9 @@ describe('ReactDOMInput', () => {
588 expect(isValueDirty(node)).toBe(true);
589 }
590
513 - ReactDOM.render(<input type="text" defaultValue="1" />, container);
591 + await act(() => {
592 + root.render(<input type="text" defaultValue="1" />);
593 + });
594
595 if (disableInputAttributeSyncing) {
596 expect(node.value).toBe('1');
@@ -523,16 +603,18 @@ describe('ReactDOMInput', () => {
603 }
604 });
605
526 - it('should update `defaultValue` for uncontrolled date/time input', () => {
527 - const node = ReactDOM.render(
528 - <input type="date" defaultValue="1980-01-01" />,
529 - container,
530 - );
606 + it('should update `defaultValue` for uncontrolled date/time input', async () => {
607 + await act(() => {
608 + root.render(<input type="date" defaultValue="1980-01-01" />);
609 + });
610 + const node = container.firstChild;
611
612 expect(node.value).toBe('1980-01-01');
613 expect(node.defaultValue).toBe('1980-01-01');
614
535 - ReactDOM.render(<input type="date" defaultValue="2000-01-01" />, container);
615 + await act(() => {
616 + root.render(<input type="date" defaultValue="2000-01-01" />);
617 + });
618
619 if (disableInputAttributeSyncing) {
620 expect(node.value).toBe('2000-01-01');
@@ -542,19 +624,23 @@ describe('ReactDOMInput', () => {
624 expect(node.defaultValue).toBe('2000-01-01');
625 }
626
545 - ReactDOM.render(<input type="date" />, container);
627 + await act(() => {
628 + root.render(<input type="date" />);
629 + });
630 });
631
548 - it('should take `defaultValue` when changing to uncontrolled input', () => {
549 - const node = ReactDOM.render(
550 - <input type="text" value="0" readOnly={true} />,
551 - container,
552 - );
632 + it('should take `defaultValue` when changing to uncontrolled input', async () => {
633 + await act(() => {
634 + root.render(<input type="text" value="0" readOnly={true} />);
635 + });
636 + const node = container.firstChild;
637 expect(node.value).toBe('0');
638 expect(isValueDirty(node)).toBe(true);
555 - expect(() =>
556 - ReactDOM.render(<input type="text" defaultValue="1" />, container),
557 - ).toErrorDev(
639 + await expect(async () => {
640 + await act(() => {
641 + root.render(<input type="text" defaultValue="1" />);
642 + });
643 + }).toErrorDev(
644 'A component is changing a controlled input to be uncontrolled.',
645 );
646 expect(node.value).toBe('0');
@@ -580,8 +666,11 @@ describe('ReactDOMInput', () => {
666 expect(div.firstChild.getAttribute('defaultValue')).toBe(null);
667 });
668
583 - it('should render name attribute if it is supplied', () => {
584 - const node = ReactDOM.render(<input type="text" name="name" />, container);
669 + it('should render name attribute if it is supplied', async () => {
670 + await act(() => {
671 + root.render(<input type="text" name="name" />);
672 + });
673 + const node = container.firstChild;
674 expect(node.name).toBe('name');
675 expect(container.firstChild.getAttribute('name')).toBe('name');
676 });
@@ -594,8 +683,10 @@ describe('ReactDOMInput', () => {
683 expect(div.firstChild.getAttribute('name')).toBe('name');
684 });
685
597 - it('should not render name attribute if it is not supplied', () => {
598 - ReactDOM.render(<input type="text" />, container);
686 + it('should not render name attribute if it is not supplied', async () => {
687 + await act(() => {
688 + root.render(<input type="text" />);
689 + });
690 expect(container.firstChild.getAttribute('name')).toBe(null);
691 });
692
@@ -607,7 +698,7 @@ describe('ReactDOMInput', () => {
698 expect(div.firstChild.getAttribute('name')).toBe(null);
699 });
700
610 - it('should display "foobar" for `defaultValue` of `objToString`', () => {
701 + it('should display "foobar" for `defaultValue` of `objToString`', async () => {
702 const objToString = {
703 toString: function () {
704 return 'foobar';
@@ -615,7 +706,10 @@ describe('ReactDOMInput', () => {
706 };
707
708 const stub = <input type="text" defaultValue={objToString} />;
618 - const node = ReactDOM.render(stub, container);
709 + await act(() => {
710 + root.render(stub);
711 + });
712 + const node = container.firstChild;
713
714 expect(node.value).toBe('foobar');
715 });
@@ -631,10 +725,12 @@ describe('ReactDOMInput', () => {
725 return '2020-01-01';
726 }
727 }
728 + const legacyContainer = document.createElement('div');
729 + document.body.appendChild(legacyContainer);
730 const test = () =>
731 ReactDOM.render(
732 <input defaultValue={new TemporalLike()} type="date" />,
637 - container,
733 + legacyContainer,
734 );
735 expect(() =>
736 expect(test).toThrowError(new TypeError('prod message')),
@@ -644,7 +740,7 @@ describe('ReactDOMInput', () => {
740 );
741 });
742
647 - it('should throw for text inputs if `defaultValue` is an object where valueOf() throws', () => {
743 + it('should throw for text inputs if `defaultValue` is an object where valueOf() throws', async () => {
744 class TemporalLike {
745 valueOf() {
746 // Throwing here is the behavior of ECMAScript "Temporal" date/time API.
@@ -655,20 +751,19 @@ describe('ReactDOMInput', () => {
751 return '2020-01-01';
752 }
753 }
658 - const test = () =>
659 - ReactDOM.render(
660 - <input defaultValue={new TemporalLike()} type="text" />,
661 - container,
754 + await expect(async () => {
755 + await expect(async () => {
756 + await act(() => {
757 + root.render(<input defaultValue={new TemporalLike()} type="text" />);
758 + });
759 + }).toErrorDev(
760 + 'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' +
761 + 'strings, not TemporalLike. This value must be coerced to a string before using it here.',
762 );
663 - expect(() =>
664 - expect(test).toThrowError(new TypeError('prod message')),
665 - ).toErrorDev(
666 - 'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' +
667 - 'strings, not TemporalLike. This value must be coerced to a string before using it here.',
668 - );
763 + }).rejects.toThrowError(new TypeError('prod message'));
764 });
765
671 - it('should throw for date inputs if `value` is an object where valueOf() throws', () => {
766 + it('should throw for date inputs if `value` is an object where valueOf() throws', async () => {
767 class TemporalLike {
768 valueOf() {
769 // Throwing here is the behavior of ECMAScript "Temporal" date/time API.
@@ -679,20 +774,25 @@ describe('ReactDOMInput', () => {
774 return '2020-01-01';
775 }
776 }
682 - const test = () =>
683 - ReactDOM.render(
684 - <input value={new TemporalLike()} type="date" onChange={() => {}} />,
685 - container,
777 + await expect(async () => {
778 + await expect(async () => {
779 + await act(() => {
780 + root.render(
781 + <input
782 + value={new TemporalLike()}
783 + type="date"
784 + onChange={() => {}}
785 + />,
786 + );
787 + });
788 + }).toErrorDev(
789 + 'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' +
790 + 'strings, not TemporalLike. This value must be coerced to a string before using it here.',
791 );
687 - expect(() =>
688 - expect(test).toThrowError(new TypeError('prod message')),
689 - ).toErrorDev(
690 - 'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' +
691 - 'strings, not TemporalLike. This value must be coerced to a string before using it here.',
692 - );
792 + }).rejects.toThrowError(new TypeError('prod message'));
793 });
794
695 - it('should throw for text inputs if `value` is an object where valueOf() throws', () => {
795 + it('should throw for text inputs if `value` is an object where valueOf() throws', async () => {
796 class TemporalLike {
797 valueOf() {
798 // Throwing here is the behavior of ECMAScript "Temporal" date/time API.
@@ -703,55 +803,66 @@ describe('ReactDOMInput', () => {
803 return '2020-01-01';
804 }
805 }
706 - const test = () =>
707 - ReactDOM.render(
708 - <input value={new TemporalLike()} type="text" onChange={() => {}} />,
709 - container,
806 + await expect(async () => {
807 + await expect(async () => {
808 + await act(() => {
809 + root.render(
810 + <input
811 + value={new TemporalLike()}
812 + type="text"
813 + onChange={() => {}}
814 + />,
815 + );
816 + });
817 + }).toErrorDev(
818 + 'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' +
819 + 'strings, not TemporalLike. This value must be coerced to a string before using it here.',
820 );
711 - expect(() =>
712 - expect(test).toThrowError(new TypeError('prod message')),
713 - ).toErrorDev(
714 - 'Form field values (value, checked, defaultValue, or defaultChecked props) must be ' +
715 - 'strings, not TemporalLike. This value must be coerced to a string before using it here.',
716 - );
821 + }).rejects.toThrowError(new TypeError('prod message'));
822 });
823
719 - it('should display `value` of number 0', () => {
720 - const stub = <input type="text" value={0} onChange={emptyFunction} />;
721 - const node = ReactDOM.render(stub, container);
824 + it('should display `value` of number 0', async () => {
825 + await act(() => {
826 + root.render(<input type="text" value={0} onChange={emptyFunction} />);
827 + });
828 + const node = container.firstChild;
829
830 expect(node.value).toBe('0');
831 });
832
726 - it('should allow setting `value` to `true`', () => {
727 - let stub = <input type="text" value="yolo" onChange={emptyFunction} />;
728 - const node = ReactDOM.render(stub, container);
833 + it('should allow setting `value` to `true`', async () => {
834 + await act(() => {
835 + root.render(<input type="text" value="yolo" onChange={emptyFunction} />);
836 + });
837 + const node = container.firstChild;
838
839 expect(node.value).toBe('yolo');
840
732 - stub = ReactDOM.render(
733 - <input type="text" value={true} onChange={emptyFunction} />,
734 - container,
735 - );
841 + await act(() => {
842 + root.render(<input type="text" value={true} onChange={emptyFunction} />);
843 + });
844 expect(node.value).toEqual('true');
845 });
846
739 - it('should allow setting `value` to `false`', () => {
740 - let stub = <input type="text" value="yolo" onChange={emptyFunction} />;
741 - const node = ReactDOM.render(stub, container);
847 + it('should allow setting `value` to `false`', async () => {
848 + await act(() => {
849 + root.render(<input type="text" value="yolo" onChange={emptyFunction} />);
850 + });
851 + const node = container.firstChild;
852
853 expect(node.value).toBe('yolo');
854
745 - stub = ReactDOM.render(
746 - <input type="text" value={false} onChange={emptyFunction} />,
747 - container,
748 - );
855 + await act(() => {
856 + root.render(<input type="text" value={false} onChange={emptyFunction} />);
857 + });
858 expect(node.value).toEqual('false');
859 });
860
752 - it('should allow setting `value` to `objToString`', () => {
753 - let stub = <input type="text" value="foo" onChange={emptyFunction} />;
754 - const node = ReactDOM.render(stub, container);
861 + it('should allow setting `value` to `objToString`', async () => {
862 + await act(() => {
863 + root.render(<input type="text" value="foo" onChange={emptyFunction} />);
864 + });
865 + const node = container.firstChild;
866
867 expect(node.value).toBe('foo');
868
@@ -760,15 +871,18 @@ describe('ReactDOMInput', () => {
871 return 'foobar';
872 },
873 };
763 - stub = ReactDOM.render(
764 - <input type="text" value={objToString} onChange={emptyFunction} />,
765 - container,
766 - );
874 + await act(() => {
875 + root.render(
876 + <input type="text" value={objToString} onChange={emptyFunction} />,
877 + );
878 + });
879 expect(node.value).toEqual('foobar');
880 });
881
770 - it('should not incur unnecessary DOM mutations', () => {
771 - ReactDOM.render(<input value="a" onChange={() => {}} />, container);
882 + it('should not incur unnecessary DOM mutations', async () => {
883 + await act(() => {
884 + root.render(<input value="a" onChange={() => {}} />);
885 + });
886
887 const node = container.firstChild;
888 let nodeValue = 'a';
@@ -782,15 +896,21 @@ describe('ReactDOMInput', () => {
896 }),
897 });
898
785 - ReactDOM.render(<input value="a" onChange={() => {}} />, container);
899 + await act(() => {
900 + root.render(<input value="a" onChange={() => {}} />);
901 + });
902 expect(nodeValueSetter).toHaveBeenCalledTimes(0);
903
788 - ReactDOM.render(<input value="b" onChange={() => {}} />, container);
904 + await act(() => {
905 + root.render(<input value="b" onChange={() => {}} />);
906 + });
907 expect(nodeValueSetter).toHaveBeenCalledTimes(1);
908 });
909
792 - it('should not incur unnecessary DOM mutations for numeric type conversion', () => {
793 - ReactDOM.render(<input value="0" onChange={() => {}} />, container);
910 + it('should not incur unnecessary DOM mutations for numeric type conversion', async () => {
911 + await act(() => {
912 + root.render(<input value="0" onChange={() => {}} />);
913 + });
914
915 const node = container.firstChild;
916 let nodeValue = '0';
@@ -804,12 +924,16 @@ describe('ReactDOMInput', () => {
924 }),
925 });
926
807 - ReactDOM.render(<input value={0} onChange={() => {}} />, container);
927 + await act(() => {
928 + root.render(<input value={0} onChange={() => {}} />);
929 + });
930 expect(nodeValueSetter).toHaveBeenCalledTimes(0);
931 });
932
811 - it('should not incur unnecessary DOM mutations for the boolean type conversion', () => {
812 - ReactDOM.render(<input value="true" onChange={() => {}} />, container);
933 + it('should not incur unnecessary DOM mutations for the boolean type conversion', async () => {
934 + await act(() => {
935 + root.render(<input value="true" onChange={() => {}} />);
936 + });
937
938 const node = container.firstChild;
939 let nodeValue = 'true';
@@ -823,34 +947,46 @@ describe('ReactDOMInput', () => {
947 }),
948 });
949
826 - ReactDOM.render(<input value={true} onChange={() => {}} />, container);
950 + await act(() => {
951 + root.render(<input value={true} onChange={() => {}} />);
952 + });
953 expect(nodeValueSetter).toHaveBeenCalledTimes(0);
954 });
955
830 - it('should properly control a value of number `0`', () => {
831 - const stub = <input type="text" value={0} onChange={emptyFunction} />;
832 - const node = ReactDOM.render(stub, container);
956 + it('should properly control a value of number `0`', async () => {
957 + await act(() => {
958 + root.render(<input type="text" value={0} onChange={emptyFunction} />);
959 + });
960 + const node = container.firstChild;
961
962 setUntrackedValue.call(node, 'giraffe');
963 dispatchEventOnNode(node, 'input');
964 expect(node.value).toBe('0');
965 });
966
839 - it('should properly control 0.0 for a text input', () => {
840 - const stub = <input type="text" value={0} onChange={emptyFunction} />;
841 - const node = ReactDOM.render(stub, container);
967 + it('should properly control 0.0 for a text input', async () => {
968 + await act(() => {
969 + root.render(<input type="text" value={0} onChange={emptyFunction} />);
970 + });
971 + const node = container.firstChild;
972
973 setUntrackedValue.call(node, '0.0');
844 - dispatchEventOnNode(node, 'input');
974 + await act(() => {
975 + dispatchEventOnNode(node, 'input');
976 + });
977 expect(node.value).toBe('0');
978 });
979
848 - it('should properly control 0.0 for a number input', () => {
849 - const stub = <input type="number" value={0} onChange={emptyFunction} />;
850 - const node = ReactDOM.render(stub, container);
980 + it('should properly control 0.0 for a number input', async () => {
981 + await act(() => {
982 + root.render(<input type="number" value={0} onChange={emptyFunction} />);
983 + });
984 + const node = container.firstChild;
985
986 setUntrackedValue.call(node, '0.0');
853 - dispatchEventOnNode(node, 'input');
987 + await act(() => {
988 + dispatchEventOnNode(node, 'input');
989 + });
990
991 if (disableInputAttributeSyncing) {
992 expect(node.value).toBe('0.0');
@@ -864,18 +1000,16 @@ describe('ReactDOMInput', () => {
1000 }
1001 });
1002
867 - it('should properly transition from an empty value to 0', function () {
868 - ReactDOM.render(
869 - <input type="text" value="" onChange={emptyFunction} />,
870 - container,
871 - );
1003 + it('should properly transition from an empty value to 0', async () => {
1004 + await act(() => {
1005 + root.render(<input type="text" value="" onChange={emptyFunction} />);
1006 + });
1007 const node = container.firstChild;
1008 expect(isValueDirty(node)).toBe(false);
1009
875 - ReactDOM.render(
876 - <input type="text" value={0} onChange={emptyFunction} />,
877 - container,
878 - );
1010 + await act(() => {
1011 + root.render(<input type="text" value={0} onChange={emptyFunction} />);
1012 + });
1013
1014 expect(node.value).toBe('0');
1015 expect(isValueDirty(node)).toBe(true);
@@ -887,33 +1021,29 @@ describe('ReactDOMInput', () => {
1021 }
1022 });
1023
890 - it('should properly transition from 0 to an empty value', function () {
891 - ReactDOM.render(
892 - <input type="text" value={0} onChange={emptyFunction} />,
893 - container,
894 - );
1024 + it('should properly transition from 0 to an empty value', async () => {
1025 + await act(() => {
1026 + root.render(<input type="text" value={0} onChange={emptyFunction} />);
1027 + });
1028 const node = container.firstChild;
1029 expect(isValueDirty(node)).toBe(true);
1030
898 - ReactDOM.render(
899 - <input type="text" value="" onChange={emptyFunction} />,
900 - container,
901 - );
1031 + await act(() => {
1032 + root.render(<input type="text" value="" onChange={emptyFunction} />);
1033 + });
1034
1035 expect(node.value).toBe('');
1036 expect(node.defaultValue).toBe('');
1037 expect(isValueDirty(node)).toBe(true);
1038 });
1039
908 - it('should properly transition a text input from 0 to an empty 0.0', function () {
909 - ReactDOM.render(
910 - <input type="text" value={0} onChange={emptyFunction} />,
911 - container,
912 - );
913 - ReactDOM.render(
914 - <input type="text" value="0.0" onChange={emptyFunction} />,
915 - container,
916 - );
1040 + it('should properly transition a text input from 0 to an empty 0.0', async () => {
1041 + await act(() => {
1042 + root.render(<input type="text" value={0} onChange={emptyFunction} />);
1043 + });
1044 + await act(() => {
1045 + root.render(<input type="text" value="0.0" onChange={emptyFunction} />);
1046 + });
1047
1048 const node = container.firstChild;
1049
@@ -925,15 +1055,13 @@ describe('ReactDOMInput', () => {
1055 }
1056 });
1057
928 - it('should properly transition a number input from "" to 0', function () {
929 - ReactDOM.render(
930 - <input type="number" value="" onChange={emptyFunction} />,
931 - container,
932 - );
933 - ReactDOM.render(
934 - <input type="number" value={0} onChange={emptyFunction} />,
935 - container,
936 - );
1058 + it('should properly transition a number input from "" to 0', async () => {
1059 + await act(() => {
1060 + root.render(<input type="number" value="" onChange={emptyFunction} />);
1061 + });
1062 + await act(() => {
1063 + root.render(<input type="number" value={0} onChange={emptyFunction} />);
1064 + });
1065
1066 const node = container.firstChild;
1067
@@ -945,15 +1073,13 @@ describe('ReactDOMInput', () => {
1073 }
1074 });
1075
948 - it('should properly transition a number input from "" to "0"', function () {
949 - ReactDOM.render(
950 - <input type="number" value="" onChange={emptyFunction} />,
951 - container,
952 - );
953 - ReactDOM.render(
954 - <input type="number" value="0" onChange={emptyFunction} />,
955 - container,
956 - );
1076 + it('should properly transition a number input from "" to "0"', async () => {
1077 + await act(() => {
1078 + root.render(<input type="number" value="" onChange={emptyFunction} />);
1079 + });
1080 + await act(() => {
1081 + root.render(<input type="number" value="0" onChange={emptyFunction} />);
1082 + });
1083
1084 const node = container.firstChild;
1085
@@ -965,31 +1091,36 @@ describe('ReactDOMInput', () => {
1091 }
1092 });
1093
968 - it('should have the correct target value', () => {
1094 + it('should have the correct target value', async () => {
1095 let handled = false;
1096 const handler = function (event) {
1097 expect(event.target.nodeName).toBe('INPUT');
1098 handled = true;
1099 };
974 - const stub = <input type="text" value={0} onChange={handler} />;
975 - const node = ReactDOM.render(stub, container);
1100 + await act(() => {
1101 + root.render(<input type="text" value={0} onChange={handler} />);
1102 + });
1103 + const node = container.firstChild;
1104
1105 setUntrackedValue.call(node, 'giraffe');
1106
979 - dispatchEventOnNode(node, 'input');
1107 + await act(() => {
1108 + dispatchEventOnNode(node, 'input');
1109 + });
1110
1111 expect(handled).toBe(true);
1112 });
1113
984 - it('should restore uncontrolled inputs to last defaultValue upon reset', () => {
1114 + it('should restore uncontrolled inputs to last defaultValue upon reset', async () => {
1115 const inputRef = React.createRef();
986 - ReactDOM.render(
987 - <form>
988 - <input defaultValue="default1" ref={inputRef} />
989 - <input type="reset" />
990 - </form>,
991 - container,
992 - );
1116 + await act(() => {
1117 + root.render(
1118 + <form>
1119 + <input defaultValue="default1" ref={inputRef} />
1120 + <input type="reset" />
1121 + </form>,
1122 + );
1123 + });
1124 expect(inputRef.current.value).toBe('default1');
1125 if (disableInputAttributeSyncing) {
1126 expect(isValueDirty(inputRef.current)).toBe(false);
@@ -1002,13 +1133,14 @@ describe('ReactDOMInput', () => {
1133 expect(inputRef.current.value).toBe('changed');
1134 expect(isValueDirty(inputRef.current)).toBe(true);
1135
1005 - ReactDOM.render(
1006 - <form>
1007 - <input defaultValue="default2" ref={inputRef} />
1008 - <input type="reset" />
1009 - </form>,
1010 - container,
1011 - );
1136 + await act(() => {
1137 + root.render(
1138 + <form>
1139 + <input defaultValue="default2" ref={inputRef} />
1140 + <input type="reset" />
1141 + </form>,
1142 + );
1143 + });
1144 expect(inputRef.current.value).toBe('changed');
1145 expect(isValueDirty(inputRef.current)).toBe(true);
1146
@@ -1020,9 +1152,11 @@ describe('ReactDOMInput', () => {
1152 expect(isValueDirty(inputRef.current)).toBe(false);
1153 });
1154
1023 - it('should not set a value for submit buttons unnecessarily', () => {
1155 + it('should not set a value for submit buttons unnecessarily', async () => {
1156 const stub = <input type="submit" />;
1025 - ReactDOM.render(stub, container);
1157 + await act(() => {
1158 + root.render(stub);
1159 + });
1160 const node = container.firstChild;
1161
1162 // The value shouldn't be '', or else the button will have no text; it
@@ -1032,18 +1166,21 @@ describe('ReactDOMInput', () => {
1166 expect(node.hasAttribute('value')).toBe(false);
1167 });
1168
1035 - it('should remove the value attribute on submit inputs when value is updated to undefined', () => {
1169 + it('should remove the value attribute on submit inputs when value is updated to undefined', async () => {
1170 const stub = <input type="submit" value="foo" onChange={emptyFunction} />;
1037 - ReactDOM.render(stub, container);
1171 + await act(() => {
1172 + root.render(stub);
1173 + });
1174
1175 // Not really relevant to this particular test, but changing to undefined
1176 // should nonetheless trigger a warning
1041 - expect(() =>
1042 - ReactDOM.render(
1043 - <input type="submit" value={undefined} onChange={emptyFunction} />,
1044 - container,
1045 - ),
1046 - ).toErrorDev(
1177 + await expect(async () => {
1178 + await act(() => {
1179 + root.render(
1180 + <input type="submit" value={undefined} onChange={emptyFunction} />,
1181 + );
1182 + });
1183 + }).toErrorDev(
1184 'A component is changing a controlled input to be uncontrolled.',
1185 );
1186
@@ -1051,18 +1188,21 @@ describe('ReactDOMInput', () => {
1188 expect(node.getAttribute('value')).toBe(null);
1189 });
1190
1054 - it('should remove the value attribute on reset inputs when value is updated to undefined', () => {
1191 + it('should remove the value attribute on reset inputs when value is updated to undefined', async () => {
1192 const stub = <input type="reset" value="foo" onChange={emptyFunction} />;
1056 - ReactDOM.render(stub, container);
1193 + await act(() => {
1194 + root.render(stub);
1195 + });
1196
1197 // Not really relevant to this particular test, but changing to undefined
1198 // should nonetheless trigger a warning
1060 - expect(() =>
1061 - ReactDOM.render(
1062 - <input type="reset" value={undefined} onChange={emptyFunction} />,
1063 - container,
1064 - ),
1065 - ).toErrorDev(
1199 + await expect(async () => {
1200 + await act(() => {
1201 + root.render(
1202 + <input type="reset" value={undefined} onChange={emptyFunction} />,
1203 + );
1204 + });
1205 + }).toErrorDev(
1206 'A component is changing a controlled input to be uncontrolled.',
1207 );
1208
@@ -1070,44 +1210,56 @@ describe('ReactDOMInput', () => {
1210 expect(node.getAttribute('value')).toBe(null);
1211 });
1212
1073 - it('should set a value on a submit input', () => {
1213 + it('should set a value on a submit input', async () => {
1214 const stub = <input type="submit" value="banana" />;
1075 - ReactDOM.render(stub, container);
1215 + await act(() => {
1216 + root.render(stub);
1217 + });
1218 const node = container.firstChild;
1219
1220 expect(node.getAttribute('value')).toBe('banana');
1221 });
1222
1081 - it('should not set an undefined value on a submit input', () => {
1223 + it('should not set an undefined value on a submit input', async () => {
1224 const stub = <input type="submit" value={undefined} />;
1083 - ReactDOM.render(stub, container);
1225 + await act(() => {
1226 + root.render(stub);
1227 + });
1228 const node = container.firstChild;
1229
1230 // Note: it shouldn't be an empty string
1231 // because that would erase the "submit" label.
1232 expect(node.getAttribute('value')).toBe(null);
1233
1090 - ReactDOM.render(stub, container);
1234 + await act(() => {
1235 + root.render(stub);
1236 + });
1237 expect(node.getAttribute('value')).toBe(null);
1238 });
1239
1094 - it('should not set an undefined value on a reset input', () => {
1240 + it('should not set an undefined value on a reset input', async () => {
1241 const stub = <input type="reset" value={undefined} />;
1096 - ReactDOM.render(stub, container);
1242 + await act(() => {
1243 + root.render(stub);
1244 + });
1245 const node = container.firstChild;
1246
1247 // Note: it shouldn't be an empty string
1248 // because that would erase the "reset" label.
1249 expect(node.getAttribute('value')).toBe(null);
1250
1103 - ReactDOM.render(stub, container);
1251 + await act(() => {
1252 + root.render(stub);
1253 + });
1254 expect(node.getAttribute('value')).toBe(null);
1255 });
1256
1107 - it('should not set a null value on a submit input', () => {
1257 + it('should not set a null value on a submit input', async () => {
1258 const stub = <input type="submit" value={null} />;
1109 - expect(() => {
1110 - ReactDOM.render(stub, container);
1259 + await expect(async () => {
1260 + await act(() => {
1261 + root.render(stub);
1262 + });
1263 }).toErrorDev('`value` prop on `input` should not be null');
1264 const node = container.firstChild;
1265
@@ -1115,14 +1267,18 @@ describe('ReactDOMInput', () => {
1267 // because that would erase the "submit" label.
1268 expect(node.getAttribute('value')).toBe(null);
1269
1118 - ReactDOM.render(stub, container);
1270 + await act(() => {
1271 + root.render(stub);
1272 + });
1273 expect(node.getAttribute('value')).toBe(null);
1274 });
1275
1122 - it('should not set a null value on a reset input', () => {
1276 + it('should not set a null value on a reset input', async () => {
1277 const stub = <input type="reset" value={null} />;
1124 - expect(() => {
1125 - ReactDOM.render(stub, container);
1278 + await expect(async () => {
1279 + await act(() => {
1280 + root.render(stub);
1281 + });
1282 }).toErrorDev('`value` prop on `input` should not be null');
1283 const node = container.firstChild;
1284
@@ -1130,35 +1286,43 @@ describe('ReactDOMInput', () => {
1286 // because that would erase the "reset" label.
1287 expect(node.getAttribute('value')).toBe(null);
1288
1133 - ReactDOM.render(stub, container);
1289 + await act(() => {
1290 + root.render(stub);
1291 + });
1292 expect(node.getAttribute('value')).toBe(null);
1293 });
1294
1137 - it('should set a value on a reset input', () => {
1295 + it('should set a value on a reset input', async () => {
1296 const stub = <input type="reset" value="banana" />;
1139 - ReactDOM.render(stub, container);
1297 + await act(() => {
1298 + root.render(stub);
1299 + });
1300 const node = container.firstChild;
1301
1302 expect(node.getAttribute('value')).toBe('banana');
1303 });
1304
1145 - it('should set an empty string value on a submit input', () => {
1305 + it('should set an empty string value on a submit input', async () => {
1306 const stub = <input type="submit" value="" />;
1147 - ReactDOM.render(stub, container);
1307 + await act(() => {
1308 + root.render(stub);
1309 + });
1310 const node = container.firstChild;
1311
1312 expect(node.getAttribute('value')).toBe('');
1313 });
1314
1153 - it('should set an empty string value on a reset input', () => {
1315 + it('should set an empty string value on a reset input', async () => {
1316 const stub = <input type="reset" value="" />;
1155 - ReactDOM.render(stub, container);
1317 + await act(() => {
1318 + root.render(stub);
1319 + });
1320 const node = container.firstChild;
1321
1322 expect(node.getAttribute('value')).toBe('');
1323 });
1324
1161 - it('should control radio buttons', () => {
1325 + it('should control radio buttons', async () => {
1326 class RadioGroup extends React.Component {
1327 aRef = React.createRef();
1328 bRef = React.createRef();
@@ -1199,7 +1363,11 @@ describe('ReactDOMInput', () => {
1363 }
1364 }
1365
1202 - const stub = ReactDOM.render(<RadioGroup />, container);
1366 + const ref = React.createRef();
1367 + await act(() => {
1368 + root.render(<RadioGroup ref={ref} />);
1369 + });
1370 + const stub = ref.current;
1371 const aNode = stub.aRef.current;
1372 const bNode = stub.bRef.current;
1373 const cNode = stub.cRef.current;
@@ -1240,7 +1408,9 @@ describe('ReactDOMInput', () => {
1408 }
1409
1410 // Now let's run the actual ReactDOMInput change event handler
1243 - dispatchEventOnNode(bNode, 'click');
1411 + await act(() => {
1412 + dispatchEventOnNode(bNode, 'click');
1413 + });
1414
1415 // The original state should have been restored
1416 expect(aNode.checked).toBe(true);
@@ -1288,6 +1458,10 @@ describe('ReactDOMInput', () => {
1458 );
1459 }
1460 const html = ReactDOMServer.renderToString(<App />);
1461 + // Create a fresh container, not attached a root yet
1462 + container.remove();
1463 + container = document.createElement('div');
1464 + document.body.appendChild(container);
1465 container.innerHTML = html;
1466 const [a, b, c] = container.querySelectorAll('input');
1467 expect(a.checked).toBe(true);
@@ -1376,6 +1550,10 @@ describe('ReactDOMInput', () => {
1550 );
1551 }
1552 const html = ReactDOMServer.renderToString(<App />);
1553 + // Create a fresh container, not attached a root yet
1554 + container.remove();
1555 + container = document.createElement('div');
1556 + document.body.appendChild(container);
1557 container.innerHTML = html;
1558 const [a, b, c] = container.querySelectorAll('input');
1559 expect(a.checked).toBe(true);
@@ -1421,7 +1599,7 @@ describe('ReactDOMInput', () => {
1599 assertInputTrackingIsCurrent(container);
1600 });
1601
1424 - it('should check the correct radio when the selected name moves', () => {
1602 + it('should check the correct radio when the selected name moves', async () => {
1603 class App extends React.Component {
1604 state = {
1605 updated: false,
@@ -1452,40 +1630,48 @@ describe('ReactDOMInput', () => {
1630 }
1631 }
1632
1455 - const stub = ReactDOM.render(<App />, container);
1456 - const buttonNode = ReactDOM.findDOMNode(stub).childNodes[0];
1457 - const firstRadioNode = ReactDOM.findDOMNode(stub).childNodes[1];
1633 + await act(() => {
1634 + root.render(<App />);
1635 + });
1636 + const node = container.firstChild;
1637 + const buttonNode = node.childNodes[0];
1638 + const firstRadioNode = node.childNodes[1];
1639 expect(isCheckedDirty(firstRadioNode)).toBe(true);
1640 expect(firstRadioNode.checked).toBe(false);
1641 assertInputTrackingIsCurrent(container);
1461 - dispatchEventOnNode(buttonNode, 'click');
1642 + await act(() => {
1643 + dispatchEventOnNode(buttonNode, 'click');
1644 + });
1645 expect(firstRadioNode.checked).toBe(true);
1646 assertInputTrackingIsCurrent(container);
1464 - dispatchEventOnNode(buttonNode, 'click');
1647 + await act(() => {
1648 + dispatchEventOnNode(buttonNode, 'click');
1649 + });
1650 expect(firstRadioNode.checked).toBe(false);
1651 assertInputTrackingIsCurrent(container);
1652 });
1653
1469 - it("shouldn't get tricked by changing radio names, part 2", () => {
1470 - ReactDOM.render(
1471 - <div>
1472 - <input
1473 - type="radio"
1474 - name="a"
1475 - value="1"
1476 - checked={true}
1477 - onChange={() => {}}
1478 - />
1479 - <input
1480 - type="radio"
1481 - name="a"
1482 - value="2"
1483 - checked={false}
1484 - onChange={() => {}}
1485 - />
1486 - </div>,
1487 - container,
1488 - );
1654 + it("shouldn't get tricked by changing radio names, part 2", async () => {
1655 + await act(() => {
1656 + root.render(
1657 + <div>
1658 + <input
1659 + type="radio"
1660 + name="a"
1661 + value="1"
1662 + checked={true}
1663 + onChange={() => {}}
1664 + />
1665 + <input
1666 + type="radio"
1667 + name="a"
1668 + value="2"
1669 + checked={false}
1670 + onChange={() => {}}
1671 + />
1672 + </div>,
1673 + );
1674 + });
1675 const one = container.querySelector('input[name="a"][value="1"]');
1676 const two = container.querySelector('input[name="a"][value="2"]');
1677 expect(one.checked).toBe(true);
@@ -1494,25 +1680,26 @@ describe('ReactDOMInput', () => {
1680 expect(isCheckedDirty(two)).toBe(true);
1681 assertInputTrackingIsCurrent(container);
1682
1497 - ReactDOM.render(
1498 - <div>
1499 - <input
1500 - type="radio"
1501 - name="a"
1502 - value="1"
1503 - checked={true}
1504 - onChange={() => {}}
1505 - />
1506 - <input
1507 - type="radio"
1508 - name="b"
1509 - value="2"
1510 - checked={true}
1511 - onChange={() => {}}
1512 - />
1513 - </div>,
1514 - container,
1515 - );
1683 + await act(() => {
1684 + root.render(
1685 + <div>
1686 + <input
1687 + type="radio"
1688 + name="a"
1689 + value="1"
1690 + checked={true}
1691 + onChange={() => {}}
1692 + />
1693 + <input
1694 + type="radio"
1695 + name="b"
1696 + value="2"
1697 + checked={true}
1698 + onChange={() => {}}
1699 + />
1700 + </div>,
1701 + );
1702 + });
1703 expect(one.checked).toBe(true);
1704 expect(two.checked).toBe(true);
1705 expect(isCheckedDirty(one)).toBe(true);
@@ -1521,6 +1708,9 @@ describe('ReactDOMInput', () => {
1708 });
1709
1710 it('should control radio buttons if the tree updates during render', () => {
1711 + container.remove();
1712 + container = document.createElement('div');
1713 + document.body.appendChild(container);
1714 const sharedParent = container;
1715 const container1 = document.createElement('div');
1716 const container2 = document.createElement('div');
@@ -1600,7 +1790,7 @@ describe('ReactDOMInput', () => {
1790 assertInputTrackingIsCurrent(container);
1791 });
1792
1603 - it('should control radio buttons if the tree updates during render (case 2; #26876)', () => {
1793 + it('should control radio buttons if the tree updates during render (case 2; #26876)', async () => {
1794 let thunk = null;
1795 function App() {
1796 const [disabled, setDisabled] = React.useState(false);
@@ -1634,7 +1824,9 @@ describe('ReactDOMInput', () => {
1824 </>
1825 );
1826 }
1637 - ReactDOM.render(<App />, container);
1827 + await act(() => {
1828 + root.render(<App />);
1829 + });
1830 const [one, two] = container.querySelectorAll('input');
1831 expect(one.checked).toBe(true);
1832 expect(two.checked).toBe(false);
@@ -1644,7 +1836,9 @@ describe('ReactDOMInput', () => {
1836
1837 // Click two
1838 setUntrackedChecked.call(two, true);
1647 - dispatchEventOnNode(two, 'click');
1839 + await act(() => {
1840 + dispatchEventOnNode(two, 'click');
1841 + });
1842 expect(one.checked).toBe(true);
1843 expect(two.checked).toBe(false);
1844 expect(isCheckedDirty(one)).toBe(true);
@@ -1652,7 +1846,7 @@ describe('ReactDOMInput', () => {
1846 assertInputTrackingIsCurrent(container);
1847
1848 // After a delay...
1655 - ReactDOM.unstable_batchedUpdates(thunk);
1849 + await act(thunk);
1850 expect(one.checked).toBe(false);
1851 expect(two.checked).toBe(true);
1852 expect(isCheckedDirty(one)).toBe(true);
@@ -1661,7 +1855,9 @@ describe('ReactDOMInput', () => {
1855
1856 // Click back to one
1857 setUntrackedChecked.call(one, true);
1664 - dispatchEventOnNode(one, 'click');
1858 + await act(() => {
1859 + dispatchEventOnNode(one, 'click');
1860 + });
1861 expect(one.checked).toBe(false);
1862 expect(two.checked).toBe(true);
1863 expect(isCheckedDirty(one)).toBe(true);
@@ -1669,7 +1865,7 @@ describe('ReactDOMInput', () => {
1865 assertInputTrackingIsCurrent(container);
1866
1867 // After a delay...
1672 - ReactDOM.unstable_batchedUpdates(thunk);
1868 + await act(thunk);
1869 expect(one.checked).toBe(true);
1870 expect(two.checked).toBe(false);
1871 expect(isCheckedDirty(one)).toBe(true);
@@ -1677,19 +1873,18 @@ describe('ReactDOMInput', () => {
1873 assertInputTrackingIsCurrent(container);
1874 });
1875
1680 - it('should warn with value and no onChange handler and readOnly specified', () => {
1681 - ReactDOM.render(
1682 - <input type="text" value="zoink" readOnly={true} />,
1683 - container,
1684 - );
1685 - ReactDOM.unmountComponentAtNode(container);
1876 + it('should warn with value and no onChange handler and readOnly specified', async () => {
1877 + await act(() => {
1878 + root.render(<input type="text" value="zoink" readOnly={true} />);
1879 + });
1880 + root.unmount();
1881 + root = ReactDOMClient.createRoot(container);
1882
1687 - expect(() =>
1688 - ReactDOM.render(
1689 - <input type="text" value="zoink" readOnly={false} />,
1690 - container,
1691 - ),
1692 - ).toErrorDev(
1883 + await expect(async () => {
1884 + await act(() => {
1885 + root.render(<input type="text" value="zoink" readOnly={false} />);
1886 + });
1887 + }).toErrorDev(
1888 'Warning: You provided a `value` prop to a form ' +
1889 'field without an `onChange` handler. This will render a read-only ' +
1890 'field. If the field should be mutable use `defaultValue`. ' +
@@ -1698,27 +1893,36 @@ describe('ReactDOMInput', () => {
1893 );
1894 });
1895
1701 - it('should have a this value of undefined if bind is not used', () => {
1896 + it('should have a this value of undefined if bind is not used', async () => {
1897 expect.assertions(1);
1898 const unboundInputOnChange = function () {
1899 expect(this).toBe(undefined);
1900 };
1901
1902 const stub = <input type="text" onChange={unboundInputOnChange} />;
1708 - const node = ReactDOM.render(stub, container);
1903 + await act(() => {
1904 + root.render(stub);
1905 + });
1906 + const node = container.firstChild;
1907
1908 setUntrackedValue.call(node, 'giraffe');
1711 - dispatchEventOnNode(node, 'input');
1909 + await act(() => {
1910 + dispatchEventOnNode(node, 'input');
1911 + });
1912 });
1913
1714 - it('should update defaultValue to empty string', () => {
1715 - ReactDOM.render(<input type="text" defaultValue={'foo'} />, container);
1914 + it('should update defaultValue to empty string', async () => {
1915 + await act(() => {
1916 + root.render(<input type="text" defaultValue={'foo'} />);
1917 + });
1918 if (disableInputAttributeSyncing) {
1919 expect(isValueDirty(container.firstChild)).toBe(false);
1920 } else {
1921 expect(isValueDirty(container.firstChild)).toBe(true);
1922 }
1721 - ReactDOM.render(<input type="text" defaultValue={''} />, container);
1923 + await act(() => {
1924 + root.render(<input type="text" defaultValue={''} />);
1925 + });
1926 expect(container.firstChild.defaultValue).toBe('');
1927 if (disableInputAttributeSyncing) {
1928 expect(isValueDirty(container.firstChild)).toBe(false);
@@ -1727,31 +1931,37 @@ describe('ReactDOMInput', () => {
1931 }
1932 });
1933
1730 - it('should warn if value is null', () => {
1731 - expect(() =>
1732 - ReactDOM.render(<input type="text" value={null} />, container),
1733 - ).toErrorDev(
1934 + it('should warn if value is null', async () => {
1935 + await expect(async () => {
1936 + await act(() => {
1937 + root.render(<input type="text" value={null} />);
1938 + });
1939 + }).toErrorDev(
1940 '`value` prop on `input` should not be null. ' +
1941 'Consider using an empty string to clear the component or `undefined` ' +
1942 'for uncontrolled components.',
1943 );
1738 - ReactDOM.unmountComponentAtNode(container);
1944 + root.unmount();
1945
1740 - ReactDOM.render(<input type="text" value={null} />, container);
1946 + root = ReactDOMClient.createRoot(container);
1947 + await act(() => {
1948 + root.render(<input type="text" value={null} />);
1949 + });
1950 });
1951
1743 - it('should warn if checked and defaultChecked props are specified', () => {
1744 - expect(() =>
1745 - ReactDOM.render(
1746 - <input
1747 - type="radio"
1748 - checked={true}
1749 - defaultChecked={true}
1750 - readOnly={true}
1751 - />,
1752 - container,
1753 - ),
1754 - ).toErrorDev(
1952 + it('should warn if checked and defaultChecked props are specified', async () => {
1953 + await expect(async () => {
1954 + await act(() => {
1955 + root.render(
1956 + <input
1957 + type="radio"
1958 + checked={true}
1959 + defaultChecked={true}
1960 + readOnly={true}
1961 + />,
1962 + );
1963 + });
1964 + }).toErrorDev(
1965 'A component contains an input of type radio with both checked and defaultChecked props. ' +
1966 'Input elements must be either controlled or uncontrolled ' +
1967 '(specify either the checked prop, or the defaultChecked prop, but not ' +
@@ -1759,26 +1969,29 @@ describe('ReactDOMInput', () => {
1969 'element and remove one of these props. More info: ' +
1970 'https://reactjs.org/link/controlled-components',
1971 );
1762 - ReactDOM.unmountComponentAtNode(container);
1763 -
1764 - ReactDOM.render(
1765 - <input
1766 - type="radio"
1767 - checked={true}
1768 - defaultChecked={true}
1769 - readOnly={true}
1770 - />,
1771 - container,
1772 - );
1972 + root.unmount();
1973 +
1974 + root = ReactDOMClient.createRoot(container);
1975 + await act(() => {
1976 + root.render(
1977 + <input
1978 + type="radio"
1979 + checked={true}
1980 + defaultChecked={true}
1981 + readOnly={true}
1982 + />,
1983 + );
1984 + });
1985 });
1986
1775 - it('should warn if value and defaultValue props are specified', () => {
1776 - expect(() =>
1777 - ReactDOM.render(
1778 - <input type="text" value="foo" defaultValue="bar" readOnly={true} />,
1779 - container,
1780 - ),
1781 - ).toErrorDev(
1987 + it('should warn if value and defaultValue props are specified', async () => {
1988 + await expect(async () => {
1989 + await act(() => {
1990 + root.render(
1991 + <input type="text" value="foo" defaultValue="bar" readOnly={true} />,
1992 + );
1993 + });
1994 + }).toErrorDev(
1995 'A component contains an input of type text with both value and defaultValue props. ' +
1996 'Input elements must be either controlled or uncontrolled ' +
1997 '(specify either the value prop, or the defaultValue prop, but not ' +
@@ -1786,20 +1999,29 @@ describe('ReactDOMInput', () => {
1999 'element and remove one of these props. More info: ' +
2000 'https://reactjs.org/link/controlled-components',
2001 );
1789 - ReactDOM.unmountComponentAtNode(container);
2002 + await (() => {
2003 + root.unmount();
2004 + });
2005
1791 - ReactDOM.render(
1792 - <input type="text" value="foo" defaultValue="bar" readOnly={true} />,
1793 - container,
1794 - );
2006 + await act(() => {
2007 + root.render(
2008 + <input type="text" value="foo" defaultValue="bar" readOnly={true} />,
2009 + );
2010 + });
2011 });
2012
1797 - it('should warn if controlled input switches to uncontrolled (value is undefined)', () => {
2013 + it('should warn if controlled input switches to uncontrolled (value is undefined)', async () => {
2014 const stub = (
2015 <input type="text" value="controlled" onChange={emptyFunction} />
2016 );
1801 - ReactDOM.render(stub, container);
1802 - expect(() => ReactDOM.render(<input type="text" />, container)).toErrorDev(
2017 + await act(() => {
2018 + root.render(stub);
2019 + });
2020 + await expect(async () => {
2021 + await act(() => {
2022 + root.render(<input type="text" />);
2023 + });
2024 + }).toErrorDev(
2025 'Warning: A component is changing a controlled input to be uncontrolled. ' +
2026 'This is likely caused by the value changing from a defined to ' +
2027 'undefined, which should not happen. ' +
@@ -1809,14 +2031,18 @@ describe('ReactDOMInput', () => {
2031 );
2032 });
2033
1812 - it('should warn if controlled input switches to uncontrolled (value is null)', () => {
2034 + it('should warn if controlled input switches to uncontrolled (value is null)', async () => {
2035 const stub = (
2036 <input type="text" value="controlled" onChange={emptyFunction} />
2037 );
1816 - ReactDOM.render(stub, container);
1817 - expect(() =>
1818 - ReactDOM.render(<input type="text" value={null} />, container),
1819 - ).toErrorDev([
2038 + await act(() => {
2039 + root.render(stub);
2040 + });
2041 + await expect(async () => {
2042 + await act(() => {
2043 + root.render(<input type="text" value={null} />);
2044 + });
2045 + }).toErrorDev([
2046 '`value` prop on `input` should not be null. ' +
2047 'Consider using an empty string to clear the component or `undefined` for uncontrolled components',
2048 'Warning: A component is changing a controlled input to be uncontrolled. ' +
@@ -1828,17 +2054,18 @@ describe('ReactDOMInput', () => {
2054 ]);
2055 });
2056
1831 - it('should warn if controlled input switches to uncontrolled with defaultValue', () => {
2057 + it('should warn if controlled input switches to uncontrolled with defaultValue', async () => {
2058 const stub = (
2059 <input type="text" value="controlled" onChange={emptyFunction} />
2060 );
1835 - ReactDOM.render(stub, container);
1836 - expect(() =>
1837 - ReactDOM.render(
1838 - <input type="text" defaultValue="uncontrolled" />,
1839 - container,
1840 - ),
1841 - ).toErrorDev(
2061 + await act(() => {
2062 + root.render(stub);
2063 + });
2064 + await expect(async () => {
2065 + await act(() => {
2066 + root.render(<input type="text" defaultValue="uncontrolled" />);
2067 + });
2068 + }).toErrorDev(
2069 'Warning: A component is changing a controlled input to be uncontrolled. ' +
2070 'This is likely caused by the value changing from a defined to ' +
2071 'undefined, which should not happen. ' +
@@ -1848,12 +2075,16 @@ describe('ReactDOMInput', () => {
2075 );
2076 });
2077
1851 - it('should warn if uncontrolled input (value is undefined) switches to controlled', () => {
2078 + it('should warn if uncontrolled input (value is undefined) switches to controlled', async () => {
2079 const stub = <input type="text" />;
1853 - ReactDOM.render(stub, container);
1854 - expect(() =>
1855 - ReactDOM.render(<input type="text" value="controlled" />, container),
1856 - ).toErrorDev(
2080 + await act(() => {
2081 + root.render(stub);
2082 + });
2083 + await expect(async () => {
2084 + await act(() => {
2085 + root.render(<input type="text" value="controlled" />);
2086 + });
2087 + }).toErrorDev(
2088 'Warning: A component is changing an uncontrolled input to be controlled. ' +
2089 'This is likely caused by the value changing from undefined to ' +
2090 'a defined value, which should not happen. ' +
@@ -1863,15 +2094,21 @@ describe('ReactDOMInput', () => {
2094 );
2095 });
2096
1866 - it('should warn if uncontrolled input (value is null) switches to controlled', () => {
2097 + it('should warn if uncontrolled input (value is null) switches to controlled', async () => {
2098 const stub = <input type="text" value={null} />;
1868 - expect(() => ReactDOM.render(stub, container)).toErrorDev(
2099 + await expect(async () => {
2100 + await act(() => {
2101 + root.render(stub);
2102 + });
2103 + }).toErrorDev(
2104 '`value` prop on `input` should not be null. ' +
2105 'Consider using an empty string to clear the component or `undefined` for uncontrolled components.',
2106 );
1872 - expect(() =>
1873 - ReactDOM.render(<input type="text" value="controlled" />, container),
1874 - ).toErrorDev(
2107 + await expect(async () => {
2108 + await act(() => {
2109 + root.render(<input type="text" value="controlled" />);
2110 + });
2111 + }).toErrorDev(
2112 'Warning: A component is changing an uncontrolled input to be controlled. ' +
2113 'This is likely caused by the value changing from undefined to ' +
2114 'a defined value, which should not happen. ' +
@@ -1881,14 +2118,18 @@ describe('ReactDOMInput', () => {
2118 );
2119 });
2120
1884 - it('should warn if controlled checkbox switches to uncontrolled (checked is undefined)', () => {
2121 + it('should warn if controlled checkbox switches to uncontrolled (checked is undefined)', async () => {
2122 const stub = (
2123 <input type="checkbox" checked={true} onChange={emptyFunction} />
2124 );
1888 - ReactDOM.render(stub, container);
1889 - expect(() =>
1890 - ReactDOM.render(<input type="checkbox" />, container),
1891 - ).toErrorDev(
2125 + await act(() => {
2126 + root.render(stub);
2127 + });
2128 + await expect(async () => {
2129 + await act(() => {
2130 + root.render(<input type="checkbox" />);
2131 + });
2132 + }).toErrorDev(
2133 'Warning: A component is changing a controlled input to be uncontrolled. ' +
2134 'This is likely caused by the value changing from a defined to ' +
2135 'undefined, which should not happen. ' +
@@ -1898,14 +2139,18 @@ describe('ReactDOMInput', () => {
2139 );
2140 });
2141
1901 - it('should warn if controlled checkbox switches to uncontrolled (checked is null)', () => {
2142 + it('should warn if controlled checkbox switches to uncontrolled (checked is null)', async () => {
2143 const stub = (
2144 <input type="checkbox" checked={true} onChange={emptyFunction} />
2145 );
1905 - ReactDOM.render(stub, container);
1906 - expect(() =>
1907 - ReactDOM.render(<input type="checkbox" checked={null} />, container),
1908 - ).toErrorDev(
2146 + await act(() => {
2147 + root.render(stub);
2148 + });
2149 + await expect(async () => {
2150 + await act(() => {
2151 + root.render(<input type="checkbox" checked={null} />);
2152 + });
2153 + }).toErrorDev(
2154 'Warning: A component is changing a controlled input to be uncontrolled. ' +
2155 'This is likely caused by the value changing from a defined to ' +
2156 'undefined, which should not happen. ' +
@@ -1915,17 +2160,18 @@ describe('ReactDOMInput', () => {
2160 );
2161 });
2162
1918 - it('should warn if controlled checkbox switches to uncontrolled with defaultChecked', () => {
2163 + it('should warn if controlled checkbox switches to uncontrolled with defaultChecked', async () => {
2164 const stub = (
2165 <input type="checkbox" checked={true} onChange={emptyFunction} />
2166 );
1922 - ReactDOM.render(stub, container);
1923 - expect(() =>
1924 - ReactDOM.render(
1925 - <input type="checkbox" defaultChecked={true} />,
1926 - container,
1927 - ),
1928 - ).toErrorDev(
2167 + await act(() => {
2168 + root.render(stub);
2169 + });
2170 + await expect(async () => {
2171 + await act(() => {
2172 + root.render(<input type="checkbox" defaultChecked={true} />);
2173 + });
2174 + }).toErrorDev(
2175 'Warning: A component is changing a controlled input to be uncontrolled. ' +
2176 'This is likely caused by the value changing from a defined to ' +
2177 'undefined, which should not happen. ' +
@@ -1935,12 +2181,16 @@ describe('ReactDOMInput', () => {
2181 );
2182 });
2183
1938 - it('should warn if uncontrolled checkbox (checked is undefined) switches to controlled', () => {
2184 + it('should warn if uncontrolled checkbox (checked is undefined) switches to controlled', async () => {
2185 const stub = <input type="checkbox" />;
1940 - ReactDOM.render(stub, container);
1941 - expect(() =>
1942 - ReactDOM.render(<input type="checkbox" checked={true} />, container),
1943 - ).toErrorDev(
2186 + await act(() => {
2187 + root.render(stub);
2188 + });
2189 + await expect(async () => {
2190 + await act(() => {
2191 + root.render(<input type="checkbox" checked={true} />);
2192 + });
2193 + }).toErrorDev(
2194 'Warning: A component is changing an uncontrolled input to be controlled. ' +
2195 'This is likely caused by the value changing from undefined to ' +
2196 'a defined value, which should not happen. ' +
@@ -1950,12 +2200,16 @@ describe('ReactDOMInput', () => {
2200 );
2201 });
2202
1953 - it('should warn if uncontrolled checkbox (checked is null) switches to controlled', () => {
2203 + it('should warn if uncontrolled checkbox (checked is null) switches to controlled', async () => {
2204 const stub = <input type="checkbox" checked={null} />;
1955 - ReactDOM.render(stub, container);
1956 - expect(() =>
1957 - ReactDOM.render(<input type="checkbox" checked={true} />, container),
1958 - ).toErrorDev(
2205 + await act(() => {
2206 + root.render(stub);
2207 + });
2208 + await expect(async () => {
2209 + await act(() => {
2210 + root.render(<input type="checkbox" checked={true} />);
2211 + });
2212 + }).toErrorDev(
2213 'Warning: A component is changing an uncontrolled input to be controlled. ' +
2214 'This is likely caused by the value changing from undefined to ' +
2215 'a defined value, which should not happen. ' +
@@ -1965,10 +2219,16 @@ describe('ReactDOMInput', () => {
2219 );
2220 });
2221
1968 - it('should warn if controlled radio switches to uncontrolled (checked is undefined)', () => {
2222 + it('should warn if controlled radio switches to uncontrolled (checked is undefined)', async () => {
2223 const stub = <input type="radio" checked={true} onChange={emptyFunction} />;
1970 - ReactDOM.render(stub, container);
1971 - expect(() => ReactDOM.render(<input type="radio" />, container)).toErrorDev(
2224 + await act(() => {
2225 + root.render(stub);
2226 + });
2227 + await expect(async () => {
2228 + await act(() => {
2229 + root.render(<input type="radio" />);
2230 + });
2231 + }).toErrorDev(
2232 'Warning: A component is changing a controlled input to be uncontrolled. ' +
2233 'This is likely caused by the value changing from a defined to ' +
2234 'undefined, which should not happen. ' +
@@ -1978,12 +2238,16 @@ describe('ReactDOMInput', () => {
2238 );
2239 });
2240
1981 - it('should warn if controlled radio switches to uncontrolled (checked is null)', () => {
2241 + it('should warn if controlled radio switches to uncontrolled (checked is null)', async () => {
2242 const stub = <input type="radio" checked={true} onChange={emptyFunction} />;
1983 - ReactDOM.render(stub, container);
1984 - expect(() =>
1985 - ReactDOM.render(<input type="radio" checked={null} />, container),
1986 - ).toErrorDev(
2243 + await act(() => {
2244 + root.render(stub);
2245 + });
2246 + await expect(async () => {
2247 + await act(() => {
2248 + root.render(<input type="radio" checked={null} />);
2249 + });
2250 + }).toErrorDev(
2251 'Warning: A component is changing a controlled input to be uncontrolled. ' +
2252 'This is likely caused by the value changing from a defined to ' +
2253 'undefined, which should not happen. ' +
@@ -1993,12 +2257,16 @@ describe('ReactDOMInput', () => {
2257 );
2258 });
2259
1996 - it('should warn if controlled radio switches to uncontrolled with defaultChecked', () => {
2260 + it('should warn if controlled radio switches to uncontrolled with defaultChecked', async () => {
2261 const stub = <input type="radio" checked={true} onChange={emptyFunction} />;
1998 - ReactDOM.render(stub, container);
1999 - expect(() =>
2000 - ReactDOM.render(<input type="radio" defaultChecked={true} />, container),
2001 - ).toErrorDev(
2262 + await act(() => {
2263 + root.render(stub);
2264 + });
2265 + await expect(async () => {
2266 + await act(() => {
2267 + root.render(<input type="radio" defaultChecked={true} />);
2268 + });
2269 + }).toErrorDev(
2270 'Warning: A component is changing a controlled input to be uncontrolled. ' +
2271 'This is likely caused by the value changing from a defined to ' +
2272 'undefined, which should not happen. ' +
@@ -2008,12 +2276,16 @@ describe('ReactDOMInput', () => {
2276 );
2277 });
2278
2011 - it('should warn if uncontrolled radio (checked is undefined) switches to controlled', () => {
2279 + it('should warn if uncontrolled radio (checked is undefined) switches to controlled', async () => {
2280 const stub = <input type="radio" />;
2013 - ReactDOM.render(stub, container);
2014 - expect(() =>
2015 - ReactDOM.render(<input type="radio" checked={true} />, container),
2016 - ).toErrorDev(
2281 + await act(() => {
2282 + root.render(stub);
2283 + });
2284 + await expect(async () => {
2285 + await act(() => {
2286 + root.render(<input type="radio" checked={true} />);
2287 + });
2288 + }).toErrorDev(
2289 'Warning: A component is changing an uncontrolled input to be controlled. ' +
2290 'This is likely caused by the value changing from undefined to ' +
2291 'a defined value, which should not happen. ' +
@@ -2023,12 +2295,16 @@ describe('ReactDOMInput', () => {
2295 );
2296 });
2297
2026 - it('should warn if uncontrolled radio (checked is null) switches to controlled', () => {
2298 + it('should warn if uncontrolled radio (checked is null) switches to controlled', async () => {
2299 const stub = <input type="radio" checked={null} />;
2028 - ReactDOM.render(stub, container);
2029 - expect(() =>
2030 - ReactDOM.render(<input type="radio" checked={true} />, container),
2031 - ).toErrorDev(
2300 + await act(() => {
2301 + root.render(stub);
2302 + });
2303 + await expect(async () => {
2304 + await act(() => {
2305 + root.render(<input type="radio" checked={true} />);
2306 + });
2307 + }).toErrorDev(
2308 'Warning: A component is changing an uncontrolled input to be controlled. ' +
2309 'This is likely caused by the value changing from undefined to ' +
2310 'a defined value, which should not happen. ' +
@@ -2038,54 +2314,61 @@ describe('ReactDOMInput', () => {
2314 );
2315 });
2316
2041 - it('should not warn if radio value changes but never becomes controlled', () => {
2042 - ReactDOM.render(<input type="radio" value="value" />, container);
2043 - ReactDOM.render(<input type="radio" />, container);
2044 - ReactDOM.render(
2045 - <input type="radio" value="value" defaultChecked={true} />,
2046 - container,
2047 - );
2048 - ReactDOM.render(
2049 - <input type="radio" value="value" onChange={() => null} />,
2050 - container,
2051 - );
2052 - ReactDOM.render(<input type="radio" />, container);
2317 + it('should not warn if radio value changes but never becomes controlled', async () => {
2318 + await act(() => {
2319 + root.render(<input type="radio" value="value" />);
2320 + });
2321 + await act(() => {
2322 + root.render(<input type="radio" />);
2323 + });
2324 + await act(() => {
2325 + root.render(<input type="radio" value="value" defaultChecked={true} />);
2326 + });
2327 + await act(() => {
2328 + root.render(<input type="radio" value="value" onChange={() => null} />);
2329 + });
2330 + await act(() => {
2331 + root.render(<input type="radio" />);
2332 + });
2333 });
2334
2055 - it('should not warn if radio value changes but never becomes uncontrolled', () => {
2056 - ReactDOM.render(
2057 - <input type="radio" checked={false} onChange={() => null} />,
2058 - container,
2059 - );
2335 + it('should not warn if radio value changes but never becomes uncontrolled', async () => {
2336 + await act(() => {
2337 + root.render(<input type="radio" checked={false} onChange={() => null} />);
2338 + });
2339 const input = container.querySelector('input');
2340 expect(isCheckedDirty(input)).toBe(true);
2062 - ReactDOM.render(
2063 - <input
2064 - type="radio"
2065 - value="value"
2066 - defaultChecked={true}
2067 - checked={false}
2068 - onChange={() => null}
2069 - />,
2070 - container,
2071 - );
2341 + await act(() => {
2342 + root.render(
2343 + <input
2344 + type="radio"
2345 + value="value"
2346 + defaultChecked={true}
2347 + checked={false}
2348 + onChange={() => null}
2349 + />,
2350 + );
2351 + });
2352 expect(isCheckedDirty(input)).toBe(true);
2353 assertInputTrackingIsCurrent(container);
2354 });
2355
2076 - it('should warn if radio checked false changes to become uncontrolled', () => {
2077 - ReactDOM.render(
2078 - <input
2079 - type="radio"
2080 - value="value"
2081 - checked={false}
2082 - onChange={() => null}
2083 - />,
2084 - container,
2085 - );
2086 - expect(() =>
2087 - ReactDOM.render(<input type="radio" value="value" />, container),
2088 - ).toErrorDev(
2356 + it('should warn if radio checked false changes to become uncontrolled', async () => {
2357 + await act(() => {
2358 + root.render(
2359 + <input
2360 + type="radio"
2361 + value="value"
2362 + checked={false}
2363 + onChange={() => null}
2364 + />,
2365 + );
2366 + });
2367 + await expect(async () => {
2368 + await act(() => {
2369 + root.render(<input type="radio" value="value" />);
2370 + });
2371 + }).toErrorDev(
2372 'Warning: A component is changing a controlled input to be uncontrolled. ' +
2373 'This is likely caused by the value changing from a defined to ' +
2374 'undefined, which should not happen. ' +
@@ -2095,7 +2378,7 @@ describe('ReactDOMInput', () => {
2378 );
2379 });
2380
2098 - it('sets type, step, min, max before value always', () => {
2381 + it('sets type, step, min, max before value always', async () => {
2382 const log = [];
2383 const originalCreateElement = document.createElement;
2384 spyOnDevAndProd(document, 'createElement').mockImplementation(
@@ -2133,17 +2416,18 @@ describe('ReactDOMInput', () => {
2416 },
2417 );
2418
2136 - ReactDOM.render(
2137 - <input
2138 - value="0"
2139 - onChange={() => {}}
2140 - type="range"
2141 - min="0"
2142 - max="100"
2143 - step="1"
2144 - />,
2145 - container,
2146 - );
2419 + await act(() => {
2420 + root.render(
2421 + <input
2422 + value="0"
2423 + onChange={() => {}}
2424 + type="range"
2425 + min="0"
2426 + max="100"
2427 + step="1"
2428 + />,
2429 + );
2430 + });
2431
2432 expect(log).toEqual([
2433 'set attribute min',
@@ -2154,12 +2438,14 @@ describe('ReactDOMInput', () => {
2438 ]);
2439 });
2440
2157 - it('sets value properly with type coming later in props', () => {
2158 - const input = ReactDOM.render(<input value="hi" type="radio" />, container);
2159 - expect(input.value).toBe('hi');
2441 + it('sets value properly with type coming later in props', async () => {
2442 + await act(() => {
2443 + root.render(<input value="hi" type="radio" />);
2444 + });
2445 + expect(container.firstChild.value).toBe('hi');
2446 });
2447
2162 - it('does not raise a validation warning when it switches types', () => {
2448 + it('does not raise a validation warning when it switches types', async () => {
2449 class Input extends React.Component {
2450 state = {type: 'number', value: 1000};
2451
@@ -2169,16 +2455,21 @@ describe('ReactDOMInput', () => {
2455 }
2456 }
2457
2172 - const input = ReactDOM.render(<Input />, container);
2173 - const node = ReactDOM.findDOMNode(input);
2458 + const ref = React.createRef();
2459 + await act(() => {
2460 + root.render(<Input ref={ref} />);
2461 + });
2462 + const node = container.firstChild;
2463
2464 // If the value is set before the type, a validation warning will raise and
2465 // the value will not be assigned.
2177 - input.setState({type: 'text', value: 'Test'});
2466 + await act(() => {
2467 + ref.current.setState({type: 'text', value: 'Test'});
2468 + });
2469 expect(node.value).toEqual('Test');
2470 });
2471
2181 - it('resets value of date/time input to fix bugs in iOS Safari', () => {
2472 + it('resets value of date/time input to fix bugs in iOS Safari', async () => {
2473 function strify(x) {
2474 return JSON.stringify(x, null, 2);
2475 }
@@ -2250,7 +2541,9 @@ describe('ReactDOMInput', () => {
2541 },
2542 );
2543
2253 - ReactDOM.render(<input type="date" defaultValue="1980-01-01" />, container);
2544 + await act(() => {
2545 + root.render(<input type="date" defaultValue="1980-01-01" />);
2546 + });
2547
2548 if (disableInputAttributeSyncing) {
2549 expect(log).toEqual([
@@ -2289,14 +2582,18 @@ describe('ReactDOMInput', () => {
2582 };
2583 }
2584
2292 - it('always sets the attribute when values change on text inputs', function () {
2585 + it('always sets the attribute when values change on text inputs', async () => {
2586 const Input = getTestInput();
2294 - const stub = ReactDOM.render(<Input type="text" />, container);
2295 - const node = ReactDOM.findDOMNode(stub);
2587 + await act(() => {
2588 + root.render(<Input type="text" />);
2589 + });
2590 + const node = container.firstChild;
2591 expect(isValueDirty(node)).toBe(false);
2592
2593 setUntrackedValue.call(node, '2');
2299 - dispatchEventOnNode(node, 'input');
2594 + await act(() => {
2595 + dispatchEventOnNode(node, 'input');
2596 + });
2597
2598 expect(isValueDirty(node)).toBe(true);
2599 if (disableInputAttributeSyncing) {
@@ -2306,13 +2603,12 @@ describe('ReactDOMInput', () => {
2603 }
2604 });
2605
2309 - it('does not set the value attribute on number inputs if focused', () => {
2606 + it('does not set the value attribute on number inputs if focused', async () => {
2607 const Input = getTestInput();
2311 - const stub = ReactDOM.render(
2312 - <Input type="number" value="1" />,
2313 - container,
2314 - );
2315 - const node = ReactDOM.findDOMNode(stub);
2608 + await act(() => {
2609 + root.render(<Input type="number" value="1" />);
2610 + });
2611 + const node = container.firstChild;
2612 expect(isValueDirty(node)).toBe(true);
2613
2614 node.focus();
@@ -2328,13 +2624,12 @@ describe('ReactDOMInput', () => {
2624 }
2625 });
2626
2331 - it('sets the value attribute on number inputs on blur', () => {
2627 + it('sets the value attribute on number inputs on blur', async () => {
2628 const Input = getTestInput();
2333 - const stub = ReactDOM.render(
2334 - <Input type="number" value="1" />,
2335 - container,
2336 - );
2337 - const node = ReactDOM.findDOMNode(stub);
2629 + await act(() => {
2630 + root.render(<Input type="number" value="1" />);
2631 + });
2632 + const node = container.firstChild;
2633 expect(isValueDirty(node)).toBe(true);
2634
2635 node.focus();
@@ -2352,11 +2647,11 @@ describe('ReactDOMInput', () => {
2647 }
2648 });
2649
2355 - it('an uncontrolled number input will not update the value attribute on blur', () => {
2356 - const node = ReactDOM.render(
2357 - <input type="number" defaultValue="1" />,
2358 - container,
2359 - );
2650 + it('an uncontrolled number input will not update the value attribute on blur', async () => {
2651 + await act(() => {
2652 + root.render(<input type="number" defaultValue="1" />);
2653 + });
2654 + const node = container.firstChild;
2655 if (disableInputAttributeSyncing) {
2656 expect(isValueDirty(node)).toBe(false);
2657 } else {
@@ -2372,11 +2667,11 @@ describe('ReactDOMInput', () => {
2667 expect(node.getAttribute('value')).toBe('1');
2668 });
2669
2375 - it('an uncontrolled text input will not update the value attribute on blur', () => {
2376 - const node = ReactDOM.render(
2377 - <input type="text" defaultValue="1" />,
2378 - container,
2379 - );
2670 + it('an uncontrolled text input will not update the value attribute on blur', async () => {
2671 + await act(() => {
2672 + root.render(<input type="text" defaultValue="1" />);
2673 + });
2674 + const node = container.firstChild;
2675 if (disableInputAttributeSyncing) {
2676 expect(isValueDirty(node)).toBe(false);
2677 } else {
@@ -2396,7 +2691,7 @@ describe('ReactDOMInput', () => {
2691 describe('setting a controlled input to undefined', () => {
2692 let input;
2693
2399 - function renderInputWithStringThenWithUndefined() {
2694 + async function renderInputWithStringThenWithUndefined() {
2695 let setValueToUndefined;
2696 class Input extends React.Component {
2697 constructor() {
@@ -2414,15 +2709,19 @@ describe('ReactDOMInput', () => {
2709 }
2710 }
2711
2417 - const stub = ReactDOM.render(<Input />, container);
2418 - input = ReactDOM.findDOMNode(stub);
2712 + await act(() => {
2713 + root.render(<Input />);
2714 + });
2715 + input = container.firstChild;
2716 setUntrackedValue.call(input, 'latest');
2717 dispatchEventOnNode(input, 'input');
2421 - setValueToUndefined();
2718 + await act(() => {
2719 + setValueToUndefined();
2720 + });
2721 }
2722
2424 - it('reverts the value attribute to the initial value', () => {
2425 - expect(renderInputWithStringThenWithUndefined).toErrorDev(
2723 + it('reverts the value attribute to the initial value', async () => {
2724 + await expect(renderInputWithStringThenWithUndefined).toErrorDev(
2725 'A component is changing a controlled input to be uncontrolled.',
2726 );
2727 if (disableInputAttributeSyncing) {
@@ -2432,8 +2731,8 @@ describe('ReactDOMInput', () => {
2731 }
2732 });
2733
2435 - it('preserves the value property', () => {
2436 - expect(renderInputWithStringThenWithUndefined).toErrorDev(
2734 + it('preserves the value property', async () => {
2735 + await expect(renderInputWithStringThenWithUndefined).toErrorDev(
2736 'A component is changing a controlled input to be uncontrolled.',
2737 );
2738 expect(input.value).toBe('latest');
@@ -2443,7 +2742,7 @@ describe('ReactDOMInput', () => {
2742 describe('setting a controlled input to null', () => {
2743 let input;
2744
2446 - function renderInputWithStringThenWithNull() {
2745 + async function renderInputWithStringThenWithNull() {
2746 let setValueToNull;
2747 class Input extends React.Component {
2748 constructor() {
@@ -2461,15 +2760,19 @@ describe('ReactDOMInput', () => {
2760 }
2761 }
2762
2464 - const stub = ReactDOM.render(<Input />, container);
2465 - input = ReactDOM.findDOMNode(stub);
2763 + await act(() => {
2764 + root.render(<Input />);
2765 + });
2766 + input = container.firstChild;
2767 setUntrackedValue.call(input, 'latest');
2768 dispatchEventOnNode(input, 'input');
2468 - setValueToNull();
2769 + await act(() => {
2770 + setValueToNull();
2771 + });
2772 }
2773
2471 - it('reverts the value attribute to the initial value', () => {
2472 - expect(renderInputWithStringThenWithNull).toErrorDev([
2774 + it('reverts the value attribute to the initial value', async () => {
2775 + await expect(renderInputWithStringThenWithNull).toErrorDev([
2776 '`value` prop on `input` should not be null. ' +
2777 'Consider using an empty string to clear the component ' +
2778 'or `undefined` for uncontrolled components.',
@@ -2482,8 +2785,8 @@ describe('ReactDOMInput', () => {
2785 }
2786 });
2787
2485 - it('preserves the value property', () => {
2486 - expect(renderInputWithStringThenWithNull).toErrorDev([
2788 + it('preserves the value property', async () => {
2789 + await expect(renderInputWithStringThenWithNull).toErrorDev([
2790 '`value` prop on `input` should not be null. ' +
2791 'Consider using an empty string to clear the component ' +
2792 'or `undefined` for uncontrolled components.',
@@ -2494,13 +2797,12 @@ describe('ReactDOMInput', () => {
2797 });
2798
2799 describe('When given a Symbol value', function () {
2497 - it('treats initial Symbol value as an empty string', function () {
2498 - expect(() =>
2499 - ReactDOM.render(
2500 - <input value={Symbol('foobar')} onChange={() => {}} />,
2501 - container,
2502 - ),
2503 - ).toErrorDev('Invalid value for prop `value`');
2800 + it('treats initial Symbol value as an empty string', async () => {
2801 + await expect(async () => {
2802 + await act(() => {
2803 + root.render(<input value={Symbol('foobar')} onChange={() => {}} />);
2804 + });
2805 + }).toErrorDev('Invalid value for prop `value`');
2806 const node = container.firstChild;
2807
2808 expect(node.value).toBe('');
@@ -2511,14 +2813,15 @@ describe('ReactDOMInput', () => {
2813 }
2814 });
2815
2514 - it('treats updated Symbol value as an empty string', function () {
2515 - ReactDOM.render(<input value="foo" onChange={() => {}} />, container);
2516 - expect(() =>
2517 - ReactDOM.render(
2518 - <input value={Symbol('foobar')} onChange={() => {}} />,
2519 - container,
2520 - ),
2521 - ).toErrorDev('Invalid value for prop `value`');
2816 + it('treats updated Symbol value as an empty string', async () => {
2817 + await act(() => {
2818 + root.render(<input value="foo" onChange={() => {}} />);
2819 + });
2820 + await expect(async () => {
2821 + await act(() => {
2822 + root.render(<input value={Symbol('foobar')} onChange={() => {}} />);
2823 + });
2824 + }).toErrorDev('Invalid value for prop `value`');
2825 const node = container.firstChild;
2826
2827 expect(node.value).toBe('');
@@ -2529,8 +2832,10 @@ describe('ReactDOMInput', () => {
2832 }
2833 });
2834
2532 - it('treats initial Symbol defaultValue as an empty string', function () {
2533 - ReactDOM.render(<input defaultValue={Symbol('foobar')} />, container);
2835 + it('treats initial Symbol defaultValue as an empty string', async () => {
2836 + await act(() => {
2837 + root.render(<input defaultValue={Symbol('foobar')} />);
2838 + });
2839 const node = container.firstChild;
2840
2841 expect(node.value).toBe('');
@@ -2538,9 +2843,13 @@ describe('ReactDOMInput', () => {
2843 // TODO: we should warn here.
2844 });
2845
2541 - it('treats updated Symbol defaultValue as an empty string', function () {
2542 - ReactDOM.render(<input defaultValue="foo" />, container);
2543 - ReactDOM.render(<input defaultValue={Symbol('foobar')} />, container);
2846 + it('treats updated Symbol defaultValue as an empty string', async () => {
2847 + await act(() => {
2848 + root.render(<input defaultValue="foo" />);
2849 + });
2850 + await act(() => {
2851 + root.render(<input defaultValue={Symbol('foobar')} />);
2852 + });
2853 const node = container.firstChild;
2854
2855 if (disableInputAttributeSyncing) {
@@ -2554,13 +2863,12 @@ describe('ReactDOMInput', () => {
2863 });
2864
2865 describe('When given a function value', function () {
2557 - it('treats initial function value as an empty string', function () {
2558 - expect(() =>
2559 - ReactDOM.render(
2560 - <input value={() => {}} onChange={() => {}} />,
2561 - container,
2562 - ),
2563 - ).toErrorDev('Invalid value for prop `value`');
2866 + it('treats initial function value as an empty string', async () => {
2867 + await expect(async () => {
2868 + await act(() => {
2869 + root.render(<input value={() => {}} onChange={() => {}} />);
2870 + });
2871 + }).toErrorDev('Invalid value for prop `value`');
2872 const node = container.firstChild;
2873
2874 expect(node.value).toBe('');
@@ -2571,14 +2879,15 @@ describe('ReactDOMInput', () => {
2879 }
2880 });
2881
2574 - it('treats updated function value as an empty string', function () {
2575 - ReactDOM.render(<input value="foo" onChange={() => {}} />, container);
2576 - expect(() =>
2577 - ReactDOM.render(
2578 - <input value={() => {}} onChange={() => {}} />,
2579 - container,
2580 - ),
2581 - ).toErrorDev('Invalid value for prop `value`');
2882 + it('treats updated function value as an empty string', async () => {
2883 + await act(() => {
2884 + root.render(<input value="foo" onChange={() => {}} />);
2885 + });
2886 + await expect(async () => {
2887 + await act(() => {
2888 + root.render(<input value={() => {}} onChange={() => {}} />);
2889 + });
2890 + }).toErrorDev('Invalid value for prop `value`');
2891 const node = container.firstChild;
2892
2893 expect(node.value).toBe('');
@@ -2589,8 +2898,10 @@ describe('ReactDOMInput', () => {
2898 }
2899 });
2900
2592 - it('treats initial function defaultValue as an empty string', function () {
2593 - ReactDOM.render(<input defaultValue={() => {}} />, container);
2901 + it('treats initial function defaultValue as an empty string', async () => {
2902 + await act(() => {
2903 + root.render(<input defaultValue={() => {}} />);
2904 + });
2905 const node = container.firstChild;
2906
2907 expect(node.value).toBe('');
@@ -2598,9 +2909,13 @@ describe('ReactDOMInput', () => {
2909 // TODO: we should warn here.
2910 });
2911
2601 - it('treats updated function defaultValue as an empty string', function () {
2602 - ReactDOM.render(<input defaultValue="foo" />, container);
2603 - ReactDOM.render(<input defaultValue={() => {}} />, container);
2912 + it('treats updated function defaultValue as an empty string', async () => {
2913 + await act(() => {
2914 + root.render(<input defaultValue="foo" />);
2915 + });
2916 + await act(() => {
2917 + root.render(<input defaultValue={() => {}} />);
2918 + });
2919 const node = container.firstChild;
2920
2921 if (disableInputAttributeSyncing) {
@@ -2619,19 +2934,20 @@ describe('ReactDOMInput', () => {
2934 // Between 16 and 16.2, we assigned a node's value to it's current
2935 // value in order to "dettach" it from defaultValue. This had the unfortunate
2936 // side-effect of assigning value="on" to radio and checkboxes
2622 - it('does not add "on" in absence of value on a checkbox', function () {
2623 - ReactDOM.render(
2624 - <input type="checkbox" defaultChecked={true} />,
2625 - container,
2626 - );
2937 + it('does not add "on" in absence of value on a checkbox', async () => {
2938 + await act(() => {
2939 + root.render(<input type="checkbox" defaultChecked={true} />);
2940 + });
2941 const node = container.firstChild;
2942
2943 expect(node.value).toBe('on');
2944 expect(node.hasAttribute('value')).toBe(false);
2945 });
2946
2633 - it('does not add "on" in absence of value on a radio', function () {
2634 - ReactDOM.render(<input type="radio" defaultChecked={true} />, container);
2947 + it('does not add "on" in absence of value on a radio', async () => {
2948 + await act(() => {
2949 + root.render(<input type="radio" defaultChecked={true} />);
2950 + });
2951 const node = container.firstChild;
2952
2953 expect(node.value).toBe('on');
@@ -2639,45 +2955,47 @@ describe('ReactDOMInput', () => {
2955 });
2956 });
2957
2642 - it('should remove previous `defaultValue`', () => {
2643 - const node = ReactDOM.render(
2644 - <input type="text" defaultValue="0" />,
2645 - container,
2646 - );
2958 + it('should remove previous `defaultValue`', async () => {
2959 + await act(() => {
2960 + root.render(<input type="text" defaultValue="0" />);
2961 + });
2962 + const node = container.firstChild;
2963
2964 expect(node.value).toBe('0');
2965 expect(node.defaultValue).toBe('0');
2966
2651 - ReactDOM.render(<input type="text" />, container);
2967 + await act(() => {
2968 + root.render(<input type="text" />);
2969 + });
2970 expect(node.defaultValue).toBe('');
2971 });
2972
2655 - it('should treat `defaultValue={null}` as missing', () => {
2656 - const node = ReactDOM.render(
2657 - <input type="text" defaultValue="0" />,
2658 - container,
2659 - );
2973 + it('should treat `defaultValue={null}` as missing', async () => {
2974 + await act(() => {
2975 + root.render(<input type="text" defaultValue="0" />);
2976 + });
2977 + const node = container.firstChild;
2978
2979 expect(node.value).toBe('0');
2980 expect(node.defaultValue).toBe('0');
2981
2664 - ReactDOM.render(<input type="text" defaultValue={null} />, container);
2982 + await act(() => {
2983 + root.render(<input type="text" defaultValue={null} />);
2984 + });
2985 expect(node.defaultValue).toBe('');
2986 });
2987
2668 - it('should notice input changes when reverting back to original value', () => {
2988 + it('should notice input changes when reverting back to original value', async () => {
2989 const log = [];
2990 function onChange(e) {
2991 log.push(e.target.value);
2992 }
2673 - ReactDOM.render(
2674 - <input type="text" value="" onChange={onChange} />,
2675 - container,
2676 - );
2677 - ReactDOM.render(
2678 - <input type="text" value="a" onChange={onChange} />,
2679 - container,
2680 - );
2993 + await act(() => {
2994 + root.render(<input type="text" value="" onChange={onChange} />);
2995 + });
2996 + await act(() => {
2997 + root.render(<input type="text" value="a" onChange={onChange} />);
2998 + });
2999
3000 const node = container.firstChild;
3001 setUntrackedValue.call(node, '');