@samitouri / QOS-React-2 / commits / 93a5f40cbf

Convert ReactFresh to createRoot (#28170)

Sebastian Silbermann committed Feb 4, 2024 at 11:09 UTC 93a5f40cbf05a6efb63a78748d238605dfe35e0c
1 file changed +372 -326
packages/react-refresh/src/__tests__/ReactFresh-test.js
+372 -326
@@ -17,13 +17,13 @@ let ReactDOMClient;
17 let ReactFreshRuntime;
18 let Scheduler;
19 let act;
20 -let internalAct;
20 let createReactClass;
21 let waitFor;
22 let assertLog;
23
24 describe('ReactFresh', () => {
25 let container;
26 + let root;
27
28 beforeEach(() => {
29 if (__DEV__) {
@@ -34,8 +34,7 @@ describe('ReactFresh', () => {
34 ReactDOM = require('react-dom');
35 ReactDOMClient = require('react-dom/client');
36 Scheduler = require('scheduler');
37 - act = React.act;
38 - internalAct = require('internal-test-utils').act;
37 + act = require('internal-test-utils').act;
38
39 const InternalTestUtils = require('internal-test-utils');
40 waitFor = InternalTestUtils.waitFor;
@@ -47,6 +46,7 @@ describe('ReactFresh', () => {
46 new React.Component().updater,
47 );
48 container = document.createElement('div');
49 + root = ReactDOMClient.createRoot(container);
50 document.body.appendChild(container);
51 }
52 });
@@ -63,10 +63,10 @@ describe('ReactFresh', () => {
63 return Component;
64 }
65
66 - function render(version, props) {
66 + async function render(version, props) {
67 const Component = version();
68 - act(() => {
69 - ReactDOM.render(<Component {...props} />, container);
68 + await act(() => {
69 + root.render(<Component {...props} />);
70 });
71 return Component;
72 }
@@ -99,9 +99,9 @@ describe('ReactFresh', () => {
99 );
100 }
101
102 - it('can preserve state for compatible types', () => {
102 + it('can preserve state for compatible types', async () => {
103 if (__DEV__) {
104 - const HelloV1 = render(() => {
104 + const HelloV1 = await render(() => {
105 function Hello() {
106 const [val, setVal] = React.useState(0);
107 return (
@@ -118,7 +118,7 @@ describe('ReactFresh', () => {
118 const el = container.firstChild;
119 expect(el.textContent).toBe('0');
120 expect(el.style.color).toBe('blue');
121 - act(() => {
121 + await act(() => {
122 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
123 });
124 expect(el.textContent).toBe('1');
@@ -143,7 +143,7 @@ describe('ReactFresh', () => {
143 expect(el.style.color).toBe('red');
144
145 // Bump the state again.
146 - act(() => {
146 + await act(() => {
147 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
148 });
149 expect(container.firstChild).toBe(el);
@@ -153,15 +153,15 @@ describe('ReactFresh', () => {
153 // Perform top-down renders with both fresh and stale types.
154 // Neither should change the state or color.
155 // They should always resolve to the latest version.
156 - render(() => HelloV1);
157 - render(() => HelloV2);
158 - render(() => HelloV1);
156 + await render(() => HelloV1);
157 + await render(() => HelloV2);
158 + await render(() => HelloV1);
159 expect(container.firstChild).toBe(el);
160 expect(el.textContent).toBe('2');
161 expect(el.style.color).toBe('red');
162
163 // Bump the state again.
164 - act(() => {
164 + await act(() => {
165 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
166 });
167 expect(container.firstChild).toBe(el);
@@ -169,7 +169,7 @@ describe('ReactFresh', () => {
169 expect(el.style.color).toBe('red');
170
171 // Finally, a render with incompatible type should reset it.
172 - render(() => {
172 + await render(() => {
173 function Hello() {
174 const [val, setVal] = React.useState(0);
175 return (
@@ -189,9 +189,9 @@ describe('ReactFresh', () => {
189 }
190 });
191
192 - it('can preserve state for forwardRef', () => {
192 + it('can preserve state for forwardRef', async () => {
193 if (__DEV__) {
194 - const OuterV1 = render(() => {
194 + const OuterV1 = await render(() => {
195 function Hello() {
196 const [val, setVal] = React.useState(0);
197 return (
@@ -211,7 +211,7 @@ describe('ReactFresh', () => {
211 const el = container.firstChild;
212 expect(el.textContent).toBe('0');
213 expect(el.style.color).toBe('blue');
214 - act(() => {
214 + await act(() => {
215 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
216 });
217 expect(el.textContent).toBe('1');
@@ -239,7 +239,7 @@ describe('ReactFresh', () => {
239 expect(el.style.color).toBe('red');
240
241 // Bump the state again.
242 - act(() => {
242 + await act(() => {
243 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
244 });
245 expect(container.firstChild).toBe(el);
@@ -249,15 +249,15 @@ describe('ReactFresh', () => {
249 // Perform top-down renders with both fresh and stale types.
250 // Neither should change the state or color.
251 // They should always resolve to the latest version.
252 - render(() => OuterV1);
253 - render(() => OuterV2);
254 - render(() => OuterV1);
252 + await render(() => OuterV1);
253 + await render(() => OuterV2);
254 + await render(() => OuterV1);
255 expect(container.firstChild).toBe(el);
256 expect(el.textContent).toBe('2');
257 expect(el.style.color).toBe('red');
258
259 // Finally, a render with incompatible type should reset it.
260 - render(() => {
260 + await render(() => {
261 function Hello() {
262 const [val, setVal] = React.useState(0);
263 return (
@@ -279,9 +279,9 @@ describe('ReactFresh', () => {
279 }
280 });
281
282 - it('should not consider two forwardRefs around the same type to be equivalent', () => {
282 + it('should not consider two forwardRefs around the same type to be equivalent', async () => {
283 if (__DEV__) {
284 - const ParentV1 = render(
284 + const ParentV1 = await render(
285 () => {
286 function Hello() {
287 const [val, setVal] = React.useState(0);
@@ -317,32 +317,32 @@ describe('ReactFresh', () => {
317 let el = container.firstChild;
318 expect(el.textContent).toBe('0');
319 expect(el.style.color).toBe('blue');
320 - act(() => {
320 + await act(() => {
321 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
322 });
323 expect(el.textContent).toBe('1');
324
325 // Switching up the inner types should reset the state.
326 - render(() => ParentV1, {cond: false});
326 + await render(() => ParentV1, {cond: false});
327 expect(el).not.toBe(container.firstChild);
328 el = container.firstChild;
329 expect(el.textContent).toBe('0');
330 expect(el.style.color).toBe('blue');
331
332 - act(() => {
332 + await act(() => {
333 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
334 });
335 expect(el.textContent).toBe('1');
336
337 // Switch them up back again.
338 - render(() => ParentV1, {cond: true});
338 + await render(() => ParentV1, {cond: true});
339 expect(el).not.toBe(container.firstChild);
340 el = container.firstChild;
341 expect(el.textContent).toBe('0');
342 expect(el.style.color).toBe('blue');
343
344 // Now bump up the state to prepare for patching.
345 - act(() => {
345 + await act(() => {
346 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
347 });
348 expect(el.textContent).toBe('1');
@@ -383,14 +383,14 @@ describe('ReactFresh', () => {
383 expect(el.style.color).toBe('red');
384
385 // Switching up the condition should still reset the state.
386 - render(() => ParentV2, {cond: false});
386 + await render(() => ParentV2, {cond: false});
387 expect(el).not.toBe(container.firstChild);
388 el = container.firstChild;
389 expect(el.textContent).toBe('0');
390 expect(el.style.color).toBe('red');
391
392 // Now bump up the state to prepare for top-level renders.
393 - act(() => {
393 + await act(() => {
394 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
395 });
396 expect(el).toBe(container.firstChild);
@@ -398,18 +398,18 @@ describe('ReactFresh', () => {
398 expect(el.style.color).toBe('red');
399
400 // Finally, verify using top-level render with stale type keeps state.
401 - render(() => ParentV1);
402 - render(() => ParentV2);
403 - render(() => ParentV1);
401 + await render(() => ParentV1);
402 + await render(() => ParentV2);
403 + await render(() => ParentV1);
404 expect(container.firstChild).toBe(el);
405 expect(el.textContent).toBe('1');
406 expect(el.style.color).toBe('red');
407 }
408 });
409
410 - it('can update forwardRef render function with its wrapper', () => {
410 + it('can update forwardRef render function with its wrapper', async () => {
411 if (__DEV__) {
412 - render(() => {
412 + await render(() => {
413 function Hello({color}) {
414 const [val, setVal] = React.useState(0);
415 return (
@@ -429,7 +429,7 @@ describe('ReactFresh', () => {
429 const el = container.firstChild;
430 expect(el.textContent).toBe('0');
431 expect(el.style.color).toBe('blue');
432 - act(() => {
432 + await act(() => {
433 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
434 });
435 expect(el.textContent).toBe('1');
@@ -458,9 +458,9 @@ describe('ReactFresh', () => {
458 }
459 });
460
461 - it('can update forwardRef render function in isolation', () => {
461 + it('can update forwardRef render function in isolation', async () => {
462 if (__DEV__) {
463 - render(() => {
463 + await render(() => {
464 function Hello({color}) {
465 const [val, setVal] = React.useState(0);
466 return (
@@ -483,7 +483,7 @@ describe('ReactFresh', () => {
483 const el = container.firstChild;
484 expect(el.textContent).toBe('0');
485 expect(el.style.color).toBe('blue');
486 - act(() => {
486 + await act(() => {
487 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
488 });
489 expect(el.textContent).toBe('1');
@@ -515,9 +515,9 @@ describe('ReactFresh', () => {
515 }
516 });
517
518 - it('can preserve state for simple memo', () => {
518 + it('can preserve state for simple memo', async () => {
519 if (__DEV__) {
520 - const OuterV1 = render(() => {
520 + const OuterV1 = await render(() => {
521 function Hello() {
522 const [val, setVal] = React.useState(0);
523 return (
@@ -537,7 +537,7 @@ describe('ReactFresh', () => {
537 const el = container.firstChild;
538 expect(el.textContent).toBe('0');
539 expect(el.style.color).toBe('blue');
540 - act(() => {
540 + await act(() => {
541 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
542 });
543 expect(el.textContent).toBe('1');
@@ -565,7 +565,7 @@ describe('ReactFresh', () => {
565 expect(el.style.color).toBe('red');
566
567 // Bump the state again.
568 - act(() => {
568 + await act(() => {
569 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
570 });
571 expect(container.firstChild).toBe(el);
@@ -575,15 +575,15 @@ describe('ReactFresh', () => {
575 // Perform top-down renders with both fresh and stale types.
576 // Neither should change the state or color.
577 // They should always resolve to the latest version.
578 - render(() => OuterV1);
579 - render(() => OuterV2);
580 - render(() => OuterV1);
578 + await render(() => OuterV1);
579 + await render(() => OuterV2);
580 + await render(() => OuterV1);
581 expect(container.firstChild).toBe(el);
582 expect(el.textContent).toBe('2');
583 expect(el.style.color).toBe('red');
584
585 // Finally, a render with incompatible type should reset it.
586 - render(() => {
586 + await render(() => {
587 function Hello() {
588 const [val, setVal] = React.useState(0);
589 return (
@@ -605,9 +605,9 @@ describe('ReactFresh', () => {
605 }
606 });
607
608 - it('can preserve state for memo with custom comparison', () => {
608 + it('can preserve state for memo with custom comparison', async () => {
609 if (__DEV__) {
610 - const OuterV1 = render(() => {
610 + const OuterV1 = await render(() => {
611 function Hello() {
612 const [val, setVal] = React.useState(0);
613 return (
@@ -626,7 +626,7 @@ describe('ReactFresh', () => {
626 const el = container.firstChild;
627 expect(el.textContent).toBe('0');
628 expect(el.style.color).toBe('blue');
629 - act(() => {
629 + await act(() => {
630 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
631 });
632 expect(el.textContent).toBe('1');
@@ -653,7 +653,7 @@ describe('ReactFresh', () => {
653 expect(el.style.color).toBe('red');
654
655 // Bump the state again.
656 - act(() => {
656 + await act(() => {
657 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
658 });
659 expect(container.firstChild).toBe(el);
@@ -663,15 +663,15 @@ describe('ReactFresh', () => {
663 // Perform top-down renders with both fresh and stale types.
664 // Neither should change the state or color.
665 // They should always resolve to the latest version.
666 - render(() => OuterV1);
667 - render(() => OuterV2);
668 - render(() => OuterV1);
666 + await render(() => OuterV1);
667 + await render(() => OuterV2);
668 + await render(() => OuterV1);
669 expect(container.firstChild).toBe(el);
670 expect(el.textContent).toBe('2');
671 expect(el.style.color).toBe('red');
672
673 // Finally, a render with incompatible type should reset it.
674 - render(() => {
674 + await render(() => {
675 function Hello() {
676 const [val, setVal] = React.useState(0);
677 return (
@@ -693,9 +693,9 @@ describe('ReactFresh', () => {
693 }
694 });
695
696 - it('can update simple memo function in isolation', () => {
696 + it('can update simple memo function in isolation', async () => {
697 if (__DEV__) {
698 - render(() => {
698 + await render(() => {
699 function Hello() {
700 const [val, setVal] = React.useState(0);
701 return (
@@ -713,7 +713,7 @@ describe('ReactFresh', () => {
713 const el = container.firstChild;
714 expect(el.textContent).toBe('0');
715 expect(el.style.color).toBe('blue');
716 - act(() => {
716 + await act(() => {
717 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
718 });
719 expect(el.textContent).toBe('1');
@@ -740,9 +740,9 @@ describe('ReactFresh', () => {
740 }
741 });
742
743 - it('can preserve state for memo(forwardRef)', () => {
743 + it('can preserve state for memo(forwardRef)', async () => {
744 if (__DEV__) {
745 - const OuterV1 = render(() => {
745 + const OuterV1 = await render(() => {
746 function Hello() {
747 const [val, setVal] = React.useState(0);
748 return (
@@ -762,7 +762,7 @@ describe('ReactFresh', () => {
762 const el = container.firstChild;
763 expect(el.textContent).toBe('0');
764 expect(el.style.color).toBe('blue');
765 - act(() => {
765 + await act(() => {
766 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
767 });
768 expect(el.textContent).toBe('1');
@@ -790,7 +790,7 @@ describe('ReactFresh', () => {
790 expect(el.style.color).toBe('red');
791
792 // Bump the state again.
793 - act(() => {
793 + await act(() => {
794 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
795 });
796 expect(container.firstChild).toBe(el);
@@ -800,15 +800,15 @@ describe('ReactFresh', () => {
800 // Perform top-down renders with both fresh and stale types.
801 // Neither should change the state or color.
802 // They should always resolve to the latest version.
803 - render(() => OuterV1);
804 - render(() => OuterV2);
805 - render(() => OuterV1);
803 + await render(() => OuterV1);
804 + await render(() => OuterV2);
805 + await render(() => OuterV1);
806 expect(container.firstChild).toBe(el);
807 expect(el.textContent).toBe('2');
808 expect(el.style.color).toBe('red');
809
810 // Finally, a render with incompatible type should reset it.
811 - render(() => {
811 + await render(() => {
812 function Hello() {
813 const [val, setVal] = React.useState(0);
814 return (
@@ -832,7 +832,8 @@ describe('ReactFresh', () => {
832
833 it('can preserve state for lazy after resolution', async () => {
834 if (__DEV__) {
835 - const AppV1 = render(() => {
835 + let resolve;
836 + const AppV1 = await render(() => {
837 function Hello() {
838 const [val, setVal] = React.useState(0);
839 return (
@@ -845,8 +846,8 @@ describe('ReactFresh', () => {
846
847 const Outer = React.lazy(
848 () =>
848 - new Promise(resolve => {
849 - setTimeout(() => resolve({default: Hello}), 100);
849 + new Promise(_resolve => {
850 + resolve = () => _resolve({default: Hello});
851 }),
852 );
853 $RefreshReg$(Outer, 'Outer');
@@ -865,7 +866,7 @@ describe('ReactFresh', () => {
866
867 expect(container.textContent).toBe('Loading');
868 await act(() => {
868 - jest.runAllTimers();
869 + resolve();
870 });
871 expect(container.textContent).toBe('0');
872
@@ -873,7 +874,7 @@ describe('ReactFresh', () => {
874 const el = container.firstChild;
875 expect(el.textContent).toBe('0');
876 expect(el.style.color).toBe('blue');
876 - act(() => {
877 + await act(() => {
878 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
879 });
880 expect(el.textContent).toBe('1');
@@ -892,8 +893,8 @@ describe('ReactFresh', () => {
893
894 const Outer = React.lazy(
895 () =>
895 - new Promise(resolve => {
896 - setTimeout(() => resolve({default: Hello}), 100);
896 + new Promise(_resolve => {
897 + resolve = () => _resolve({default: Hello});
898 }),
899 );
900 $RefreshReg$(Outer, 'Outer');
@@ -916,7 +917,7 @@ describe('ReactFresh', () => {
917 expect(el.style.color).toBe('red');
918
919 // Bump the state again.
919 - act(() => {
920 + await act(() => {
921 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
922 });
923 expect(container.firstChild).toBe(el);
@@ -926,15 +927,15 @@ describe('ReactFresh', () => {
927 // Perform top-down renders with both fresh and stale types.
928 // Neither should change the state or color.
929 // They should always resolve to the latest version.
929 - render(() => AppV1);
930 - render(() => AppV2);
931 - render(() => AppV1);
930 + await render(() => AppV1);
931 + await render(() => AppV2);
932 + await render(() => AppV1);
933 expect(container.firstChild).toBe(el);
934 expect(el.textContent).toBe('2');
935 expect(el.style.color).toBe('red');
936
937 // Finally, a render with incompatible type should reset it.
937 - render(() => {
938 + await render(() => {
939 function Hello() {
940 const [val, setVal] = React.useState(0);
941 return (
@@ -968,7 +969,8 @@ describe('ReactFresh', () => {
969
970 it('can patch lazy before resolution', async () => {
971 if (__DEV__) {
971 - render(() => {
972 + let resolve;
973 + await render(() => {
974 function Hello() {
975 const [val, setVal] = React.useState(0);
976 return (
@@ -981,8 +983,8 @@ describe('ReactFresh', () => {
983
984 const Outer = React.lazy(
985 () =>
984 - new Promise(resolve => {
985 - setTimeout(() => resolve({default: Hello}), 100);
986 + new Promise(_resolve => {
987 + resolve = () => _resolve({default: Hello});
988 }),
989 );
990 $RefreshReg$(Outer, 'Outer');
@@ -1014,7 +1016,7 @@ describe('ReactFresh', () => {
1016 });
1017
1018 await act(() => {
1017 - jest.runAllTimers();
1019 + resolve();
1020 });
1021
1022 // Expect different color on initial mount.
@@ -1023,7 +1025,7 @@ describe('ReactFresh', () => {
1025 expect(el.style.color).toBe('red');
1026
1027 // Bump state.
1026 - act(() => {
1028 + await act(() => {
1029 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1030 });
1031 expect(container.firstChild).toBe(el);
@@ -1050,7 +1052,8 @@ describe('ReactFresh', () => {
1052
1053 it('can patch lazy(forwardRef) before resolution', async () => {
1054 if (__DEV__) {
1053 - render(() => {
1055 + let resolve;
1056 + await render(() => {
1057 function renderHello() {
1058 const [val, setVal] = React.useState(0);
1059 return (
@@ -1064,8 +1067,8 @@ describe('ReactFresh', () => {
1067
1068 const Outer = React.lazy(
1069 () =>
1067 - new Promise(resolve => {
1068 - setTimeout(() => resolve({default: Hello}), 100);
1070 + new Promise(_resolve => {
1071 + resolve = () => _resolve({default: Hello});
1072 }),
1073 );
1074 $RefreshReg$(Outer, 'Outer');
@@ -1098,7 +1101,7 @@ describe('ReactFresh', () => {
1101 });
1102
1103 await act(() => {
1101 - jest.runAllTimers();
1104 + resolve();
1105 });
1106
1107 // Expect different color on initial mount.
@@ -1107,7 +1110,7 @@ describe('ReactFresh', () => {
1110 expect(el.style.color).toBe('red');
1111
1112 // Bump state.
1110 - act(() => {
1113 + await act(() => {
1114 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1115 });
1116 expect(container.firstChild).toBe(el);
@@ -1135,7 +1138,8 @@ describe('ReactFresh', () => {
1138
1139 it('can patch lazy(memo) before resolution', async () => {
1140 if (__DEV__) {
1138 - render(() => {
1141 + let resolve;
1142 + await render(() => {
1143 function renderHello() {
1144 const [val, setVal] = React.useState(0);
1145 return (
@@ -1149,8 +1153,8 @@ describe('ReactFresh', () => {
1153
1154 const Outer = React.lazy(
1155 () =>
1152 - new Promise(resolve => {
1153 - setTimeout(() => resolve({default: Hello}), 100);
1156 + new Promise(_resolve => {
1157 + resolve = () => _resolve({default: Hello});
1158 }),
1159 );
1160 $RefreshReg$(Outer, 'Outer');
@@ -1183,7 +1187,7 @@ describe('ReactFresh', () => {
1187 });
1188
1189 await act(() => {
1186 - jest.runAllTimers();
1190 + resolve();
1191 });
1192
1193 // Expect different color on initial mount.
@@ -1192,7 +1196,7 @@ describe('ReactFresh', () => {
1196 expect(el.style.color).toBe('red');
1197
1198 // Bump state.
1195 - act(() => {
1199 + await act(() => {
1200 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1201 });
1202 expect(container.firstChild).toBe(el);
@@ -1220,7 +1224,8 @@ describe('ReactFresh', () => {
1224
1225 it('can patch lazy(memo(forwardRef)) before resolution', async () => {
1226 if (__DEV__) {
1223 - render(() => {
1227 + let resolve;
1228 + await render(() => {
1229 function renderHello() {
1230 const [val, setVal] = React.useState(0);
1231 return (
@@ -1234,8 +1239,8 @@ describe('ReactFresh', () => {
1239
1240 const Outer = React.lazy(
1241 () =>
1237 - new Promise(resolve => {
1238 - setTimeout(() => resolve({default: Hello}), 100);
1242 + new Promise(_resolve => {
1243 + resolve = () => _resolve({default: Hello});
1244 }),
1245 );
1246 $RefreshReg$(Outer, 'Outer');
@@ -1268,7 +1273,7 @@ describe('ReactFresh', () => {
1273 });
1274
1275 await act(() => {
1271 - jest.runAllTimers();
1276 + resolve();
1277 });
1278
1279 // Expect different color on initial mount.
@@ -1277,7 +1282,7 @@ describe('ReactFresh', () => {
1282 expect(el.style.color).toBe('red');
1283
1284 // Bump state.
1280 - act(() => {
1285 + await act(() => {
1286 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1287 });
1288 expect(container.firstChild).toBe(el);
@@ -1303,9 +1308,9 @@ describe('ReactFresh', () => {
1308 }
1309 });
1310
1306 - it('can patch both trees while suspense is displaying the fallback', async () => {
1311 + it('only patches the fallback tree while suspended', async () => {
1312 if (__DEV__) {
1308 - const AppV1 = render(
1313 + const AppV1 = await render(
1314 () => {
1315 function Hello({children}) {
1316 const [val, setVal] = React.useState(0);
@@ -1343,7 +1348,7 @@ describe('ReactFresh', () => {
1348 expect(primaryChild.style.display).toBe('');
1349
1350 // Bump primary content state.
1346 - act(() => {
1351 + await act(() => {
1352 primaryChild.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1353 });
1354 expect(container.childNodes.length).toBe(1);
@@ -1371,7 +1376,7 @@ describe('ReactFresh', () => {
1376 expect(primaryChild.style.display).toBe('');
1377
1378 // Now force the tree to suspend.
1374 - render(() => AppV1, {shouldSuspend: true});
1379 + await render(() => AppV1, {shouldSuspend: true});
1380
1381 // Expect to see two trees, one of them is hidden.
1382 expect(container.childNodes.length).toBe(2);
@@ -1385,7 +1390,7 @@ describe('ReactFresh', () => {
1390 expect(fallbackChild.style.display).toBe('');
1391
1392 // Bump fallback state.
1388 - act(() => {
1393 + await act(() => {
1394 fallbackChild.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1395 });
1396 expect(container.childNodes.length).toBe(2);
@@ -1411,19 +1416,19 @@ describe('ReactFresh', () => {
1416 $RefreshReg$(Hello, 'Hello');
1417 });
1418
1414 - // Colors inside both trees should change:
1419 + // Only update color in the visible child
1420 expect(container.childNodes.length).toBe(2);
1421 expect(container.childNodes[0]).toBe(primaryChild);
1422 expect(container.childNodes[1]).toBe(fallbackChild);
1423 expect(primaryChild.textContent).toBe('Content 1');
1419 - expect(primaryChild.style.color).toBe('red');
1424 + expect(primaryChild.style.color).toBe('green');
1425 expect(primaryChild.style.display).toBe('none');
1426 expect(fallbackChild.textContent).toBe('Fallback 1');
1427 expect(fallbackChild.style.color).toBe('red');
1428 expect(fallbackChild.style.display).toBe('');
1429
1430 // Only primary tree should exist now:
1426 - render(() => AppV1, {shouldSuspend: false});
1431 + await render(() => AppV1, {shouldSuspend: false});
1432 expect(container.childNodes.length).toBe(1);
1433 expect(container.childNodes[0]).toBe(primaryChild);
1434 expect(primaryChild.textContent).toBe('Content 1');
@@ -1450,11 +1455,11 @@ describe('ReactFresh', () => {
1455 }
1456 });
1457
1453 - it('does not re-render ancestor components unnecessarily during a hot update', () => {
1458 + it('does not re-render ancestor components unnecessarily during a hot update', async () => {
1459 if (__DEV__) {
1460 let appRenders = 0;
1461
1457 - render(() => {
1462 + await render(() => {
1463 function Hello() {
1464 const [val, setVal] = React.useState(0);
1465 return (
@@ -1478,7 +1483,7 @@ describe('ReactFresh', () => {
1483 const el = container.firstChild;
1484 expect(el.textContent).toBe('0');
1485 expect(el.style.color).toBe('blue');
1481 - act(() => {
1486 + await act(() => {
1487 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1488 });
1489 expect(el.textContent).toBe('1');
@@ -1508,7 +1513,7 @@ describe('ReactFresh', () => {
1513 expect(appRenders).toBe(1);
1514
1515 // Bump the state.
1511 - act(() => {
1516 + await act(() => {
1517 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1518 });
1519 expect(el.textContent).toBe('2');
@@ -1518,11 +1523,11 @@ describe('ReactFresh', () => {
1523 }
1524 });
1525
1521 - it('batches re-renders during a hot update', () => {
1526 + it('batches re-renders during a hot update', async () => {
1527 if (__DEV__) {
1528 let helloRenders = 0;
1529
1525 - render(() => {
1530 + await render(() => {
1531 function Hello({children}) {
1532 helloRenders++;
1533 return <div>X{children}X</div>;
@@ -1559,9 +1564,9 @@ describe('ReactFresh', () => {
1564 }
1565 });
1566
1562 - it('does not leak state between components', () => {
1567 + it('does not leak state between components', async () => {
1568 if (__DEV__) {
1564 - const AppV1 = render(
1569 + const AppV1 = await render(
1570 () => {
1571 function Hello1() {
1572 const [val, setVal] = React.useState(0);
@@ -1594,21 +1599,21 @@ describe('ReactFresh', () => {
1599 const el = container.firstChild;
1600 expect(el.textContent).toBe('0');
1601 expect(el.style.color).toBe('blue');
1597 - act(() => {
1602 + await act(() => {
1603 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1604 });
1605 expect(el.textContent).toBe('1');
1606
1607 // Switch the condition, flipping inner content.
1608 // This should reset the state.
1604 - render(() => AppV1, {cond: true});
1609 + await render(() => AppV1, {cond: true});
1610 const el2 = container.firstChild;
1611 expect(el2).not.toBe(el);
1612 expect(el2.textContent).toBe('0');
1613 expect(el2.style.color).toBe('blue');
1614
1615 // Bump it again.
1611 - act(() => {
1616 + await act(() => {
1617 el2.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1618 });
1619 expect(el2.textContent).toBe('1');
@@ -1641,7 +1646,7 @@ describe('ReactFresh', () => {
1646 expect(el2.style.color).toBe('red');
1647
1648 // Flip the condition again.
1644 - render(() => AppV1, {cond: false});
1649 + await render(() => AppV1, {cond: false});
1650 const el3 = container.firstChild;
1651 expect(el3).not.toBe(el2);
1652 expect(el3.textContent).toBe('0');
@@ -1649,9 +1654,9 @@ describe('ReactFresh', () => {
1654 }
1655 });
1656
1652 - it('can force remount by changing signature', () => {
1657 + it('can force remount by changing signature', async () => {
1658 if (__DEV__) {
1654 - const HelloV1 = render(() => {
1659 + const HelloV1 = await render(() => {
1660 function Hello() {
1661 const [val, setVal] = React.useState(0);
1662 return (
@@ -1670,7 +1675,7 @@ describe('ReactFresh', () => {
1675 const el = container.firstChild;
1676 expect(el.textContent).toBe('0');
1677 expect(el.style.color).toBe('blue');
1673 - act(() => {
1678 + await act(() => {
1679 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1680 });
1681 expect(el.textContent).toBe('1');
@@ -1719,7 +1724,7 @@ describe('ReactFresh', () => {
1724 expect(newEl.style.color).toBe('yellow');
1725
1726 // Bump state again.
1722 - act(() => {
1727 + await act(() => {
1728 newEl.dispatchEvent(new MouseEvent('click', {bubbles: true}));
1729 });
1730 expect(newEl.textContent).toBe('1');
@@ -1728,11 +1733,11 @@ describe('ReactFresh', () => {
1733 // Perform top-down renders with both fresh and stale types.
1734 // Neither should change the state or color.
1735 // They should always resolve to the latest version.
1731 - render(() => HelloV1);
1732 - render(() => HelloV2);
1733 - render(() => HelloV3);
1734 - render(() => HelloV2);
1735 - render(() => HelloV1);
1736 + await render(() => HelloV1);
1737 + await render(() => HelloV2);
1738 + await render(() => HelloV3);
1739 + await render(() => HelloV2);
1740 + await render(() => HelloV1);
1741 expect(container.firstChild).toBe(newEl);
1742 expect(newEl.textContent).toBe('1');
1743 expect(newEl.style.color).toBe('yellow');
@@ -1780,7 +1785,7 @@ describe('ReactFresh', () => {
1785 }
1786 });
1787
1783 - it('keeps a valid tree when forcing remount', () => {
1788 + it('keeps a valid tree when forcing remount', async () => {
1789 if (__DEV__) {
1790 const HelloV1 = prepare(() => {
1791 function Hello() {
@@ -1910,26 +1915,31 @@ describe('ReactFresh', () => {
1915 </HelloV1>,
1916 ];
1917
1913 - // First, check that each tree handles remounts in isolation.
1914 - ReactDOM.render(null, container);
1918 + await act(() => {
1919 + root.render(null);
1920 + });
1921 +
1922 for (let i = 0; i < trees.length; i++) {
1916 - runRemountingStressTest(trees[i]);
1923 + await runRemountingStressTest(trees[i]);
1924 }
1925
1926 // Then check that each tree is resilient to updates from another tree.
1927 for (let i = 0; i < trees.length; i++) {
1928 for (let j = 0; j < trees.length; j++) {
1922 - ReactDOM.render(null, container);
1929 + await act(() => {
1930 + root.render(null);
1931 + });
1932 +
1933 // Intentionally don't clean up between the tests:
1924 - runRemountingStressTest(trees[i]);
1925 - runRemountingStressTest(trees[j]);
1926 - runRemountingStressTest(trees[i]);
1934 + await runRemountingStressTest(trees[i]);
1935 + await runRemountingStressTest(trees[j]);
1936 + await runRemountingStressTest(trees[i]);
1937 }
1938 }
1939 }
1930 - });
1940 + }, 10000);
1941
1932 - function runRemountingStressTest(tree) {
1942 + async function runRemountingStressTest(tree) {
1943 patch(() => {
1944 function Hello({children}) {
1945 return <section data-color="blue">{children}</section>;
@@ -1939,7 +1949,10 @@ describe('ReactFresh', () => {
1949 return Hello;
1950 });
1951
1942 - ReactDOM.render(tree, container);
1952 + await act(() => {
1953 + root.render(tree);
1954 + });
1955 +
1956 const elements = container.querySelectorAll('section');
1957 // Each tree above produces exactly three <section> elements:
1958 expect(elements.length).toBe(3);
@@ -2003,8 +2016,10 @@ describe('ReactFresh', () => {
2016 expect(el.dataset.color).toBe('black');
2017 });
2018
2006 - // Do another render just in case.
2007 - ReactDOM.render(tree, container);
2019 + await act(() => {
2020 + root.render(tree);
2021 + });
2022 +
2023 expect(container.querySelectorAll('section').length).toBe(3);
2024 container.querySelectorAll('section').forEach((el, index) => {
2025 expect(el).toBe(elementsAfterRemount[index]);
@@ -2012,21 +2027,21 @@ describe('ReactFresh', () => {
2027 });
2028 }
2029
2015 - it('can remount on signature change within a <root> wrapper', () => {
2030 + it('can remount on signature change within a <root> wrapper', async () => {
2031 if (__DEV__) {
2017 - testRemountingWithWrapper(Hello => Hello);
2032 + await testRemountingWithWrapper(Hello => Hello);
2033 }
2034 });
2035
2021 - it('can remount on signature change within a simple memo wrapper', () => {
2036 + it('can remount on signature change within a simple memo wrapper', async () => {
2037 if (__DEV__) {
2023 - testRemountingWithWrapper(Hello => React.memo(Hello));
2038 + await testRemountingWithWrapper(Hello => React.memo(Hello));
2039 }
2040 });
2041
2027 - it('can remount on signature change within a lazy simple memo wrapper', () => {
2042 + it('can remount on signature change within a lazy simple memo wrapper', async () => {
2043 if (__DEV__) {
2029 - testRemountingWithWrapper(Hello =>
2044 + await testRemountingWithWrapper(Hello =>
2045 React.lazy(() => ({
2046 then(cb) {
2047 cb({default: React.memo(Hello)});
@@ -2036,35 +2051,37 @@ describe('ReactFresh', () => {
2051 }
2052 });
2053
2039 - it('can remount on signature change within forwardRef', () => {
2054 + it('can remount on signature change within forwardRef', async () => {
2055 if (__DEV__) {
2041 - testRemountingWithWrapper(Hello => React.forwardRef(Hello));
2056 + await testRemountingWithWrapper(Hello => React.forwardRef(Hello));
2057 }
2058 });
2059
2045 - it('can remount on signature change within forwardRef render function', () => {
2060 + it('can remount on signature change within forwardRef render function', async () => {
2061 if (__DEV__) {
2047 - testRemountingWithWrapper(Hello => React.forwardRef(() => <Hello />));
2062 + await testRemountingWithWrapper(Hello =>
2063 + React.forwardRef(() => <Hello />),
2064 + );
2065 }
2066 });
2067
2051 - it('can remount on signature change within nested memo', () => {
2068 + it('can remount on signature change within nested memo', async () => {
2069 if (__DEV__) {
2053 - testRemountingWithWrapper(Hello =>
2070 + await testRemountingWithWrapper(Hello =>
2071 React.memo(React.memo(React.memo(Hello))),
2072 );
2073 }
2074 });
2075
2059 - it('can remount on signature change within a memo wrapper and custom comparison', () => {
2076 + it('can remount on signature change within a memo wrapper and custom comparison', async () => {
2077 if (__DEV__) {
2061 - testRemountingWithWrapper(Hello => React.memo(Hello, () => true));
2078 + await testRemountingWithWrapper(Hello => React.memo(Hello, () => true));
2079 }
2080 });
2081
2065 - it('can remount on signature change within a class', () => {
2082 + it('can remount on signature change within a class', async () => {
2083 if (__DEV__) {
2067 - testRemountingWithWrapper(Hello => {
2084 + await testRemountingWithWrapper(Hello => {
2085 const child = <Hello />;
2086 return class Wrapper extends React.PureComponent {
2087 render() {
@@ -2075,9 +2092,9 @@ describe('ReactFresh', () => {
2092 }
2093 });
2094
2078 - it('can remount on signature change within a context provider', () => {
2095 + it('can remount on signature change within a context provider', async () => {
2096 if (__DEV__) {
2080 - testRemountingWithWrapper(Hello => {
2097 + await testRemountingWithWrapper(Hello => {
2098 const Context = React.createContext();
2099 const child = (
2100 <Context.Provider value="constant">
@@ -2091,9 +2108,9 @@ describe('ReactFresh', () => {
2108 }
2109 });
2110
2094 - it('can remount on signature change within a context consumer', () => {
2111 + it('can remount on signature change within a context consumer', async () => {
2112 if (__DEV__) {
2096 - testRemountingWithWrapper(Hello => {
2113 + await testRemountingWithWrapper(Hello => {
2114 const Context = React.createContext();
2115 const child = <Context.Consumer>{() => <Hello />}</Context.Consumer>;
2116 return function Wrapper() {
@@ -2103,9 +2120,9 @@ describe('ReactFresh', () => {
2120 }
2121 });
2122
2106 - it('can remount on signature change within a suspense node', () => {
2123 + it('can remount on signature change within a suspense node', async () => {
2124 if (__DEV__) {
2108 - testRemountingWithWrapper(Hello => {
2125 + await testRemountingWithWrapper(Hello => {
2126 // TODO: we'll probably want to test fallback trees too.
2127 const child = (
2128 <React.Suspense>
@@ -2119,9 +2136,9 @@ describe('ReactFresh', () => {
2136 }
2137 });
2138
2122 - it('can remount on signature change within a mode node', () => {
2139 + it('can remount on signature change within a mode node', async () => {
2140 if (__DEV__) {
2124 - testRemountingWithWrapper(Hello => {
2141 + await testRemountingWithWrapper(Hello => {
2142 const child = (
2143 <React.StrictMode>
2144 <Hello />
@@ -2134,9 +2151,9 @@ describe('ReactFresh', () => {
2151 }
2152 });
2153
2137 - it('can remount on signature change within a fragment node', () => {
2154 + it('can remount on signature change within a fragment node', async () => {
2155 if (__DEV__) {
2139 - testRemountingWithWrapper(Hello => {
2156 + await testRemountingWithWrapper(Hello => {
2157 const child = (
2158 <>
2159 <Hello />
@@ -2149,9 +2166,9 @@ describe('ReactFresh', () => {
2166 }
2167 });
2168
2152 - it('can remount on signature change within multiple siblings', () => {
2169 + it('can remount on signature change within multiple siblings', async () => {
2170 if (__DEV__) {
2154 - testRemountingWithWrapper(Hello => {
2171 + await testRemountingWithWrapper(Hello => {
2172 const child = (
2173 <>
2174 <>
@@ -2168,9 +2185,9 @@ describe('ReactFresh', () => {
2185 }
2186 });
2187
2171 - it('can remount on signature change within a profiler node', () => {
2188 + it('can remount on signature change within a profiler node', async () => {
2189 if (__DEV__) {
2173 - testRemountingWithWrapper(Hello => {
2190 + await testRemountingWithWrapper(Hello => {
2191 const child = <Hello />;
2192 return function Wrapper() {
2193 return (
@@ -2183,8 +2200,8 @@ describe('ReactFresh', () => {
2200 }
2201 });
2202
2186 - function testRemountingWithWrapper(wrap) {
2187 - render(() => {
2203 + async function testRemountingWithWrapper(wrap) {
2204 + await render(() => {
2205 function Hello() {
2206 const [val, setVal] = React.useState(0);
2207 return (
@@ -2206,7 +2223,7 @@ describe('ReactFresh', () => {
2223 const el = container.firstChild;
2224 expect(el.textContent).toBe('0');
2225 expect(el.style.color).toBe('blue');
2209 - act(() => {
2226 + await act(() => {
2227 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
2228 });
2229 expect(el.textContent).toBe('1');
@@ -2255,7 +2272,7 @@ describe('ReactFresh', () => {
2272 expect(newEl.style.color).toBe('yellow');
2273
2274 // Bump state again.
2258 - act(() => {
2275 + await act(() => {
2276 newEl.dispatchEvent(new MouseEvent('click', {bubbles: true}));
2277 });
2278 expect(newEl.textContent).toBe('1');
@@ -2303,11 +2320,11 @@ describe('ReactFresh', () => {
2320 expect(finalEl.style.color).toBe('orange');
2321 }
2322
2306 - it('resets hooks with dependencies on hot reload', () => {
2323 + it('resets hooks with dependencies on hot reload', async () => {
2324 if (__DEV__) {
2325 let useEffectWithEmptyArrayCalls = 0;
2326
2310 - render(() => {
2327 + await render(() => {
2328 function Hello() {
2329 const [val, setVal] = React.useState(0);
2330 const tranformed = React.useMemo(() => val * 2, [val]);
@@ -2332,33 +2349,31 @@ describe('ReactFresh', () => {
2349 expect(el.textContent).toBe('0');
2350 expect(el.style.color).toBe('blue');
2351 expect(useEffectWithEmptyArrayCalls).toBe(1); // useEffect ran
2335 - act(() => {
2352 + await act(() => {
2353 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
2354 });
2355 expect(el.textContent).toBe('2'); // val * 2
2356 expect(useEffectWithEmptyArrayCalls).toBe(1); // useEffect didn't re-run
2357
2358 // Perform a hot update.
2342 - act(() => {
2343 - patch(() => {
2344 - function Hello() {
2345 - const [val, setVal] = React.useState(0);
2346 - const tranformed = React.useMemo(() => val * 10, [val]);
2347 - const handleClick = React.useCallback(() => setVal(v => v - 1), []);
2359 + patch(() => {
2360 + function Hello() {
2361 + const [val, setVal] = React.useState(0);
2362 + const tranformed = React.useMemo(() => val * 10, [val]);
2363 + const handleClick = React.useCallback(() => setVal(v => v - 1), []);
2364
2349 - React.useEffect(() => {
2350 - useEffectWithEmptyArrayCalls++;
2351 - }, []);
2365 + React.useEffect(() => {
2366 + useEffectWithEmptyArrayCalls++;
2367 + }, []);
2368
2353 - return (
2354 - <p style={{color: 'red'}} onClick={handleClick}>
2355 - {tranformed}
2356 - </p>
2357 - );
2358 - }
2359 - $RefreshReg$(Hello, 'Hello');
2360 - return Hello;
2361 - });
2369 + return (
2370 + <p style={{color: 'red'}} onClick={handleClick}>
2371 + {tranformed}
2372 + </p>
2373 + );
2374 + }
2375 + $RefreshReg$(Hello, 'Hello');
2376 + return Hello;
2377 });
2378
2379 // Assert the state was preserved but memo was evicted.
@@ -2368,7 +2383,7 @@ describe('ReactFresh', () => {
2383 expect(useEffectWithEmptyArrayCalls).toBe(2); // useEffect re-ran
2384
2385 // This should fire the new callback which decreases the counter.
2371 - act(() => {
2386 + await act(() => {
2387 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
2388 });
2389 expect(el.textContent).toBe('0');
@@ -2378,9 +2393,9 @@ describe('ReactFresh', () => {
2393 });
2394
2395 // This pattern is inspired by useSubscription and similar mechanisms.
2381 - it('does not get into infinite loops during render phase updates', () => {
2396 + it('does not get into infinite loops during render phase updates', async () => {
2397 if (__DEV__) {
2383 - render(() => {
2398 + await render(() => {
2399 function Hello() {
2400 const source = React.useMemo(() => ({value: 10}), []);
2401 const [state, setState] = React.useState({value: null});
@@ -2398,20 +2413,18 @@ describe('ReactFresh', () => {
2413 expect(el.style.color).toBe('blue');
2414
2415 // Perform a hot update.
2401 - act(() => {
2402 - patch(() => {
2403 - function Hello() {
2404 - const source = React.useMemo(() => ({value: 20}), []);
2405 - const [state, setState] = React.useState({value: null});
2406 - if (state !== source) {
2407 - // This should perform a single render-phase update.
2408 - setState(source);
2409 - }
2410 - return <p style={{color: 'red'}}>{state.value}</p>;
2416 + patch(() => {
2417 + function Hello() {
2418 + const source = React.useMemo(() => ({value: 20}), []);
2419 + const [state, setState] = React.useState({value: null});
2420 + if (state !== source) {
2421 + // This should perform a single render-phase update.
2422 + setState(source);
2423 }
2412 - $RefreshReg$(Hello, 'Hello');
2413 - return Hello;
2414 - });
2424 + return <p style={{color: 'red'}}>{state.value}</p>;
2425 + }
2426 + $RefreshReg$(Hello, 'Hello');
2427 + return Hello;
2428 });
2429
2430 expect(container.firstChild).toBe(el);
@@ -2448,7 +2461,6 @@ describe('ReactFresh', () => {
2461 };
2462 });
2463
2451 - const root = ReactDOMClient.createRoot(container);
2464 root.render(<AppV1 offscreen={true} />);
2465 await waitFor(['App#layout']);
2466 const el = container.firstChild;
@@ -2482,7 +2494,7 @@ describe('ReactFresh', () => {
2494 expect(el.firstChild.textContent).toBe('0');
2495 expect(el.firstChild.style.color).toBe('red');
2496
2485 - await internalAct(() => {
2497 + await act(() => {
2498 el.firstChild.dispatchEvent(
2499 new MouseEvent('click', {
2500 bubbles: true,
@@ -2522,9 +2534,9 @@ describe('ReactFresh', () => {
2534 expect(el.firstChild.style.color).toBe('orange');
2535 });
2536
2525 - it('remounts failed error boundaries (componentDidCatch)', () => {
2537 + it('remounts failed error boundaries (componentDidCatch)', async () => {
2538 if (__DEV__) {
2527 - render(() => {
2539 + await render(() => {
2540 function Hello() {
2541 return <h1>Hi</h1>;
2542 }
@@ -2600,9 +2612,9 @@ describe('ReactFresh', () => {
2612 }
2613 });
2614
2603 - it('remounts failed error boundaries (getDerivedStateFromError)', () => {
2615 + it('remounts failed error boundaries (getDerivedStateFromError)', async () => {
2616 if (__DEV__) {
2605 - render(() => {
2617 + await render(() => {
2618 function Hello() {
2619 return <h1>Hi</h1>;
2620 }
@@ -2678,9 +2690,9 @@ describe('ReactFresh', () => {
2690 }
2691 });
2692
2681 - it('remounts error boundaries that failed asynchronously after hot update', () => {
2693 + it('remounts error boundaries that failed asynchronously after hot update', async () => {
2694 if (__DEV__) {
2683 - render(() => {
2695 + await render(() => {
2696 function Hello() {
2697 const [x] = React.useState('');
2698 React.useEffect(() => {}, []);
@@ -2722,26 +2734,25 @@ describe('ReactFresh', () => {
2734 const secondP = firstP.nextSibling.nextSibling;
2735
2736 // Perform a hot update that fails.
2725 - act(() => {
2726 - patch(() => {
2727 - function Hello() {
2728 - const [x, setX] = React.useState('');
2729 - React.useEffect(() => {
2730 - setTimeout(() => {
2731 - setX(42); // This will crash next render.
2732 - }, 1);
2733 - }, []);
2734 - x.slice();
2735 - return <h1>Hi</h1>;
2736 - }
2737 - $RefreshReg$(Hello, 'Hello');
2738 - });
2737 + let crash;
2738 + patch(() => {
2739 + function Hello() {
2740 + const [x, setX] = React.useState('');
2741 + React.useEffect(() => {
2742 + crash = () => {
2743 + setX(42); // This will crash next render.
2744 + };
2745 + }, []);
2746 + x.slice();
2747 + return <h1>Hi</h1>;
2748 + }
2749 + $RefreshReg$(Hello, 'Hello');
2750 });
2751
2752 expect(container.innerHTML).toBe('<p>A</p><h1>Hi</h1><p>B</p>');
2753 // Run timeout inside effect:
2743 - act(() => {
2744 - jest.runAllTimers();
2754 + await act(() => {
2755 + crash();
2756 });
2757 expect(container.innerHTML).toBe(
2758 '<p>A</p><h1>Oops: x.slice is not a function</h1><p>B</p>',
@@ -2750,16 +2761,14 @@ describe('ReactFresh', () => {
2761 expect(container.firstChild.nextSibling.nextSibling).toBe(secondP);
2762
2763 // Perform a hot update that fixes the error.
2753 - act(() => {
2754 - patch(() => {
2755 - function Hello() {
2756 - const [x] = React.useState('');
2757 - React.useEffect(() => {}, []); // Removes the bad effect code.
2758 - x.slice(); // Doesn't throw initially.
2759 - return <h1>Fixed!</h1>;
2760 - }
2761 - $RefreshReg$(Hello, 'Hello');
2762 - });
2764 + patch(() => {
2765 + function Hello() {
2766 + const [x] = React.useState('');
2767 + React.useEffect(() => {}, []); // Removes the bad effect code.
2768 + x.slice(); // Doesn't throw initially.
2769 + return <h1>Fixed!</h1>;
2770 + }
2771 + $RefreshReg$(Hello, 'Hello');
2772 });
2773
2774 // This should remount the error boundary (but not anything above it).
@@ -2769,16 +2778,14 @@ describe('ReactFresh', () => {
2778
2779 // Verify next hot reload doesn't remount anything.
2780 const helloNode = container.firstChild.nextSibling;
2772 - act(() => {
2773 - patch(() => {
2774 - function Hello() {
2775 - const [x] = React.useState('');
2776 - React.useEffect(() => {}, []);
2777 - x.slice();
2778 - return <h1>Nice.</h1>;
2779 - }
2780 - $RefreshReg$(Hello, 'Hello');
2781 - });
2781 + patch(() => {
2782 + function Hello() {
2783 + const [x] = React.useState('');
2784 + React.useEffect(() => {}, []);
2785 + x.slice();
2786 + return <h1>Nice.</h1>;
2787 + }
2788 + $RefreshReg$(Hello, 'Hello');
2789 });
2790
2791 expect(container.firstChild.nextSibling).toBe(helloNode);
@@ -2786,9 +2793,9 @@ describe('ReactFresh', () => {
2793 }
2794 });
2795
2789 - it('remounts a failed root on mount', () => {
2796 + it('remounts a failed root on mount', async () => {
2797 if (__DEV__) {
2791 - expect(() => {
2798 + await expect(
2799 render(() => {
2800 function Hello() {
2801 throw new Error('No');
@@ -2796,8 +2803,8 @@ describe('ReactFresh', () => {
2803 $RefreshReg$(Hello, 'Hello');
2804
2805 return Hello;
2799 - });
2800 - }).toThrow('No');
2806 + }),
2807 + ).rejects.toThrow('No');
2808 expect(container.innerHTML).toBe('');
2809
2810 // A bad retry
@@ -2849,7 +2856,9 @@ describe('ReactFresh', () => {
2856 expect(container.innerHTML).toBe('<h1>Fixed 2!</h1>');
2857
2858 // Updates after intentional unmount are ignored.
2852 - ReactDOM.unmountComponentAtNode(container);
2859 + await act(() => {
2860 + root.unmount();
2861 + });
2862 patch(() => {
2863 function Hello() {
2864 throw new Error('Ignored');
@@ -2867,9 +2876,9 @@ describe('ReactFresh', () => {
2876 }
2877 });
2878
2870 - it('does not retry an intentionally unmounted failed root', () => {
2879 + it('does not retry an intentionally unmounted failed root', async () => {
2880 if (__DEV__) {
2872 - expect(() => {
2881 + await expect(
2882 render(() => {
2883 function Hello() {
2884 throw new Error('No');
@@ -2877,12 +2886,14 @@ describe('ReactFresh', () => {
2886 $RefreshReg$(Hello, 'Hello');
2887
2888 return Hello;
2880 - });
2881 - }).toThrow('No');
2889 + }),
2890 + ).rejects.toThrow('No');
2891 expect(container.innerHTML).toBe('');
2892
2893 // Intentional unmount.
2885 - ReactDOM.unmountComponentAtNode(container);
2894 + await act(() => {
2895 + root.unmount();
2896 + });
2897
2898 // Perform a hot update that fixes the error.
2899 patch(() => {
@@ -2896,9 +2907,9 @@ describe('ReactFresh', () => {
2907 }
2908 });
2909
2899 - it('remounts a failed root on update', () => {
2910 + it('remounts a failed root on update', async () => {
2911 if (__DEV__) {
2901 - render(() => {
2912 + await render(() => {
2913 function Hello() {
2914 return <h1>Hi</h1>;
2915 }
@@ -2974,7 +2985,9 @@ describe('ReactFresh', () => {
2985 expect(container.innerHTML).toBe('<h1>At last.</h1>');
2986
2987 // Check we don't attempt to reverse an intentional unmount.
2977 - ReactDOM.unmountComponentAtNode(container);
2988 + await act(() => {
2989 + root.unmount();
2990 + });
2991 expect(container.innerHTML).toBe('');
2992 patch(() => {
2993 function Hello() {
@@ -2985,7 +2998,8 @@ describe('ReactFresh', () => {
2998 expect(container.innerHTML).toBe('');
2999
3000 // Mount a new container.
2988 - render(() => {
3001 + root = ReactDOMClient.createRoot(container);
3002 + await render(() => {
3003 function Hello() {
3004 return <h1>Hi</h1>;
3005 }
@@ -3007,7 +3021,9 @@ describe('ReactFresh', () => {
3021 expect(container.innerHTML).toBe('');
3022
3023 // Check we don't attempt to reverse an intentional unmount, even after an error.
3010 - ReactDOM.unmountComponentAtNode(container);
3024 + await act(() => {
3025 + root.unmount();
3026 + });
3027 expect(container.innerHTML).toBe('');
3028 patch(() => {
3029 function Hello() {
@@ -3019,10 +3035,12 @@ describe('ReactFresh', () => {
3035 }
3036 });
3037
3022 - it('regression test: does not get into an infinite loop', () => {
3038 + it('regression test: does not get into an infinite loop', async () => {
3039 if (__DEV__) {
3040 const containerA = document.createElement('div');
3041 const containerB = document.createElement('div');
3042 + const rootA = ReactDOMClient.createRoot(containerA);
3043 + const rootB = ReactDOMClient.createRoot(containerB);
3044
3045 // Initially, nothing interesting.
3046 const RootAV1 = () => {
@@ -3034,9 +3052,9 @@ describe('ReactFresh', () => {
3052 };
3053 $RefreshReg$(RootBV1, 'RootB');
3054
3037 - act(() => {
3038 - ReactDOM.render(<RootAV1 />, containerA);
3039 - ReactDOM.render(<RootBV1 />, containerB);
3055 + await act(() => {
3056 + rootA.render(<RootAV1 />);
3057 + rootB.render(<RootBV1 />);
3058 });
3059 expect(containerA.innerHTML).toBe('A1');
3060 expect(containerB.innerHTML).toBe('B1');
@@ -3046,7 +3064,11 @@ describe('ReactFresh', () => {
3064 throw new Error('A2!');
3065 };
3066 $RefreshReg$(RootAV2, 'RootA');
3049 - expect(() => ReactFreshRuntime.performReactRefresh()).toThrow('A2!');
3067 + await expect(
3068 + act(() => {
3069 + ReactFreshRuntime.performReactRefresh();
3070 + }),
3071 + ).rejects.toThrow('A2!');
3072 expect(containerA.innerHTML).toBe('');
3073 expect(containerB.innerHTML).toBe('B1');
3074
@@ -3060,7 +3082,11 @@ describe('ReactFresh', () => {
3082 return 'A3';
3083 };
3084 $RefreshReg$(RootAV3, 'RootA');
3063 - expect(() => ReactFreshRuntime.performReactRefresh()).toThrow('A3!');
3085 + await expect(
3086 + act(() => {
3087 + ReactFreshRuntime.performReactRefresh();
3088 + }),
3089 + ).rejects.toThrow('A3!');
3090 expect(containerA.innerHTML).toBe('');
3091 expect(containerB.innerHTML).toBe('B1');
3092
@@ -3068,15 +3094,17 @@ describe('ReactFresh', () => {
3094 return 'A4';
3095 };
3096 $RefreshReg$(RootAV4, 'RootA');
3071 - ReactFreshRuntime.performReactRefresh();
3097 + await act(() => {
3098 + ReactFreshRuntime.performReactRefresh();
3099 + });
3100 expect(containerA.innerHTML).toBe('A4');
3101 expect(containerB.innerHTML).toBe('B1');
3102 }
3103 });
3104
3077 - it('remounts classes on every edit', () => {
3105 + it('remounts classes on every edit', async () => {
3106 if (__DEV__) {
3079 - const HelloV1 = render(() => {
3107 + const HelloV1 = await render(() => {
3108 class Hello extends React.Component {
3109 state = {count: 0};
3110 handleClick = () => {
@@ -3105,7 +3133,7 @@ describe('ReactFresh', () => {
3133 const el = container.firstChild;
3134 expect(el.textContent).toBe('0');
3135 expect(el.style.color).toBe('blue');
3108 - act(() => {
3136 + await act(() => {
3137 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
3138 });
3139 expect(el.textContent).toBe('1');
@@ -3136,14 +3164,14 @@ describe('ReactFresh', () => {
3164 const newEl = container.firstChild;
3165 expect(newEl.textContent).toBe('0');
3166 expect(newEl.style.color).toBe('red');
3139 - act(() => {
3167 + await act(() => {
3168 newEl.dispatchEvent(new MouseEvent('click', {bubbles: true}));
3169 });
3170 expect(newEl.textContent).toBe('1');
3171
3172 // Now top-level renders of both types resolve to latest.
3145 - render(() => HelloV1);
3146 - render(() => HelloV2);
3173 + await render(() => HelloV1);
3174 + await render(() => HelloV2);
3175 expect(container.firstChild).toBe(newEl);
3176 expect(newEl.style.color).toBe('red');
3177 expect(newEl.textContent).toBe('1');
@@ -3173,24 +3201,24 @@ describe('ReactFresh', () => {
3201 const finalEl = container.firstChild;
3202 expect(finalEl.textContent).toBe('0');
3203 expect(finalEl.style.color).toBe('orange');
3176 - act(() => {
3204 + await act(() => {
3205 finalEl.dispatchEvent(new MouseEvent('click', {bubbles: true}));
3206 });
3207 expect(finalEl.textContent).toBe('1');
3208
3181 - render(() => HelloV3);
3182 - render(() => HelloV2);
3183 - render(() => HelloV1);
3209 + await render(() => HelloV3);
3210 + await render(() => HelloV2);
3211 + await render(() => HelloV1);
3212 expect(container.firstChild).toBe(finalEl);
3213 expect(finalEl.style.color).toBe('orange');
3214 expect(finalEl.textContent).toBe('1');
3215 }
3216 });
3217
3190 - it('updates refs when remounting', () => {
3218 + it('updates refs when remounting', async () => {
3219 if (__DEV__) {
3220 const testRef = React.createRef();
3193 - render(
3221 + await render(
3222 () => {
3223 class Hello extends React.Component {
3224 getColor() {
@@ -3261,9 +3289,9 @@ describe('ReactFresh', () => {
3289 }
3290 });
3291
3264 - it('remounts on conversion from class to function and back', () => {
3292 + it('remounts on conversion from class to function and back', async () => {
3293 if (__DEV__) {
3266 - const HelloV1 = render(() => {
3294 + const HelloV1 = await render(() => {
3295 function Hello() {
3296 const [val, setVal] = React.useState(0);
3297 return (
@@ -3280,7 +3308,7 @@ describe('ReactFresh', () => {
3308 const el = container.firstChild;
3309 expect(el.textContent).toBe('0');
3310 expect(el.style.color).toBe('blue');
3283 - act(() => {
3311 + await act(() => {
3312 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
3313 });
3314 expect(el.textContent).toBe('1');
@@ -3311,14 +3339,14 @@ describe('ReactFresh', () => {
3339 const newEl = container.firstChild;
3340 expect(newEl.textContent).toBe('0');
3341 expect(newEl.style.color).toBe('red');
3314 - act(() => {
3342 + await act(() => {
3343 newEl.dispatchEvent(new MouseEvent('click', {bubbles: true}));
3344 });
3345 expect(newEl.textContent).toBe('1');
3346
3347 // Now top-level renders of both types resolve to latest.
3320 - render(() => HelloV1);
3321 - render(() => HelloV2);
3348 + await render(() => HelloV1);
3349 + await render(() => HelloV2);
3350 expect(container.firstChild).toBe(newEl);
3351 expect(newEl.style.color).toBe('red');
3352 expect(newEl.textContent).toBe('1');
@@ -3342,14 +3370,14 @@ describe('ReactFresh', () => {
3370 const finalEl = container.firstChild;
3371 expect(finalEl.textContent).toBe('0');
3372 expect(finalEl.style.color).toBe('orange');
3345 - act(() => {
3373 + await act(() => {
3374 finalEl.dispatchEvent(new MouseEvent('click', {bubbles: true}));
3375 });
3376 expect(finalEl.textContent).toBe('1');
3377
3350 - render(() => HelloV3);
3351 - render(() => HelloV2);
3352 - render(() => HelloV1);
3378 + await render(() => HelloV3);
3379 + await render(() => HelloV2);
3380 + await render(() => HelloV1);
3381 expect(container.firstChild).toBe(finalEl);
3382 expect(finalEl.style.color).toBe('orange');
3383 expect(finalEl.textContent).toBe('1');
@@ -3373,9 +3401,9 @@ describe('ReactFresh', () => {
3401 }
3402 });
3403
3376 - it('can find host instances for a family', () => {
3404 + it('can find host instances for a family', async () => {
3405 if (__DEV__) {
3378 - render(() => {
3406 + await render(() => {
3407 function Child({children}) {
3408 return <div className="Child">{children}</div>;
3409 }
@@ -3481,7 +3509,7 @@ describe('ReactFresh', () => {
3509 });
3510 }
3511
3484 - it('can update multiple roots independently', () => {
3512 + it('can update multiple roots independently', async () => {
3513 if (__DEV__) {
3514 // Declare the first version.
3515 const HelloV1 = () => {
@@ -3504,7 +3532,9 @@ describe('ReactFresh', () => {
3532 );
3533 };
3534 $RefreshReg$(HelloV2, 'Hello');
3507 - ReactFreshRuntime.performReactRefresh();
3535 + await act(() => {
3536 + ReactFreshRuntime.performReactRefresh();
3537 + });
3538
3539 // Mount three roots.
3540 const cont1 = document.createElement('div');
@@ -3513,10 +3543,19 @@ describe('ReactFresh', () => {
3543 document.body.appendChild(cont1);
3544 document.body.appendChild(cont2);
3545 document.body.appendChild(cont3);
3546 + const root1 = ReactDOMClient.createRoot(cont1);
3547 + const root2 = ReactDOMClient.createRoot(cont2);
3548 + const root3 = ReactDOMClient.createRoot(cont3);
3549 try {
3517 - ReactDOM.render(<HelloV1 id={1} />, cont1);
3518 - ReactDOM.render(<HelloV2 id={2} />, cont2);
3519 - ReactDOM.render(<HelloV1 id={3} />, cont3);
3550 + await act(() => {
3551 + root1.render(<HelloV1 id={1} />);
3552 + });
3553 + await act(() => {
3554 + root2.render(<HelloV2 id={2} />);
3555 + });
3556 + await act(() => {
3557 + root3.render(<HelloV1 id={3} />);
3558 + });
3559
3560 // Expect we see the V2 color.
3561 expect(cont1.firstChild.style.color).toBe('red');
@@ -3527,7 +3566,7 @@ describe('ReactFresh', () => {
3566 expect(cont3.firstChild.textContent).toBe('0');
3567
3568 // Bump the state for each of them.
3530 - act(() => {
3569 + await act(() => {
3570 cont1.firstChild.dispatchEvent(
3571 new MouseEvent('click', {bubbles: true}),
3572 );
@@ -3555,7 +3594,9 @@ describe('ReactFresh', () => {
3594 );
3595 };
3596 $RefreshReg$(HelloV3, 'Hello');
3558 - ReactFreshRuntime.performReactRefresh();
3597 + await act(() => {
3598 + ReactFreshRuntime.performReactRefresh();
3599 + });
3600
3601 // It should affect all roots.
3602 expect(cont1.firstChild.style.color).toBe('green');
@@ -3566,7 +3607,9 @@ describe('ReactFresh', () => {
3607 expect(cont3.firstChild.textContent).toBe('1');
3608
3609 // Unmount the second root.
3569 - ReactDOM.unmountComponentAtNode(cont2);
3610 + await act(() => {
3611 + root2.unmount();
3612 + });
3613 // Make the first root throw and unmount on hot update.
3614 const HelloV4 = ({id}) => {
3615 if (id === 1) {
@@ -3580,9 +3623,11 @@ describe('ReactFresh', () => {
3623 );
3624 };
3625 $RefreshReg$(HelloV4, 'Hello');
3583 - expect(() => {
3584 - ReactFreshRuntime.performReactRefresh();
3585 - }).toThrow('Oops.');
3626 + await expect(
3627 + act(() => {
3628 + ReactFreshRuntime.performReactRefresh();
3629 + }),
3630 + ).rejects.toThrow('Oops.');
3631
3632 // Still, we expect the last root to be updated.
3633 expect(cont1.innerHTML).toBe('');
@@ -3791,18 +3836,20 @@ describe('ReactFresh', () => {
3836 jest.resetModules();
3837 React = require('react');
3838 ReactDOM = require('react-dom');
3839 + ReactDOMClient = require('react-dom/client');
3840 Scheduler = require('scheduler');
3795 - act = React.act;
3796 - internalAct = require('internal-test-utils').act;
3841 + act = require('internal-test-utils').act;
3842
3843 // Important! Inject into the global hook *after* ReactDOM runs:
3844 ReactFreshRuntime = require('react-refresh/runtime');
3845 ReactFreshRuntime.injectIntoGlobalHook(global);
3846
3847 + root = ReactDOMClient.createRoot(container);
3848 +
3849 // We're verifying that we're able to track roots mounted after this point.
3850 // The rest of this test is taken from the simplest first test case.
3851
3805 - render(() => {
3852 + await render(() => {
3853 function Hello() {
3854 const [val, setVal] = React.useState(0);
3855 return (
@@ -3819,7 +3866,7 @@ describe('ReactFresh', () => {
3866 const el = container.firstChild;
3867 expect(el.textContent).toBe('0');
3868 expect(el.style.color).toBe('blue');
3822 - act(() => {
3869 + await act(() => {
3870 el.dispatchEvent(new MouseEvent('click', {bubbles: true}));
3871 });
3872 expect(el.textContent).toBe('1');
@@ -3846,7 +3893,7 @@ describe('ReactFresh', () => {
3893 });
3894
3895 // This simulates the scenario in https://github.com/facebook/react/issues/20100
3849 - it('does not block DevTools when an unsupported renderer is injected', () => {
3896 + it('does not block DevTools when an unsupported legacy renderer is injected', () => {
3897 if (__DEV__) {
3898 initFauxDevToolsHook();
3899
@@ -3872,13 +3919,12 @@ describe('ReactFresh', () => {
3919 ReactFreshRuntime = require('react-refresh/runtime');
3920 ReactFreshRuntime.injectIntoGlobalHook(global);
3921
3875 - render(() => {
3876 - function Hello() {
3877 - return <div>Hi!</div>;
3878 - }
3879 - $RefreshReg$(Hello, 'Hello');
3880 - return Hello;
3881 - });
3922 + const Hello = () => {
3923 + return <div>Hi!</div>;
3924 + };
3925 + $RefreshReg$(Hello, 'Hello');
3926 + const Component = Hello;
3927 + ReactDOM.render(<Component />, container);
3928
3929 expect(onCommitFiberRoot).toHaveBeenCalled();
3930 }