5
let ReactFeatureFlags;
6
let Suspense;
7
let lazy;
8
+let waitForAll;
9
+let waitForThrow;
10
+let assertLog;
11
+
12
+let fakeModuleCache;
13
14
function normalizeCodeLocInfo(str) {
15
return (
32
lazy = React.lazy;
33
ReactTestRenderer = require('react-test-renderer');
34
Scheduler = require('scheduler');
35
+
36
+ const InternalTestUtils = require('internal-test-utils');
37
+ waitForAll = InternalTestUtils.waitForAll;
38
+ waitForThrow = InternalTestUtils.waitForThrow;
39
+ assertLog = InternalTestUtils.assertLog;
40
+
41
+ fakeModuleCache = new Map();
42
});
43
44
function Text(props) {
46
return props.text;
47
}
48
37
- function delay(ms) {
38
- return new Promise(resolve => setTimeout(() => resolve(), ms));
49
+ async function fakeImport(Component) {
50
+ const record = fakeModuleCache.get(Component);
51
+ if (record === undefined) {
52
+ const newRecord = {
53
+ status: 'pending',
54
+ value: {default: Component},
55
+ pings: [],
56
+ then(ping) {
57
+ switch (newRecord.status) {
58
+ case 'pending': {
59
+ newRecord.pings.push(ping);
60
+ return;
61
+ }
62
+ case 'resolved': {
63
+ ping(newRecord.value);
64
+ return;
65
+ }
66
+ case 'rejected': {
67
+ throw newRecord.value;
68
+ }
69
+ }
70
+ },
71
+ };
72
+ fakeModuleCache.set(Component, newRecord);
73
+ return newRecord;
74
+ }
75
+ return record;
76
}
77
41
- async function fakeImport(result) {
42
- return {default: result};
78
+ function resolveFakeImport(moduleName) {
79
+ const record = fakeModuleCache.get(moduleName);
80
+ if (record === undefined) {
81
+ throw new Error('Module not found');
82
+ }
83
+ if (record.status !== 'pending') {
84
+ throw new Error('Module already resolved');
85
+ }
86
+ record.status = 'resolved';
87
+ record.pings.forEach(ping => ping(record.value));
88
}
89
90
it('suspends until module has loaded', async () => {
99
},
100
);
101
57
- expect(Scheduler).toFlushAndYield(['Loading...']);
102
+ await waitForAll(['Loading...']);
103
expect(root).not.toMatchRenderedOutput('Hi');
104
60
- await Promise.resolve();
105
+ await resolveFakeImport(Text);
106
62
- expect(Scheduler).toFlushAndYield(['Hi']);
107
+ await waitForAll(['Hi']);
108
expect(root).toMatchRenderedOutput('Hi');
109
110
// Should not suspend on update
113
<LazyText text="Hi again" />
114
</Suspense>,
115
);
71
- expect(Scheduler).toFlushAndYield(['Hi again']);
116
+ await waitForAll(['Hi again']);
117
expect(root).toMatchRenderedOutput('Hi again');
118
});
119
130
</Suspense>,
131
);
132
88
- expect(Scheduler).toHaveYielded(['Hi']);
133
+ assertLog(['Hi']);
134
expect(root).toMatchRenderedOutput('Hi');
135
});
136
160
</Suspense>
161
</ErrorBoundary>,
162
);
118
- expect(Scheduler).toHaveYielded([]);
163
+ assertLog([]);
164
expect(root).toMatchRenderedOutput('Error: oh no');
165
});
166
173
return <Text text="Bar" />;
174
}
175
131
- const promiseForFoo = delay(100).then(() => fakeImport(Foo));
132
- const promiseForBar = delay(500).then(() => fakeImport(Bar));
133
-
134
- const LazyFoo = lazy(() => promiseForFoo);
135
- const LazyBar = lazy(() => promiseForBar);
176
+ const LazyFoo = lazy(() => fakeImport(Foo));
177
+ const LazyBar = lazy(() => fakeImport(Bar));
178
179
const root = ReactTestRenderer.create(
180
<Suspense fallback={<Text text="Loading..." />}>
186
},
187
);
188
147
- expect(Scheduler).toFlushAndYield(['Loading...']);
189
+ await waitForAll(['Loading...']);
190
expect(root).not.toMatchRenderedOutput('FooBar');
191
150
- jest.advanceTimersByTime(100);
151
- await promiseForFoo;
192
+ await resolveFakeImport(Foo);
193
153
- expect(Scheduler).toFlushAndYield(['Foo']);
194
+ await waitForAll(['Foo']);
195
expect(root).not.toMatchRenderedOutput('FooBar');
196
156
- jest.advanceTimersByTime(500);
157
- await promiseForBar;
197
+ await resolveFakeImport(Bar);
198
159
- expect(Scheduler).toFlushAndYield(['Foo', 'Bar']);
199
+ await waitForAll(['Foo', 'Bar']);
200
expect(root).toMatchRenderedOutput('FooBar');
201
});
202
213
unstable_isConcurrent: true,
214
},
215
);
176
- expect(Scheduler).toFlushAndYield(['Loading...']);
216
+ await waitForThrow('Element type is invalid');
217
+ assertLog(['Loading...']);
218
expect(root).not.toMatchRenderedOutput('Hi');
178
-
179
- await Promise.resolve();
180
-
181
- expect(Scheduler).toFlushAndThrow('Element type is invalid');
219
if (__DEV__) {
220
expect(console.error).toHaveBeenCalledTimes(3);
221
expect(console.error.mock.calls[0][0]).toContain(
238
},
239
);
240
204
- expect(Scheduler).toFlushAndYield(['Loading...']);
241
+ await waitForThrow('Bad network');
242
+ assertLog(['Loading...']);
243
expect(root).not.toMatchRenderedOutput('Hi');
206
-
207
- try {
208
- await Promise.resolve();
209
- } catch (e) {}
210
-
211
- expect(Scheduler).toFlushAndThrow('Bad network');
244
});
245
246
it('mount and reorder', async () => {
279
unstable_isConcurrent: true,
280
});
281
250
- expect(Scheduler).toFlushAndYield(['Loading...']);
282
+ await waitForAll(['Loading...']);
283
expect(root).not.toMatchRenderedOutput('AB');
284
253
- await LazyChildA;
254
- await LazyChildB;
285
+ await resolveFakeImport(Child);
286
256
- expect(Scheduler).toFlushAndYield([
257
- 'A',
258
- 'B',
259
- 'Did mount: A',
260
- 'Did mount: B',
261
- ]);
287
+ await waitForAll(['A', 'B', 'Did mount: A', 'Did mount: B']);
288
expect(root).toMatchRenderedOutput('AB');
289
290
// Swap the position of A and B
291
root.update(<Parent swap={true} />);
266
- expect(Scheduler).toFlushAndYield([
267
- 'B',
268
- 'A',
269
- 'Did update: B',
270
- 'Did update: A',
271
- ]);
292
+ await waitForAll(['B', 'A', 'Did update: B', 'Did update: A']);
293
expect(root).toMatchRenderedOutput('BA');
294
});
295
309
},
310
);
311
291
- expect(Scheduler).toFlushAndYield(['Loading...']);
312
+ await waitForAll(['Loading...']);
313
expect(root).not.toMatchRenderedOutput('Hi');
314
294
- await Promise.resolve();
315
+ await resolveFakeImport(T);
316
317
expect(() => expect(Scheduler).toFlushAndYield(['Hi'])).toErrorDev(
318
'Warning: T: Support for defaultProps ' +
328
<LazyText />
329
</Suspense>,
330
);
310
- expect(Scheduler).toFlushAndYield(['Hi again']);
331
+ await waitForAll(['Hi again']);
332
expect(root).toMatchRenderedOutput('Hi again');
333
});
334
364
unstable_isConcurrent: true,
365
},
366
);
346
- expect(Scheduler).toFlushAndYield(['Loading...']);
367
+ await waitForAll(['Loading...']);
368
expect(root).not.toMatchRenderedOutput('SiblingA');
369
349
- await Promise.resolve();
370
+ await resolveFakeImport(LazyImpl);
371
372
expect(() =>
373
expect(Scheduler).toFlushAndYield(['Lazy', 'Sibling', 'A']),
381
382
// Lazy should not re-render
383
stateful.current.setState({text: 'B'});
363
- expect(Scheduler).toFlushAndYield(['B']);
384
+ await waitForAll(['B']);
385
expect(root).toMatchRenderedOutput('SiblingB');
386
});
387
411
unstable_isConcurrent: true,
412
},
413
);
393
- expect(Scheduler).toFlushAndYield(['Not lazy: 0', 'Loading...']);
414
+ await waitForAll(['Not lazy: 0', 'Loading...']);
415
expect(root).not.toMatchRenderedOutput('Not lazy: 0Lazy: 0');
416
396
- await Promise.resolve();
417
+ await resolveFakeImport(LazyImpl);
418
398
- expect(Scheduler).toFlushAndYield(['Lazy: 0']);
419
+ await waitForAll(['Lazy: 0']);
420
expect(root).toMatchRenderedOutput('Not lazy: 0Lazy: 0');
421
422
// Should bailout due to unchanged props and state
423
instance1.current.setState(null);
403
- expect(Scheduler).toFlushAndYield([]);
424
+ await waitForAll([]);
425
expect(root).toMatchRenderedOutput('Not lazy: 0Lazy: 0');
426
427
// Should bailout due to unchanged props and state
428
instance2.current.setState(null);
408
- expect(Scheduler).toFlushAndYield([]);
429
+ await waitForAll([]);
430
expect(root).toMatchRenderedOutput('Not lazy: 0Lazy: 0');
431
});
432
457
unstable_isConcurrent: true,
458
},
459
);
439
- expect(Scheduler).toFlushAndYield(['Not lazy: 0', 'Loading...']);
460
+ await waitForAll(['Not lazy: 0', 'Loading...']);
461
expect(root).not.toMatchRenderedOutput('Not lazy: 0Lazy: 0');
462
442
- await Promise.resolve();
463
+ await resolveFakeImport(LazyImpl);
464
444
- expect(Scheduler).toFlushAndYield(['Lazy: 0']);
465
+ await waitForAll(['Lazy: 0']);
466
expect(root).toMatchRenderedOutput('Not lazy: 0Lazy: 0');
467
468
// Should bailout due to shallow equal props and state
469
instance1.current.setState({});
449
- expect(Scheduler).toFlushAndYield([]);
470
+ await waitForAll([]);
471
expect(root).toMatchRenderedOutput('Not lazy: 0Lazy: 0');
472
473
// Should bailout due to shallow equal props and state
474
instance2.current.setState({});
454
- expect(Scheduler).toFlushAndYield([]);
475
+ await waitForAll([]);
476
expect(root).toMatchRenderedOutput('Not lazy: 0Lazy: 0');
477
});
478
539
},
540
);
541
521
- expect(Scheduler).toFlushAndYield(['Loading...']);
542
+ await waitForAll(['Loading...']);
543
expect(root).not.toMatchRenderedOutput('A1');
544
524
- await Promise.resolve();
545
+ await resolveFakeImport(C);
546
526
- expect(Scheduler).toFlushAndYield([
547
+ await waitForAll([
548
'constructor: A',
549
'getDerivedStateFromProps: A',
550
'A1',
556
<LazyClass num={2} />
557
</Suspense>,
558
);
538
- expect(Scheduler).toFlushAndYield([
559
+ await waitForAll([
560
'getDerivedStateFromProps: A',
561
'shouldComponentUpdate: A -> A',
562
'A2',
570
<LazyClass num={3} />
571
</Suspense>,
572
);
552
- expect(Scheduler).toFlushAndYield([
573
+ await waitForAll([
574
'getDerivedStateFromProps: A',
575
'shouldComponentUpdate: A -> A',
576
'A3',
616
</Suspense>,
617
);
618
598
- expect(Scheduler).toHaveYielded(['Loading...']);
599
- expect(Scheduler).toFlushAndYield([]);
619
+ assertLog(['Loading...']);
620
+ await waitForAll([]);
621
expect(root).toMatchRenderedOutput('Loading...');
622
602
- await Promise.resolve();
623
+ await resolveFakeImport(C);
624
604
- expect(Scheduler).toHaveYielded([]);
625
+ assertLog([]);
626
627
root.update(
628
<Suspense fallback={<Text text="Loading..." />}>
630
</Suspense>,
631
);
632
612
- expect(Scheduler).toHaveYielded(['UNSAFE_componentWillMount: A', 'A2']);
633
+ assertLog(['UNSAFE_componentWillMount: A', 'A2']);
634
expect(root).toMatchRenderedOutput('A2');
635
636
root.update(
638
<LazyClass num={3} />
639
</Suspense>,
640
);
620
- expect(Scheduler).toHaveYielded([
641
+ assertLog([
642
'UNSAFE_componentWillReceiveProps: A -> A',
643
'UNSAFE_componentWillUpdate: A -> A',
644
'A3',
645
]);
625
- expect(Scheduler).toFlushAndYield([]);
646
+ await waitForAll([]);
647
expect(root).toMatchRenderedOutput('A3');
648
});
649
672
},
673
);
674
654
- expect(Scheduler).toFlushAndYield(['Loading...']);
675
+ await waitForAll(['Loading...']);
676
expect(root).not.toMatchRenderedOutput('Hi Bye');
677
657
- await Promise.resolve();
678
+ await resolveFakeImport(T);
679
expect(() => expect(Scheduler).toFlushAndYield(['Hi Bye'])).toErrorDev(
680
'Warning: T: Support for defaultProps ' +
681
'will be removed from function components in a future major ' +
689
<LazyText outer="World" />
690
</Suspense>,
691
);
671
- expect(Scheduler).toFlushAndYield(['Hi World']);
692
+ await waitForAll(['Hi World']);
693
expect(root).toMatchRenderedOutput('Hi World');
694
695
root.update(
697
<LazyText inner="Friends" />
698
</Suspense>,
699
);
679
- expect(Scheduler).toFlushAndYield(['Friends Bye']);
700
+ await waitForAll(['Friends Bye']);
701
expect(root).toMatchRenderedOutput('Friends Bye');
702
});
703
713
},
714
);
715
695
- expect(Scheduler).toFlushAndYield(['Loading...']);
716
+ await waitForAll(['Loading...']);
717
697
- await Promise.resolve();
718
+ await resolveFakeImport(42);
719
root.update(
720
<Suspense fallback={<Text text="Loading..." />}>
721
<BadLazy />
740
},
741
);
742
722
- expect(Scheduler).toFlushAndYield(['Loading...']);
743
+ await waitForAll(['Loading...']);
744
expect(root).not.toMatchRenderedOutput('Hello');
745
725
- await Promise.resolve();
746
+ await resolveFakeImport(Lazy1);
747
root.update(
748
<Suspense fallback={<Text text="Loading..." />}>
749
<Lazy2 text="Hello" />
794
},
795
);
796
776
- expect(Scheduler).toFlushAndYield(['Loading...']);
797
+ await waitForAll(['Loading...']);
798
799
expect(root).not.toMatchRenderedOutput('22');
800
801
// Mount
781
- await Promise.resolve();
782
- expect(() => {
783
- Scheduler.unstable_flushAll();
802
+ await resolveFakeImport(Add);
803
+ await expect(async () => {
804
+ await waitForAll([]);
805
}).toErrorDev(
806
shouldWarnAboutFunctionDefaultProps
807
? [
820
expect(root).toMatchRenderedOutput('22');
821
822
// Update
802
- expect(() => {
823
+ await expect(async () => {
824
root.update(
825
<Suspense fallback={<Text text="Loading..." />}>
826
<LazyAdd inner={false} outer={false} />
827
</Suspense>,
828
);
808
- expect(Scheduler).toFlushWithoutYielding();
829
+ await waitForAll([]);
830
}).toErrorDev(
831
'Invalid prop `inner` of type `boolean` supplied to `Add`, expected `number`.',
832
);
992
},
993
);
994
974
- expect(Scheduler).toFlushAndYield(['Loading...']);
995
+ await waitForAll(['Loading...']);
996
expect(root).not.toMatchRenderedOutput('Inner default text');
997
998
// Mount
978
- await Promise.resolve();
979
- expect(() => {
980
- expect(Scheduler).toFlushAndYield(['Inner default text']);
999
+ await resolveFakeImport(T);
1000
+ await expect(async () => {
1001
+ await waitForAll(['Inner default text']);
1002
}).toErrorDev([
1003
'T: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
1004
'The prop `text` is marked as required in `T`, but its value is `undefined`',
1006
expect(root).toMatchRenderedOutput('Inner default text');
1007
1008
// Update
988
- expect(() => {
1009
+ await expect(async () => {
1010
root.update(
1011
<Suspense fallback={<Text text="Loading..." />}>
1012
<LazyText text={null} />
1013
</Suspense>,
1014
);
994
- expect(Scheduler).toFlushAndYield([null]);
1015
+ await waitForAll([null]);
1016
}).toErrorDev(
1017
'The prop `text` is marked as required in `T`, but its value is `null`',
1018
);
1020
});
1021
1022
it('includes lazy-loaded component in warning stack', async () => {
1023
+ const Foo = props => <div>{[<Text text="A" />, <Text text="B" />]}</div>;
1024
const LazyFoo = lazy(() => {
1025
Scheduler.unstable_yieldValue('Started loading');
1004
- const Foo = props => <div>{[<Text text="A" />, <Text text="B" />]}</div>;
1026
return fakeImport(Foo);
1027
});
1028
1035
},
1036
);
1037
1017
- expect(Scheduler).toFlushAndYield(['Started loading', 'Loading...']);
1038
+ await waitForAll(['Started loading', 'Loading...']);
1039
expect(root).not.toMatchRenderedOutput(<div>AB</div>);
1040
1020
- await Promise.resolve();
1041
+ await resolveFakeImport(Foo);
1042
1022
- expect(() => {
1023
- expect(Scheduler).toFlushAndYield(['A', 'B']);
1043
+ await expect(async () => {
1044
+ await waitForAll(['A', 'B']);
1045
}).toErrorDev(' in Text (at **)\n' + ' in Foo (at **)');
1046
expect(root).toMatchRenderedOutput(<div>AB</div>);
1047
});
1048
1049
it('supports class and forwardRef components', async () => {
1029
- const LazyClass = lazy(() => {
1030
- class Foo extends React.Component {
1031
- render() {
1032
- return <Text text="Foo" />;
1033
- }
1050
+ class Foo extends React.Component {
1051
+ render() {
1052
+ return <Text text="Foo" />;
1053
}
1054
+ }
1055
+ const LazyClass = lazy(() => {
1056
return fakeImport(Foo);
1057
});
1058
1038
- const LazyForwardRef = lazy(() => {
1039
- class Bar extends React.Component {
1040
- render() {
1041
- return <Text text="Bar" />;
1042
- }
1059
+ class Bar extends React.Component {
1060
+ render() {
1061
+ return <Text text="Bar" />;
1062
}
1044
- return fakeImport(
1045
- React.forwardRef((props, ref) => {
1046
- Scheduler.unstable_yieldValue('forwardRef');
1047
- return <Bar ref={ref} />;
1048
- }),
1049
- );
1063
+ }
1064
+ const ForwardRefBar = React.forwardRef((props, ref) => {
1065
+ Scheduler.unstable_yieldValue('forwardRef');
1066
+ return <Bar ref={ref} />;
1067
+ });
1068
+
1069
+ const LazyForwardRef = lazy(() => {
1070
+ return fakeImport(ForwardRefBar);
1071
});
1072
1073
const ref = React.createRef();
1081
},
1082
);
1083
1063
- expect(Scheduler).toFlushAndYield(['Loading...']);
1084
+ await waitForAll(['Loading...']);
1085
expect(root).not.toMatchRenderedOutput('FooBar');
1086
expect(ref.current).toBe(null);
1087
1067
- await Promise.resolve();
1088
+ await resolveFakeImport(Foo);
1089
+ await waitForAll(['Foo']);
1090
1069
- expect(Scheduler).toFlushAndYield(['Foo', 'forwardRef', 'Bar']);
1091
+ await resolveFakeImport(ForwardRefBar);
1092
+
1093
+ await waitForAll(['Foo', 'forwardRef', 'Bar']);
1094
expect(root).toMatchRenderedOutput('FooBar');
1095
expect(ref.current).not.toBe(null);
1096
});
1112
unstable_isConcurrent: true,
1113
},
1114
);
1091
- expect(Scheduler).toFlushAndYield(['Loading...']);
1115
+ await waitForAll(['Loading...']);
1116
expect(root).not.toMatchRenderedOutput('4');
1117
1118
// Mount
1095
- await Promise.resolve();
1096
- expect(() => {
1097
- expect(Scheduler).toFlushWithoutYielding();
1119
+ await resolveFakeImport(Add);
1120
+ await expect(async () => {
1121
+ await waitForAll([]);
1122
}).toErrorDev(
1123
'Unknown: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
1124
);
1130
<LazyAdd outer={2} />
1131
</Suspense>,
1132
);
1109
- expect(Scheduler).toFlushWithoutYielding();
1133
+ await waitForAll([]);
1134
expect(root).toMatchRenderedOutput('4');
1135
1136
// Update
1139
<LazyAdd outer={3} />
1140
</Suspense>,
1141
);
1118
- expect(Scheduler).toFlushWithoutYielding();
1142
+ await waitForAll([]);
1143
expect(root).toMatchRenderedOutput('5');
1144
1145
// Update (shallowly equal)
1148
<LazyAdd outer={3} />
1149
</Suspense>,
1150
);
1127
- expect(Scheduler).toFlushWithoutYielding();
1151
+ await waitForAll([]);
1152
expect(root).toMatchRenderedOutput('5');
1153
1154
// Update (explicit props)
1157
<LazyAdd outer={1} inner={1} />
1158
</Suspense>,
1159
);
1136
- expect(Scheduler).toFlushWithoutYielding();
1160
+ await waitForAll([]);
1161
expect(root).toMatchRenderedOutput('2');
1162
1163
// Update (explicit props, shallowly equal)
1166
<LazyAdd outer={1} inner={1} />
1167
</Suspense>,
1168
);
1145
- expect(Scheduler).toFlushWithoutYielding();
1169
+ await waitForAll([]);
1170
expect(root).toMatchRenderedOutput('2');
1171
1172
// Update
1175
<LazyAdd outer={1} />
1176
</Suspense>,
1177
);
1154
- expect(Scheduler).toFlushWithoutYielding();
1178
+ await waitForAll([]);
1179
expect(root).toMatchRenderedOutput('3');
1180
});
1181
1200
unstable_isConcurrent: true,
1201
},
1202
);
1179
- expect(Scheduler).toFlushAndYield(['Loading...']);
1203
+ await waitForAll(['Loading...']);
1204
expect(root).not.toMatchRenderedOutput('4');
1205
1206
// Mount
1183
- await Promise.resolve();
1184
- expect(() => {
1185
- expect(Scheduler).toFlushWithoutYielding();
1207
+ await resolveFakeImport(Add);
1208
+ await expect(async () => {
1209
+ await waitForAll([]);
1210
}).toErrorDev([
1211
'Memo: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
1212
'Unknown: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
1219
<LazyAdd outer={3} />
1220
</Suspense>,
1221
);
1198
- expect(Scheduler).toFlushWithoutYielding();
1222
+ await waitForAll([]);
1223
expect(root).toMatchRenderedOutput('5');
1224
1225
// Update
1228
<LazyAdd />
1229
</Suspense>,
1230
);
1207
- expect(Scheduler).toFlushWithoutYielding();
1231
+ await waitForAll([]);
1232
expect(root).toMatchRenderedOutput('2');
1233
});
1234
1235
it('warns about ref on functions for lazy-loaded components', async () => {
1236
+ const Foo = props => <div />;
1237
const LazyFoo = lazy(() => {
1213
- const Foo = props => <div />;
1238
return fakeImport(Foo);
1239
});
1240
1248
},
1249
);
1250
1227
- expect(Scheduler).toFlushAndYield(['Loading...']);
1228
- await Promise.resolve();
1229
- expect(() => {
1230
- expect(Scheduler).toFlushAndYield([]);
1251
+ await waitForAll(['Loading...']);
1252
+ await resolveFakeImport(Foo);
1253
+ await expect(async () => {
1254
+ await waitForAll([]);
1255
}).toErrorDev('Function components cannot be given refs');
1256
});
1257
1258
it('should error with a component stack naming the resolved component', async () => {
1259
let componentStackMessage;
1260
1237
- const LazyText = lazy(() =>
1238
- fakeImport(function ResolvedText() {
1239
- throw new Error('oh no');
1240
- }),
1241
- );
1261
+ function ResolvedText() {
1262
+ throw new Error('oh no');
1263
+ }
1264
+ const LazyText = lazy(() => fakeImport(ResolvedText));
1265
1266
class ErrorBoundary extends React.Component {
1267
state = {error: null};
1287
{unstable_isConcurrent: true},
1288
);
1289
1267
- expect(Scheduler).toFlushAndYield(['Loading...']);
1268
-
1269
- try {
1270
- await Promise.resolve();
1271
- } catch (e) {}
1290
+ await waitForAll(['Loading...']);
1291
1273
- expect(Scheduler).toFlushAndYield([]);
1292
+ await resolveFakeImport(ResolvedText);
1293
+ await waitForAll([]);
1294
1295
expect(componentStackMessage).toContain('in ResolvedText');
1296
});
1327
</ErrorBoundary>,
1328
);
1329
1310
- expect(Scheduler).toHaveYielded([]);
1330
+ assertLog([]);
1331
1332
expect(componentStackMessage).toContain('in Lazy');
1333
});
1395
unstable_isConcurrent: true,
1396
});
1397
1378
- expect(Scheduler).toFlushAndYield(['Init A', 'Init B', 'Loading...']);
1398
+ await waitForAll(['Init A', 'Init B', 'Loading...']);
1399
expect(root).not.toMatchRenderedOutput('AB');
1400
1381
- await LazyChildA;
1382
- await LazyChildB;
1401
+ await resolveFakeImport(ChildA);
1402
+ await resolveFakeImport(ChildB);
1403
1384
- expect(Scheduler).toFlushAndYield([
1385
- 'A',
1386
- 'B',
1387
- 'Did mount: A',
1388
- 'Did mount: B',
1389
- ]);
1404
+ await waitForAll(['A', 'B', 'Did mount: A', 'Did mount: B']);
1405
expect(root).toMatchRenderedOutput('AB');
1406
1407
// Swap the position of A and B
1408
root.update(<Parent swap={true} />);
1394
- expect(Scheduler).toFlushAndYield(['Init B2', 'Loading...']);
1409
+ await waitForAll(['Init B2', 'Loading...']);
1410
jest.runAllTimers();
1411
1397
- expect(Scheduler).toHaveYielded(['Did unmount: A', 'Did unmount: B']);
1412
+ assertLog(['Did unmount: A', 'Did unmount: B']);
1413
1414
// The suspense boundary should've triggered now.
1415
expect(root).toMatchRenderedOutput('Loading...');
1416
await resolveB2({default: ChildB});
1417
1418
// We need to flush to trigger the second one to load.
1404
- expect(Scheduler).toFlushAndYield(['Init A2']);
1405
- await LazyChildA2;
1406
-
1407
- expect(Scheduler).toFlushAndYield([
1408
- 'b',
1409
- 'a',
1410
- 'Did mount: b',
1411
- 'Did mount: a',
1412
- ]);
1419
+ await waitForAll(['Init A2', 'b', 'a', 'Did mount: b', 'Did mount: a']);
1420
expect(root).toMatchRenderedOutput('ba');
1421
});
1422
1477
unstable_isConcurrent: false,
1478
});
1479
1473
- expect(Scheduler).toHaveYielded(['Init A', 'Init B', 'Loading...']);
1480
+ assertLog(['Init A', 'Init B', 'Loading...']);
1481
expect(root).not.toMatchRenderedOutput('AB');
1482
1476
- await LazyChildA;
1477
- await LazyChildB;
1483
+ await resolveFakeImport(ChildA);
1484
+ await resolveFakeImport(ChildB);
1485
1479
- expect(Scheduler).toFlushAndYield([
1480
- 'A',
1481
- 'B',
1482
- 'Did mount: A',
1483
- 'Did mount: B',
1484
- ]);
1486
+ await waitForAll(['A', 'B', 'Did mount: A', 'Did mount: B']);
1487
expect(root).toMatchRenderedOutput('AB');
1488
1489
// Swap the position of A and B
1490
root.update(<Parent swap={true} />);
1489
- expect(Scheduler).toHaveYielded(['Init B2', 'Loading...']);
1490
- await LazyChildB2;
1491
- // We need to flush to trigger the second one to load.
1492
- expect(Scheduler).toFlushAndYield(['Init A2']);
1493
- await LazyChildA2;
1494
-
1495
- expect(Scheduler).toFlushAndYield([
1496
- 'b',
1497
- 'a',
1498
- 'Did update: b',
1499
- 'Did update: a',
1500
- ]);
1491
+ assertLog(['Init B2', 'Loading...']);
1492
+ await waitForAll(['Init A2', 'b', 'a', 'Did update: b', 'Did update: a']);
1493
expect(root).toMatchRenderedOutput('ba');
1494
});
1495
1506
}
1507
}
1508
1509
+ const ChildA = <Child key="A" label="A" />;
1510
const lazyChildA = lazy(() => {
1511
Scheduler.unstable_yieldValue('Init A');
1519
- return fakeImport(<Child key="A" label="A" />);
1512
+ return fakeImport(ChildA);
1513
});
1514
+ const ChildB = <Child key="B" label="B" />;
1515
const lazyChildB = lazy(() => {
1516
Scheduler.unstable_yieldValue('Init B');
1523
- return fakeImport(<Child key="B" label="B" />);
1517
+ return fakeImport(ChildB);
1518
});
1519
+ const ChildA2 = <Child key="A" label="a" />;
1520
const lazyChildA2 = lazy(() => {
1521
Scheduler.unstable_yieldValue('Init A2');
1527
- return fakeImport(<Child key="A" label="a" />);
1522
+ return fakeImport(ChildA2);
1523
});
1524
+ const ChildB2 = <Child key="B" label="b" />;
1525
const lazyChildB2 = lazy(() => {
1526
Scheduler.unstable_yieldValue('Init B2');
1531
- return fakeImport(<Child key="B" label="b" />);
1527
+ return fakeImport(ChildB2);
1528
});
1529
1530
function Parent({swap}) {
1539
unstable_isConcurrent: true,
1540
});
1541
1546
- expect(Scheduler).toFlushAndYield(['Init A', 'Loading...']);
1542
+ await waitForAll(['Init A', 'Loading...']);
1543
expect(root).not.toMatchRenderedOutput('AB');
1544
1549
- await lazyChildA;
1545
+ await resolveFakeImport(ChildA);
1546
// We need to flush to trigger the B to load.
1551
- expect(Scheduler).toFlushAndYield(['Init B']);
1552
- await lazyChildB;
1553
-
1554
- expect(Scheduler).toFlushAndYield([
1555
- 'A',
1556
- 'B',
1557
- 'Did mount: A',
1558
- 'Did mount: B',
1559
- ]);
1547
+ await waitForAll(['Init B']);
1548
+ await resolveFakeImport(ChildB);
1549
+
1550
+ await waitForAll(['A', 'B', 'Did mount: A', 'Did mount: B']);
1551
expect(root).toMatchRenderedOutput('AB');
1552
1553
// Swap the position of A and B
1554
React.startTransition(() => {
1555
root.update(<Parent swap={true} />);
1556
});
1566
- expect(Scheduler).toFlushAndYield(['Init B2', 'Loading...']);
1567
- await lazyChildB2;
1557
+ await waitForAll(['Init B2', 'Loading...']);
1558
+ await resolveFakeImport(ChildB2);
1559
// We need to flush to trigger the second one to load.
1569
- expect(Scheduler).toFlushAndYield(['Init A2', 'Loading...']);
1570
- await lazyChildA2;
1571
-
1572
- expect(Scheduler).toFlushAndYield([
1573
- 'b',
1574
- 'a',
1575
- 'Did update: b',
1576
- 'Did update: a',
1577
- ]);
1560
+ await waitForAll(['Init A2', 'Loading...']);
1561
+ await resolveFakeImport(ChildA2);
1562
+
1563
+ await waitForAll(['b', 'a', 'Did update: b', 'Did update: a']);
1564
expect(root).toMatchRenderedOutput('ba');
1565
});
1566
1577
}
1578
}
1579
1580
+ const ChildA = <Child key="A" label="A" />;
1581
const lazyChildA = lazy(() => {
1582
Scheduler.unstable_yieldValue('Init A');
1596
- return fakeImport(<Child key="A" label="A" />);
1583
+ return fakeImport(ChildA);
1584
});
1585
+ const ChildB = <Child key="B" label="B" />;
1586
const lazyChildB = lazy(() => {
1587
Scheduler.unstable_yieldValue('Init B');
1600
- return fakeImport(<Child key="B" label="B" />);
1588
+ return fakeImport(ChildB);
1589
});
1590
+ const ChildA2 = <Child key="A" label="a" />;
1591
const lazyChildA2 = lazy(() => {
1592
Scheduler.unstable_yieldValue('Init A2');
1604
- return fakeImport(<Child key="A" label="a" />);
1593
+ return fakeImport(ChildA2);
1594
});
1595
+ const ChildB2 = <Child key="B" label="b" />;
1596
const lazyChildB2 = lazy(() => {
1597
Scheduler.unstable_yieldValue('Init B2');
1608
- return fakeImport(<Child key="B" label="b" />);
1598
+ return fakeImport(ChildB2);
1599
});
1600
1601
function Parent({swap}) {
1610
unstable_isConcurrent: false,
1611
});
1612
1623
- expect(Scheduler).toHaveYielded(['Init A', 'Loading...']);
1613
+ assertLog(['Init A', 'Loading...']);
1614
expect(root).not.toMatchRenderedOutput('AB');
1615
1626
- await lazyChildA;
1616
+ await resolveFakeImport(ChildA);
1617
// We need to flush to trigger the B to load.
1628
- expect(Scheduler).toFlushAndYield(['Init B']);
1629
- await lazyChildB;
1630
-
1631
- expect(Scheduler).toFlushAndYield([
1632
- 'A',
1633
- 'B',
1634
- 'Did mount: A',
1635
- 'Did mount: B',
1636
- ]);
1618
+ await waitForAll(['Init B']);
1619
+ await resolveFakeImport(ChildB);
1620
+
1621
+ await waitForAll(['A', 'B', 'Did mount: A', 'Did mount: B']);
1622
expect(root).toMatchRenderedOutput('AB');
1623
1624
// Swap the position of A and B
1625
root.update(<Parent swap={true} />);
1641
- expect(Scheduler).toHaveYielded(['Init B2', 'Loading...']);
1642
- await lazyChildB2;
1626
+ assertLog(['Init B2', 'Loading...']);
1627
+ await resolveFakeImport(ChildB2);
1628
// We need to flush to trigger the second one to load.
1644
- expect(Scheduler).toFlushAndYield(['Init A2']);
1645
- await lazyChildA2;
1646
-
1647
- expect(Scheduler).toFlushAndYield([
1648
- 'b',
1649
- 'a',
1650
- 'Did update: b',
1651
- 'Did update: a',
1652
- ]);
1629
+ await waitForAll(['Init A2']);
1630
+ await resolveFakeImport(ChildA2);
1631
+
1632
+ await waitForAll(['b', 'a', 'Did update: b', 'Did update: a']);
1633
expect(root).toMatchRenderedOutput('ba');
1634
});
1635
});