@samitouri / QOS-React-2 / commits / d89f79d6e6

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 });