1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @flow
    
  8.  */
    
  9. 
    
  10. describe('StoreStressConcurrent', () => {
    
  11.   let React;
    
  12.   let ReactDOMClient;
    
  13.   let act;
    
  14.   let actAsync;
    
  15.   let bridge;
    
  16.   let store;
    
  17.   let print;
    
  18. 
    
  19.   beforeEach(() => {
    
  20.     bridge = global.bridge;
    
  21.     store = global.store;
    
  22.     store.collapseNodesByDefault = false;
    
  23. 
    
  24.     React = require('react');
    
  25.     ReactDOMClient = require('react-dom/client');
    
  26.     act = require('./utils').act;
    
  27.     // TODO: Figure out recommendation for concurrent mode tests, then replace
    
  28.     // this helper with the real thing.
    
  29.     actAsync = require('./utils').actAsync;
    
  30. 
    
  31.     print = require('./__serializers__/storeSerializer').print;
    
  32.   });
    
  33. 
    
  34.   // TODO: Remove this in favor of @gate pragma
    
  35.   if (!__EXPERIMENTAL__) {
    
  36.     it("empty test so Jest doesn't complain", () => {});
    
  37.     return;
    
  38.   }
    
  39. 
    
  40.   // This is a stress test for the tree mount/update/unmount traversal.
    
  41.   // It renders different trees that should produce the same output.
    
  42.   // @reactVersion >= 18.0
    
  43.   it('should handle a stress test with different tree operations (Concurrent Mode)', () => {
    
  44.     let setShowX;
    
  45.     const A = () => 'a';
    
  46.     const B = () => 'b';
    
  47.     const C = () => {
    
  48.       // We'll be manually flipping this component back and forth in the test.
    
  49.       // We only do this for a single node in order to verify that DevTools
    
  50.       // can handle a subtree switching alternates while other subtrees are memoized.
    
  51.       const [showX, _setShowX] = React.useState(false);
    
  52.       setShowX = _setShowX;
    
  53.       return showX ? <X /> : 'c';
    
  54.     };
    
  55.     const D = () => 'd';
    
  56.     const E = () => 'e';
    
  57.     const X = () => 'x';
    
  58.     const a = <A key="a" />;
    
  59.     const b = <B key="b" />;
    
  60.     const c = <C key="c" />;
    
  61.     const d = <D key="d" />;
    
  62.     const e = <E key="e" />;
    
  63. 
    
  64.     function Parent({children}) {
    
  65.       return children;
    
  66.     }
    
  67. 
    
  68.     // 1. Render a normal version of [a, b, c, d, e].
    
  69.     let container = document.createElement('div');
    
  70.     let root = ReactDOMClient.createRoot(container);
    
  71.     act(() => root.render(<Parent>{[a, b, c, d, e]}</Parent>));
    
  72.     expect(store).toMatchInlineSnapshot(
    
  73.       `
    
  74.       [root]
    
  75.         ▾ <Parent>
    
  76.             <A key="a">
    
  77.             <B key="b">
    
  78.             <C key="c">
    
  79.             <D key="d">
    
  80.             <E key="e">
    
  81.     `,
    
  82.     );
    
  83.     expect(container.textContent).toMatch('abcde');
    
  84.     const snapshotForABCDE = print(store);
    
  85. 
    
  86.     // 2. Render a version where <C /> renders an <X /> child instead of 'c'.
    
  87.     // This is how we'll test an update to a single component.
    
  88.     act(() => {
    
  89.       setShowX(true);
    
  90.     });
    
  91.     expect(store).toMatchInlineSnapshot(
    
  92.       `
    
  93.       [root]
    
  94.         ▾ <Parent>
    
  95.             <A key="a">
    
  96.             <B key="b">
    
  97.           ▾ <C key="c">
    
  98.               <X>
    
  99.             <D key="d">
    
  100.             <E key="e">
    
  101.     `,
    
  102.     );
    
  103.     expect(container.textContent).toMatch('abxde');
    
  104.     const snapshotForABXDE = print(store);
    
  105. 
    
  106.     // 3. Verify flipping it back produces the original result.
    
  107.     act(() => {
    
  108.       setShowX(false);
    
  109.     });
    
  110.     expect(container.textContent).toMatch('abcde');
    
  111.     expect(print(store)).toBe(snapshotForABCDE);
    
  112. 
    
  113.     // 4. Clean up.
    
  114.     act(() => root.unmount());
    
  115.     expect(print(store)).toBe('');
    
  116. 
    
  117.     // Now comes the interesting part.
    
  118.     // All of these cases are equivalent to [a, b, c, d, e] in output.
    
  119.     // We'll verify that DevTools produces the same snapshots for them.
    
  120.     // These cases are picked so that rendering them sequentially in the same
    
  121.     // container results in a combination of mounts, updates, unmounts, and reorders.
    
  122.     // prettier-ignore
    
  123.     const cases = [
    
  124.       [a, b, c, d, e],
    
  125.       [[a], b, c, d, e],
    
  126.       [[a, b], c, d, e],
    
  127.       [[a, b], c, [d, e]],
    
  128.       [[a, b], c, [d, '', e]],
    
  129.       [[a], b, c, d, [e]],
    
  130.       [a, b, [[c]], d, e],
    
  131.       [[a, ''], [b], [c], [d], [e]],
    
  132.       [a, b, [c, [d, ['', e]]]],
    
  133.       [a, b, c, d, e],
    
  134.       [<div key="0">{a}</div>, b, c, d, e],
    
  135.       [<div key="0">{a}{b}</div>, c, d, e],
    
  136.       [<div key="0">{a}{b}</div>, c, <div key="1">{d}{e}</div>],
    
  137.       [<div key="1">{a}{b}</div>, c, <div key="0">{d}{e}</div>],
    
  138.       [<div key="0">{a}{b}</div>, c, <div key="1">{d}{e}</div>],
    
  139.       [<div key="2">{a}{b}</div>, c, <div key="3">{d}{e}</div>],
    
  140.       [<span key="0">{a}</span>, b, c, d, [e]],
    
  141.       [a, b, <span key="0"><span>{c}</span></span>, d, e],
    
  142.       [<div key="0">{a}</div>, [b], <span key="1">{c}</span>, [d], <div key="2">{e}</div>],
    
  143.       [a, b, [c, <div key="0">{d}<span>{e}</span></div>], ''],
    
  144.       [a, [[]], b, c, [d, [[]], e]],
    
  145.       [[[a, b, c, d], e]],
    
  146.       [a, b, c, d, e],
    
  147.     ];
    
  148. 
    
  149.     // 5. Test fresh mount for each case.
    
  150.     for (let i = 0; i < cases.length; i++) {
    
  151.       // Ensure fresh mount.
    
  152.       container = document.createElement('div');
    
  153.       root = ReactDOMClient.createRoot(container);
    
  154. 
    
  155.       // Verify mounting 'abcde'.
    
  156.       act(() => root.render(<Parent>{cases[i]}</Parent>));
    
  157.       expect(container.textContent).toMatch('abcde');
    
  158.       expect(print(store)).toEqual(snapshotForABCDE);
    
  159. 
    
  160.       // Verify switching to 'abxde'.
    
  161.       act(() => {
    
  162.         setShowX(true);
    
  163.       });
    
  164.       expect(container.textContent).toMatch('abxde');
    
  165.       expect(print(store)).toBe(snapshotForABXDE);
    
  166. 
    
  167.       // Verify switching back to 'abcde'.
    
  168.       act(() => {
    
  169.         setShowX(false);
    
  170.       });
    
  171.       expect(container.textContent).toMatch('abcde');
    
  172.       expect(print(store)).toBe(snapshotForABCDE);
    
  173. 
    
  174.       // Clean up.
    
  175.       act(() => root.unmount());
    
  176.       expect(print(store)).toBe('');
    
  177.     }
    
  178. 
    
  179.     // 6. Verify *updates* by reusing the container between iterations.
    
  180.     // There'll be no unmounting until the very end.
    
  181.     container = document.createElement('div');
    
  182.     root = ReactDOMClient.createRoot(container);
    
  183.     for (let i = 0; i < cases.length; i++) {
    
  184.       // Verify mounting 'abcde'.
    
  185.       act(() => root.render(<Parent>{cases[i]}</Parent>));
    
  186.       expect(container.textContent).toMatch('abcde');
    
  187.       expect(print(store)).toEqual(snapshotForABCDE);
    
  188. 
    
  189.       // Verify switching to 'abxde'.
    
  190.       act(() => {
    
  191.         setShowX(true);
    
  192.       });
    
  193.       expect(container.textContent).toMatch('abxde');
    
  194.       expect(print(store)).toBe(snapshotForABXDE);
    
  195. 
    
  196.       // Verify switching back to 'abcde'.
    
  197.       act(() => {
    
  198.         setShowX(false);
    
  199.       });
    
  200.       expect(container.textContent).toMatch('abcde');
    
  201.       expect(print(store)).toBe(snapshotForABCDE);
    
  202.       // Don't unmount. Reuse the container between iterations.
    
  203.     }
    
  204.     act(() => root.unmount());
    
  205.     expect(print(store)).toBe('');
    
  206.   });
    
  207. 
    
  208.   // @reactVersion >= 18.0
    
  209.   it('should handle stress test with reordering (Concurrent Mode)', () => {
    
  210.     const A = () => 'a';
    
  211.     const B = () => 'b';
    
  212.     const C = () => 'c';
    
  213.     const D = () => 'd';
    
  214.     const E = () => 'e';
    
  215.     const a = <A key="a" />;
    
  216.     const b = <B key="b" />;
    
  217.     const c = <C key="c" />;
    
  218.     const d = <D key="d" />;
    
  219.     const e = <E key="e" />;
    
  220. 
    
  221.     // prettier-ignore
    
  222.     const steps = [
    
  223.       a,
    
  224.       b,
    
  225.       c,
    
  226.       d,
    
  227.       e,
    
  228.       [a],
    
  229.       [b],
    
  230.       [c],
    
  231.       [d],
    
  232.       [e],
    
  233.       [a, b],
    
  234.       [b, a],
    
  235.       [b, c],
    
  236.       [c, b],
    
  237.       [a, c],
    
  238.       [c, a],
    
  239.     ];
    
  240. 
    
  241.     const Root = ({children}) => {
    
  242.       return children;
    
  243.     };
    
  244. 
    
  245.     // 1. Capture the expected render result.
    
  246.     const snapshots = [];
    
  247.     let container = document.createElement('div');
    
  248.     for (let i = 0; i < steps.length; i++) {
    
  249.       const root = ReactDOMClient.createRoot(container);
    
  250.       act(() => root.render(<Root>{steps[i]}</Root>));
    
  251.       // We snapshot each step once so it doesn't regress.
    
  252.       snapshots.push(print(store));
    
  253.       act(() => root.unmount());
    
  254.       expect(print(store)).toBe('');
    
  255.     }
    
  256. 
    
  257.     expect(snapshots).toMatchInlineSnapshot(`
    
  258.       [
    
  259.         "[root]
    
  260.         ▾ <Root>
    
  261.             <A key="a">",
    
  262.         "[root]
    
  263.         ▾ <Root>
    
  264.             <B key="b">",
    
  265.         "[root]
    
  266.         ▾ <Root>
    
  267.             <C key="c">",
    
  268.         "[root]
    
  269.         ▾ <Root>
    
  270.             <D key="d">",
    
  271.         "[root]
    
  272.         ▾ <Root>
    
  273.             <E key="e">",
    
  274.         "[root]
    
  275.         ▾ <Root>
    
  276.             <A key="a">",
    
  277.         "[root]
    
  278.         ▾ <Root>
    
  279.             <B key="b">",
    
  280.         "[root]
    
  281.         ▾ <Root>
    
  282.             <C key="c">",
    
  283.         "[root]
    
  284.         ▾ <Root>
    
  285.             <D key="d">",
    
  286.         "[root]
    
  287.         ▾ <Root>
    
  288.             <E key="e">",
    
  289.         "[root]
    
  290.         ▾ <Root>
    
  291.             <A key="a">
    
  292.             <B key="b">",
    
  293.         "[root]
    
  294.         ▾ <Root>
    
  295.             <B key="b">
    
  296.             <A key="a">",
    
  297.         "[root]
    
  298.         ▾ <Root>
    
  299.             <B key="b">
    
  300.             <C key="c">",
    
  301.         "[root]
    
  302.         ▾ <Root>
    
  303.             <C key="c">
    
  304.             <B key="b">",
    
  305.         "[root]
    
  306.         ▾ <Root>
    
  307.             <A key="a">
    
  308.             <C key="c">",
    
  309.         "[root]
    
  310.         ▾ <Root>
    
  311.             <C key="c">
    
  312.             <A key="a">",
    
  313.       ]
    
  314.     `);
    
  315. 
    
  316.     // 2. Verify that we can update from every step to every other step and back.
    
  317.     for (let i = 0; i < steps.length; i++) {
    
  318.       for (let j = 0; j < steps.length; j++) {
    
  319.         container = document.createElement('div');
    
  320.         const root = ReactDOMClient.createRoot(container);
    
  321.         act(() => root.render(<Root>{steps[i]}</Root>));
    
  322.         expect(print(store)).toMatch(snapshots[i]);
    
  323.         act(() => root.render(<Root>{steps[j]}</Root>));
    
  324.         expect(print(store)).toMatch(snapshots[j]);
    
  325.         act(() => root.render(<Root>{steps[i]}</Root>));
    
  326.         expect(print(store)).toMatch(snapshots[i]);
    
  327.         act(() => root.unmount());
    
  328.         expect(print(store)).toBe('');
    
  329.       }
    
  330.     }
    
  331. 
    
  332.     // 3. Same test as above, but this time we wrap children in a host component.
    
  333.     for (let i = 0; i < steps.length; i++) {
    
  334.       for (let j = 0; j < steps.length; j++) {
    
  335.         container = document.createElement('div');
    
  336.         const root = ReactDOMClient.createRoot(container);
    
  337.         act(() =>
    
  338.           root.render(
    
  339.             <Root>
    
  340.               <div>{steps[i]}</div>
    
  341.             </Root>,
    
  342.           ),
    
  343.         );
    
  344.         expect(print(store)).toMatch(snapshots[i]);
    
  345.         act(() =>
    
  346.           root.render(
    
  347.             <Root>
    
  348.               <div>{steps[j]}</div>
    
  349.             </Root>,
    
  350.           ),
    
  351.         );
    
  352.         expect(print(store)).toMatch(snapshots[j]);
    
  353.         act(() =>
    
  354.           root.render(
    
  355.             <Root>
    
  356.               <div>{steps[i]}</div>
    
  357.             </Root>,
    
  358.           ),
    
  359.         );
    
  360.         expect(print(store)).toMatch(snapshots[i]);
    
  361.         act(() => root.unmount());
    
  362.         expect(print(store)).toBe('');
    
  363.       }
    
  364.     }
    
  365.   });
    
  366. 
    
  367.   // @reactVersion >= 18.0
    
  368.   it('should handle a stress test for Suspense (Concurrent Mode)', async () => {
    
  369.     const A = () => 'a';
    
  370.     const B = () => 'b';
    
  371.     const C = () => 'c';
    
  372.     const X = () => 'x';
    
  373.     const Y = () => 'y';
    
  374.     const Z = () => 'z';
    
  375.     const a = <A key="a" />;
    
  376.     const b = <B key="b" />;
    
  377.     const c = <C key="c" />;
    
  378.     const z = <Z key="z" />;
    
  379. 
    
  380.     // prettier-ignore
    
  381.     const steps = [
    
  382.       a,
    
  383.       [a],
    
  384.       [a, b, c],
    
  385.       [c, b, a],
    
  386.       [c, null, a],
    
  387.       <React.Fragment>{c}{a}</React.Fragment>,
    
  388.       <div>{c}{a}</div>,
    
  389.       <div><span>{a}</span>{b}</div>,
    
  390.       [[a]],
    
  391.       null,
    
  392.       b,
    
  393.       a,
    
  394.     ];
    
  395. 
    
  396.     const Never = () => {
    
  397.       throw new Promise(() => {});
    
  398.     };
    
  399. 
    
  400.     const Root = ({children}) => {
    
  401.       return children;
    
  402.     };
    
  403. 
    
  404.     // 1. For each step, check Suspense can render them as initial primary content.
    
  405.     // This is the only step where we use Jest snapshots.
    
  406.     const snapshots = [];
    
  407.     let container = document.createElement('div');
    
  408.     for (let i = 0; i < steps.length; i++) {
    
  409.       const root = ReactDOMClient.createRoot(container);
    
  410.       act(() =>
    
  411.         root.render(
    
  412.           <Root>
    
  413.             <X />
    
  414.             <React.Suspense fallback={z}>{steps[i]}</React.Suspense>
    
  415.             <Y />
    
  416.           </Root>,
    
  417.         ),
    
  418.       );
    
  419.       // We snapshot each step once so it doesn't regress.d
    
  420.       snapshots.push(print(store));
    
  421.       act(() => root.unmount());
    
  422.       expect(print(store)).toBe('');
    
  423.     }
    
  424. 
    
  425.     expect(snapshots).toMatchInlineSnapshot(`
    
  426.       [
    
  427.         "[root]
    
  428.         ▾ <Root>
    
  429.             <X>
    
  430.           ▾ <Suspense>
    
  431.               <A key="a">
    
  432.             <Y>",
    
  433.         "[root]
    
  434.         ▾ <Root>
    
  435.             <X>
    
  436.           ▾ <Suspense>
    
  437.               <A key="a">
    
  438.             <Y>",
    
  439.         "[root]
    
  440.         ▾ <Root>
    
  441.             <X>
    
  442.           ▾ <Suspense>
    
  443.               <A key="a">
    
  444.               <B key="b">
    
  445.               <C key="c">
    
  446.             <Y>",
    
  447.         "[root]
    
  448.         ▾ <Root>
    
  449.             <X>
    
  450.           ▾ <Suspense>
    
  451.               <C key="c">
    
  452.               <B key="b">
    
  453.               <A key="a">
    
  454.             <Y>",
    
  455.         "[root]
    
  456.         ▾ <Root>
    
  457.             <X>
    
  458.           ▾ <Suspense>
    
  459.               <C key="c">
    
  460.               <A key="a">
    
  461.             <Y>",
    
  462.         "[root]
    
  463.         ▾ <Root>
    
  464.             <X>
    
  465.           ▾ <Suspense>
    
  466.               <C key="c">
    
  467.               <A key="a">
    
  468.             <Y>",
    
  469.         "[root]
    
  470.         ▾ <Root>
    
  471.             <X>
    
  472.           ▾ <Suspense>
    
  473.               <C key="c">
    
  474.               <A key="a">
    
  475.             <Y>",
    
  476.         "[root]
    
  477.         ▾ <Root>
    
  478.             <X>
    
  479.           ▾ <Suspense>
    
  480.               <A key="a">
    
  481.               <B key="b">
    
  482.             <Y>",
    
  483.         "[root]
    
  484.         ▾ <Root>
    
  485.             <X>
    
  486.           ▾ <Suspense>
    
  487.               <A key="a">
    
  488.             <Y>",
    
  489.         "[root]
    
  490.         ▾ <Root>
    
  491.             <X>
    
  492.             <Suspense>
    
  493.             <Y>",
    
  494.         "[root]
    
  495.         ▾ <Root>
    
  496.             <X>
    
  497.           ▾ <Suspense>
    
  498.               <B key="b">
    
  499.             <Y>",
    
  500.         "[root]
    
  501.         ▾ <Root>
    
  502.             <X>
    
  503.           ▾ <Suspense>
    
  504.               <A key="a">
    
  505.             <Y>",
    
  506.       ]
    
  507.     `);
    
  508. 
    
  509.     // 2. Verify check Suspense can render same steps as initial fallback content.
    
  510.     for (let i = 0; i < steps.length; i++) {
    
  511.       const root = ReactDOMClient.createRoot(container);
    
  512.       act(() =>
    
  513.         root.render(
    
  514.           <Root>
    
  515.             <X />
    
  516.             <React.Suspense fallback={steps[i]}>
    
  517.               <Z />
    
  518.               <Never />
    
  519.               <Z />
    
  520.             </React.Suspense>
    
  521.             <Y />
    
  522.           </Root>,
    
  523.         ),
    
  524.       );
    
  525.       expect(print(store)).toEqual(snapshots[i]);
    
  526.       act(() => root.unmount());
    
  527.       expect(print(store)).toBe('');
    
  528.     }
    
  529. 
    
  530.     // 3. Verify we can update from each step to each step in primary mode.
    
  531.     for (let i = 0; i < steps.length; i++) {
    
  532.       for (let j = 0; j < steps.length; j++) {
    
  533.         // Always start with a fresh container and steps[i].
    
  534.         container = document.createElement('div');
    
  535.         const root = ReactDOMClient.createRoot(container);
    
  536.         act(() =>
    
  537.           root.render(
    
  538.             <Root>
    
  539.               <X />
    
  540.               <React.Suspense fallback={z}>{steps[i]}</React.Suspense>
    
  541.               <Y />
    
  542.             </Root>,
    
  543.           ),
    
  544.         );
    
  545.         expect(print(store)).toEqual(snapshots[i]);
    
  546.         // Re-render with steps[j].
    
  547.         act(() =>
    
  548.           root.render(
    
  549.             <Root>
    
  550.               <X />
    
  551.               <React.Suspense fallback={z}>{steps[j]}</React.Suspense>
    
  552.               <Y />
    
  553.             </Root>,
    
  554.           ),
    
  555.         );
    
  556.         // Verify the successful transition to steps[j].
    
  557.         expect(print(store)).toEqual(snapshots[j]);
    
  558.         // Check that we can transition back again.
    
  559.         act(() =>
    
  560.           root.render(
    
  561.             <Root>
    
  562.               <X />
    
  563.               <React.Suspense fallback={z}>{steps[i]}</React.Suspense>
    
  564.               <Y />
    
  565.             </Root>,
    
  566.           ),
    
  567.         );
    
  568.         expect(print(store)).toEqual(snapshots[i]);
    
  569.         // Clean up after every iteration.
    
  570.         act(() => root.unmount());
    
  571.         expect(print(store)).toBe('');
    
  572.       }
    
  573.     }
    
  574. 
    
  575.     // 4. Verify we can update from each step to each step in fallback mode.
    
  576.     for (let i = 0; i < steps.length; i++) {
    
  577.       for (let j = 0; j < steps.length; j++) {
    
  578.         // Always start with a fresh container and steps[i].
    
  579.         container = document.createElement('div');
    
  580.         const root = ReactDOMClient.createRoot(container);
    
  581.         act(() =>
    
  582.           root.render(
    
  583.             <Root>
    
  584.               <X />
    
  585.               <React.Suspense fallback={steps[i]}>
    
  586.                 <Z />
    
  587.                 <Never />
    
  588.                 <Z />
    
  589.               </React.Suspense>
    
  590.               <Y />
    
  591.             </Root>,
    
  592.           ),
    
  593.         );
    
  594.         expect(print(store)).toEqual(snapshots[i]);
    
  595.         // Re-render with steps[j].
    
  596.         act(() =>
    
  597.           root.render(
    
  598.             <Root>
    
  599.               <X />
    
  600.               <React.Suspense fallback={steps[j]}>
    
  601.                 <Z />
    
  602.                 <Never />
    
  603.                 <Z />
    
  604.               </React.Suspense>
    
  605.               <Y />
    
  606.             </Root>,
    
  607.           ),
    
  608.         );
    
  609.         // Verify the successful transition to steps[j].
    
  610.         expect(print(store)).toEqual(snapshots[j]);
    
  611.         // Check that we can transition back again.
    
  612.         act(() =>
    
  613.           root.render(
    
  614.             <Root>
    
  615.               <X />
    
  616.               <React.Suspense fallback={steps[i]}>
    
  617.                 <Z />
    
  618.                 <Never />
    
  619.                 <Z />
    
  620.               </React.Suspense>
    
  621.               <Y />
    
  622.             </Root>,
    
  623.           ),
    
  624.         );
    
  625.         expect(print(store)).toEqual(snapshots[i]);
    
  626.         // Clean up after every iteration.
    
  627.         act(() => root.unmount());
    
  628.         expect(print(store)).toBe('');
    
  629.       }
    
  630.     }
    
  631. 
    
  632.     // 5. Verify we can update from each step to each step when moving primary -> fallback.
    
  633.     for (let i = 0; i < steps.length; i++) {
    
  634.       for (let j = 0; j < steps.length; j++) {
    
  635.         // Always start with a fresh container and steps[i].
    
  636.         container = document.createElement('div');
    
  637.         const root = ReactDOMClient.createRoot(container);
    
  638.         act(() =>
    
  639.           root.render(
    
  640.             <Root>
    
  641.               <X />
    
  642.               <React.Suspense fallback={z}>{steps[i]}</React.Suspense>
    
  643.               <Y />
    
  644.             </Root>,
    
  645.           ),
    
  646.         );
    
  647.         expect(print(store)).toEqual(snapshots[i]);
    
  648.         // Re-render with steps[j].
    
  649.         act(() =>
    
  650.           root.render(
    
  651.             <Root>
    
  652.               <X />
    
  653.               <React.Suspense fallback={steps[j]}>
    
  654.                 <Z />
    
  655.                 <Never />
    
  656.                 <Z />
    
  657.               </React.Suspense>
    
  658.               <Y />
    
  659.             </Root>,
    
  660.           ),
    
  661.         );
    
  662.         // Verify the successful transition to steps[j].
    
  663.         expect(print(store)).toEqual(snapshots[j]);
    
  664.         // Check that we can transition back again.
    
  665.         act(() =>
    
  666.           root.render(
    
  667.             <Root>
    
  668.               <X />
    
  669.               <React.Suspense fallback={z}>{steps[i]}</React.Suspense>
    
  670.               <Y />
    
  671.             </Root>,
    
  672.           ),
    
  673.         );
    
  674.         expect(print(store)).toEqual(snapshots[i]);
    
  675.         // Clean up after every iteration.
    
  676.         act(() => root.unmount());
    
  677.         expect(print(store)).toBe('');
    
  678.       }
    
  679.     }
    
  680. 
    
  681.     // 6. Verify we can update from each step to each step when moving fallback -> primary.
    
  682.     for (let i = 0; i < steps.length; i++) {
    
  683.       for (let j = 0; j < steps.length; j++) {
    
  684.         // Always start with a fresh container and steps[i].
    
  685.         container = document.createElement('div');
    
  686.         const root = ReactDOMClient.createRoot(container);
    
  687.         act(() =>
    
  688.           root.render(
    
  689.             <Root>
    
  690.               <X />
    
  691.               <React.Suspense fallback={steps[i]}>
    
  692.                 <Z />
    
  693.                 <Never />
    
  694.                 <Z />
    
  695.               </React.Suspense>
    
  696.               <Y />
    
  697.             </Root>,
    
  698.           ),
    
  699.         );
    
  700.         expect(print(store)).toEqual(snapshots[i]);
    
  701.         // Re-render with steps[j].
    
  702.         act(() =>
    
  703.           root.render(
    
  704.             <Root>
    
  705.               <X />
    
  706.               <React.Suspense fallback={z}>{steps[j]}</React.Suspense>
    
  707.               <Y />
    
  708.             </Root>,
    
  709.           ),
    
  710.         );
    
  711.         // Verify the successful transition to steps[j].
    
  712.         expect(print(store)).toEqual(snapshots[j]);
    
  713.         // Check that we can transition back again.
    
  714.         act(() =>
    
  715.           root.render(
    
  716.             <Root>
    
  717.               <X />
    
  718.               <React.Suspense fallback={steps[i]}>
    
  719.                 <Z />
    
  720.                 <Never />
    
  721.                 <Z />
    
  722.               </React.Suspense>
    
  723.               <Y />
    
  724.             </Root>,
    
  725.           ),
    
  726.         );
    
  727.         expect(print(store)).toEqual(snapshots[i]);
    
  728.         // Clean up after every iteration.
    
  729.         act(() => root.unmount());
    
  730.         expect(print(store)).toBe('');
    
  731.       }
    
  732.     }
    
  733. 
    
  734.     // 7. Verify we can update from each step to each step when toggling Suspense.
    
  735.     for (let i = 0; i < steps.length; i++) {
    
  736.       for (let j = 0; j < steps.length; j++) {
    
  737.         // Always start with a fresh container and steps[i].
    
  738.         container = document.createElement('div');
    
  739.         const root = ReactDOMClient.createRoot(container);
    
  740.         act(() =>
    
  741.           root.render(
    
  742.             <Root>
    
  743.               <X />
    
  744.               <React.Suspense fallback={steps[j]}>{steps[i]}</React.Suspense>
    
  745.               <Y />
    
  746.             </Root>,
    
  747.           ),
    
  748.         );
    
  749. 
    
  750.         // We get ID from the index in the tree above:
    
  751.         // Root, X, Suspense, ...
    
  752.         //          ^ (index is 2)
    
  753.         const suspenseID = store.getElementIDAtIndex(2);
    
  754. 
    
  755.         // Force fallback.
    
  756.         expect(print(store)).toEqual(snapshots[i]);
    
  757.         await actAsync(async () => {
    
  758.           bridge.send('overrideSuspense', {
    
  759.             id: suspenseID,
    
  760.             rendererID: store.getRendererIDForElement(suspenseID),
    
  761.             forceFallback: true,
    
  762.           });
    
  763.         });
    
  764.         expect(print(store)).toEqual(snapshots[j]);
    
  765. 
    
  766.         // Stop forcing fallback.
    
  767.         await actAsync(async () => {
    
  768.           bridge.send('overrideSuspense', {
    
  769.             id: suspenseID,
    
  770.             rendererID: store.getRendererIDForElement(suspenseID),
    
  771.             forceFallback: false,
    
  772.           });
    
  773.         });
    
  774.         expect(print(store)).toEqual(snapshots[i]);
    
  775. 
    
  776.         // Trigger actual fallback.
    
  777.         act(() =>
    
  778.           root.render(
    
  779.             <Root>
    
  780.               <X />
    
  781.               <React.Suspense fallback={steps[j]}>
    
  782.                 <Z />
    
  783.                 <Never />
    
  784.                 <Z />
    
  785.               </React.Suspense>
    
  786.               <Y />
    
  787.             </Root>,
    
  788.           ),
    
  789.         );
    
  790.         expect(print(store)).toEqual(snapshots[j]);
    
  791. 
    
  792.         // Force fallback while we're in fallback mode.
    
  793.         act(() => {
    
  794.           bridge.send('overrideSuspense', {
    
  795.             id: suspenseID,
    
  796.             rendererID: store.getRendererIDForElement(suspenseID),
    
  797.             forceFallback: true,
    
  798.           });
    
  799.         });
    
  800.         // Keep seeing fallback content.
    
  801.         expect(print(store)).toEqual(snapshots[j]);
    
  802. 
    
  803.         // Switch to primary mode.
    
  804.         act(() =>
    
  805.           root.render(
    
  806.             <Root>
    
  807.               <X />
    
  808.               <React.Suspense fallback={steps[j]}>{steps[i]}</React.Suspense>
    
  809.               <Y />
    
  810.             </Root>,
    
  811.           ),
    
  812.         );
    
  813.         // Fallback is still forced though.
    
  814.         expect(print(store)).toEqual(snapshots[j]);
    
  815. 
    
  816.         // Stop forcing fallback. This reverts to primary content.
    
  817.         await actAsync(async () => {
    
  818.           bridge.send('overrideSuspense', {
    
  819.             id: suspenseID,
    
  820.             rendererID: store.getRendererIDForElement(suspenseID),
    
  821.             forceFallback: false,
    
  822.           });
    
  823.         });
    
  824.         // Now we see primary content.
    
  825.         expect(print(store)).toEqual(snapshots[i]);
    
  826. 
    
  827.         // Clean up after every iteration.
    
  828.         await actAsync(async () => root.unmount());
    
  829.         expect(print(store)).toBe('');
    
  830.       }
    
  831.     }
    
  832.   });
    
  833. 
    
  834.   // @reactVersion >= 18.0
    
  835.   it('should handle a stress test for Suspense without type change (Concurrent Mode)', async () => {
    
  836.     const A = () => 'a';
    
  837.     const B = () => 'b';
    
  838.     const C = () => 'c';
    
  839.     const X = () => 'x';
    
  840.     const Y = () => 'y';
    
  841.     const Z = () => 'z';
    
  842.     const a = <A key="a" />;
    
  843.     const b = <B key="b" />;
    
  844.     const c = <C key="c" />;
    
  845.     const z = <Z key="z" />;
    
  846. 
    
  847.     // prettier-ignore
    
  848.     const steps = [
    
  849.       a,
    
  850.       [a],
    
  851.       [a, b, c],
    
  852.       [c, b, a],
    
  853.       [c, null, a],
    
  854.       <React.Fragment>{c}{a}</React.Fragment>,
    
  855.       <div>{c}{a}</div>,
    
  856.       <div><span>{a}</span>{b}</div>,
    
  857.       [[a]],
    
  858.       null,
    
  859.       b,
    
  860.       a,
    
  861.     ];
    
  862. 
    
  863.     const Never = () => {
    
  864.       throw new Promise(() => {});
    
  865.     };
    
  866. 
    
  867.     const MaybeSuspend = ({children, suspend}) => {
    
  868.       if (suspend) {
    
  869.         return (
    
  870.           <div>
    
  871.             {children}
    
  872.             <Never />
    
  873.             <X />
    
  874.           </div>
    
  875.         );
    
  876.       }
    
  877.       return (
    
  878.         <div>
    
  879.           {children}
    
  880.           <Z />
    
  881.         </div>
    
  882.       );
    
  883.     };
    
  884. 
    
  885.     const Root = ({children}) => {
    
  886.       return children;
    
  887.     };
    
  888. 
    
  889.     // 1. For each step, check Suspense can render them as initial primary content.
    
  890.     // This is the only step where we use Jest snapshots.
    
  891.     const snapshots = [];
    
  892.     let container = document.createElement('div');
    
  893.     for (let i = 0; i < steps.length; i++) {
    
  894.       const root = ReactDOMClient.createRoot(container);
    
  895.       act(() =>
    
  896.         root.render(
    
  897.           <Root>
    
  898.             <X />
    
  899.             <React.Suspense fallback={z}>
    
  900.               <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
    
  901.             </React.Suspense>
    
  902.             <Y />
    
  903.           </Root>,
    
  904.         ),
    
  905.       );
    
  906.       // We snapshot each step once so it doesn't regress.
    
  907.       snapshots.push(print(store));
    
  908.       act(() => root.unmount());
    
  909.       expect(print(store)).toBe('');
    
  910.     }
    
  911. 
    
  912.     // 2. Verify check Suspense can render same steps as initial fallback content.
    
  913.     // We don't actually assert here because the tree includes <MaybeSuspend>
    
  914.     // which is different from the snapshots above. So we take more snapshots.
    
  915.     const fallbackSnapshots = [];
    
  916.     for (let i = 0; i < steps.length; i++) {
    
  917.       const root = ReactDOMClient.createRoot(container);
    
  918.       act(() =>
    
  919.         root.render(
    
  920.           <Root>
    
  921.             <X />
    
  922.             <React.Suspense fallback={steps[i]}>
    
  923.               <Z />
    
  924.               <MaybeSuspend suspend={true}>{steps[i]}</MaybeSuspend>
    
  925.               <Z />
    
  926.             </React.Suspense>
    
  927.             <Y />
    
  928.           </Root>,
    
  929.         ),
    
  930.       );
    
  931.       // We snapshot each step once so it doesn't regress.
    
  932.       fallbackSnapshots.push(print(store));
    
  933.       act(() => root.unmount());
    
  934.       expect(print(store)).toBe('');
    
  935.     }
    
  936. 
    
  937.     expect(snapshots).toMatchInlineSnapshot(`
    
  938.       [
    
  939.         "[root]
    
  940.         ▾ <Root>
    
  941.             <X>
    
  942.           ▾ <Suspense>
    
  943.             ▾ <MaybeSuspend>
    
  944.                 <A key="a">
    
  945.                 <Z>
    
  946.             <Y>",
    
  947.         "[root]
    
  948.         ▾ <Root>
    
  949.             <X>
    
  950.           ▾ <Suspense>
    
  951.             ▾ <MaybeSuspend>
    
  952.                 <A key="a">
    
  953.                 <Z>
    
  954.             <Y>",
    
  955.         "[root]
    
  956.         ▾ <Root>
    
  957.             <X>
    
  958.           ▾ <Suspense>
    
  959.             ▾ <MaybeSuspend>
    
  960.                 <A key="a">
    
  961.                 <B key="b">
    
  962.                 <C key="c">
    
  963.                 <Z>
    
  964.             <Y>",
    
  965.         "[root]
    
  966.         ▾ <Root>
    
  967.             <X>
    
  968.           ▾ <Suspense>
    
  969.             ▾ <MaybeSuspend>
    
  970.                 <C key="c">
    
  971.                 <B key="b">
    
  972.                 <A key="a">
    
  973.                 <Z>
    
  974.             <Y>",
    
  975.         "[root]
    
  976.         ▾ <Root>
    
  977.             <X>
    
  978.           ▾ <Suspense>
    
  979.             ▾ <MaybeSuspend>
    
  980.                 <C key="c">
    
  981.                 <A key="a">
    
  982.                 <Z>
    
  983.             <Y>",
    
  984.         "[root]
    
  985.         ▾ <Root>
    
  986.             <X>
    
  987.           ▾ <Suspense>
    
  988.             ▾ <MaybeSuspend>
    
  989.                 <C key="c">
    
  990.                 <A key="a">
    
  991.                 <Z>
    
  992.             <Y>",
    
  993.         "[root]
    
  994.         ▾ <Root>
    
  995.             <X>
    
  996.           ▾ <Suspense>
    
  997.             ▾ <MaybeSuspend>
    
  998.                 <C key="c">
    
  999.                 <A key="a">
    
  1000.                 <Z>
    
  1001.             <Y>",
    
  1002.         "[root]
    
  1003.         ▾ <Root>
    
  1004.             <X>
    
  1005.           ▾ <Suspense>
    
  1006.             ▾ <MaybeSuspend>
    
  1007.                 <A key="a">
    
  1008.                 <B key="b">
    
  1009.                 <Z>
    
  1010.             <Y>",
    
  1011.         "[root]
    
  1012.         ▾ <Root>
    
  1013.             <X>
    
  1014.           ▾ <Suspense>
    
  1015.             ▾ <MaybeSuspend>
    
  1016.                 <A key="a">
    
  1017.                 <Z>
    
  1018.             <Y>",
    
  1019.         "[root]
    
  1020.         ▾ <Root>
    
  1021.             <X>
    
  1022.           ▾ <Suspense>
    
  1023.             ▾ <MaybeSuspend>
    
  1024.                 <Z>
    
  1025.             <Y>",
    
  1026.         "[root]
    
  1027.         ▾ <Root>
    
  1028.             <X>
    
  1029.           ▾ <Suspense>
    
  1030.             ▾ <MaybeSuspend>
    
  1031.                 <B key="b">
    
  1032.                 <Z>
    
  1033.             <Y>",
    
  1034.         "[root]
    
  1035.         ▾ <Root>
    
  1036.             <X>
    
  1037.           ▾ <Suspense>
    
  1038.             ▾ <MaybeSuspend>
    
  1039.                 <A key="a">
    
  1040.                 <Z>
    
  1041.             <Y>",
    
  1042.       ]
    
  1043.     `);
    
  1044. 
    
  1045.     // 3. Verify we can update from each step to each step in primary mode.
    
  1046.     for (let i = 0; i < steps.length; i++) {
    
  1047.       for (let j = 0; j < steps.length; j++) {
    
  1048.         // Always start with a fresh container and steps[i].
    
  1049.         container = document.createElement('div');
    
  1050.         const root = ReactDOMClient.createRoot(container);
    
  1051.         act(() =>
    
  1052.           root.render(
    
  1053.             <Root>
    
  1054.               <X />
    
  1055.               <React.Suspense fallback={z}>
    
  1056.                 <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
    
  1057.               </React.Suspense>
    
  1058.               <Y />
    
  1059.             </Root>,
    
  1060.           ),
    
  1061.         );
    
  1062.         expect(print(store)).toEqual(snapshots[i]);
    
  1063.         // Re-render with steps[j].
    
  1064.         act(() =>
    
  1065.           root.render(
    
  1066.             <Root>
    
  1067.               <X />
    
  1068.               <React.Suspense fallback={z}>
    
  1069.                 <MaybeSuspend suspend={false}>{steps[j]}</MaybeSuspend>
    
  1070.               </React.Suspense>
    
  1071.               <Y />
    
  1072.             </Root>,
    
  1073.           ),
    
  1074.         );
    
  1075.         // Verify the successful transition to steps[j].
    
  1076.         expect(print(store)).toEqual(snapshots[j]);
    
  1077.         // Check that we can transition back again.
    
  1078.         act(() =>
    
  1079.           root.render(
    
  1080.             <Root>
    
  1081.               <X />
    
  1082.               <React.Suspense fallback={z}>
    
  1083.                 <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
    
  1084.               </React.Suspense>
    
  1085.               <Y />
    
  1086.             </Root>,
    
  1087.           ),
    
  1088.         );
    
  1089.         expect(print(store)).toEqual(snapshots[i]);
    
  1090.         // Clean up after every iteration.
    
  1091.         act(() => root.unmount());
    
  1092.         expect(print(store)).toBe('');
    
  1093.       }
    
  1094.     }
    
  1095. 
    
  1096.     // 4. Verify we can update from each step to each step in fallback mode.
    
  1097.     for (let i = 0; i < steps.length; i++) {
    
  1098.       for (let j = 0; j < steps.length; j++) {
    
  1099.         // Always start with a fresh container and steps[i].
    
  1100.         container = document.createElement('div');
    
  1101.         const root = ReactDOMClient.createRoot(container);
    
  1102.         act(() =>
    
  1103.           root.render(
    
  1104.             <Root>
    
  1105.               <X />
    
  1106.               <React.Suspense fallback={steps[i]}>
    
  1107.                 <Z />
    
  1108.                 <MaybeSuspend suspend={true}>
    
  1109.                   <X />
    
  1110.                   <Y />
    
  1111.                 </MaybeSuspend>
    
  1112.                 <Z />
    
  1113.               </React.Suspense>
    
  1114.               <Y />
    
  1115.             </Root>,
    
  1116.           ),
    
  1117.         );
    
  1118.         expect(print(store)).toEqual(fallbackSnapshots[i]);
    
  1119.         // Re-render with steps[j].
    
  1120.         act(() =>
    
  1121.           root.render(
    
  1122.             <Root>
    
  1123.               <X />
    
  1124.               <React.Suspense fallback={steps[j]}>
    
  1125.                 <Z />
    
  1126.                 <MaybeSuspend suspend={true}>
    
  1127.                   <Y />
    
  1128.                   <X />
    
  1129.                 </MaybeSuspend>
    
  1130.                 <Z />
    
  1131.               </React.Suspense>
    
  1132.               <Y />
    
  1133.             </Root>,
    
  1134.           ),
    
  1135.         );
    
  1136.         // Verify the successful transition to steps[j].
    
  1137.         expect(print(store)).toEqual(fallbackSnapshots[j]);
    
  1138.         // Check that we can transition back again.
    
  1139.         act(() =>
    
  1140.           root.render(
    
  1141.             <Root>
    
  1142.               <X />
    
  1143.               <React.Suspense fallback={steps[i]}>
    
  1144.                 <Z />
    
  1145.                 <MaybeSuspend suspend={true}>
    
  1146.                   <X />
    
  1147.                   <Y />
    
  1148.                 </MaybeSuspend>
    
  1149.                 <Z />
    
  1150.               </React.Suspense>
    
  1151.               <Y />
    
  1152.             </Root>,
    
  1153.           ),
    
  1154.         );
    
  1155.         expect(print(store)).toEqual(fallbackSnapshots[i]);
    
  1156.         // Clean up after every iteration.
    
  1157.         act(() => root.unmount());
    
  1158.         expect(print(store)).toBe('');
    
  1159.       }
    
  1160.     }
    
  1161. 
    
  1162.     // 5. Verify we can update from each step to each step when moving primary -> fallback.
    
  1163.     for (let i = 0; i < steps.length; i++) {
    
  1164.       for (let j = 0; j < steps.length; j++) {
    
  1165.         // Always start with a fresh container and steps[i].
    
  1166.         container = document.createElement('div');
    
  1167.         const root = ReactDOMClient.createRoot(container);
    
  1168.         act(() =>
    
  1169.           root.render(
    
  1170.             <Root>
    
  1171.               <X />
    
  1172.               <React.Suspense fallback={z}>
    
  1173.                 <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
    
  1174.               </React.Suspense>
    
  1175.               <Y />
    
  1176.             </Root>,
    
  1177.           ),
    
  1178.         );
    
  1179.         expect(print(store)).toEqual(snapshots[i]);
    
  1180.         // Re-render with steps[j].
    
  1181.         act(() =>
    
  1182.           root.render(
    
  1183.             <Root>
    
  1184.               <X />
    
  1185.               <React.Suspense fallback={steps[j]}>
    
  1186.                 <MaybeSuspend suspend={true}>{steps[i]}</MaybeSuspend>
    
  1187.               </React.Suspense>
    
  1188.               <Y />
    
  1189.             </Root>,
    
  1190.           ),
    
  1191.         );
    
  1192.         // Verify the successful transition to steps[j].
    
  1193.         expect(print(store)).toEqual(fallbackSnapshots[j]);
    
  1194.         // Check that we can transition back again.
    
  1195.         act(() =>
    
  1196.           root.render(
    
  1197.             <Root>
    
  1198.               <X />
    
  1199.               <React.Suspense fallback={z}>
    
  1200.                 <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
    
  1201.               </React.Suspense>
    
  1202.               <Y />
    
  1203.             </Root>,
    
  1204.           ),
    
  1205.         );
    
  1206.         expect(print(store)).toEqual(snapshots[i]);
    
  1207.         // Clean up after every iteration.
    
  1208.         act(() => root.unmount());
    
  1209.         expect(print(store)).toBe('');
    
  1210.       }
    
  1211.     }
    
  1212. 
    
  1213.     // 6. Verify we can update from each step to each step when moving fallback -> primary.
    
  1214.     for (let i = 0; i < steps.length; i++) {
    
  1215.       for (let j = 0; j < steps.length; j++) {
    
  1216.         // Always start with a fresh container and steps[i].
    
  1217.         container = document.createElement('div');
    
  1218.         const root = ReactDOMClient.createRoot(container);
    
  1219.         act(() =>
    
  1220.           root.render(
    
  1221.             <Root>
    
  1222.               <X />
    
  1223.               <React.Suspense fallback={steps[i]}>
    
  1224.                 <MaybeSuspend suspend={true}>{steps[j]}</MaybeSuspend>
    
  1225.               </React.Suspense>
    
  1226.               <Y />
    
  1227.             </Root>,
    
  1228.           ),
    
  1229.         );
    
  1230.         expect(print(store)).toEqual(fallbackSnapshots[i]);
    
  1231.         // Re-render with steps[j].
    
  1232.         act(() =>
    
  1233.           root.render(
    
  1234.             <Root>
    
  1235.               <X />
    
  1236.               <React.Suspense fallback={steps[i]}>
    
  1237.                 <MaybeSuspend suspend={false}>{steps[j]}</MaybeSuspend>
    
  1238.               </React.Suspense>
    
  1239.               <Y />
    
  1240.             </Root>,
    
  1241.           ),
    
  1242.         );
    
  1243.         // Verify the successful transition to steps[j].
    
  1244.         expect(print(store)).toEqual(snapshots[j]);
    
  1245.         // Check that we can transition back again.
    
  1246.         act(() =>
    
  1247.           root.render(
    
  1248.             <Root>
    
  1249.               <X />
    
  1250.               <React.Suspense fallback={steps[i]}>
    
  1251.                 <MaybeSuspend suspend={true}>{steps[j]}</MaybeSuspend>
    
  1252.               </React.Suspense>
    
  1253.               <Y />
    
  1254.             </Root>,
    
  1255.           ),
    
  1256.         );
    
  1257.         expect(print(store)).toEqual(fallbackSnapshots[i]);
    
  1258.         // Clean up after every iteration.
    
  1259.         act(() => root.unmount());
    
  1260.         expect(print(store)).toBe('');
    
  1261.       }
    
  1262.     }
    
  1263. 
    
  1264.     // 7. Verify we can update from each step to each step when toggling Suspense.
    
  1265.     for (let i = 0; i < steps.length; i++) {
    
  1266.       for (let j = 0; j < steps.length; j++) {
    
  1267.         // Always start with a fresh container and steps[i].
    
  1268.         container = document.createElement('div');
    
  1269.         const root = ReactDOMClient.createRoot(container);
    
  1270.         act(() =>
    
  1271.           root.render(
    
  1272.             <Root>
    
  1273.               <X />
    
  1274.               <React.Suspense fallback={steps[j]}>
    
  1275.                 <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
    
  1276.               </React.Suspense>
    
  1277.               <Y />
    
  1278.             </Root>,
    
  1279.           ),
    
  1280.         );
    
  1281. 
    
  1282.         // We get ID from the index in the tree above:
    
  1283.         // Root, X, Suspense, ...
    
  1284.         //          ^ (index is 2)
    
  1285.         const suspenseID = store.getElementIDAtIndex(2);
    
  1286. 
    
  1287.         // Force fallback.
    
  1288.         expect(print(store)).toEqual(snapshots[i]);
    
  1289.         await actAsync(async () => {
    
  1290.           bridge.send('overrideSuspense', {
    
  1291.             id: suspenseID,
    
  1292.             rendererID: store.getRendererIDForElement(suspenseID),
    
  1293.             forceFallback: true,
    
  1294.           });
    
  1295.         });
    
  1296.         expect(print(store)).toEqual(fallbackSnapshots[j]);
    
  1297. 
    
  1298.         // Stop forcing fallback.
    
  1299.         await actAsync(async () => {
    
  1300.           bridge.send('overrideSuspense', {
    
  1301.             id: suspenseID,
    
  1302.             rendererID: store.getRendererIDForElement(suspenseID),
    
  1303.             forceFallback: false,
    
  1304.           });
    
  1305.         });
    
  1306.         expect(print(store)).toEqual(snapshots[i]);
    
  1307. 
    
  1308.         // Trigger actual fallback.
    
  1309.         act(() =>
    
  1310.           root.render(
    
  1311.             <Root>
    
  1312.               <X />
    
  1313.               <React.Suspense fallback={steps[j]}>
    
  1314.                 <MaybeSuspend suspend={true}>{steps[i]}</MaybeSuspend>
    
  1315.               </React.Suspense>
    
  1316.               <Y />
    
  1317.             </Root>,
    
  1318.           ),
    
  1319.         );
    
  1320.         expect(print(store)).toEqual(fallbackSnapshots[j]);
    
  1321. 
    
  1322.         // Force fallback while we're in fallback mode.
    
  1323.         act(() => {
    
  1324.           bridge.send('overrideSuspense', {
    
  1325.             id: suspenseID,
    
  1326.             rendererID: store.getRendererIDForElement(suspenseID),
    
  1327.             forceFallback: true,
    
  1328.           });
    
  1329.         });
    
  1330.         // Keep seeing fallback content.
    
  1331.         expect(print(store)).toEqual(fallbackSnapshots[j]);
    
  1332. 
    
  1333.         // Switch to primary mode.
    
  1334.         act(() =>
    
  1335.           root.render(
    
  1336.             <Root>
    
  1337.               <X />
    
  1338.               <React.Suspense fallback={steps[j]}>
    
  1339.                 <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>
    
  1340.               </React.Suspense>
    
  1341.               <Y />
    
  1342.             </Root>,
    
  1343.           ),
    
  1344.         );
    
  1345.         // Fallback is still forced though.
    
  1346.         expect(print(store)).toEqual(fallbackSnapshots[j]);
    
  1347. 
    
  1348.         // Stop forcing fallback. This reverts to primary content.
    
  1349.         await actAsync(async () => {
    
  1350.           bridge.send('overrideSuspense', {
    
  1351.             id: suspenseID,
    
  1352.             rendererID: store.getRendererIDForElement(suspenseID),
    
  1353.             forceFallback: false,
    
  1354.           });
    
  1355.         });
    
  1356.         // Now we see primary content.
    
  1357.         expect(print(store)).toEqual(snapshots[i]);
    
  1358. 
    
  1359.         // Clean up after every iteration.
    
  1360.         act(() => root.unmount());
    
  1361.         expect(print(store)).toBe('');
    
  1362.       }
    
  1363.     }
    
  1364.   });
    
  1365. });