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

Convert DOMPropertyOperations-test to createRoot (#27911)

Convert DOMPropertyOperations-test to createRoot --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/27911). * #27914 * __->__ #27911

Jan Kassens committed Jan 9, 2024 at 15:30 UTC ef2859d50bb6b4b31b304f4fe4bb43dc273a9b54
1 file changed +468 -273
packages/react-dom/src/__tests__/DOMPropertyOperations-test.js
+468 -273
@@ -17,12 +17,14 @@ const {
17
18 describe('DOMPropertyOperations', () => {
19 let React;
20 - let ReactDOM;
20 + let ReactDOMClient;
21 + let act;
22
23 beforeEach(() => {
24 jest.resetModules();
25 React = require('react');
25 - ReactDOM = require('react-dom');
26 + ReactDOMClient = require('react-dom/client');
27 + ({act} = require('internal-test-utils'));
28 });
29
30 // Sets a value in a way that React doesn't see,
@@ -37,25 +39,34 @@ describe('DOMPropertyOperations', () => {
39 ).set;
40
41 describe('setValueForProperty', () => {
40 - it('should set values as properties by default', () => {
42 + it('should set values as properties by default', async () => {
43 const container = document.createElement('div');
42 - ReactDOM.render(<div title="Tip!" />, container);
44 + const root = ReactDOMClient.createRoot(container);
45 + await act(() => {
46 + root.render(<div title="Tip!" />);
47 + });
48 expect(container.firstChild.title).toBe('Tip!');
49 });
50
46 - it('should set values as attributes if necessary', () => {
51 + it('should set values as attributes if necessary', async () => {
52 const container = document.createElement('div');
48 - ReactDOM.render(<div role="#" />, container);
53 + const root = ReactDOMClient.createRoot(container);
54 + await act(() => {
55 + root.render(<div role="#" />);
56 + });
57 expect(container.firstChild.getAttribute('role')).toBe('#');
58 expect(container.firstChild.role).toBeUndefined();
59 });
60
53 - it('should set values as namespace attributes if necessary', () => {
61 + it('should set values as namespace attributes if necessary', async () => {
62 const container = document.createElementNS(
63 'http://www.w3.org/2000/svg',
64 'svg',
65 );
58 - ReactDOM.render(<image xlinkHref="about:blank" />, container);
66 + const root = ReactDOMClient.createRoot(container);
67 + await act(() => {
68 + root.render(<image xlinkHref="about:blank" />);
69 + });
70 expect(
71 container.firstChild.getAttributeNS(
72 'http://www.w3.org/1999/xlink',
@@ -64,23 +75,38 @@ describe('DOMPropertyOperations', () => {
75 ).toBe('about:blank');
76 });
77
67 - it('should set values as boolean properties', () => {
78 + it('should set values as boolean properties', async () => {
79 const container = document.createElement('div');
69 - ReactDOM.render(<div disabled="disabled" />, container);
80 + const root = ReactDOMClient.createRoot(container);
81 + await act(() => {
82 + root.render(<div disabled="disabled" />);
83 + });
84 expect(container.firstChild.getAttribute('disabled')).toBe('');
71 - ReactDOM.render(<div disabled={true} />, container);
85 + await act(() => {
86 + root.render(<div disabled={true} />);
87 + });
88 expect(container.firstChild.getAttribute('disabled')).toBe('');
73 - ReactDOM.render(<div disabled={false} />, container);
89 + await act(() => {
90 + root.render(<div disabled={false} />);
91 + });
92 expect(container.firstChild.getAttribute('disabled')).toBe(null);
75 - ReactDOM.render(<div disabled={true} />, container);
76 - ReactDOM.render(<div disabled={null} />, container);
93 + await act(() => {
94 + root.render(<div disabled={true} />);
95 + });
96 + await act(() => {
97 + root.render(<div disabled={null} />);
98 + });
99 expect(container.firstChild.getAttribute('disabled')).toBe(null);
78 - ReactDOM.render(<div disabled={true} />, container);
79 - ReactDOM.render(<div disabled={undefined} />, container);
100 + await act(() => {
101 + root.render(<div disabled={true} />);
102 + });
103 + await act(() => {
104 + root.render(<div disabled={undefined} />);
105 + });
106 expect(container.firstChild.getAttribute('disabled')).toBe(null);
107 });
108
83 - it('should convert attribute values to string first', () => {
109 + it('should convert attribute values to string first', async () => {
110 // Browsers default to this behavior, but some test environments do not.
111 // This ensures that we have consistent behavior.
112 const obj = {
@@ -90,13 +116,19 @@ describe('DOMPropertyOperations', () => {
116 };
117
118 const container = document.createElement('div');
93 - ReactDOM.render(<div className={obj} />, container);
119 + const root = ReactDOMClient.createRoot(container);
120 + await act(() => {
121 + root.render(<div className={obj} />);
122 + });
123 expect(container.firstChild.getAttribute('class')).toBe('css-class');
124 });
125
97 - it('should not remove empty attributes for special input properties', () => {
126 + it('should not remove empty attributes for special input properties', async () => {
127 const container = document.createElement('div');
99 - ReactDOM.render(<input value="" onChange={() => {}} />, container);
128 + const root = ReactDOMClient.createRoot(container);
129 + await act(() => {
130 + root.render(<input value="" onChange={() => {}} />);
131 + });
132 if (disableInputAttributeSyncing) {
133 expect(container.firstChild.hasAttribute('value')).toBe(false);
134 } else {
@@ -105,79 +137,114 @@ describe('DOMPropertyOperations', () => {
137 expect(container.firstChild.value).toBe('');
138 });
139
108 - it('should not remove empty attributes for special option properties', () => {
140 + it('should not remove empty attributes for special option properties', async () => {
141 const container = document.createElement('div');
110 - ReactDOM.render(
111 - <select>
112 - <option value="">empty</option>
113 - <option>filled</option>
114 - </select>,
115 - container,
116 - );
142 + const root = ReactDOMClient.createRoot(container);
143 + await act(() => {
144 + root.render(
145 + <select>
146 + <option value="">empty</option>
147 + <option>filled</option>
148 + </select>,
149 + );
150 + });
151 // Regression test for https://github.com/facebook/react/issues/6219
152 expect(container.firstChild.firstChild.value).toBe('');
153 expect(container.firstChild.lastChild.value).toBe('filled');
154 });
155
122 - it('should remove for falsey boolean properties', () => {
156 + it('should remove for falsey boolean properties', async () => {
157 const container = document.createElement('div');
124 - ReactDOM.render(<div allowFullScreen={false} />, container);
158 + const root = ReactDOMClient.createRoot(container);
159 + await act(() => {
160 + root.render(<div allowFullScreen={false} />);
161 + });
162 expect(container.firstChild.hasAttribute('allowFullScreen')).toBe(false);
163 });
164
128 - it('should remove when setting custom attr to null', () => {
165 + it('should remove when setting custom attr to null', async () => {
166 const container = document.createElement('div');
130 - ReactDOM.render(<div data-foo="bar" />, container);
167 + const root = ReactDOMClient.createRoot(container);
168 + await act(() => {
169 + root.render(<div data-foo="bar" />);
170 + });
171 expect(container.firstChild.hasAttribute('data-foo')).toBe(true);
132 - ReactDOM.render(<div data-foo={null} />, container);
172 + await act(() => {
173 + root.render(<div data-foo={null} />);
174 + });
175 expect(container.firstChild.hasAttribute('data-foo')).toBe(false);
176 });
177
136 - it('should set className to empty string instead of null', () => {
178 + it('should set className to empty string instead of null', async () => {
179 const container = document.createElement('div');
138 - ReactDOM.render(<div className="selected" />, container);
180 + const root = ReactDOMClient.createRoot(container);
181 + await act(() => {
182 + root.render(<div className="selected" />);
183 + });
184 expect(container.firstChild.className).toBe('selected');
140 - ReactDOM.render(<div className={null} />, container);
185 + await act(() => {
186 + root.render(<div className={null} />);
187 + });
188 // className should be '', not 'null' or null (which becomes 'null' in
189 // some browsers)
190 expect(container.firstChild.className).toBe('');
191 expect(container.firstChild.getAttribute('class')).toBe(null);
192 });
193
147 - it('should remove property properly for boolean properties', () => {
194 + it('should remove property properly for boolean properties', async () => {
195 const container = document.createElement('div');
149 - ReactDOM.render(<div hidden={true} />, container);
196 + const root = ReactDOMClient.createRoot(container);
197 + await act(() => {
198 + root.render(<div hidden={true} />);
199 + });
200 expect(container.firstChild.hasAttribute('hidden')).toBe(true);
151 - ReactDOM.render(<div hidden={false} />, container);
201 + await act(() => {
202 + root.render(<div hidden={false} />);
203 + });
204 expect(container.firstChild.hasAttribute('hidden')).toBe(false);
205 });
206
155 - it('should always assign the value attribute for non-inputs', function () {
207 + it('should always assign the value attribute for non-inputs', async () => {
208 const container = document.createElement('div');
157 - ReactDOM.render(<progress />, container);
209 + const root = ReactDOMClient.createRoot(container);
210 + await act(() => {
211 + root.render(<progress />);
212 + });
213 spyOnDevAndProd(container.firstChild, 'setAttribute');
159 - ReactDOM.render(<progress value={30} />, container);
160 - ReactDOM.render(<progress value="30" />, container);
214 + await act(() => {
215 + root.render(<progress value={30} />);
216 + });
217 + await act(() => {
218 + root.render(<progress value="30" />);
219 + });
220 expect(container.firstChild.setAttribute).toHaveBeenCalledTimes(2);
221 });
222
164 - it('should return the progress to intermediate state on null value', () => {
223 + it('should return the progress to intermediate state on null value', async () => {
224 const container = document.createElement('div');
166 - ReactDOM.render(<progress value={30} />, container);
167 - ReactDOM.render(<progress value={null} />, container);
225 + const root = ReactDOMClient.createRoot(container);
226 + await act(() => {
227 + root.render(<progress value={30} />);
228 + });
229 + await act(() => {
230 + root.render(<progress value={null} />);
231 + });
232 // Ensure we move progress back to an indeterminate state.
233 // Regression test for https://github.com/facebook/react/issues/6119
234 expect(container.firstChild.hasAttribute('value')).toBe(false);
235 });
236
237 // @gate enableCustomElementPropertySupport
174 - it('custom element custom events lowercase', () => {
238 + it('custom element custom events lowercase', async () => {
239 const oncustomevent = jest.fn();
240 function Test() {
241 return <my-custom-element oncustomevent={oncustomevent} />;
242 }
243 const container = document.createElement('div');
180 - ReactDOM.render(<Test />, container);
244 + const root = ReactDOMClient.createRoot(container);
245 + await act(() => {
246 + root.render(<Test />);
247 + });
248 container
249 .querySelector('my-custom-element')
250 .dispatchEvent(new Event('customevent'));
@@ -185,13 +252,16 @@ describe('DOMPropertyOperations', () => {
252 });
253
254 // @gate enableCustomElementPropertySupport
188 - it('custom element custom events uppercase', () => {
255 + it('custom element custom events uppercase', async () => {
256 const oncustomevent = jest.fn();
257 function Test() {
258 return <my-custom-element onCustomevent={oncustomevent} />;
259 }
260 const container = document.createElement('div');
194 - ReactDOM.render(<Test />, container);
261 + const root = ReactDOMClient.createRoot(container);
262 + await act(() => {
263 + root.render(<Test />);
264 + });
265 container
266 .querySelector('my-custom-element')
267 .dispatchEvent(new Event('Customevent'));
@@ -199,13 +269,16 @@ describe('DOMPropertyOperations', () => {
269 });
270
271 // @gate enableCustomElementPropertySupport
202 - it('custom element custom event with dash in name', () => {
272 + it('custom element custom event with dash in name', async () => {
273 const oncustomevent = jest.fn();
274 function Test() {
275 return <my-custom-element oncustom-event={oncustomevent} />;
276 }
277 const container = document.createElement('div');
208 - ReactDOM.render(<Test />, container);
278 + const root = ReactDOMClient.createRoot(container);
279 + await act(() => {
280 + root.render(<Test />);
281 + });
282 container
283 .querySelector('my-custom-element')
284 .dispatchEvent(new Event('custom-event'));
@@ -213,48 +286,59 @@ describe('DOMPropertyOperations', () => {
286 });
287
288 // @gate enableCustomElementPropertySupport
216 - it('custom element remove event handler', () => {
289 + it('custom element remove event handler', async () => {
290 const oncustomevent = jest.fn();
291 function Test(props) {
292 return <my-custom-element oncustomevent={props.handler} />;
293 }
294
295 const container = document.createElement('div');
223 - ReactDOM.render(<Test handler={oncustomevent} />, container);
296 + const root = ReactDOMClient.createRoot(container);
297 + await act(() => {
298 + root.render(<Test handler={oncustomevent} />);
299 + });
300 const customElement = container.querySelector('my-custom-element');
301 customElement.dispatchEvent(new Event('customevent'));
302 expect(oncustomevent).toHaveBeenCalledTimes(1);
303
228 - ReactDOM.render(<Test handler={false} />, container);
304 + await act(() => {
305 + root.render(<Test handler={false} />);
306 + });
307 // Make sure that the second render didn't create a new element. We want
308 // to make sure removeEventListener actually gets called on the same element.
309 expect(customElement).toBe(customElement);
310 customElement.dispatchEvent(new Event('customevent'));
311 expect(oncustomevent).toHaveBeenCalledTimes(1);
312
235 - ReactDOM.render(<Test handler={oncustomevent} />, container);
313 + await act(() => {
314 + root.render(<Test handler={oncustomevent} />);
315 + });
316 customElement.dispatchEvent(new Event('customevent'));
317 expect(oncustomevent).toHaveBeenCalledTimes(2);
318
319 const oncustomevent2 = jest.fn();
240 - ReactDOM.render(<Test handler={oncustomevent2} />, container);
320 + await act(() => {
321 + root.render(<Test handler={oncustomevent2} />);
322 + });
323 customElement.dispatchEvent(new Event('customevent'));
324 expect(oncustomevent).toHaveBeenCalledTimes(2);
325 expect(oncustomevent2).toHaveBeenCalledTimes(1);
326 });
327
246 - it('custom elements shouldnt have non-functions for on* attributes treated as event listeners', () => {
328 + it('custom elements shouldnt have non-functions for on* attributes treated as event listeners', async () => {
329 const container = document.createElement('div');
248 - ReactDOM.render(
249 - <my-custom-element
250 - onstring={'hello'}
251 - onobj={{hello: 'world'}}
252 - onarray={['one', 'two']}
253 - ontrue={true}
254 - onfalse={false}
255 - />,
256 - container,
257 - );
330 + const root = ReactDOMClient.createRoot(container);
331 + await act(() => {
332 + root.render(
333 + <my-custom-element
334 + onstring={'hello'}
335 + onobj={{hello: 'world'}}
336 + onarray={['one', 'two']}
337 + ontrue={true}
338 + onfalse={false}
339 + />,
340 + );
341 + });
342 const customElement = container.querySelector('my-custom-element');
343 expect(customElement.getAttribute('onstring')).toBe('hello');
344 expect(customElement.getAttribute('onobj')).toBe('[object Object]');
@@ -274,7 +358,7 @@ describe('DOMPropertyOperations', () => {
358 customElement.dispatchEvent(new Event('false'));
359 });
360
277 - it('custom elements should still have onClick treated like regular elements', () => {
361 + it('custom elements should still have onClick treated like regular elements', async () => {
362 let syntheticClickEvent = null;
363 const syntheticEventHandler = jest.fn(
364 event => (syntheticClickEvent = event),
@@ -287,7 +371,10 @@ describe('DOMPropertyOperations', () => {
371
372 const container = document.createElement('div');
373 document.body.appendChild(container);
290 - ReactDOM.render(<Test />, container);
374 + const root = ReactDOMClient.createRoot(container);
375 + await act(() => {
376 + root.render(<Test />);
377 + });
378
379 const customElement = container.querySelector('my-custom-element');
380 customElement.onclick = nativeEventHandler;
@@ -299,12 +386,15 @@ describe('DOMPropertyOperations', () => {
386 });
387
388 // @gate enableCustomElementPropertySupport
302 - it('custom elements should have working onChange event listeners', () => {
389 + it('custom elements should have working onChange event listeners', async () => {
390 let reactChangeEvent = null;
391 const eventHandler = jest.fn(event => (reactChangeEvent = event));
392 const container = document.createElement('div');
393 document.body.appendChild(container);
307 - ReactDOM.render(<my-custom-element onChange={eventHandler} />, container);
394 + const root = ReactDOMClient.createRoot(container);
395 + await act(() => {
396 + root.render(<my-custom-element onChange={eventHandler} />);
397 + });
398 const customElement = container.querySelector('my-custom-element');
399 let expectedHandlerCallCount = 0;
400
@@ -315,21 +405,28 @@ describe('DOMPropertyOperations', () => {
405 expect(reactChangeEvent.nativeEvent).toBe(changeEvent);
406
407 // Also make sure that removing and re-adding the event listener works
318 - ReactDOM.render(<my-custom-element />, container);
408 + await act(() => {
409 + root.render(<my-custom-element />);
410 + });
411 customElement.dispatchEvent(new Event('change', {bubbles: true}));
412 expect(eventHandler).toHaveBeenCalledTimes(expectedHandlerCallCount);
321 - ReactDOM.render(<my-custom-element onChange={eventHandler} />, container);
413 + await act(() => {
414 + root.render(<my-custom-element onChange={eventHandler} />);
415 + });
416 customElement.dispatchEvent(new Event('change', {bubbles: true}));
417 expectedHandlerCallCount++;
418 expect(eventHandler).toHaveBeenCalledTimes(expectedHandlerCallCount);
419 });
420
327 - it('custom elements should have working onInput event listeners', () => {
421 + it('custom elements should have working onInput event listeners', async () => {
422 let reactInputEvent = null;
423 const eventHandler = jest.fn(event => (reactInputEvent = event));
424 const container = document.createElement('div');
425 document.body.appendChild(container);
332 - ReactDOM.render(<my-custom-element onInput={eventHandler} />, container);
426 + const root = ReactDOMClient.createRoot(container);
427 + await act(() => {
428 + root.render(<my-custom-element onInput={eventHandler} />);
429 + });
430 const customElement = container.querySelector('my-custom-element');
431 let expectedHandlerCallCount = 0;
432
@@ -340,28 +437,34 @@ describe('DOMPropertyOperations', () => {
437 expect(reactInputEvent.nativeEvent).toBe(inputEvent);
438
439 // Also make sure that removing and re-adding the event listener works
343 - ReactDOM.render(<my-custom-element />, container);
440 + await act(() => {
441 + root.render(<my-custom-element />);
442 + });
443 customElement.dispatchEvent(new Event('input', {bubbles: true}));
444 expect(eventHandler).toHaveBeenCalledTimes(expectedHandlerCallCount);
346 - ReactDOM.render(<my-custom-element onInput={eventHandler} />, container);
445 + await act(() => {
446 + root.render(<my-custom-element onInput={eventHandler} />);
447 + });
448 customElement.dispatchEvent(new Event('input', {bubbles: true}));
449 expectedHandlerCallCount++;
450 expect(eventHandler).toHaveBeenCalledTimes(expectedHandlerCallCount);
451 });
452
453 // @gate enableCustomElementPropertySupport
353 - it('custom elements should have separate onInput and onChange handling', () => {
454 + it('custom elements should have separate onInput and onChange handling', async () => {
455 const container = document.createElement('div');
456 document.body.appendChild(container);
457 + const root = ReactDOMClient.createRoot(container);
458 const inputEventHandler = jest.fn();
459 const changeEventHandler = jest.fn();
358 - ReactDOM.render(
359 - <my-custom-element
360 - onInput={inputEventHandler}
361 - onChange={changeEventHandler}
362 - />,
363 - container,
364 - );
460 + await act(() => {
461 + root.render(
462 + <my-custom-element
463 + onInput={inputEventHandler}
464 + onChange={changeEventHandler}
465 + />,
466 + );
467 + });
468 const customElement = container.querySelector('my-custom-element');
469
470 customElement.dispatchEvent(new Event('input', {bubbles: true}));
@@ -374,34 +477,36 @@ describe('DOMPropertyOperations', () => {
477 });
478
479 // @gate enableCustomElementPropertySupport
377 - it('custom elements should be able to remove and re-add custom event listeners', () => {
480 + it('custom elements should be able to remove and re-add custom event listeners', async () => {
481 const container = document.createElement('div');
482 document.body.appendChild(container);
483 + const root = ReactDOMClient.createRoot(container);
484 const eventHandler = jest.fn();
381 - ReactDOM.render(
382 - <my-custom-element oncustomevent={eventHandler} />,
383 - container,
384 - );
485 + await act(() => {
486 + root.render(<my-custom-element oncustomevent={eventHandler} />);
487 + });
488
489 const customElement = container.querySelector('my-custom-element');
490 customElement.dispatchEvent(new Event('customevent'));
491 expect(eventHandler).toHaveBeenCalledTimes(1);
492
390 - ReactDOM.render(<my-custom-element />, container);
493 + await act(() => {
494 + root.render(<my-custom-element />);
495 + });
496 customElement.dispatchEvent(new Event('customevent'));
497 expect(eventHandler).toHaveBeenCalledTimes(1);
498
394 - ReactDOM.render(
395 - <my-custom-element oncustomevent={eventHandler} />,
396 - container,
397 - );
499 + await act(() => {
500 + root.render(<my-custom-element oncustomevent={eventHandler} />);
501 + });
502 customElement.dispatchEvent(new Event('customevent'));
503 expect(eventHandler).toHaveBeenCalledTimes(2);
504 });
505
402 - it('<input is=...> should have the same onChange/onInput/onClick behavior as <input>', () => {
506 + it('<input is=...> should have the same onChange/onInput/onClick behavior as <input>', async () => {
507 const container = document.createElement('div');
508 document.body.appendChild(container);
509 + const root = ReactDOMClient.createRoot(container);
510 const regularOnInputHandler = jest.fn();
511 const regularOnChangeHandler = jest.fn();
512 const regularOnClickHandler = jest.fn();
@@ -416,22 +521,23 @@ describe('DOMPropertyOperations', () => {
521 customOnChangeHandler.mockClear();
522 customOnClickHandler.mockClear();
523 }
419 - ReactDOM.render(
420 - <div>
421 - <input
422 - onInput={regularOnInputHandler}
423 - onChange={regularOnChangeHandler}
424 - onClick={regularOnClickHandler}
425 - />
426 - <input
427 - is="my-custom-element"
428 - onInput={customOnInputHandler}
429 - onChange={customOnChangeHandler}
430 - onClick={customOnClickHandler}
431 - />
432 - </div>,
433 - container,
434 - );
524 + await act(() => {
525 + root.render(
526 + <div>
527 + <input
528 + onInput={regularOnInputHandler}
529 + onChange={regularOnChangeHandler}
530 + onClick={regularOnClickHandler}
531 + />
532 + <input
533 + is="my-custom-element"
534 + onInput={customOnInputHandler}
535 + onChange={customOnChangeHandler}
536 + onClick={customOnClickHandler}
537 + />
538 + </div>,
539 + );
540 + });
541
542 const regularInput = container.querySelector(
543 'input:not([is=my-custom-element])',
@@ -490,9 +596,10 @@ describe('DOMPropertyOperations', () => {
596 expect(customOnClickHandler).toHaveBeenCalledTimes(0);
597 });
598
493 - it('<input type=radio is=...> should have the same onChange/onInput/onClick behavior as <input type=radio>', () => {
599 + it('<input type=radio is=...> should have the same onChange/onInput/onClick behavior as <input type=radio>', async () => {
600 const container = document.createElement('div');
601 document.body.appendChild(container);
602 + const root = ReactDOMClient.createRoot(container);
603 const regularOnInputHandler = jest.fn();
604 const regularOnChangeHandler = jest.fn();
605 const regularOnClickHandler = jest.fn();
@@ -507,24 +614,25 @@ describe('DOMPropertyOperations', () => {
614 customOnChangeHandler.mockClear();
615 customOnClickHandler.mockClear();
616 }
510 - ReactDOM.render(
511 - <div>
512 - <input
513 - type="radio"
514 - onInput={regularOnInputHandler}
515 - onChange={regularOnChangeHandler}
516 - onClick={regularOnClickHandler}
517 - />
518 - <input
519 - is="my-custom-element"
520 - type="radio"
521 - onInput={customOnInputHandler}
522 - onChange={customOnChangeHandler}
523 - onClick={customOnClickHandler}
524 - />
525 - </div>,
526 - container,
527 - );
617 + await act(() => {
618 + root.render(
619 + <div>
620 + <input
621 + type="radio"
622 + onInput={regularOnInputHandler}
623 + onChange={regularOnChangeHandler}
624 + onClick={regularOnClickHandler}
625 + />
626 + <input
627 + is="my-custom-element"
628 + type="radio"
629 + onInput={customOnInputHandler}
630 + onChange={customOnChangeHandler}
631 + onClick={customOnClickHandler}
632 + />
633 + </div>,
634 + );
635 + });
636
637 const regularInput = container.querySelector(
638 'input:not([is=my-custom-element])',
@@ -572,9 +680,10 @@ describe('DOMPropertyOperations', () => {
680 expect(customOnClickHandler).toHaveBeenCalledTimes(1);
681 });
682
575 - it('<select is=...> should have the same onChange/onInput/onClick behavior as <select>', () => {
683 + it('<select is=...> should have the same onChange/onInput/onClick behavior as <select>', async () => {
684 const container = document.createElement('div');
685 document.body.appendChild(container);
686 + const root = ReactDOMClient.createRoot(container);
687 const regularOnInputHandler = jest.fn();
688 const regularOnChangeHandler = jest.fn();
689 const regularOnClickHandler = jest.fn();
@@ -589,22 +698,23 @@ describe('DOMPropertyOperations', () => {
698 customOnChangeHandler.mockClear();
699 customOnClickHandler.mockClear();
700 }
592 - ReactDOM.render(
593 - <div>
594 - <select
595 - onInput={regularOnInputHandler}
596 - onChange={regularOnChangeHandler}
597 - onClick={regularOnClickHandler}
598 - />
599 - <select
600 - is="my-custom-element"
601 - onInput={customOnInputHandler}
602 - onChange={customOnChangeHandler}
603 - onClick={customOnClickHandler}
604 - />
605 - </div>,
606 - container,
607 - );
701 + await act(() => {
702 + root.render(
703 + <div>
704 + <select
705 + onInput={regularOnInputHandler}
706 + onChange={regularOnChangeHandler}
707 + onClick={regularOnClickHandler}
708 + />
709 + <select
710 + is="my-custom-element"
711 + onInput={customOnInputHandler}
712 + onChange={customOnChangeHandler}
713 + onClick={customOnClickHandler}
714 + />
715 + </div>,
716 + );
717 + });
718
719 const regularSelect = container.querySelector(
720 'select:not([is=my-custom-element])',
@@ -649,9 +759,10 @@ describe('DOMPropertyOperations', () => {
759 });
760
761 // @gate enableCustomElementPropertySupport
652 - it('onChange/onInput/onClick on div with various types of children', () => {
762 + it('onChange/onInput/onClick on div with various types of children', async () => {
763 const container = document.createElement('div');
764 document.body.appendChild(container);
765 + const root = ReactDOMClient.createRoot(container);
766 const onChangeHandler = jest.fn();
767 const onInputHandler = jest.fn();
768 const onClickHandler = jest.fn();
@@ -660,17 +771,18 @@ describe('DOMPropertyOperations', () => {
771 onInputHandler.mockClear();
772 onClickHandler.mockClear();
773 }
663 - ReactDOM.render(
664 - <div
665 - onChange={onChangeHandler}
666 - onInput={onInputHandler}
667 - onClick={onClickHandler}>
668 - <my-custom-element />
669 - <input />
670 - <input is="my-custom-element" />
671 - </div>,
672 - container,
673 - );
774 + await act(() => {
775 + root.render(
776 + <div
777 + onChange={onChangeHandler}
778 + onInput={onInputHandler}
779 + onClick={onClickHandler}>
780 + <my-custom-element />
781 + <input />
782 + <input is="my-custom-element" />
783 + </div>,
784 + );
785 + });
786 const customElement = container.querySelector('my-custom-element');
787 const regularInput = container.querySelector(
788 'input:not([is="my-custom-element"])',
@@ -728,21 +840,23 @@ describe('DOMPropertyOperations', () => {
840 expect(onClickHandler).toBeCalledTimes(1);
841 });
842
731 - it('custom element onChange/onInput/onClick with event target input child', () => {
843 + it('custom element onChange/onInput/onClick with event target input child', async () => {
844 const container = document.createElement('div');
845 document.body.appendChild(container);
846 + const root = ReactDOMClient.createRoot(container);
847 const onChangeHandler = jest.fn();
848 const onInputHandler = jest.fn();
849 const onClickHandler = jest.fn();
737 - ReactDOM.render(
738 - <my-custom-element
739 - onChange={onChangeHandler}
740 - onInput={onInputHandler}
741 - onClick={onClickHandler}>
742 - <input />
743 - </my-custom-element>,
744 - container,
745 - );
850 + await act(() => {
851 + root.render(
852 + <my-custom-element
853 + onChange={onChangeHandler}
854 + onInput={onInputHandler}
855 + onClick={onClickHandler}>
856 + <input />
857 + </my-custom-element>,
858 + );
859 + });
860
861 const input = container.querySelector('input');
862 setUntrackedValue.call(input, 'hello');
@@ -763,21 +877,23 @@ describe('DOMPropertyOperations', () => {
877 expect(onClickHandler).toBeCalledTimes(1);
878 });
879
766 - it('custom element onChange/onInput/onClick with event target div child', () => {
880 + it('custom element onChange/onInput/onClick with event target div child', async () => {
881 const container = document.createElement('div');
882 document.body.appendChild(container);
883 + const root = ReactDOMClient.createRoot(container);
884 const onChangeHandler = jest.fn();
885 const onInputHandler = jest.fn();
886 const onClickHandler = jest.fn();
772 - ReactDOM.render(
773 - <my-custom-element
774 - onChange={onChangeHandler}
775 - onInput={onInputHandler}
776 - onClick={onClickHandler}>
777 - <div />
778 - </my-custom-element>,
779 - container,
780 - );
887 + await act(() => {
888 + root.render(
889 + <my-custom-element
890 + onChange={onChangeHandler}
891 + onInput={onInputHandler}
892 + onClick={onClickHandler}>
893 + <div />
894 + </my-custom-element>,
895 + );
896 + });
897
898 const div = container.querySelector('div');
899 div.dispatchEvent(new Event('input', {bubbles: true}));
@@ -798,21 +914,23 @@ describe('DOMPropertyOperations', () => {
914 expect(onClickHandler).toBeCalledTimes(1);
915 });
916
801 - it('div onChange/onInput/onClick with event target div child', () => {
917 + it('div onChange/onInput/onClick with event target div child', async () => {
918 const container = document.createElement('div');
919 document.body.appendChild(container);
920 + const root = ReactDOMClient.createRoot(container);
921 const onChangeHandler = jest.fn();
922 const onInputHandler = jest.fn();
923 const onClickHandler = jest.fn();
807 - ReactDOM.render(
808 - <div
809 - onChange={onChangeHandler}
810 - onInput={onInputHandler}
811 - onClick={onClickHandler}>
812 - <div />
813 - </div>,
814 - container,
815 - );
924 + await act(() => {
925 + root.render(
926 + <div
927 + onChange={onChangeHandler}
928 + onInput={onInputHandler}
929 + onClick={onClickHandler}>
930 + <div />
931 + </div>,
932 + );
933 + });
934
935 const div = container.querySelector('div > div');
936 div.dispatchEvent(new Event('input', {bubbles: true}));
@@ -834,21 +952,23 @@ describe('DOMPropertyOperations', () => {
952 });
953
954 // @gate enableCustomElementPropertySupport
837 - it('custom element onChange/onInput/onClick with event target custom element child', () => {
955 + it('custom element onChange/onInput/onClick with event target custom element child', async () => {
956 const container = document.createElement('div');
957 document.body.appendChild(container);
958 + const root = ReactDOMClient.createRoot(container);
959 const onChangeHandler = jest.fn();
960 const onInputHandler = jest.fn();
961 const onClickHandler = jest.fn();
843 - ReactDOM.render(
844 - <my-custom-element
845 - onChange={onChangeHandler}
846 - onInput={onInputHandler}
847 - onClick={onClickHandler}>
848 - <other-custom-element />
849 - </my-custom-element>,
850 - container,
851 - );
962 + await act(() => {
963 + root.render(
964 + <my-custom-element
965 + onChange={onChangeHandler}
966 + onInput={onInputHandler}
967 + onClick={onClickHandler}>
968 + <other-custom-element />
969 + </my-custom-element>,
970 + );
971 + });
972
973 const customChild = container.querySelector('other-custom-element');
974 customChild.dispatchEvent(new Event('input', {bubbles: true}));
@@ -868,7 +988,7 @@ describe('DOMPropertyOperations', () => {
988 });
989
990 // @gate enableCustomElementPropertySupport
871 - it('custom elements should allow custom events with capture event listeners', () => {
991 + it('custom elements should allow custom events with capture event listeners', async () => {
992 const oncustomeventCapture = jest.fn();
993 const oncustomevent = jest.fn();
994 function Test() {
@@ -881,7 +1001,10 @@ describe('DOMPropertyOperations', () => {
1001 );
1002 }
1003 const container = document.createElement('div');
884 - ReactDOM.render(<Test />, container);
1004 + const root = ReactDOMClient.createRoot(container);
1005 + await act(() => {
1006 + root.render(<Test />);
1007 + });
1008 container
1009 .querySelector('my-custom-element > div')
1010 .dispatchEvent(new Event('customevent', {bubbles: false}));
@@ -889,106 +1012,148 @@ describe('DOMPropertyOperations', () => {
1012 expect(oncustomevent).toHaveBeenCalledTimes(0);
1013 });
1014
892 - it('innerHTML should not work on custom elements', () => {
1015 + it('innerHTML should not work on custom elements', async () => {
1016 const container = document.createElement('div');
894 - ReactDOM.render(<my-custom-element innerHTML="foo" />, container);
1017 + const root = ReactDOMClient.createRoot(container);
1018 + await act(() => {
1019 + root.render(<my-custom-element innerHTML="foo" />);
1020 + });
1021 const customElement = container.querySelector('my-custom-element');
1022 expect(customElement.getAttribute('innerHTML')).toBe(null);
1023 expect(customElement.hasChildNodes()).toBe(false);
1024
1025 // Render again to verify the update codepath doesn't accidentally let
1026 // something through.
901 - ReactDOM.render(<my-custom-element innerHTML="bar" />, container);
1027 + await act(() => {
1028 + root.render(<my-custom-element innerHTML="bar" />);
1029 + });
1030 expect(customElement.getAttribute('innerHTML')).toBe(null);
1031 expect(customElement.hasChildNodes()).toBe(false);
1032 });
1033
1034 // @gate enableCustomElementPropertySupport
907 - it('innerText should not work on custom elements', () => {
1035 + it('innerText should not work on custom elements', async () => {
1036 const container = document.createElement('div');
909 - ReactDOM.render(<my-custom-element innerText="foo" />, container);
1037 + const root = ReactDOMClient.createRoot(container);
1038 + await act(() => {
1039 + root.render(<my-custom-element innerText="foo" />);
1040 + });
1041 const customElement = container.querySelector('my-custom-element');
1042 expect(customElement.getAttribute('innerText')).toBe(null);
1043 expect(customElement.hasChildNodes()).toBe(false);
1044
1045 // Render again to verify the update codepath doesn't accidentally let
1046 // something through.
916 - ReactDOM.render(<my-custom-element innerText="bar" />, container);
1047 + await act(() => {
1048 + root.render(<my-custom-element innerText="bar" />);
1049 + });
1050 expect(customElement.getAttribute('innerText')).toBe(null);
1051 expect(customElement.hasChildNodes()).toBe(false);
1052 });
1053
1054 // @gate enableCustomElementPropertySupport
922 - it('textContent should not work on custom elements', () => {
1055 + it('textContent should not work on custom elements', async () => {
1056 const container = document.createElement('div');
924 - ReactDOM.render(<my-custom-element textContent="foo" />, container);
1057 + const root = ReactDOMClient.createRoot(container);
1058 + await act(() => {
1059 + root.render(<my-custom-element textContent="foo" />);
1060 + });
1061 const customElement = container.querySelector('my-custom-element');
1062 expect(customElement.getAttribute('textContent')).toBe(null);
1063 expect(customElement.hasChildNodes()).toBe(false);
1064
1065 // Render again to verify the update codepath doesn't accidentally let
1066 // something through.
931 - ReactDOM.render(<my-custom-element textContent="bar" />, container);
1067 + await act(() => {
1068 + root.render(<my-custom-element textContent="bar" />);
1069 + });
1070 expect(customElement.getAttribute('textContent')).toBe(null);
1071 expect(customElement.hasChildNodes()).toBe(false);
1072 });
1073
1074 // @gate enableCustomElementPropertySupport
937 - it('values should not be converted to booleans when assigning into custom elements', () => {
1075 + it('values should not be converted to booleans when assigning into custom elements', async () => {
1076 const container = document.createElement('div');
1077 document.body.appendChild(container);
940 - ReactDOM.render(<my-custom-element />, container);
1078 + const root = ReactDOMClient.createRoot(container);
1079 + await act(() => {
1080 + root.render(<my-custom-element />);
1081 + });
1082 const customElement = container.querySelector('my-custom-element');
1083 customElement.foo = null;
1084
1085 // true => string
945 - ReactDOM.render(<my-custom-element foo={true} />, container);
1086 + await act(() => {
1087 + root.render(<my-custom-element foo={true} />);
1088 + });
1089 expect(customElement.foo).toBe(true);
947 - ReactDOM.render(<my-custom-element foo="bar" />, container);
1090 + await act(() => {
1091 + root.render(<my-custom-element foo="bar" />);
1092 + });
1093 expect(customElement.foo).toBe('bar');
1094
1095 // false => string
951 - ReactDOM.render(<my-custom-element foo={false} />, container);
1096 + await act(() => {
1097 + root.render(<my-custom-element foo={false} />);
1098 + });
1099 expect(customElement.foo).toBe(false);
953 - ReactDOM.render(<my-custom-element foo="bar" />, container);
1100 + await act(() => {
1101 + root.render(<my-custom-element foo="bar" />);
1102 + });
1103 expect(customElement.foo).toBe('bar');
1104
1105 // true => null
957 - ReactDOM.render(<my-custom-element foo={true} />, container);
1106 + await act(() => {
1107 + root.render(<my-custom-element foo={true} />);
1108 + });
1109 expect(customElement.foo).toBe(true);
959 - ReactDOM.render(<my-custom-element foo={null} />, container);
1110 + await act(() => {
1111 + root.render(<my-custom-element foo={null} />);
1112 + });
1113 expect(customElement.foo).toBe(null);
1114
1115 // false => null
963 - ReactDOM.render(<my-custom-element foo={false} />, container);
1116 + await act(() => {
1117 + root.render(<my-custom-element foo={false} />);
1118 + });
1119 expect(customElement.foo).toBe(false);
965 - ReactDOM.render(<my-custom-element foo={null} />, container);
1120 + await act(() => {
1121 + root.render(<my-custom-element foo={null} />);
1122 + });
1123 expect(customElement.foo).toBe(null);
1124 });
1125
1126 // @gate enableCustomElementPropertySupport
970 - it('boolean props should not be stringified in attributes', () => {
1127 + it('boolean props should not be stringified in attributes', async () => {
1128 const container = document.createElement('div');
1129 document.body.appendChild(container);
973 - ReactDOM.render(<my-custom-element foo={true} />, container);
1130 + const root = ReactDOMClient.createRoot(container);
1131 + await act(() => {
1132 + root.render(<my-custom-element foo={true} />);
1133 + });
1134 const customElement = container.querySelector('my-custom-element');
1135
1136 expect(customElement.getAttribute('foo')).toBe('');
1137
1138 // true => false
979 - ReactDOM.render(<my-custom-element foo={false} />, container);
1139 + await act(() => {
1140 + root.render(<my-custom-element foo={false} />);
1141 + });
1142
1143 expect(customElement.getAttribute('foo')).toBe(null);
1144 });
1145
1146 // @gate enableCustomElementPropertySupport
985 - it('custom element custom event handlers assign multiple types', () => {
1147 + it('custom element custom event handlers assign multiple types', async () => {
1148 const container = document.createElement('div');
1149 document.body.appendChild(container);
1150 + const root = ReactDOMClient.createRoot(container);
1151 const oncustomevent = jest.fn();
1152
1153 // First render with string
991 - ReactDOM.render(<my-custom-element oncustomevent={'foo'} />, container);
1154 + await act(() => {
1155 + root.render(<my-custom-element oncustomevent={'foo'} />);
1156 + });
1157 const customelement = container.querySelector('my-custom-element');
1158 customelement.dispatchEvent(new Event('customevent'));
1159 expect(oncustomevent).toHaveBeenCalledTimes(0);
@@ -996,34 +1161,36 @@ describe('DOMPropertyOperations', () => {
1161 expect(customelement.getAttribute('oncustomevent')).toBe('foo');
1162
1163 // string => event listener
999 - ReactDOM.render(
1000 - <my-custom-element oncustomevent={oncustomevent} />,
1001 - container,
1002 - );
1164 + await act(() => {
1165 + root.render(<my-custom-element oncustomevent={oncustomevent} />);
1166 + });
1167 customelement.dispatchEvent(new Event('customevent'));
1168 expect(oncustomevent).toHaveBeenCalledTimes(1);
1169 expect(customelement.oncustomevent).toBe(undefined);
1170 expect(customelement.getAttribute('oncustomevent')).toBe(null);
1171
1172 // event listener => string
1009 - ReactDOM.render(<my-custom-element oncustomevent={'foo'} />, container);
1173 + await act(() => {
1174 + root.render(<my-custom-element oncustomevent={'foo'} />);
1175 + });
1176 customelement.dispatchEvent(new Event('customevent'));
1177 expect(oncustomevent).toHaveBeenCalledTimes(1);
1178 expect(customelement.oncustomevent).toBe(undefined);
1179 expect(customelement.getAttribute('oncustomevent')).toBe('foo');
1180
1181 // string => nothing
1016 - ReactDOM.render(<my-custom-element />, container);
1182 + await act(() => {
1183 + root.render(<my-custom-element />);
1184 + });
1185 customelement.dispatchEvent(new Event('customevent'));
1186 expect(oncustomevent).toHaveBeenCalledTimes(1);
1187 expect(customelement.oncustomevent).toBe(undefined);
1188 expect(customelement.getAttribute('oncustomevent')).toBe(null);
1189
1190 // nothing => event listener
1023 - ReactDOM.render(
1024 - <my-custom-element oncustomevent={oncustomevent} />,
1025 - container,
1026 - );
1191 + await act(() => {
1192 + root.render(<my-custom-element oncustomevent={oncustomevent} />);
1193 + });
1194 customelement.dispatchEvent(new Event('customevent'));
1195 expect(oncustomevent).toHaveBeenCalledTimes(2);
1196 expect(customelement.oncustomevent).toBe(undefined);
@@ -1031,13 +1198,16 @@ describe('DOMPropertyOperations', () => {
1198 });
1199
1200 // @gate enableCustomElementPropertySupport
1034 - it('custom element custom event handlers assign multiple types with setter', () => {
1201 + it('custom element custom event handlers assign multiple types with setter', async () => {
1202 const container = document.createElement('div');
1203 document.body.appendChild(container);
1204 + const root = ReactDOMClient.createRoot(container);
1205 const oncustomevent = jest.fn();
1206
1207 // First render with nothing
1040 - ReactDOM.render(<my-custom-element />, container);
1208 + await act(() => {
1209 + root.render(<my-custom-element />);
1210 + });
1211 const customelement = container.querySelector('my-custom-element');
1212 // Install a setter to activate the `in` heuristic
1213 Object.defineProperty(customelement, 'oncustomevent', {
@@ -1051,34 +1221,36 @@ describe('DOMPropertyOperations', () => {
1221 expect(customelement.oncustomevent).toBe(undefined);
1222
1223 // nothing => event listener
1054 - ReactDOM.render(
1055 - <my-custom-element oncustomevent={oncustomevent} />,
1056 - container,
1057 - );
1224 + await act(() => {
1225 + root.render(<my-custom-element oncustomevent={oncustomevent} />);
1226 + });
1227 customelement.dispatchEvent(new Event('customevent'));
1228 expect(oncustomevent).toHaveBeenCalledTimes(1);
1229 expect(customelement.oncustomevent).toBe(null);
1230 expect(customelement.getAttribute('oncustomevent')).toBe(null);
1231
1232 // event listener => string
1064 - ReactDOM.render(<my-custom-element oncustomevent={'foo'} />, container);
1233 + await act(() => {
1234 + root.render(<my-custom-element oncustomevent={'foo'} />);
1235 + });
1236 customelement.dispatchEvent(new Event('customevent'));
1237 expect(oncustomevent).toHaveBeenCalledTimes(1);
1238 expect(customelement.oncustomevent).toBe('foo');
1239 expect(customelement.getAttribute('oncustomevent')).toBe(null);
1240
1241 // string => event listener
1071 - ReactDOM.render(
1072 - <my-custom-element oncustomevent={oncustomevent} />,
1073 - container,
1074 - );
1242 + await act(() => {
1243 + root.render(<my-custom-element oncustomevent={oncustomevent} />);
1244 + });
1245 customelement.dispatchEvent(new Event('customevent'));
1246 expect(oncustomevent).toHaveBeenCalledTimes(2);
1247 expect(customelement.oncustomevent).toBe(null);
1248 expect(customelement.getAttribute('oncustomevent')).toBe(null);
1249
1250 // event listener => nothing
1081 - ReactDOM.render(<my-custom-element />, container);
1251 + await act(() => {
1252 + root.render(<my-custom-element />);
1253 + });
1254 customelement.dispatchEvent(new Event('customevent'));
1255 expect(oncustomevent).toHaveBeenCalledTimes(2);
1256 expect(customelement.oncustomevent).toBe(null);
@@ -1086,10 +1258,13 @@ describe('DOMPropertyOperations', () => {
1258 });
1259
1260 // @gate enableCustomElementPropertySupport
1089 - it('assigning to a custom element property should not remove attributes', () => {
1261 + it('assigning to a custom element property should not remove attributes', async () => {
1262 const container = document.createElement('div');
1263 document.body.appendChild(container);
1092 - ReactDOM.render(<my-custom-element foo="one" />, container);
1264 + const root = ReactDOMClient.createRoot(container);
1265 + await act(() => {
1266 + root.render(<my-custom-element foo="one" />);
1267 + });
1268 const customElement = container.querySelector('my-custom-element');
1269 expect(customElement.getAttribute('foo')).toBe('one');
1270
@@ -1102,16 +1277,21 @@ describe('DOMPropertyOperations', () => {
1277 return this._foo;
1278 },
1279 });
1105 - ReactDOM.render(<my-custom-element foo="two" />, container);
1280 + await act(() => {
1281 + root.render(<my-custom-element foo="two" />);
1282 + });
1283 expect(customElement.foo).toBe('two');
1284 expect(customElement.getAttribute('foo')).toBe('one');
1285 });
1286
1287 // @gate enableCustomElementPropertySupport
1111 - it('custom element properties should accept functions', () => {
1288 + it('custom element properties should accept functions', async () => {
1289 const container = document.createElement('div');
1290 document.body.appendChild(container);
1114 - ReactDOM.render(<my-custom-element />, container);
1291 + const root = ReactDOMClient.createRoot(container);
1292 + await act(() => {
1293 + root.render(<my-custom-element />);
1294 + });
1295 const customElement = container.querySelector('my-custom-element');
1296
1297 // Install a setter to activate the `in` heuristic
@@ -1126,44 +1306,56 @@ describe('DOMPropertyOperations', () => {
1306 function myFunction() {
1307 return 'this is myFunction';
1308 }
1129 - ReactDOM.render(<my-custom-element foo={myFunction} />, container);
1309 + await act(() => {
1310 + root.render(<my-custom-element foo={myFunction} />);
1311 + });
1312 expect(customElement.foo).toBe(myFunction);
1313
1314 // Also remove and re-add the property for good measure
1133 - ReactDOM.render(<my-custom-element />, container);
1315 + await act(() => {
1316 + root.render(<my-custom-element />);
1317 + });
1318 expect(customElement.foo).toBe(null);
1135 - ReactDOM.render(<my-custom-element foo={myFunction} />, container);
1319 + await act(() => {
1320 + root.render(<my-custom-element foo={myFunction} />);
1321 + });
1322 expect(customElement.foo).toBe(myFunction);
1323 });
1324 });
1325
1326 describe('deleteValueForProperty', () => {
1141 - it('should remove attributes for normal properties', () => {
1327 + it('should remove attributes for normal properties', async () => {
1328 const container = document.createElement('div');
1143 - ReactDOM.render(<div title="foo" />, container);
1329 + const root = ReactDOMClient.createRoot(container);
1330 + await act(() => {
1331 + root.render(<div title="foo" />);
1332 + });
1333 expect(container.firstChild.getAttribute('title')).toBe('foo');
1145 - ReactDOM.render(<div />, container);
1334 + await act(() => {
1335 + root.render(<div />);
1336 + });
1337 expect(container.firstChild.getAttribute('title')).toBe(null);
1338 });
1339
1149 - it('should not remove attributes for special properties', () => {
1340 + it('should not remove attributes for special properties', async () => {
1341 const container = document.createElement('div');
1151 - ReactDOM.render(
1152 - <input type="text" value="foo" onChange={function () {}} />,
1153 - container,
1154 - );
1342 + const root = ReactDOMClient.createRoot(container);
1343 + await act(() => {
1344 + root.render(
1345 + <input type="text" value="foo" onChange={function () {}} />,
1346 + );
1347 + });
1348 if (disableInputAttributeSyncing) {
1349 expect(container.firstChild.hasAttribute('value')).toBe(false);
1350 } else {
1351 expect(container.firstChild.getAttribute('value')).toBe('foo');
1352 }
1353 expect(container.firstChild.value).toBe('foo');
1161 - expect(() =>
1162 - ReactDOM.render(
1163 - <input type="text" onChange={function () {}} />,
1164 - container,
1165 - ),
1166 - ).toErrorDev(
1354 + await expect(async () => {
1355 + await act(() => {
1356 + root.render(<input type="text" onChange={function () {}} />);
1357 + });
1358 + }).toErrorDev(
1359 'A component is changing a controlled input to be uncontrolled',
1360 );
1361 if (disableInputAttributeSyncing) {
@@ -1174,9 +1366,12 @@ describe('DOMPropertyOperations', () => {
1366 expect(container.firstChild.value).toBe('foo');
1367 });
1368
1177 - it('should not remove attributes for custom component tag', () => {
1369 + it('should not remove attributes for custom component tag', async () => {
1370 const container = document.createElement('div');
1179 - ReactDOM.render(<my-icon size="5px" />, container);
1371 + const root = ReactDOMClient.createRoot(container);
1372 + await act(() => {
1373 + root.render(<my-icon size="5px" />);
1374 + });
1375 expect(container.firstChild.getAttribute('size')).toBe('5px');
1376 });
1377 });