Codemod more tests to waitFor pattern (#26494)
Andrew Clark committed
Mar 28, 2023 at 00:03 UTC
fc90eb636876d54d99ace2773dd4923f3e848106
2 files changed
+78
-46
packages/react-dom/src/__tests__/ReactUpdates-test.js
+7
-12
@@ -1627,7 +1627,6 @@ describe('ReactUpdates', () => {
1627
const [step, setStep] = React.useState(0);
1628
React.useEffect(() => {
1629
setStep(x => x + 1);
1630
- Scheduler.log(step);
1630
});
1631
return step;
1632
}
@@ -1642,23 +1641,19 @@ describe('ReactUpdates', () => {
1641
console.error = (e, s) => {
1642
error = e;
1643
stack = s;
1644
+ Scheduler.log('stop');
1645
};
1646
try {
1647
const container = document.createElement('div');
1648
- expect(() => {
1649
- const root = ReactDOMClient.createRoot(container);
1650
- root.render(<App />);
1651
- while (error === null) {
1652
- Scheduler.unstable_flushNumberOfYields(1);
1653
- Scheduler.unstable_clearLog();
1654
- }
1655
- expect(stack).toContain(' NonTerminating');
1656
- // rethrow error to prevent going into an infinite loop when act() exits
1657
- throw error;
1658
- }).toThrow('Maximum update depth exceeded.');
1648
+ const root = ReactDOMClient.createRoot(container);
1649
+ root.render(<App />);
1650
+ await waitFor(['stop']);
1651
} finally {
1652
console.error = originalConsoleError;
1653
}
1654
+
1655
+ expect(error).toContain('Maximum update depth exceeded');
1656
+ expect(stack).toContain('at NonTerminating');
1657
});
1658
1659
it('can have nested updates if they do not cross the limit', async () => {
packages/react-reconciler/src/__tests__/ReactIncrementalUpdates-test.js
+71
-34
@@ -36,13 +36,9 @@ describe('ReactIncrementalUpdates', () => {
36
assertLog = InternalTestUtils.assertLog;
37
});
38
39
- function flushNextRenderIfExpired() {
40
- // This will start rendering the next level of work. If the work hasn't
41
- // expired yet, React will exit without doing anything. If it has expired,
42
- // it will schedule a sync task.
43
- Scheduler.unstable_flushExpired();
44
- // Flush the sync task.
45
- ReactNoop.flushSync();
39
+ function Text({text}) {
40
+ Scheduler.log(text);
41
+ return text;
42
}
43
44
it('applies updates in order of priority', async () => {
@@ -528,35 +524,38 @@ describe('ReactIncrementalUpdates', () => {
524
setCount = _setCount;
525
Scheduler.log('Render: ' + count);
526
useLayoutEffect(() => {
531
- setCount(prevCount => prevCount + 1);
527
+ setCount(1);
528
Scheduler.log('Commit: ' + count);
529
}, []);
534
- return null;
530
+ return <Text text="Child" />;
531
}
532
533
await act(async () => {
534
React.startTransition(() => {
535
ReactNoop.render(<App />);
536
});
541
- flushNextRenderIfExpired();
537
assertLog([]);
543
- await waitForAll(['Render: 0', 'Commit: 0', 'Render: 1']);
538
+ await waitForAll([
539
+ 'Render: 0',
540
+ 'Child',
541
+ 'Commit: 0',
542
+ 'Render: 1',
543
+ 'Child',
544
+ ]);
545
546
Scheduler.unstable_advanceTime(10000);
547
React.startTransition(() => {
548
setCount(2);
549
});
549
- flushNextRenderIfExpired();
550
- assertLog([]);
550
+ // The transition should not have expired, so we should be able to
551
+ // partially render it.
552
+ await waitFor(['Render: 2']);
553
+ // Now do the rest
554
+ await waitForAll(['Child']);
555
});
556
});
557
554
- it('regression: does not expire soon due to previous flushSync', () => {
555
- function Text({text}) {
556
- Scheduler.log(text);
557
- return text;
558
- }
559
-
558
+ it('regression: does not expire soon due to previous flushSync', async () => {
559
ReactNoop.flushSync(() => {
560
ReactNoop.render(<Text text="A" />);
561
});
@@ -565,32 +564,70 @@ describe('ReactIncrementalUpdates', () => {
564
Scheduler.unstable_advanceTime(10000);
565
566
React.startTransition(() => {
568
- ReactNoop.render(<Text text="B" />);
567
+ ReactNoop.render(
568
+ <>
569
+ <Text text="A" />
570
+ <Text text="B" />
571
+ <Text text="C" />
572
+ <Text text="D" />
573
+ </>,
574
+ );
575
+ });
576
+ // The transition should not have expired, so we should be able to
577
+ // partially render it.
578
+ await waitFor(['A']);
579
+
580
+ // FIXME: We should be able to partially render B, too, but currently it
581
+ // expires. This is an existing bug that I discovered, which will be fixed
582
+ // in a PR that I'm currently working on.
583
+ //
584
+ // Correct behavior:
585
+ // await waitFor(['B']);
586
+ // await waitForAll(['C', 'D']);
587
+ //
588
+ // Current behavior:
589
+ await waitFor(['B'], {
590
+ additionalLogsAfterAttemptingToYield: ['C', 'D'],
591
});
570
- flushNextRenderIfExpired();
571
- assertLog([]);
592
});
593
574
- it('regression: does not expire soon due to previous expired work', () => {
575
- function Text({text}) {
576
- Scheduler.log(text);
577
- return text;
578
- }
579
-
594
+ it('regression: does not expire soon due to previous expired work', async () => {
595
React.startTransition(() => {
581
- ReactNoop.render(<Text text="A" />);
596
+ ReactNoop.render(
597
+ <>
598
+ <Text text="A" />
599
+ <Text text="B" />
600
+ <Text text="C" />
601
+ <Text text="D" />
602
+ </>,
603
+ );
604
});
605
+ await waitFor(['A']);
606
+
607
+ // This will expire the rest of the update
608
Scheduler.unstable_advanceTime(10000);
584
- flushNextRenderIfExpired();
585
- assertLog(['A']);
609
+ await waitFor(['B'], {
610
+ additionalLogsAfterAttemptingToYield: ['C', 'D'],
611
+ });
612
613
Scheduler.unstable_advanceTime(10000);
614
615
+ // Now do another transition. This one should not expire.
616
React.startTransition(() => {
590
- ReactNoop.render(<Text text="B" />);
617
+ ReactNoop.render(
618
+ <>
619
+ <Text text="A" />
620
+ <Text text="B" />
621
+ <Text text="C" />
622
+ <Text text="D" />
623
+ </>,
624
+ );
625
});
592
- flushNextRenderIfExpired();
593
- assertLog([]);
626
+ // The transition should not have expired, so we should be able to
627
+ // partially render it.
628
+ await waitFor(['A']);
629
+ await waitFor(['B']);
630
+ await waitForAll(['C', 'D']);
631
});
632
633
it('when rebasing, does not exclude updates that were already committed, regardless of priority', async () => {