Add Suspense stress test
Dan committed
Apr 16, 2019 at 00:14 UTC
d89f79d6e6ce7b8eaada9d8596e594250651cb2f
3 files changed
+456
src/__tests__/__snapshots__/store-test.js.snap
+115
@@ -25,6 +25,121 @@ exports[`Store should filter DOM nodes from the store tree: 1: mount 1`] = `
25
<Child>
26
`;
27
28
+exports[`Store should handle a stress test for Suspense 1`] = `
29
+[root]
30
+ ▾ <Root>
31
+ <X>
32
+ ▾ <Suspense>
33
+ <A key="a">
34
+ <Y>
35
+`;
36
+
37
+exports[`Store should handle a stress test for Suspense 2`] = `
38
+[root]
39
+ ▾ <Root>
40
+ <X>
41
+ ▾ <Suspense>
42
+ <A key="a">
43
+ <Y>
44
+`;
45
+
46
+exports[`Store should handle a stress test for Suspense 3`] = `
47
+[root]
48
+ ▾ <Root>
49
+ <X>
50
+ ▾ <Suspense>
51
+ <A key="a">
52
+ <B key="b">
53
+ <C key="c">
54
+ <Y>
55
+`;
56
+
57
+exports[`Store should handle a stress test for Suspense 4`] = `
58
+[root]
59
+ ▾ <Root>
60
+ <X>
61
+ ▾ <Suspense>
62
+ <C key="c">
63
+ <B key="b">
64
+ <A key="a">
65
+ <Y>
66
+`;
67
+
68
+exports[`Store should handle a stress test for Suspense 5`] = `
69
+[root]
70
+ ▾ <Root>
71
+ <X>
72
+ ▾ <Suspense>
73
+ <C key="c">
74
+ <A key="a">
75
+ <Y>
76
+`;
77
+
78
+exports[`Store should handle a stress test for Suspense 6`] = `
79
+[root]
80
+ ▾ <Root>
81
+ <X>
82
+ ▾ <Suspense>
83
+ <C key="c">
84
+ <A key="a">
85
+ <Y>
86
+`;
87
+
88
+exports[`Store should handle a stress test for Suspense 7`] = `
89
+[root]
90
+ ▾ <Root>
91
+ <X>
92
+ ▾ <Suspense>
93
+ <C key="c">
94
+ <A key="a">
95
+ <Y>
96
+`;
97
+
98
+exports[`Store should handle a stress test for Suspense 8`] = `
99
+[root]
100
+ ▾ <Root>
101
+ <X>
102
+ ▾ <Suspense>
103
+ <A key="a">
104
+ <B key="b">
105
+ <Y>
106
+`;
107
+
108
+exports[`Store should handle a stress test for Suspense 9`] = `
109
+[root]
110
+ ▾ <Root>
111
+ <X>
112
+ ▾ <Suspense>
113
+ <A key="a">
114
+ <Y>
115
+`;
116
+
117
+exports[`Store should handle a stress test for Suspense 10`] = `
118
+[root]
119
+ ▾ <Root>
120
+ <X>
121
+ <Suspense>
122
+ <Y>
123
+`;
124
+
125
+exports[`Store should handle a stress test for Suspense 11`] = `
126
+[root]
127
+ ▾ <Root>
128
+ <X>
129
+ ▾ <Suspense>
130
+ <B key="b">
131
+ <Y>
132
+`;
133
+
134
+exports[`Store should handle a stress test for Suspense 12`] = `
135
+[root]
136
+ ▾ <Root>
137
+ <X>
138
+ ▾ <Suspense>
139
+ <A key="a">
140
+ <Y>
141
+`;
142
+
143
exports[`Store should handle a stress test with different tree operations: 1: abcde 1`] = `
144
[root]
145
▾ <Parent>
src/__tests__/setupTests.js
+1
@@ -33,6 +33,7 @@ env.beforeEach(() => {
33
34
initBackend(global.__REACT_DEVTOOLS_GLOBAL_HOOK__, agent, global);
35
36
+ global.bridge = bridge;
37
global.store = new Store(bridge);
38
});
39
env.afterEach(() => {
src/__tests__/store-test.js
+340
@@ -4,6 +4,7 @@ describe('Store', () => {
4
let React;
5
let ReactDOM;
6
let TestUtils;
7
+ // let bridge;
8
let store;
9
let print;
10
@@ -15,6 +16,7 @@ describe('Store', () => {
16
};
17
18
beforeEach(() => {
19
+ // bridge = global.bridge;
20
store = global.store;
21
22
React = require('react');
@@ -336,4 +338,342 @@ describe('Store', () => {
338
act(() => root.unmount());
339
expect(print(store)).toBe('');
340
});
341
+
342
+ it('should handle a stress test for Suspense', async () => {
343
+ const A = () => 'a';
344
+ const B = () => 'b';
345
+ const C = () => 'c';
346
+ const X = () => 'x';
347
+ const Y = () => 'y';
348
+ const Z = () => 'z';
349
+ const a = <A key="a" />;
350
+ const b = <B key="b" />;
351
+ const c = <C key="c" />;
352
+ // const x = <X key="x" />;
353
+ // const y = <Y key="y" />;
354
+ const z = <Z key="z" />;
355
+
356
+ // prettier-ignore
357
+ const steps = [
358
+ a,
359
+ [a],
360
+ [a, b, c],
361
+ [c, b, a],
362
+ [c, null, a],
363
+ <React.Fragment>{c}{a}</React.Fragment>,
364
+ <div>{c}{a}</div>,
365
+ <div><span>{a}</span>{b}</div>,
366
+ [[a]],
367
+ null,
368
+ b,
369
+ a
370
+ ];
371
+
372
+ const Never = () => {
373
+ throw new Promise(() => {});
374
+ };
375
+
376
+ const Root = ({ children }) => {
377
+ return children;
378
+ };
379
+
380
+ // 1. For each step, check Suspense can render them as initial primary content.
381
+ // This is the only step where we use Jest snapshots.
382
+ let snapshots = [];
383
+ let container = document.createElement('div');
384
+ for (let i = 0; i < steps.length; i++) {
385
+ act(() =>
386
+ ReactDOM.render(
387
+ <Root>
388
+ <X />
389
+ <React.Suspense fallback={z}>{steps[i]}</React.Suspense>
390
+ <Y />
391
+ </Root>,
392
+ container
393
+ )
394
+ );
395
+ // We snapshot each step once so it doesn't regress.
396
+ expect(store).toMatchSnapshot();
397
+ snapshots.push(print(store));
398
+ act(() => ReactDOM.unmountComponentAtNode(container));
399
+ expect(print(store)).toBe('');
400
+ }
401
+
402
+ // 2. Verify check Suspense can render same steps as initial fallback content.
403
+ for (let i = 0; i < steps.length; i++) {
404
+ act(() =>
405
+ ReactDOM.render(
406
+ <Root>
407
+ <X />
408
+ <React.Suspense fallback={steps[i]}>
409
+ <Z />
410
+ <Never />
411
+ <Z />
412
+ </React.Suspense>
413
+ <Y />
414
+ </Root>,
415
+ container
416
+ )
417
+ );
418
+ expect(print(store)).toEqual(snapshots[i]);
419
+ act(() => ReactDOM.unmountComponentAtNode(container));
420
+ expect(print(store)).toBe('');
421
+ }
422
+
423
+ // 3. Verify we can update from each step to each step in primary mode.
424
+ for (let i = 0; i < steps.length; i++) {
425
+ for (let j = 0; j < steps.length; j++) {
426
+ // Always start with a fresh container and steps[i].
427
+ container = document.createElement('div');
428
+ act(() =>
429
+ ReactDOM.render(
430
+ <Root>
431
+ <X />
432
+ <React.Suspense fallback={z}>{steps[i]}</React.Suspense>
433
+ <Y />
434
+ </Root>,
435
+ container
436
+ )
437
+ );
438
+ expect(print(store)).toEqual(snapshots[i]);
439
+ // Re-render with steps[j].
440
+ act(() =>
441
+ ReactDOM.render(
442
+ <Root>
443
+ <X />
444
+ <React.Suspense fallback={z}>{steps[j]}</React.Suspense>
445
+ <Y />
446
+ </Root>,
447
+ container
448
+ )
449
+ );
450
+ // Verify the successful transition to steps[j].
451
+ expect(print(store)).toEqual(snapshots[j]);
452
+ // Clean up after every iteration.
453
+ act(() => ReactDOM.unmountComponentAtNode(container));
454
+ expect(print(store)).toBe('');
455
+ }
456
+ }
457
+
458
+ // TODO: fix the bugs
459
+ // // 4. Verify we can update from each step to each step in fallback mode.
460
+ // for (let i = 0; i < steps.length; i++) {
461
+ // for (let j = 0; j < steps.length; j++) {
462
+ // // Always start with a fresh container and steps[i].
463
+ // container = document.createElement('div');
464
+ // act(() => ReactDOM.render(
465
+ // <Root>
466
+ // <X />
467
+ // <React.Suspense fallback={steps[i]}>
468
+ // <Z />
469
+ // <Never />
470
+ // <Z />
471
+ // </React.Suspense>
472
+ // <Y />
473
+ // </Root>,
474
+ // container
475
+ // ));
476
+ // expect(print(store)).toEqual(snapshots[i]);
477
+ // // Re-render with steps[j].
478
+ // act(() => ReactDOM.render(
479
+ // <Root>
480
+ // <X />
481
+ // <React.Suspense fallback={steps[j]}>
482
+ // <Z />
483
+ // <Never />
484
+ // <Z />
485
+ // </React.Suspense>
486
+ // <Y />
487
+ // </Root>,
488
+ // container
489
+ // ));
490
+ // // Verify the successful transition to steps[j].
491
+ // expect(print(store)).toEqual(snapshots[j]);
492
+ // // Clean up after every iteration.
493
+ // act(() => ReactDOM.unmountComponentAtNode(container));
494
+ // expect(print(store)).toBe('');
495
+ // }
496
+ // }
497
+
498
+ // 5. Verify we can update from each step to each step when moving primary -> fallback.
499
+ for (let i = 0; i < steps.length; i++) {
500
+ for (let j = 0; j < steps.length; j++) {
501
+ // Always start with a fresh container and steps[i].
502
+ container = document.createElement('div');
503
+ act(() =>
504
+ ReactDOM.render(
505
+ <Root>
506
+ <X />
507
+ <React.Suspense fallback={z}>{steps[i]}</React.Suspense>
508
+ <Y />
509
+ </Root>,
510
+ container
511
+ )
512
+ );
513
+ expect(print(store)).toEqual(snapshots[i]);
514
+ // Re-render with steps[j].
515
+ act(() =>
516
+ ReactDOM.render(
517
+ <Root>
518
+ <X />
519
+ <React.Suspense fallback={steps[j]}>
520
+ <Z />
521
+ <Never />
522
+ <Z />
523
+ </React.Suspense>
524
+ <Y />
525
+ </Root>,
526
+ container
527
+ )
528
+ );
529
+ // Verify the successful transition to steps[j].
530
+ expect(print(store)).toEqual(snapshots[j]);
531
+ // Clean up after every iteration.
532
+ act(() => ReactDOM.unmountComponentAtNode(container));
533
+ expect(print(store)).toBe('');
534
+ }
535
+ }
536
+
537
+ // TODO: fix the bugs
538
+ // 6. Verify we can update from each step to each step when moving fallback -> primary.
539
+ // for (let i = 0; i < steps.length; i++) {
540
+ // for (let j = 0; j < steps.length; j++) {
541
+ // // Always start with a fresh container and steps[i].
542
+ // container = document.createElement('div');
543
+ // act(() => ReactDOM.render(
544
+ // <Root>
545
+ // <X />
546
+ // <React.Suspense fallback={steps[i]}>
547
+ // <Z />
548
+ // <Never />
549
+ // <Z />
550
+ // </React.Suspense>
551
+ // <Y />
552
+ // </Root>,
553
+ // container
554
+ // ));
555
+ // expect(print(store)).toEqual(snapshots[i]);
556
+ // // Re-render with steps[j].
557
+ // act(() => ReactDOM.render(
558
+ // <Root>
559
+ // <X />
560
+ // <React.Suspense fallback={z}>
561
+ // {steps[j]}
562
+ // </React.Suspense>
563
+ // <Y />
564
+ // </Root>,
565
+ // container
566
+ // ));
567
+ // // Verify the successful transition to steps[j].
568
+ // expect(print(store)).toEqual(snapshots[j]);
569
+ // // Clean up after every iteration.
570
+ // act(() => ReactDOM.unmountComponentAtNode(container));
571
+ // expect(print(store)).toBe('');
572
+ // }
573
+ // }
574
+
575
+ // TODO: fix the bugs
576
+ // 7. Verify we can update from each step to each step when toggling Suspense.
577
+ // for (let i = 0; i < steps.length; i++) {
578
+ // for (let j = 0; j < steps.length; j++) {
579
+ // // Always start with a fresh container and steps[i].
580
+ // container = document.createElement('div');
581
+ // act(() => ReactDOM.render(
582
+ // <Root>
583
+ // <X />
584
+ // <React.Suspense fallback={steps[j]}>
585
+ // {steps[i]}
586
+ // </React.Suspense>
587
+ // <Y />
588
+ // </Root>,
589
+ // container
590
+ // ));
591
+
592
+ // // We get ID from the index in the tree above:
593
+ // // Root, X, Suspense, ...
594
+ // // ^ (index is 2)
595
+ // const suspenseID = store.getElementIDAtIndex(2);
596
+
597
+ // // Force fallback.
598
+ // expect(print(store)).toEqual(snapshots[i]);
599
+ // act(() => {
600
+ // const suspenseID = store.getElementIDAtIndex(2);
601
+ // bridge.send('overrideSuspense', {
602
+ // id: suspenseID,
603
+ // rendererID: store.getRendererIDForElement(suspenseID),
604
+ // forceFallback: true
605
+ // });
606
+ // })
607
+ // expect(print(store)).toEqual(snapshots[j]);
608
+
609
+ // // Stop forcing fallback.
610
+ // act(() => {
611
+ // bridge.send('overrideSuspense', {
612
+ // id: suspenseID,
613
+ // rendererID: store.getRendererIDForElement(suspenseID),
614
+ // forceFallback: false
615
+ // });
616
+ // })
617
+ // expect(print(store)).toEqual(snapshots[i]);
618
+
619
+ // // Trigger actual fallback.
620
+ // act(() => ReactDOM.render(
621
+ // <Root>
622
+ // <X />
623
+ // <React.Suspense fallback={steps[j]}>
624
+ // <Z />
625
+ // <Never />
626
+ // <Z />
627
+ // </React.Suspense>
628
+ // <Y />
629
+ // </Root>,
630
+ // container
631
+ // ));
632
+ // expect(print(store)).toEqual(snapshots[j]);
633
+
634
+ // // Force fallback while we're in fallback mode.
635
+ // act(() => {
636
+ // bridge.send('overrideSuspense', {
637
+ // id: suspenseID,
638
+ // rendererID: store.getRendererIDForElement(suspenseID),
639
+ // forceFallback: true
640
+ // });
641
+ // })
642
+ // // Keep seeing fallback content.
643
+ // expect(print(store)).toEqual(snapshots[j]);
644
+
645
+ // // Switch to primary mode.
646
+ // act(() => ReactDOM.render(
647
+ // <Root>
648
+ // <X />
649
+ // <React.Suspense fallback={steps[j]}>
650
+ // {steps[i]}
651
+ // </React.Suspense>
652
+ // <Y />
653
+ // </Root>,
654
+ // container
655
+ // ));
656
+ // // Fallback is still forced though.
657
+ // expect(print(store)).toEqual(snapshots[j]);
658
+
659
+ // // Stop forcing fallback. This reverts to primary content.
660
+ // act(() => {
661
+ // bridge.send('overrideSuspense', {
662
+ // id: suspenseID,
663
+ // rendererID: store.getRendererIDForElement(suspenseID),
664
+ // forceFallback: false
665
+ // });
666
+ // })
667
+ // // Now we see primary content.
668
+ // expect(print(store)).toEqual(snapshots[i]);
669
+
670
+ // // Clean up after every iteration.
671
+ // act(() => ReactDOM.unmountComponentAtNode(container));
672
+ // expect(print(store)).toBe('');
673
+ // }
674
+ // }
675
+
676
+ // TODO:
677
+ // Test Concurrent Mode
678
+ });
679
});