2
let Fragment;
3
let ReactNoop;
4
let Scheduler;
5
+let act;
6
let Suspense;
7
let getCacheForType;
8
17
Fragment = React.Fragment;
18
ReactNoop = require('react-noop-renderer');
19
Scheduler = require('scheduler');
20
+ act = require('jest-react').act;
21
Suspense = React.Suspense;
22
23
getCacheForType = React.unstable_getCacheForType;
673
674
// Mount the Suspense boundary without suspending, so that the subsequent
675
// updates suspend with a delay.
674
- await ReactNoop.act(async () => {
676
+ await act(async () => {
677
root.render(<App step={0} shouldSuspend={false} />);
678
});
679
await advanceTimers(1000);
680
expect(Scheduler).toHaveYielded(['Sibling', 'Step 0']);
681
682
// Schedule an update at several distinct expiration times
681
- await ReactNoop.act(async () => {
683
+ await act(async () => {
684
if (gate(flags => flags.enableSyncDefaultUpdates)) {
685
React.startTransition(() => {
686
root.render(<App step={1} shouldSuspend={true} />);
1230
expect(Scheduler).toHaveYielded(['Suspend! [Result]', 'Loading...']);
1231
expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1232
1231
- await ReactNoop.act(async () => {
1233
+ await act(async () => {
1234
resolveText('Result');
1235
});
1236
1300
</>,
1301
);
1302
1301
- await ReactNoop.act(async () => {
1303
+ await act(async () => {
1304
resolveText('Step: 2');
1305
});
1306
expect(Scheduler).toHaveYielded(['Step: 2']);
1373
</>,
1374
);
1375
1374
- await ReactNoop.act(async () => {
1376
+ await act(async () => {
1377
resolveText('B');
1378
});
1379
1419
]);
1420
expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1421
1420
- await ReactNoop.act(async () => {
1422
+ await act(async () => {
1423
resolveText('Hi');
1424
});
1425
1466
'Loading...',
1467
]);
1468
expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1467
- await ReactNoop.act(async () => {
1469
+ await act(async () => {
1470
resolveText('Hi');
1471
});
1472
expect(Scheduler).toHaveYielded(['Hi']);
1512
</>,
1513
]);
1514
1513
- await ReactNoop.act(async () => {
1515
+ await act(async () => {
1516
resolveText('Hi');
1517
});
1518
expect(Scheduler).toHaveYielded(['Hi']);
1555
'Child is hidden: true',
1556
]);
1557
1556
- await ReactNoop.act(async () => {
1558
+ await act(async () => {
1559
resolveText('Hi');
1560
});
1561
1628
}
1629
1630
const root = ReactNoop.createLegacyRoot(null);
1629
- await ReactNoop.act(async () => {
1631
+ await act(async () => {
1632
root.render(<App />);
1633
});
1634
expect(Scheduler).toHaveYielded(['Mount']);
1635
expect(root).toMatchRenderedOutput('Child');
1636
1637
// Suspend the child. This puts it into an inconsistent state.
1636
- await ReactNoop.act(async () => {
1638
+ await act(async () => {
1639
setShouldSuspend(true);
1640
});
1641
expect(root).toMatchRenderedOutput('Loading...');
1642
1643
// Unmount everying
1642
- await ReactNoop.act(async () => {
1644
+ await act(async () => {
1645
root.render(null);
1646
});
1647
expect(Scheduler).toHaveYielded(['Unmount']);
1803
</>,
1804
);
1805
1804
- await ReactNoop.act(async () => {
1806
+ await act(async () => {
1807
resolveText('B');
1808
});
1809
1839
'Effect [Loading...]',
1840
]);
1841
1840
- await ReactNoop.act(async () => {
1842
+ await act(async () => {
1843
resolveText('B2');
1844
});
1845
2093
);
2094
}
2095
2094
- await ReactNoop.act(async () => {
2096
+ await act(async () => {
2097
ReactNoop.render(<App />);
2098
});
2099
2100
// TODO: assert toErrorDev() when the warning is implemented again.
2099
- ReactNoop.act(() => {
2101
+ act(() => {
2102
ReactNoop.flushSync(() => _setShow(true));
2103
});
2104
});
2120
}
2121
}
2122
2121
- await ReactNoop.act(async () => {
2123
+ await act(async () => {
2124
ReactNoop.render(<App />);
2125
});
2126
2127
// TODO: assert toErrorDev() when the warning is implemented again.
2126
- ReactNoop.act(() => {
2128
+ act(() => {
2129
ReactNoop.flushSync(() => show());
2130
});
2131
});
2147
}
2148
}
2149
2148
- await ReactNoop.act(async () => {
2150
+ await act(async () => {
2151
ReactNoop.render(<App />);
2152
});
2153
2154
expect(Scheduler).toHaveYielded(['Suspend! [A]']);
2155
expect(ReactNoop).toMatchRenderedOutput('Loading...');
2156
2155
- ReactNoop.act(() => {
2157
+ act(() => {
2158
ReactNoop.flushSync(() => showB());
2159
});
2160
2180
</Suspense>
2181
);
2182
}
2181
- await ReactNoop.act(async () => {
2183
+ await act(async () => {
2184
ReactNoop.render(<App />);
2185
});
2186
2187
// TODO: assert toErrorDev() when the warning is implemented again.
2186
- ReactNoop.act(() => {
2188
+ act(() => {
2189
ReactNoop.flushSync(() => _setShow(true));
2190
});
2191
},
2207
}
2208
}
2209
2208
- await ReactNoop.act(async () => {
2210
+ await act(async () => {
2211
ReactNoop.render(<App />);
2212
});
2213
2214
// also make sure lowpriority is okay
2213
- await ReactNoop.act(async () => show(true));
2215
+ await act(async () => show(true));
2216
2217
expect(Scheduler).toHaveYielded(['Suspend! [A]']);
2218
await resolveText('A');
2233
);
2234
}
2235
2234
- await ReactNoop.act(async () => {
2236
+ await act(async () => {
2237
ReactNoop.render(<App />);
2238
});
2239
2240
// also make sure lowpriority is okay
2239
- await ReactNoop.act(async () => _setShow(true));
2241
+ await act(async () => _setShow(true));
2242
2243
expect(Scheduler).toHaveYielded(['Suspend! [A]']);
2244
await resolveText('A');
2501
expect(Scheduler).toFlushAndYield([]);
2502
2503
// Initial render.
2502
- await ReactNoop.act(async () => {
2504
+ await act(async () => {
2505
React.startTransition(() => transitionToPage('A'));
2506
2507
expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
2517
expect(ReactNoop.getChildren()).toEqual([span('A')]);
2518
2519
// Start transition.
2518
- await ReactNoop.act(async () => {
2520
+ await act(async () => {
2521
React.startTransition(() => transitionToPage('B'));
2522
2523
expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
2556
expect(Scheduler).toFlushAndYield([]);
2557
2558
// Initial render.
2557
- await ReactNoop.act(async () => {
2559
+ await act(async () => {
2560
React.startTransition(() => transitionToPage('A'));
2561
2562
expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
2572
expect(ReactNoop.getChildren()).toEqual([span('A')]);
2573
2574
// Start transition.
2573
- await ReactNoop.act(async () => {
2575
+ await act(async () => {
2576
React.startTransition(() => transitionToPage('B'));
2577
2578
expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
2660
expect(Scheduler).toFlushAndYield([]);
2661
2662
// Initial render.
2661
- await ReactNoop.act(async () => {
2663
+ await act(async () => {
2664
React.startTransition(() => transitionToPage('A'));
2665
2666
expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
2676
expect(ReactNoop.getChildren()).toEqual([span('A')]);
2677
2678
// Start transition.
2677
- await ReactNoop.act(async () => {
2679
+ await act(async () => {
2680
React.startTransition(() => transitionToPage('B'));
2681
2682
expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
2694
expect(ReactNoop.getChildren()).toEqual([span('B')]);
2695
2696
// Start a long (infinite) transition.
2695
- await ReactNoop.act(async () => {
2697
+ await act(async () => {
2698
React.startTransition(() => transitionToPage('C'));
2699
2700
expect(Scheduler).toFlushAndYield(['Suspend! [C]', 'Loading...']);
2730
expect(Scheduler).toFlushAndYield([]);
2731
2732
// Initial render.
2731
- await ReactNoop.act(async () => {
2733
+ await act(async () => {
2734
React.startTransition(() => transitionToPage('A'));
2735
2736
expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
2746
expect(ReactNoop.getChildren()).toEqual([span('A')]);
2747
2748
// Start transition.
2747
- await ReactNoop.act(async () => {
2749
+ await act(async () => {
2750
React.startTransition(() => transitionToPage('B'));
2751
2752
expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
2763
expect(ReactNoop.getChildren()).toEqual([span('B')]);
2764
2765
// Start a long (infinite) transition.
2764
- await ReactNoop.act(async () => {
2766
+ await act(async () => {
2767
React.startTransition(() => transitionToPage('C'));
2768
2769
expect(Scheduler).toFlushAndYield(['Suspend! [C]', 'Loading...']);
2886
}
2887
2888
const root = ReactNoop.createRoot();
2887
- await ReactNoop.act(async () => {
2889
+ await act(async () => {
2890
root.render(<App text="Initial" />);
2891
});
2892
expect(Scheduler).toHaveYielded(['Suspend! [Initial]']);
2893
2894
// Resolve initial render
2893
- await ReactNoop.act(async () => {
2895
+ await act(async () => {
2896
await resolveText('Initial');
2897
});
2898
expect(Scheduler).toHaveYielded(['Initial']);
2899
expect(root).toMatchRenderedOutput(<span prop="Initial" />);
2900
2899
- await ReactNoop.act(async () => {
2901
+ await act(async () => {
2902
// Update. Since showing a fallback would hide content that's already
2903
// visible, it should suspend for a JND without committing.
2904
if (gate(flags => flags.enableSyncDefaultUpdates)) {
2955
}
2956
2957
const root = ReactNoop.createRoot();
2956
- await ReactNoop.act(async () => {
2958
+ await act(async () => {
2959
root.render(<App />);
2960
});
2961
2962
expect(Scheduler).toHaveYielded(['Foo']);
2963
2962
- await ReactNoop.act(async () => {
2964
+ await act(async () => {
2965
foo.setState({suspend: true});
2966
2967
// In the regression that this covers, we would neglect to reset the
3112
}
3113
3114
await seedNextTextCache('A');
3113
- await ReactNoop.act(async () => {
3115
+ await act(async () => {
3116
root.render(<Parent />);
3117
});
3118
expect(Scheduler).toHaveYielded(['A']);
3119
expect(root).toMatchRenderedOutput(<span prop="A" />);
3120
3119
- await ReactNoop.act(async () => {
3121
+ await act(async () => {
3122
// Schedule two updates that originate inside the Suspense boundary.
3123
// The first one causes the boundary to suspend. The second one is at
3124
// lower priority and unsuspends the tree.
3166
}
3167
3168
await seedNextTextCache('A');
3167
- await ReactNoop.act(async () => {
3169
+ await act(async () => {
3170
root.render(<Parent />);
3171
});
3172
expect(Scheduler).toHaveYielded(['A']);
3173
expect(root).toMatchRenderedOutput(<span prop="A" />);
3174
3173
- await ReactNoop.act(async () => {
3175
+ await act(async () => {
3176
// Schedule two updates that originate inside the Suspense boundary.
3177
// The first one causes the boundary to suspend. The second one is at
3178
// lower priority and unsuspends it by hiding the async component.
3235
3236
// Resolve the initial tree
3237
await seedNextTextCache('A');
3236
- await ReactNoop.act(async () => {
3238
+ await act(async () => {
3239
root.render(<App />);
3240
});
3241
expect(Scheduler).toHaveYielded(['A']);
3242
expect(root).toMatchRenderedOutput(<span prop="A" />);
3243
3242
- await ReactNoop.act(async () => {
3244
+ await act(async () => {
3245
// Schedule an update inside the Suspense boundary that suspends.
3246
setAppText('B');
3247
expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
3257
// Schedule a default pri update on the boundary, and a lower pri update
3258
// on the fallback. We're testing to make sure the fallback can still
3259
// update even though the primary tree is suspended.
3258
- await ReactNoop.act(async () => {
3260
+ await act(async () => {
3261
setAppText('C');
3262
React.startTransition(() => {
3263
setFallbackText('Still loading...');
3320
3321
// Mount an initial tree. Resolve A so that it doesn't suspend.
3322
await seedNextTextCache('A');
3321
- await ReactNoop.act(async () => {
3323
+ await act(async () => {
3324
root.render(<Parent />);
3325
});
3326
expect(Scheduler).toHaveYielded(['A']);
3329
3330
// Schedule another update. This will "flip" the alternate pairs.
3331
await resolveText('B');
3330
- await ReactNoop.act(async () => {
3332
+ await act(async () => {
3333
setText('B');
3334
});
3335
expect(Scheduler).toHaveYielded(['B']);
3337
expect(root).toMatchRenderedOutput(<span prop="B" />);
3338
3339
// Schedule another update. This time, we'll suspend.
3338
- await ReactNoop.act(async () => {
3340
+ await act(async () => {
3341
setText('C');
3342
});
3343
expect(Scheduler).toHaveYielded(['Suspend! [C]', 'Loading...']);
3344
3345
// Commit. This will insert a fragment fiber to wrap around the component
3346
// that triggered the update.
3345
- await ReactNoop.act(async () => {
3347
+ await act(async () => {
3348
await advanceTimers(250);
3349
});
3350
// The fragment fiber is part of the current tree, but the setState return
3359
3360
// Update again. This should unsuspend the tree.
3361
await resolveText('D');
3360
- await ReactNoop.act(async () => {
3362
+ await act(async () => {
3363
setText('D');
3364
});
3365
// Even though the fragment fiber is not part of the return path, we should
3397
3398
// Mount an initial tree. Resolve A so that it doesn't suspend.
3399
await seedNextTextCache('A');
3398
- await ReactNoop.act(async () => {
3400
+ await act(async () => {
3401
root.render(<Parent />);
3402
});
3403
expect(Scheduler).toHaveYielded(['A']);
3406
3407
// Schedule another update. This will "flip" the alternate pairs.
3408
await resolveText('B');
3407
- await ReactNoop.act(async () => {
3409
+ await act(async () => {
3410
setText('B');
3411
});
3412
expect(Scheduler).toHaveYielded(['B']);
3414
expect(root).toMatchRenderedOutput(<span prop="B" />);
3415
3416
// Schedule another update. This time, we'll suspend.
3415
- await ReactNoop.act(async () => {
3417
+ await act(async () => {
3418
setText('C');
3419
});
3420
expect(Scheduler).toHaveYielded(['Suspend! [C]', 'Loading...']);
3421
3422
// Commit. This will insert a fragment fiber to wrap around the component
3423
// that triggered the update.
3422
- await ReactNoop.act(async () => {
3424
+ await act(async () => {
3425
await advanceTimers(250);
3426
});
3427
// The fragment fiber is part of the current tree, but the setState return
3434
</>,
3435
);
3436
3435
- await ReactNoop.act(async () => {
3437
+ await act(async () => {
3438
// Schedule a normal pri update. This will suspend again.
3439
setText('D');
3440
3506
3507
// Mount an initial tree. Resolve A so that it doesn't suspend.
3508
await seedNextTextCache('Inner text: A');
3507
- await ReactNoop.act(async () => {
3509
+ await act(async () => {
3510
root.render(<Parent step={0} />);
3511
});
3512
expect(Scheduler).toHaveYielded([
3526
);
3527
3528
// Update. This causes the inner component to suspend.
3527
- await ReactNoop.act(async () => {
3529
+ await act(async () => {
3530
setText('B');
3531
});
3532
expect(Scheduler).toHaveYielded([
3549
);
3550
3551
// Schedule a high pri update on the parent.
3550
- await ReactNoop.act(async () => {
3552
+ await act(async () => {
3553
ReactNoop.discreteUpdates(() => {
3554
root.render(<Parent step={1} />);
3555
});
3576
);
3577
3578
// Now finish resolving the inner text
3577
- await ReactNoop.act(async () => {
3579
+ await act(async () => {
3580
await resolveText('Inner text: B');
3581
});
3582
expect(Scheduler).toHaveYielded([
3645
3646
// Mount an initial tree. Resolve A so that it doesn't suspend.
3647
await seedNextTextCache('Inner: A0');
3646
- await ReactNoop.act(async () => {
3648
+ await act(async () => {
3649
root.render(<Parent step={0} />);
3650
});
3651
expect(Scheduler).toHaveYielded(['Outer: A0', 'Inner: A0', 'Commit Child']);
3657
);
3658
3659
// Update. This causes the inner component to suspend.
3658
- await ReactNoop.act(async () => {
3660
+ await act(async () => {
3661
setText('B');
3662
});
3663
expect(Scheduler).toHaveYielded([
3677
3678
// Schedule a high pri update on the parent. This will unblock the content.
3679
await resolveText('Inner: B1');
3678
- await ReactNoop.act(async () => {
3680
+ await act(async () => {
3681
ReactNoop.discreteUpdates(() => {
3682
root.render(<Parent step={1} />);
3683
});
3733
}
3734
3735
const root = ReactNoop.createRoot();
3734
- await ReactNoop.act(async () => {
3736
+ await act(async () => {
3737
await seedNextTextCache('A');
3738
await seedNextTextCache('B');
3739
root.render(<App />);
3746
</>,
3747
);
3748
3747
- await ReactNoop.act(async () => {
3749
+ await act(async () => {
3750
// Triggers suspense at normal pri
3751
setTextA('A1');
3752
// Triggers in an unrelated tree at a different pri
3849
}
3850
3851
const root = ReactNoop.createRoot();
3850
- await ReactNoop.act(async () => {
3852
+ await act(async () => {
3853
root.render(<Root />);
3854
});
3855
expect(Scheduler).toHaveYielded(['']);
3856
expect(root).toMatchRenderedOutput(<span prop="" />);
3857
3858
// Update to "a". That will suspend.
3857
- await ReactNoop.act(async () => {
3859
+ await act(async () => {
3860
setTextWithShortTransition('a');
3861
expect(Scheduler).toFlushAndYield([
3862
'Pending...',
3874
);
3875
3876
// Update to "b". That will suspend, too.
3875
- await ReactNoop.act(async () => {
3877
+ await act(async () => {
3878
setTextWithLongTransition('b');
3879
expect(Scheduler).toFlushAndYield([
3880
// Neither is resolved yet.
3893
);
3894
3895
// Resolve "a". But "b" is still pending.
3894
- await ReactNoop.act(async () => {
3896
+ await act(async () => {
3897
await resolveText('a');
3898
3899
expect(Scheduler).toFlushAndYield(['Suspend! [b]', 'Loading...']);
3905
);
3906
3907
// Resolve "b". This should remove the pending state.
3906
- await ReactNoop.act(async () => {
3908
+ await act(async () => {
3909
await resolveText('b');
3910
});
3911
expect(Scheduler).toHaveYielded(['b']);
3926
}
3927
3928
const root = ReactNoop.createRoot();
3927
- await ReactNoop.act(async () => {
3929
+ await act(async () => {
3930
await seedNextTextCache('A');
3931
root.render(
3932
<Suspense fallback={<Text text="Loading..." />}>
3937
expect(Scheduler).toHaveYielded(['A']);
3938
expect(root).toMatchRenderedOutput(<span prop="A" />);
3939
3938
- await ReactNoop.act(async () => {
3940
+ await act(async () => {
3941
setText('B');
3942
ReactNoop.idleUpdates(() => {
3943
setText('B');
3953
expect(Scheduler).toFlushAndYield(['Suspend! [B]']);
3954
});
3955
3954
- await ReactNoop.act(async () => {
3956
+ await act(async () => {
3957
setText('B');
3958
await resolveText('B');
3959
});
3973
}
3974
3975
const root = ReactNoop.createRoot();
3974
- await ReactNoop.act(async () => {
3976
+ await act(async () => {
3977
root.render(
3978
<>
3979
<UpdatingText />
3991
</>,
3992
);
3993
3992
- await ReactNoop.act(async () => {
3994
+ await act(async () => {
3995
// Resolve the promise. This will trigger a retry.
3996
await resolveText('Async');
3997
// Before the retry happens, schedule a new update.
4041
4042
const root = ReactNoop.createRoot();
4043
4042
- await ReactNoop.act(async () => {
4044
+ await act(async () => {
4045
root.render(<App show={false} />);
4046
});
4047
expect(Scheduler).toHaveYielded(['Mount Child']);
4048
expect(root).toMatchRenderedOutput(<span prop="Child" />);
4049
4048
- await ReactNoop.act(async () => {
4050
+ await act(async () => {
4051
root.render(<App show={true} />);
4052
});
4053
expect(Scheduler).toHaveYielded(['Suspend! [Async]', 'Loading...']);
4058
</>,
4059
);
4060
4059
- await ReactNoop.act(async () => {
4061
+ await act(async () => {
4062
root.render(null);
4063
});
4064
expect(Scheduler).toHaveYielded(['Unmount Child']);
4089
4090
const root = ReactNoop.createLegacyRoot();
4091
4090
- await ReactNoop.act(async () => {
4092
+ await act(async () => {
4093
root.render(<App show={false} />);
4094
});
4095
expect(Scheduler).toHaveYielded(['Mount Child']);
4096
expect(root).toMatchRenderedOutput(<span prop="Child" />);
4097
4096
- await ReactNoop.act(async () => {
4098
+ await act(async () => {
4099
root.render(<App show={true} />);
4100
});
4101
expect(Scheduler).toHaveYielded(['Suspend! [Async]', 'Loading...']);
4106
</>,
4107
);
4108
4107
- await ReactNoop.act(async () => {
4109
+ await act(async () => {
4110
root.render(null);
4111
});
4112
expect(Scheduler).toHaveYielded(['Unmount Child']);