1. let PropTypes;
    
  2. let React;
    
  3. let ReactTestRenderer;
    
  4. let Scheduler;
    
  5. let ReactFeatureFlags;
    
  6. let Suspense;
    
  7. let lazy;
    
  8. let waitFor;
    
  9. let waitForAll;
    
  10. let waitForThrow;
    
  11. let assertLog;
    
  12. let act;
    
  13. 
    
  14. let fakeModuleCache;
    
  15. 
    
  16. function normalizeCodeLocInfo(str) {
    
  17.   return (
    
  18.     str &&
    
  19.     str.replace(/\n +(?:at|in) ([\S]+)[^\n]*/g, function (m, name) {
    
  20.       return '\n    in ' + name + ' (at **)';
    
  21.     })
    
  22.   );
    
  23. }
    
  24. 
    
  25. describe('ReactLazy', () => {
    
  26.   beforeEach(() => {
    
  27.     jest.resetModules();
    
  28.     ReactFeatureFlags = require('shared/ReactFeatureFlags');
    
  29. 
    
  30.     ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
    
  31.     PropTypes = require('prop-types');
    
  32.     React = require('react');
    
  33.     Suspense = React.Suspense;
    
  34.     lazy = React.lazy;
    
  35.     ReactTestRenderer = require('react-test-renderer');
    
  36.     Scheduler = require('scheduler');
    
  37. 
    
  38.     const InternalTestUtils = require('internal-test-utils');
    
  39.     waitFor = InternalTestUtils.waitFor;
    
  40.     waitForAll = InternalTestUtils.waitForAll;
    
  41.     waitForThrow = InternalTestUtils.waitForThrow;
    
  42.     assertLog = InternalTestUtils.assertLog;
    
  43.     act = InternalTestUtils.act;
    
  44. 
    
  45.     fakeModuleCache = new Map();
    
  46.   });
    
  47. 
    
  48.   function Text(props) {
    
  49.     Scheduler.log(props.text);
    
  50.     return props.text;
    
  51.   }
    
  52. 
    
  53.   async function fakeImport(Component) {
    
  54.     const record = fakeModuleCache.get(Component);
    
  55.     if (record === undefined) {
    
  56.       const newRecord = {
    
  57.         status: 'pending',
    
  58.         value: {default: Component},
    
  59.         pings: [],
    
  60.         then(ping) {
    
  61.           switch (newRecord.status) {
    
  62.             case 'pending': {
    
  63.               newRecord.pings.push(ping);
    
  64.               return;
    
  65.             }
    
  66.             case 'resolved': {
    
  67.               ping(newRecord.value);
    
  68.               return;
    
  69.             }
    
  70.             case 'rejected': {
    
  71.               throw newRecord.value;
    
  72.             }
    
  73.           }
    
  74.         },
    
  75.       };
    
  76.       fakeModuleCache.set(Component, newRecord);
    
  77.       return newRecord;
    
  78.     }
    
  79.     return record;
    
  80.   }
    
  81. 
    
  82.   function resolveFakeImport(moduleName) {
    
  83.     const record = fakeModuleCache.get(moduleName);
    
  84.     if (record === undefined) {
    
  85.       throw new Error('Module not found');
    
  86.     }
    
  87.     if (record.status !== 'pending') {
    
  88.       throw new Error('Module already resolved');
    
  89.     }
    
  90.     record.status = 'resolved';
    
  91.     record.pings.forEach(ping => ping(record.value));
    
  92.   }
    
  93. 
    
  94.   it('suspends until module has loaded', async () => {
    
  95.     const LazyText = lazy(() => fakeImport(Text));
    
  96. 
    
  97.     const root = ReactTestRenderer.create(
    
  98.       <Suspense fallback={<Text text="Loading..." />}>
    
  99.         <LazyText text="Hi" />
    
  100.       </Suspense>,
    
  101.       {
    
  102.         unstable_isConcurrent: true,
    
  103.       },
    
  104.     );
    
  105. 
    
  106.     await waitForAll(['Loading...']);
    
  107.     expect(root).not.toMatchRenderedOutput('Hi');
    
  108. 
    
  109.     await act(() => resolveFakeImport(Text));
    
  110.     assertLog(['Hi']);
    
  111.     expect(root).toMatchRenderedOutput('Hi');
    
  112. 
    
  113.     // Should not suspend on update
    
  114.     root.update(
    
  115.       <Suspense fallback={<Text text="Loading..." />}>
    
  116.         <LazyText text="Hi again" />
    
  117.       </Suspense>,
    
  118.     );
    
  119.     await waitForAll(['Hi again']);
    
  120.     expect(root).toMatchRenderedOutput('Hi again');
    
  121.   });
    
  122. 
    
  123.   it('can resolve synchronously without suspending', async () => {
    
  124.     const LazyText = lazy(() => ({
    
  125.       then(cb) {
    
  126.         cb({default: Text});
    
  127.       },
    
  128.     }));
    
  129. 
    
  130.     const root = ReactTestRenderer.create(
    
  131.       <Suspense fallback={<Text text="Loading..." />}>
    
  132.         <LazyText text="Hi" />
    
  133.       </Suspense>,
    
  134.     );
    
  135. 
    
  136.     assertLog(['Hi']);
    
  137.     expect(root).toMatchRenderedOutput('Hi');
    
  138.   });
    
  139. 
    
  140.   it('can reject synchronously without suspending', async () => {
    
  141.     const LazyText = lazy(() => ({
    
  142.       then(resolve, reject) {
    
  143.         reject(new Error('oh no'));
    
  144.       },
    
  145.     }));
    
  146. 
    
  147.     class ErrorBoundary extends React.Component {
    
  148.       state = {};
    
  149.       static getDerivedStateFromError(error) {
    
  150.         return {message: error.message};
    
  151.       }
    
  152.       render() {
    
  153.         return this.state.message
    
  154.           ? `Error: ${this.state.message}`
    
  155.           : this.props.children;
    
  156.       }
    
  157.     }
    
  158. 
    
  159.     const root = ReactTestRenderer.create(
    
  160.       <ErrorBoundary>
    
  161.         <Suspense fallback={<Text text="Loading..." />}>
    
  162.           <LazyText text="Hi" />
    
  163.         </Suspense>
    
  164.       </ErrorBoundary>,
    
  165.     );
    
  166.     assertLog([]);
    
  167.     expect(root).toMatchRenderedOutput('Error: oh no');
    
  168.   });
    
  169. 
    
  170.   it('multiple lazy components', async () => {
    
  171.     function Foo() {
    
  172.       return <Text text="Foo" />;
    
  173.     }
    
  174. 
    
  175.     function Bar() {
    
  176.       return <Text text="Bar" />;
    
  177.     }
    
  178. 
    
  179.     const LazyFoo = lazy(() => fakeImport(Foo));
    
  180.     const LazyBar = lazy(() => fakeImport(Bar));
    
  181. 
    
  182.     const root = ReactTestRenderer.create(
    
  183.       <Suspense fallback={<Text text="Loading..." />}>
    
  184.         <LazyFoo />
    
  185.         <LazyBar />
    
  186.       </Suspense>,
    
  187.       {
    
  188.         unstable_isConcurrent: true,
    
  189.       },
    
  190.     );
    
  191. 
    
  192.     await waitForAll(['Loading...']);
    
  193.     expect(root).not.toMatchRenderedOutput('FooBar');
    
  194. 
    
  195.     await resolveFakeImport(Foo);
    
  196. 
    
  197.     await waitForAll(['Foo']);
    
  198.     expect(root).not.toMatchRenderedOutput('FooBar');
    
  199. 
    
  200.     await act(() => resolveFakeImport(Bar));
    
  201.     assertLog(['Foo', 'Bar']);
    
  202.     expect(root).toMatchRenderedOutput('FooBar');
    
  203.   });
    
  204. 
    
  205.   it('does not support arbitrary promises, only module objects', async () => {
    
  206.     spyOnDev(console, 'error').mockImplementation(() => {});
    
  207. 
    
  208.     const LazyText = lazy(async () => Text);
    
  209. 
    
  210.     const root = ReactTestRenderer.create(null, {
    
  211.       unstable_isConcurrent: true,
    
  212.     });
    
  213. 
    
  214.     let error;
    
  215.     try {
    
  216.       await act(() => {
    
  217.         root.update(
    
  218.           <Suspense fallback={<Text text="Loading..." />}>
    
  219.             <LazyText text="Hi" />
    
  220.           </Suspense>,
    
  221.         );
    
  222.       });
    
  223.     } catch (e) {
    
  224.       error = e;
    
  225.     }
    
  226. 
    
  227.     expect(error.message).toMatch('Element type is invalid');
    
  228.     assertLog(['Loading...']);
    
  229.     expect(root).not.toMatchRenderedOutput('Hi');
    
  230.     if (__DEV__) {
    
  231.       expect(console.error).toHaveBeenCalledTimes(3);
    
  232.       expect(console.error.mock.calls[0][0]).toContain(
    
  233.         'Expected the result of a dynamic import() call',
    
  234.       );
    
  235.     }
    
  236.   });
    
  237. 
    
  238.   it('throws if promise rejects', async () => {
    
  239.     const networkError = new Error('Bad network');
    
  240.     const LazyText = lazy(async () => {
    
  241.       throw networkError;
    
  242.     });
    
  243. 
    
  244.     const root = ReactTestRenderer.create(null, {
    
  245.       unstable_isConcurrent: true,
    
  246.     });
    
  247. 
    
  248.     let error;
    
  249.     try {
    
  250.       await act(() => {
    
  251.         root.update(
    
  252.           <Suspense fallback={<Text text="Loading..." />}>
    
  253.             <LazyText text="Hi" />
    
  254.           </Suspense>,
    
  255.         );
    
  256.       });
    
  257.     } catch (e) {
    
  258.       error = e;
    
  259.     }
    
  260. 
    
  261.     expect(error).toBe(networkError);
    
  262.     assertLog(['Loading...']);
    
  263.     expect(root).not.toMatchRenderedOutput('Hi');
    
  264.   });
    
  265. 
    
  266.   it('mount and reorder', async () => {
    
  267.     class Child extends React.Component {
    
  268.       componentDidMount() {
    
  269.         Scheduler.log('Did mount: ' + this.props.label);
    
  270.       }
    
  271.       componentDidUpdate() {
    
  272.         Scheduler.log('Did update: ' + this.props.label);
    
  273.       }
    
  274.       render() {
    
  275.         return <Text text={this.props.label} />;
    
  276.       }
    
  277.     }
    
  278. 
    
  279.     const LazyChildA = lazy(() => {
    
  280.       Scheduler.log('Suspend! [LazyChildA]');
    
  281.       return fakeImport(Child);
    
  282.     });
    
  283.     const LazyChildB = lazy(() => {
    
  284.       Scheduler.log('Suspend! [LazyChildB]');
    
  285.       return fakeImport(Child);
    
  286.     });
    
  287. 
    
  288.     function Parent({swap}) {
    
  289.       return (
    
  290.         <Suspense fallback={<Text text="Loading..." />}>
    
  291.           {swap
    
  292.             ? [
    
  293.                 <LazyChildB key="B" label="B" />,
    
  294.                 <LazyChildA key="A" label="A" />,
    
  295.               ]
    
  296.             : [
    
  297.                 <LazyChildA key="A" label="A" />,
    
  298.                 <LazyChildB key="B" label="B" />,
    
  299.               ]}
    
  300.         </Suspense>
    
  301.       );
    
  302.     }
    
  303. 
    
  304.     const root = ReactTestRenderer.create(<Parent swap={false} />, {
    
  305.       unstable_isConcurrent: true,
    
  306.     });
    
  307. 
    
  308.     await waitForAll(['Suspend! [LazyChildA]', 'Loading...']);
    
  309.     expect(root).not.toMatchRenderedOutput('AB');
    
  310. 
    
  311.     await act(async () => {
    
  312.       await resolveFakeImport(Child);
    
  313. 
    
  314.       // B suspends even though it happens to share the same import as A.
    
  315.       // TODO: React.lazy should implement the `status` and `value` fields, so
    
  316.       // we can unwrap the result synchronously if it already loaded. Like `use`.
    
  317.       await waitFor(['A', 'Suspend! [LazyChildB]']);
    
  318.     });
    
  319.     assertLog(['A', 'B', 'Did mount: A', 'Did mount: B']);
    
  320.     expect(root).toMatchRenderedOutput('AB');
    
  321. 
    
  322.     // Swap the position of A and B
    
  323.     root.update(<Parent swap={true} />);
    
  324.     await waitForAll(['B', 'A', 'Did update: B', 'Did update: A']);
    
  325.     expect(root).toMatchRenderedOutput('BA');
    
  326.   });
    
  327. 
    
  328.   it('resolves defaultProps, on mount and update', async () => {
    
  329.     function T(props) {
    
  330.       return <Text {...props} />;
    
  331.     }
    
  332.     T.defaultProps = {text: 'Hi'};
    
  333.     const LazyText = lazy(() => fakeImport(T));
    
  334. 
    
  335.     const root = ReactTestRenderer.create(
    
  336.       <Suspense fallback={<Text text="Loading..." />}>
    
  337.         <LazyText />
    
  338.       </Suspense>,
    
  339.       {
    
  340.         unstable_isConcurrent: true,
    
  341.       },
    
  342.     );
    
  343. 
    
  344.     await waitForAll(['Loading...']);
    
  345.     expect(root).not.toMatchRenderedOutput('Hi');
    
  346. 
    
  347.     await expect(async () => {
    
  348.       await act(() => resolveFakeImport(T));
    
  349.       assertLog(['Hi']);
    
  350.     }).toErrorDev(
    
  351.       'Warning: T: Support for defaultProps ' +
    
  352.         'will be removed from function components in a future major ' +
    
  353.         'release. Use JavaScript default parameters instead.',
    
  354.     );
    
  355. 
    
  356.     expect(root).toMatchRenderedOutput('Hi');
    
  357. 
    
  358.     T.defaultProps = {text: 'Hi again'};
    
  359.     root.update(
    
  360.       <Suspense fallback={<Text text="Loading..." />}>
    
  361.         <LazyText />
    
  362.       </Suspense>,
    
  363.     );
    
  364.     await waitForAll(['Hi again']);
    
  365.     expect(root).toMatchRenderedOutput('Hi again');
    
  366.   });
    
  367. 
    
  368.   it('resolves defaultProps without breaking memoization', async () => {
    
  369.     function LazyImpl(props) {
    
  370.       Scheduler.log('Lazy');
    
  371.       return (
    
  372.         <>
    
  373.           <Text text={props.siblingText} />
    
  374.           {props.children}
    
  375.         </>
    
  376.       );
    
  377.     }
    
  378.     LazyImpl.defaultProps = {siblingText: 'Sibling'};
    
  379.     const Lazy = lazy(() => fakeImport(LazyImpl));
    
  380. 
    
  381.     class Stateful extends React.Component {
    
  382.       state = {text: 'A'};
    
  383.       render() {
    
  384.         return <Text text={this.state.text} />;
    
  385.       }
    
  386.     }
    
  387. 
    
  388.     const stateful = React.createRef(null);
    
  389. 
    
  390.     const root = ReactTestRenderer.create(
    
  391.       <Suspense fallback={<Text text="Loading..." />}>
    
  392.         <Lazy>
    
  393.           <Stateful ref={stateful} />
    
  394.         </Lazy>
    
  395.       </Suspense>,
    
  396.       {
    
  397.         unstable_isConcurrent: true,
    
  398.       },
    
  399.     );
    
  400.     await waitForAll(['Loading...']);
    
  401.     expect(root).not.toMatchRenderedOutput('SiblingA');
    
  402. 
    
  403.     await expect(async () => {
    
  404.       await act(() => resolveFakeImport(LazyImpl));
    
  405.       assertLog(['Lazy', 'Sibling', 'A']);
    
  406.     }).toErrorDev(
    
  407.       'Warning: LazyImpl: Support for defaultProps ' +
    
  408.         'will be removed from function components in a future major ' +
    
  409.         'release. Use JavaScript default parameters instead.',
    
  410.     );
    
  411. 
    
  412.     expect(root).toMatchRenderedOutput('SiblingA');
    
  413. 
    
  414.     // Lazy should not re-render
    
  415.     stateful.current.setState({text: 'B'});
    
  416.     await waitForAll(['B']);
    
  417.     expect(root).toMatchRenderedOutput('SiblingB');
    
  418.   });
    
  419. 
    
  420.   it('resolves defaultProps without breaking bailout due to unchanged props and state, #17151', async () => {
    
  421.     class LazyImpl extends React.Component {
    
  422.       static defaultProps = {value: 0};
    
  423. 
    
  424.       render() {
    
  425.         const text = `${this.props.label}: ${this.props.value}`;
    
  426.         return <Text text={text} />;
    
  427.       }
    
  428.     }
    
  429. 
    
  430.     const Lazy = lazy(() => fakeImport(LazyImpl));
    
  431. 
    
  432.     const instance1 = React.createRef(null);
    
  433.     const instance2 = React.createRef(null);
    
  434. 
    
  435.     const root = ReactTestRenderer.create(
    
  436.       <>
    
  437.         <LazyImpl ref={instance1} label="Not lazy" />
    
  438.         <Suspense fallback={<Text text="Loading..." />}>
    
  439.           <Lazy ref={instance2} label="Lazy" />
    
  440.         </Suspense>
    
  441.       </>,
    
  442.       {
    
  443.         unstable_isConcurrent: true,
    
  444.       },
    
  445.     );
    
  446.     await waitForAll(['Not lazy: 0', 'Loading...']);
    
  447.     expect(root).not.toMatchRenderedOutput('Not lazy: 0Lazy: 0');
    
  448. 
    
  449.     await act(() => resolveFakeImport(LazyImpl));
    
  450.     assertLog(['Lazy: 0']);
    
  451.     expect(root).toMatchRenderedOutput('Not lazy: 0Lazy: 0');
    
  452. 
    
  453.     // Should bailout due to unchanged props and state
    
  454.     instance1.current.setState(null);
    
  455.     await waitForAll([]);
    
  456.     expect(root).toMatchRenderedOutput('Not lazy: 0Lazy: 0');
    
  457. 
    
  458.     // Should bailout due to unchanged props and state
    
  459.     instance2.current.setState(null);
    
  460.     await waitForAll([]);
    
  461.     expect(root).toMatchRenderedOutput('Not lazy: 0Lazy: 0');
    
  462.   });
    
  463. 
    
  464.   it('resolves defaultProps without breaking bailout in PureComponent, #17151', async () => {
    
  465.     class LazyImpl extends React.PureComponent {
    
  466.       static defaultProps = {value: 0};
    
  467.       state = {};
    
  468. 
    
  469.       render() {
    
  470.         const text = `${this.props.label}: ${this.props.value}`;
    
  471.         return <Text text={text} />;
    
  472.       }
    
  473.     }
    
  474. 
    
  475.     const Lazy = lazy(() => fakeImport(LazyImpl));
    
  476. 
    
  477.     const instance1 = React.createRef(null);
    
  478.     const instance2 = React.createRef(null);
    
  479. 
    
  480.     const root = ReactTestRenderer.create(
    
  481.       <>
    
  482.         <LazyImpl ref={instance1} label="Not lazy" />
    
  483.         <Suspense fallback={<Text text="Loading..." />}>
    
  484.           <Lazy ref={instance2} label="Lazy" />
    
  485.         </Suspense>
    
  486.       </>,
    
  487.       {
    
  488.         unstable_isConcurrent: true,
    
  489.       },
    
  490.     );
    
  491.     await waitForAll(['Not lazy: 0', 'Loading...']);
    
  492.     expect(root).not.toMatchRenderedOutput('Not lazy: 0Lazy: 0');
    
  493. 
    
  494.     await act(() => resolveFakeImport(LazyImpl));
    
  495.     assertLog(['Lazy: 0']);
    
  496.     expect(root).toMatchRenderedOutput('Not lazy: 0Lazy: 0');
    
  497. 
    
  498.     // Should bailout due to shallow equal props and state
    
  499.     instance1.current.setState({});
    
  500.     await waitForAll([]);
    
  501.     expect(root).toMatchRenderedOutput('Not lazy: 0Lazy: 0');
    
  502. 
    
  503.     // Should bailout due to shallow equal props and state
    
  504.     instance2.current.setState({});
    
  505.     await waitForAll([]);
    
  506.     expect(root).toMatchRenderedOutput('Not lazy: 0Lazy: 0');
    
  507.   });
    
  508. 
    
  509.   it('sets defaultProps for modern lifecycles', async () => {
    
  510.     class C extends React.Component {
    
  511.       static defaultProps = {text: 'A'};
    
  512.       state = {};
    
  513. 
    
  514.       static getDerivedStateFromProps(props) {
    
  515.         Scheduler.log(`getDerivedStateFromProps: ${props.text}`);
    
  516.         return null;
    
  517.       }
    
  518. 
    
  519.       constructor(props) {
    
  520.         super(props);
    
  521.         Scheduler.log(`constructor: ${this.props.text}`);
    
  522.       }
    
  523. 
    
  524.       componentDidMount() {
    
  525.         Scheduler.log(`componentDidMount: ${this.props.text}`);
    
  526.       }
    
  527. 
    
  528.       componentDidUpdate(prevProps) {
    
  529.         Scheduler.log(
    
  530.           `componentDidUpdate: ${prevProps.text} -> ${this.props.text}`,
    
  531.         );
    
  532.       }
    
  533. 
    
  534.       componentWillUnmount() {
    
  535.         Scheduler.log(`componentWillUnmount: ${this.props.text}`);
    
  536.       }
    
  537. 
    
  538.       shouldComponentUpdate(nextProps) {
    
  539.         Scheduler.log(
    
  540.           `shouldComponentUpdate: ${this.props.text} -> ${nextProps.text}`,
    
  541.         );
    
  542.         return true;
    
  543.       }
    
  544. 
    
  545.       getSnapshotBeforeUpdate(prevProps) {
    
  546.         Scheduler.log(
    
  547.           `getSnapshotBeforeUpdate: ${prevProps.text} -> ${this.props.text}`,
    
  548.         );
    
  549.         return null;
    
  550.       }
    
  551. 
    
  552.       render() {
    
  553.         return <Text text={this.props.text + this.props.num} />;
    
  554.       }
    
  555.     }
    
  556. 
    
  557.     const LazyClass = lazy(() => fakeImport(C));
    
  558. 
    
  559.     const root = ReactTestRenderer.create(
    
  560.       <Suspense fallback={<Text text="Loading..." />}>
    
  561.         <LazyClass num={1} />
    
  562.       </Suspense>,
    
  563.       {
    
  564.         unstable_isConcurrent: true,
    
  565.       },
    
  566.     );
    
  567. 
    
  568.     await waitForAll(['Loading...']);
    
  569.     expect(root).not.toMatchRenderedOutput('A1');
    
  570. 
    
  571.     await act(() => resolveFakeImport(C));
    
  572.     assertLog([
    
  573.       'constructor: A',
    
  574.       'getDerivedStateFromProps: A',
    
  575.       'A1',
    
  576.       'componentDidMount: A',
    
  577.     ]);
    
  578. 
    
  579.     root.update(
    
  580.       <Suspense fallback={<Text text="Loading..." />}>
    
  581.         <LazyClass num={2} />
    
  582.       </Suspense>,
    
  583.     );
    
  584.     await waitForAll([
    
  585.       'getDerivedStateFromProps: A',
    
  586.       'shouldComponentUpdate: A -> A',
    
  587.       'A2',
    
  588.       'getSnapshotBeforeUpdate: A -> A',
    
  589.       'componentDidUpdate: A -> A',
    
  590.     ]);
    
  591.     expect(root).toMatchRenderedOutput('A2');
    
  592. 
    
  593.     root.update(
    
  594.       <Suspense fallback={<Text text="Loading..." />}>
    
  595.         <LazyClass num={3} />
    
  596.       </Suspense>,
    
  597.     );
    
  598.     await waitForAll([
    
  599.       'getDerivedStateFromProps: A',
    
  600.       'shouldComponentUpdate: A -> A',
    
  601.       'A3',
    
  602.       'getSnapshotBeforeUpdate: A -> A',
    
  603.       'componentDidUpdate: A -> A',
    
  604.     ]);
    
  605.     expect(root).toMatchRenderedOutput('A3');
    
  606.   });
    
  607. 
    
  608.   it('sets defaultProps for legacy lifecycles', async () => {
    
  609.     class C extends React.Component {
    
  610.       static defaultProps = {text: 'A'};
    
  611.       state = {};
    
  612. 
    
  613.       UNSAFE_componentWillMount() {
    
  614.         Scheduler.log(`UNSAFE_componentWillMount: ${this.props.text}`);
    
  615.       }
    
  616. 
    
  617.       UNSAFE_componentWillUpdate(nextProps) {
    
  618.         Scheduler.log(
    
  619.           `UNSAFE_componentWillUpdate: ${this.props.text} -> ${nextProps.text}`,
    
  620.         );
    
  621.       }
    
  622. 
    
  623.       UNSAFE_componentWillReceiveProps(nextProps) {
    
  624.         Scheduler.log(
    
  625.           `UNSAFE_componentWillReceiveProps: ${this.props.text} -> ${nextProps.text}`,
    
  626.         );
    
  627.       }
    
  628. 
    
  629.       render() {
    
  630.         return <Text text={this.props.text + this.props.num} />;
    
  631.       }
    
  632.     }
    
  633. 
    
  634.     const LazyClass = lazy(() => fakeImport(C));
    
  635. 
    
  636.     const root = ReactTestRenderer.create(
    
  637.       <Suspense fallback={<Text text="Loading..." />}>
    
  638.         <LazyClass num={1} />
    
  639.       </Suspense>,
    
  640.     );
    
  641. 
    
  642.     assertLog(['Loading...']);
    
  643.     await waitForAll([]);
    
  644.     expect(root).toMatchRenderedOutput('Loading...');
    
  645. 
    
  646.     await resolveFakeImport(C);
    
  647. 
    
  648.     assertLog([]);
    
  649. 
    
  650.     root.update(
    
  651.       <Suspense fallback={<Text text="Loading..." />}>
    
  652.         <LazyClass num={2} />
    
  653.       </Suspense>,
    
  654.     );
    
  655. 
    
  656.     assertLog(['UNSAFE_componentWillMount: A', 'A2']);
    
  657.     expect(root).toMatchRenderedOutput('A2');
    
  658. 
    
  659.     root.update(
    
  660.       <Suspense fallback={<Text text="Loading..." />}>
    
  661.         <LazyClass num={3} />
    
  662.       </Suspense>,
    
  663.     );
    
  664.     assertLog([
    
  665.       'UNSAFE_componentWillReceiveProps: A -> A',
    
  666.       'UNSAFE_componentWillUpdate: A -> A',
    
  667.       'A3',
    
  668.     ]);
    
  669.     await waitForAll([]);
    
  670.     expect(root).toMatchRenderedOutput('A3');
    
  671.   });
    
  672. 
    
  673.   it('resolves defaultProps on the outer wrapper but warns', async () => {
    
  674.     function T(props) {
    
  675.       Scheduler.log(props.inner + ' ' + props.outer);
    
  676.       return props.inner + ' ' + props.outer;
    
  677.     }
    
  678.     T.defaultProps = {inner: 'Hi'};
    
  679.     const LazyText = lazy(() => fakeImport(T));
    
  680.     expect(() => {
    
  681.       LazyText.defaultProps = {outer: 'Bye'};
    
  682.     }).toErrorDev(
    
  683.       'React.lazy(...): It is not supported to assign `defaultProps` to ' +
    
  684.         'a lazy component import. Either specify them where the component ' +
    
  685.         'is defined, or create a wrapping component around it.',
    
  686.       {withoutStack: true},
    
  687.     );
    
  688. 
    
  689.     const root = ReactTestRenderer.create(
    
  690.       <Suspense fallback={<Text text="Loading..." />}>
    
  691.         <LazyText />
    
  692.       </Suspense>,
    
  693.       {
    
  694.         unstable_isConcurrent: true,
    
  695.       },
    
  696.     );
    
  697. 
    
  698.     await waitForAll(['Loading...']);
    
  699.     expect(root).not.toMatchRenderedOutput('Hi Bye');
    
  700. 
    
  701.     await expect(async () => {
    
  702.       await act(() => resolveFakeImport(T));
    
  703.       assertLog(['Hi Bye']);
    
  704.     }).toErrorDev(
    
  705.       'Warning: T: Support for defaultProps ' +
    
  706.         'will be removed from function components in a future major ' +
    
  707.         'release. Use JavaScript default parameters instead.',
    
  708.     );
    
  709. 
    
  710.     expect(root).toMatchRenderedOutput('Hi Bye');
    
  711. 
    
  712.     root.update(
    
  713.       <Suspense fallback={<Text text="Loading..." />}>
    
  714.         <LazyText outer="World" />
    
  715.       </Suspense>,
    
  716.     );
    
  717.     await waitForAll(['Hi World']);
    
  718.     expect(root).toMatchRenderedOutput('Hi World');
    
  719. 
    
  720.     root.update(
    
  721.       <Suspense fallback={<Text text="Loading..." />}>
    
  722.         <LazyText inner="Friends" />
    
  723.       </Suspense>,
    
  724.     );
    
  725.     await waitForAll(['Friends Bye']);
    
  726.     expect(root).toMatchRenderedOutput('Friends Bye');
    
  727.   });
    
  728. 
    
  729.   it('throws with a useful error when wrapping invalid type with lazy()', async () => {
    
  730.     const BadLazy = lazy(() => fakeImport(42));
    
  731. 
    
  732.     const root = ReactTestRenderer.create(
    
  733.       <Suspense fallback={<Text text="Loading..." />}>
    
  734.         <BadLazy />
    
  735.       </Suspense>,
    
  736.       {
    
  737.         unstable_isConcurrent: true,
    
  738.       },
    
  739.     );
    
  740. 
    
  741.     await waitForAll(['Loading...']);
    
  742. 
    
  743.     await resolveFakeImport(42);
    
  744.     root.update(
    
  745.       <Suspense fallback={<Text text="Loading..." />}>
    
  746.         <BadLazy />
    
  747.       </Suspense>,
    
  748.     );
    
  749.     await waitForThrow(
    
  750.       'Element type is invalid. Received a promise that resolves to: 42. ' +
    
  751.         'Lazy element type must resolve to a class or function.',
    
  752.     );
    
  753.   });
    
  754. 
    
  755.   it('throws with a useful error when wrapping lazy() multiple times', async () => {
    
  756.     const Lazy1 = lazy(() => fakeImport(Text));
    
  757.     const Lazy2 = lazy(() => fakeImport(Lazy1));
    
  758. 
    
  759.     const root = ReactTestRenderer.create(
    
  760.       <Suspense fallback={<Text text="Loading..." />}>
    
  761.         <Lazy2 text="Hello" />
    
  762.       </Suspense>,
    
  763.       {
    
  764.         unstable_isConcurrent: true,
    
  765.       },
    
  766.     );
    
  767. 
    
  768.     await waitForAll(['Loading...']);
    
  769.     expect(root).not.toMatchRenderedOutput('Hello');
    
  770. 
    
  771.     await resolveFakeImport(Lazy1);
    
  772.     root.update(
    
  773.       <Suspense fallback={<Text text="Loading..." />}>
    
  774.         <Lazy2 text="Hello" />
    
  775.       </Suspense>,
    
  776.     );
    
  777.     await waitForThrow(
    
  778.       'Element type is invalid. Received a promise that resolves to: [object Object]. ' +
    
  779.         'Lazy element type must resolve to a class or function.' +
    
  780.         (__DEV__
    
  781.           ? ' Did you wrap a component in React.lazy() more than once?'
    
  782.           : ''),
    
  783.     );
    
  784.   });
    
  785. 
    
  786.   it('warns about defining propTypes on the outer wrapper', () => {
    
  787.     const LazyText = lazy(() => fakeImport(Text));
    
  788.     expect(() => {
    
  789.       LazyText.propTypes = {hello: () => {}};
    
  790.     }).toErrorDev(
    
  791.       'React.lazy(...): It is not supported to assign `propTypes` to ' +
    
  792.         'a lazy component import. Either specify them where the component ' +
    
  793.         'is defined, or create a wrapping component around it.',
    
  794.       {withoutStack: true},
    
  795.     );
    
  796.   });
    
  797. 
    
  798.   async function verifyInnerPropTypesAreChecked(
    
  799.     Add,
    
  800.     shouldWarnAboutFunctionDefaultProps,
    
  801.     shouldWarnAboutMemoDefaultProps,
    
  802.   ) {
    
  803.     const LazyAdd = lazy(() => fakeImport(Add));
    
  804.     expect(() => {
    
  805.       LazyAdd.propTypes = {};
    
  806.     }).toErrorDev(
    
  807.       'React.lazy(...): It is not supported to assign `propTypes` to ' +
    
  808.         'a lazy component import. Either specify them where the component ' +
    
  809.         'is defined, or create a wrapping component around it.',
    
  810.       {withoutStack: true},
    
  811.     );
    
  812. 
    
  813.     const root = ReactTestRenderer.create(
    
  814.       <Suspense fallback={<Text text="Loading..." />}>
    
  815.         <LazyAdd inner="2" outer="2" />
    
  816.       </Suspense>,
    
  817.       {
    
  818.         unstable_isConcurrent: true,
    
  819.       },
    
  820.     );
    
  821. 
    
  822.     await waitForAll(['Loading...']);
    
  823. 
    
  824.     expect(root).not.toMatchRenderedOutput('22');
    
  825. 
    
  826.     // Mount
    
  827.     await expect(async () => {
    
  828.       await act(() => resolveFakeImport(Add));
    
  829.     }).toErrorDev(
    
  830.       shouldWarnAboutFunctionDefaultProps
    
  831.         ? [
    
  832.             'Add: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
    
  833.             'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.',
    
  834.           ]
    
  835.         : shouldWarnAboutMemoDefaultProps
    
  836.         ? [
    
  837.             'Add: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
    
  838.             'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.',
    
  839.           ]
    
  840.         : [
    
  841.             'Invalid prop `inner` of type `string` supplied to `Add`, expected `number`.',
    
  842.           ],
    
  843.     );
    
  844.     expect(root).toMatchRenderedOutput('22');
    
  845. 
    
  846.     // Update
    
  847.     await expect(async () => {
    
  848.       root.update(
    
  849.         <Suspense fallback={<Text text="Loading..." />}>
    
  850.           <LazyAdd inner={false} outer={false} />
    
  851.         </Suspense>,
    
  852.       );
    
  853.       await waitForAll([]);
    
  854.     }).toErrorDev(
    
  855.       'Invalid prop `inner` of type `boolean` supplied to `Add`, expected `number`.',
    
  856.     );
    
  857.     expect(root).toMatchRenderedOutput('0');
    
  858.   }
    
  859. 
    
  860.   // Note: all "with defaultProps" tests below also verify defaultProps works as expected.
    
  861.   // If we ever delete or move propTypes-related tests, make sure not to delete these.
    
  862.   it('respects propTypes on function component with defaultProps', async () => {
    
  863.     function Add(props) {
    
  864.       expect(props.innerWithDefault).toBe(42);
    
  865.       return props.inner + props.outer;
    
  866.     }
    
  867.     Add.propTypes = {
    
  868.       inner: PropTypes.number.isRequired,
    
  869.       innerWithDefault: PropTypes.number.isRequired,
    
  870.     };
    
  871.     Add.defaultProps = {
    
  872.       innerWithDefault: 42,
    
  873.     };
    
  874.     await verifyInnerPropTypesAreChecked(Add, true);
    
  875.   });
    
  876. 
    
  877.   it('respects propTypes on function component without defaultProps', async () => {
    
  878.     function Add(props) {
    
  879.       return props.inner + props.outer;
    
  880.     }
    
  881.     Add.propTypes = {
    
  882.       inner: PropTypes.number.isRequired,
    
  883.     };
    
  884.     await verifyInnerPropTypesAreChecked(Add);
    
  885.   });
    
  886. 
    
  887.   it('respects propTypes on class component with defaultProps', async () => {
    
  888.     class Add extends React.Component {
    
  889.       render() {
    
  890.         expect(this.props.innerWithDefault).toBe(42);
    
  891.         return this.props.inner + this.props.outer;
    
  892.       }
    
  893.     }
    
  894.     Add.propTypes = {
    
  895.       inner: PropTypes.number.isRequired,
    
  896.       innerWithDefault: PropTypes.number.isRequired,
    
  897.     };
    
  898.     Add.defaultProps = {
    
  899.       innerWithDefault: 42,
    
  900.     };
    
  901.     await verifyInnerPropTypesAreChecked(Add);
    
  902.   });
    
  903. 
    
  904.   it('respects propTypes on class component without defaultProps', async () => {
    
  905.     class Add extends React.Component {
    
  906.       render() {
    
  907.         return this.props.inner + this.props.outer;
    
  908.       }
    
  909.     }
    
  910.     Add.propTypes = {
    
  911.       inner: PropTypes.number.isRequired,
    
  912.     };
    
  913.     await verifyInnerPropTypesAreChecked(Add);
    
  914.   });
    
  915. 
    
  916.   it('respects propTypes on forwardRef component with defaultProps', async () => {
    
  917.     const Add = React.forwardRef((props, ref) => {
    
  918.       expect(props.innerWithDefault).toBe(42);
    
  919.       return props.inner + props.outer;
    
  920.     });
    
  921.     Add.displayName = 'Add';
    
  922.     Add.propTypes = {
    
  923.       inner: PropTypes.number.isRequired,
    
  924.       innerWithDefault: PropTypes.number.isRequired,
    
  925.     };
    
  926.     Add.defaultProps = {
    
  927.       innerWithDefault: 42,
    
  928.     };
    
  929.     await verifyInnerPropTypesAreChecked(Add);
    
  930.   });
    
  931. 
    
  932.   it('respects propTypes on forwardRef component without defaultProps', async () => {
    
  933.     const Add = React.forwardRef((props, ref) => {
    
  934.       return props.inner + props.outer;
    
  935.     });
    
  936.     Add.displayName = 'Add';
    
  937.     Add.propTypes = {
    
  938.       inner: PropTypes.number.isRequired,
    
  939.     };
    
  940.     await verifyInnerPropTypesAreChecked(Add);
    
  941.   });
    
  942. 
    
  943.   it('respects propTypes on outer memo component with defaultProps', async () => {
    
  944.     let Add = props => {
    
  945.       expect(props.innerWithDefault).toBe(42);
    
  946.       return props.inner + props.outer;
    
  947.     };
    
  948.     Add = React.memo(Add);
    
  949.     Add.propTypes = {
    
  950.       inner: PropTypes.number.isRequired,
    
  951.       innerWithDefault: PropTypes.number.isRequired,
    
  952.     };
    
  953.     Add.defaultProps = {
    
  954.       innerWithDefault: 42,
    
  955.     };
    
  956.     await verifyInnerPropTypesAreChecked(Add, false, true);
    
  957.   });
    
  958. 
    
  959.   it('respects propTypes on outer memo component without defaultProps', async () => {
    
  960.     let Add = props => {
    
  961.       return props.inner + props.outer;
    
  962.     };
    
  963.     Add = React.memo(Add);
    
  964.     Add.propTypes = {
    
  965.       inner: PropTypes.number.isRequired,
    
  966.     };
    
  967.     await verifyInnerPropTypesAreChecked(Add);
    
  968.   });
    
  969. 
    
  970.   it('respects propTypes on inner memo component with defaultProps', async () => {
    
  971.     const Add = props => {
    
  972.       expect(props.innerWithDefault).toBe(42);
    
  973.       return props.inner + props.outer;
    
  974.     };
    
  975.     Add.displayName = 'Add';
    
  976.     Add.propTypes = {
    
  977.       inner: PropTypes.number.isRequired,
    
  978.       innerWithDefault: PropTypes.number.isRequired,
    
  979.     };
    
  980.     Add.defaultProps = {
    
  981.       innerWithDefault: 42,
    
  982.     };
    
  983.     await verifyInnerPropTypesAreChecked(React.memo(Add), true);
    
  984.   });
    
  985. 
    
  986.   it('respects propTypes on inner memo component without defaultProps', async () => {
    
  987.     const Add = props => {
    
  988.       return props.inner + props.outer;
    
  989.     };
    
  990.     Add.displayName = 'Add';
    
  991.     Add.propTypes = {
    
  992.       inner: PropTypes.number.isRequired,
    
  993.     };
    
  994.     await verifyInnerPropTypesAreChecked(React.memo(Add));
    
  995.   });
    
  996. 
    
  997.   it('uses outer resolved props for validating propTypes on memo', async () => {
    
  998.     let T = props => {
    
  999.       return <Text text={props.text} />;
    
  1000.     };
    
  1001.     T.defaultProps = {
    
  1002.       text: 'Inner default text',
    
  1003.     };
    
  1004.     T = React.memo(T);
    
  1005.     T.propTypes = {
    
  1006.       // Should not be satisfied by the *inner* defaultProps.
    
  1007.       text: PropTypes.string.isRequired,
    
  1008.     };
    
  1009.     const LazyText = lazy(() => fakeImport(T));
    
  1010.     const root = ReactTestRenderer.create(
    
  1011.       <Suspense fallback={<Text text="Loading..." />}>
    
  1012.         <LazyText />
    
  1013.       </Suspense>,
    
  1014.       {
    
  1015.         unstable_isConcurrent: true,
    
  1016.       },
    
  1017.     );
    
  1018. 
    
  1019.     await waitForAll(['Loading...']);
    
  1020.     expect(root).not.toMatchRenderedOutput('Inner default text');
    
  1021. 
    
  1022.     // Mount
    
  1023.     await expect(async () => {
    
  1024.       await act(() => resolveFakeImport(T));
    
  1025.       assertLog(['Inner default text']);
    
  1026.     }).toErrorDev([
    
  1027.       'T: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
    
  1028.       'The prop `text` is marked as required in `T`, but its value is `undefined`',
    
  1029.     ]);
    
  1030.     expect(root).toMatchRenderedOutput('Inner default text');
    
  1031. 
    
  1032.     // Update
    
  1033.     await expect(async () => {
    
  1034.       root.update(
    
  1035.         <Suspense fallback={<Text text="Loading..." />}>
    
  1036.           <LazyText text={null} />
    
  1037.         </Suspense>,
    
  1038.       );
    
  1039.       await waitForAll([null]);
    
  1040.     }).toErrorDev(
    
  1041.       'The prop `text` is marked as required in `T`, but its value is `null`',
    
  1042.     );
    
  1043.     expect(root).toMatchRenderedOutput(null);
    
  1044.   });
    
  1045. 
    
  1046.   it('includes lazy-loaded component in warning stack', async () => {
    
  1047.     const Foo = props => <div>{[<Text text="A" />, <Text text="B" />]}</div>;
    
  1048.     const LazyFoo = lazy(() => {
    
  1049.       Scheduler.log('Started loading');
    
  1050.       return fakeImport(Foo);
    
  1051.     });
    
  1052. 
    
  1053.     const root = ReactTestRenderer.create(
    
  1054.       <Suspense fallback={<Text text="Loading..." />}>
    
  1055.         <LazyFoo />
    
  1056.       </Suspense>,
    
  1057.       {
    
  1058.         unstable_isConcurrent: true,
    
  1059.       },
    
  1060.     );
    
  1061. 
    
  1062.     await waitForAll(['Started loading', 'Loading...']);
    
  1063.     expect(root).not.toMatchRenderedOutput(<div>AB</div>);
    
  1064. 
    
  1065.     await expect(async () => {
    
  1066.       await act(() => resolveFakeImport(Foo));
    
  1067.       assertLog(['A', 'B']);
    
  1068.     }).toErrorDev('    in Text (at **)\n' + '    in Foo (at **)');
    
  1069.     expect(root).toMatchRenderedOutput(<div>AB</div>);
    
  1070.   });
    
  1071. 
    
  1072.   it('supports class and forwardRef components', async () => {
    
  1073.     class Foo extends React.Component {
    
  1074.       render() {
    
  1075.         return <Text text="Foo" />;
    
  1076.       }
    
  1077.     }
    
  1078.     const LazyClass = lazy(() => {
    
  1079.       return fakeImport(Foo);
    
  1080.     });
    
  1081. 
    
  1082.     class Bar extends React.Component {
    
  1083.       render() {
    
  1084.         return <Text text="Bar" />;
    
  1085.       }
    
  1086.     }
    
  1087.     const ForwardRefBar = React.forwardRef((props, ref) => {
    
  1088.       Scheduler.log('forwardRef');
    
  1089.       return <Bar ref={ref} />;
    
  1090.     });
    
  1091. 
    
  1092.     const LazyForwardRef = lazy(() => {
    
  1093.       return fakeImport(ForwardRefBar);
    
  1094.     });
    
  1095. 
    
  1096.     const ref = React.createRef();
    
  1097.     const root = ReactTestRenderer.create(
    
  1098.       <Suspense fallback={<Text text="Loading..." />}>
    
  1099.         <LazyClass />
    
  1100.         <LazyForwardRef ref={ref} />
    
  1101.       </Suspense>,
    
  1102.       {
    
  1103.         unstable_isConcurrent: true,
    
  1104.       },
    
  1105.     );
    
  1106. 
    
  1107.     await waitForAll(['Loading...']);
    
  1108.     expect(root).not.toMatchRenderedOutput('FooBar');
    
  1109.     expect(ref.current).toBe(null);
    
  1110. 
    
  1111.     await act(() => resolveFakeImport(Foo));
    
  1112.     assertLog(['Foo']);
    
  1113. 
    
  1114.     await act(() => resolveFakeImport(ForwardRefBar));
    
  1115.     assertLog(['Foo', 'forwardRef', 'Bar']);
    
  1116.     expect(root).toMatchRenderedOutput('FooBar');
    
  1117.     expect(ref.current).not.toBe(null);
    
  1118.   });
    
  1119. 
    
  1120.   // Regression test for #14310
    
  1121.   it('supports defaultProps defined on the memo() return value', async () => {
    
  1122.     const Add = React.memo(props => {
    
  1123.       return props.inner + props.outer;
    
  1124.     });
    
  1125.     Add.defaultProps = {
    
  1126.       inner: 2,
    
  1127.     };
    
  1128.     const LazyAdd = lazy(() => fakeImport(Add));
    
  1129.     const root = ReactTestRenderer.create(
    
  1130.       <Suspense fallback={<Text text="Loading..." />}>
    
  1131.         <LazyAdd outer={2} />
    
  1132.       </Suspense>,
    
  1133.       {
    
  1134.         unstable_isConcurrent: true,
    
  1135.       },
    
  1136.     );
    
  1137.     await waitForAll(['Loading...']);
    
  1138.     expect(root).not.toMatchRenderedOutput('4');
    
  1139. 
    
  1140.     // Mount
    
  1141.     await expect(async () => {
    
  1142.       await act(() => resolveFakeImport(Add));
    
  1143.     }).toErrorDev(
    
  1144.       'Unknown: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
    
  1145.     );
    
  1146.     expect(root).toMatchRenderedOutput('4');
    
  1147. 
    
  1148.     // Update (shallowly equal)
    
  1149.     root.update(
    
  1150.       <Suspense fallback={<Text text="Loading..." />}>
    
  1151.         <LazyAdd outer={2} />
    
  1152.       </Suspense>,
    
  1153.     );
    
  1154.     await waitForAll([]);
    
  1155.     expect(root).toMatchRenderedOutput('4');
    
  1156. 
    
  1157.     // Update
    
  1158.     root.update(
    
  1159.       <Suspense fallback={<Text text="Loading..." />}>
    
  1160.         <LazyAdd outer={3} />
    
  1161.       </Suspense>,
    
  1162.     );
    
  1163.     await waitForAll([]);
    
  1164.     expect(root).toMatchRenderedOutput('5');
    
  1165. 
    
  1166.     // Update (shallowly equal)
    
  1167.     root.update(
    
  1168.       <Suspense fallback={<Text text="Loading..." />}>
    
  1169.         <LazyAdd outer={3} />
    
  1170.       </Suspense>,
    
  1171.     );
    
  1172.     await waitForAll([]);
    
  1173.     expect(root).toMatchRenderedOutput('5');
    
  1174. 
    
  1175.     // Update (explicit props)
    
  1176.     root.update(
    
  1177.       <Suspense fallback={<Text text="Loading..." />}>
    
  1178.         <LazyAdd outer={1} inner={1} />
    
  1179.       </Suspense>,
    
  1180.     );
    
  1181.     await waitForAll([]);
    
  1182.     expect(root).toMatchRenderedOutput('2');
    
  1183. 
    
  1184.     // Update (explicit props, shallowly equal)
    
  1185.     root.update(
    
  1186.       <Suspense fallback={<Text text="Loading..." />}>
    
  1187.         <LazyAdd outer={1} inner={1} />
    
  1188.       </Suspense>,
    
  1189.     );
    
  1190.     await waitForAll([]);
    
  1191.     expect(root).toMatchRenderedOutput('2');
    
  1192. 
    
  1193.     // Update
    
  1194.     root.update(
    
  1195.       <Suspense fallback={<Text text="Loading..." />}>
    
  1196.         <LazyAdd outer={1} />
    
  1197.       </Suspense>,
    
  1198.     );
    
  1199.     await waitForAll([]);
    
  1200.     expect(root).toMatchRenderedOutput('3');
    
  1201.   });
    
  1202. 
    
  1203.   it('merges defaultProps in the correct order', async () => {
    
  1204.     let Add = React.memo(props => {
    
  1205.       return props.inner + props.outer;
    
  1206.     });
    
  1207.     Add.defaultProps = {
    
  1208.       inner: 100,
    
  1209.     };
    
  1210.     Add = React.memo(Add);
    
  1211.     Add.defaultProps = {
    
  1212.       inner: 2,
    
  1213.       outer: 0,
    
  1214.     };
    
  1215.     const LazyAdd = lazy(() => fakeImport(Add));
    
  1216.     const root = ReactTestRenderer.create(
    
  1217.       <Suspense fallback={<Text text="Loading..." />}>
    
  1218.         <LazyAdd outer={2} />
    
  1219.       </Suspense>,
    
  1220.       {
    
  1221.         unstable_isConcurrent: true,
    
  1222.       },
    
  1223.     );
    
  1224.     await waitForAll(['Loading...']);
    
  1225.     expect(root).not.toMatchRenderedOutput('4');
    
  1226. 
    
  1227.     // Mount
    
  1228.     await expect(async () => {
    
  1229.       await act(() => resolveFakeImport(Add));
    
  1230.     }).toErrorDev([
    
  1231.       'Memo: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
    
  1232.       'Unknown: Support for defaultProps will be removed from memo components in a future major release. Use JavaScript default parameters instead.',
    
  1233.     ]);
    
  1234.     expect(root).toMatchRenderedOutput('4');
    
  1235. 
    
  1236.     // Update
    
  1237.     root.update(
    
  1238.       <Suspense fallback={<Text text="Loading..." />}>
    
  1239.         <LazyAdd outer={3} />
    
  1240.       </Suspense>,
    
  1241.     );
    
  1242.     await waitForAll([]);
    
  1243.     expect(root).toMatchRenderedOutput('5');
    
  1244. 
    
  1245.     // Update
    
  1246.     root.update(
    
  1247.       <Suspense fallback={<Text text="Loading..." />}>
    
  1248.         <LazyAdd />
    
  1249.       </Suspense>,
    
  1250.     );
    
  1251.     await waitForAll([]);
    
  1252.     expect(root).toMatchRenderedOutput('2');
    
  1253.   });
    
  1254. 
    
  1255.   it('warns about ref on functions for lazy-loaded components', async () => {
    
  1256.     const Foo = props => <div />;
    
  1257.     const LazyFoo = lazy(() => {
    
  1258.       return fakeImport(Foo);
    
  1259.     });
    
  1260. 
    
  1261.     const ref = React.createRef();
    
  1262.     ReactTestRenderer.create(
    
  1263.       <Suspense fallback={<Text text="Loading..." />}>
    
  1264.         <LazyFoo ref={ref} />
    
  1265.       </Suspense>,
    
  1266.       {
    
  1267.         unstable_isConcurrent: true,
    
  1268.       },
    
  1269.     );
    
  1270. 
    
  1271.     await waitForAll(['Loading...']);
    
  1272.     await resolveFakeImport(Foo);
    
  1273.     await expect(async () => {
    
  1274.       await waitForAll([]);
    
  1275.     }).toErrorDev('Function components cannot be given refs');
    
  1276.   });
    
  1277. 
    
  1278.   it('should error with a component stack naming the resolved component', async () => {
    
  1279.     let componentStackMessage;
    
  1280. 
    
  1281.     function ResolvedText() {
    
  1282.       throw new Error('oh no');
    
  1283.     }
    
  1284.     const LazyText = lazy(() => fakeImport(ResolvedText));
    
  1285. 
    
  1286.     class ErrorBoundary extends React.Component {
    
  1287.       state = {error: null};
    
  1288. 
    
  1289.       componentDidCatch(error, errMessage) {
    
  1290.         componentStackMessage = normalizeCodeLocInfo(errMessage.componentStack);
    
  1291.         this.setState({
    
  1292.           error,
    
  1293.         });
    
  1294.       }
    
  1295. 
    
  1296.       render() {
    
  1297.         return this.state.error ? null : this.props.children;
    
  1298.       }
    
  1299.     }
    
  1300. 
    
  1301.     ReactTestRenderer.create(
    
  1302.       <ErrorBoundary>
    
  1303.         <Suspense fallback={<Text text="Loading..." />}>
    
  1304.           <LazyText text="Hi" />
    
  1305.         </Suspense>
    
  1306.       </ErrorBoundary>,
    
  1307.       {unstable_isConcurrent: true},
    
  1308.     );
    
  1309. 
    
  1310.     await waitForAll(['Loading...']);
    
  1311. 
    
  1312.     await act(() => resolveFakeImport(ResolvedText));
    
  1313.     assertLog([]);
    
  1314. 
    
  1315.     expect(componentStackMessage).toContain('in ResolvedText');
    
  1316.   });
    
  1317. 
    
  1318.   it('should error with a component stack containing Lazy if unresolved', () => {
    
  1319.     let componentStackMessage;
    
  1320. 
    
  1321.     const LazyText = lazy(() => ({
    
  1322.       then(resolve, reject) {
    
  1323.         reject(new Error('oh no'));
    
  1324.       },
    
  1325.     }));
    
  1326. 
    
  1327.     class ErrorBoundary extends React.Component {
    
  1328.       state = {error: null};
    
  1329. 
    
  1330.       componentDidCatch(error, errMessage) {
    
  1331.         componentStackMessage = normalizeCodeLocInfo(errMessage.componentStack);
    
  1332.         this.setState({
    
  1333.           error,
    
  1334.         });
    
  1335.       }
    
  1336. 
    
  1337.       render() {
    
  1338.         return this.state.error ? null : this.props.children;
    
  1339.       }
    
  1340.     }
    
  1341. 
    
  1342.     ReactTestRenderer.create(
    
  1343.       <ErrorBoundary>
    
  1344.         <Suspense fallback={<Text text="Loading..." />}>
    
  1345.           <LazyText text="Hi" />
    
  1346.         </Suspense>
    
  1347.       </ErrorBoundary>,
    
  1348.     );
    
  1349. 
    
  1350.     assertLog([]);
    
  1351. 
    
  1352.     expect(componentStackMessage).toContain('in Lazy');
    
  1353.   });
    
  1354. 
    
  1355.   it('mount and reorder lazy types', async () => {
    
  1356.     class Child extends React.Component {
    
  1357.       componentWillUnmount() {
    
  1358.         Scheduler.log('Did unmount: ' + this.props.label);
    
  1359.       }
    
  1360.       componentDidMount() {
    
  1361.         Scheduler.log('Did mount: ' + this.props.label);
    
  1362.       }
    
  1363.       componentDidUpdate() {
    
  1364.         Scheduler.log('Did update: ' + this.props.label);
    
  1365.       }
    
  1366.       render() {
    
  1367.         return <Text text={this.props.label} />;
    
  1368.       }
    
  1369.     }
    
  1370. 
    
  1371.     function ChildA({lowerCase}) {
    
  1372.       return <Child label={lowerCase ? 'a' : 'A'} />;
    
  1373.     }
    
  1374. 
    
  1375.     function ChildB({lowerCase}) {
    
  1376.       return <Child label={lowerCase ? 'b' : 'B'} />;
    
  1377.     }
    
  1378. 
    
  1379.     const LazyChildA = lazy(() => {
    
  1380.       Scheduler.log('Init A');
    
  1381.       return fakeImport(ChildA);
    
  1382.     });
    
  1383.     const LazyChildB = lazy(() => {
    
  1384.       Scheduler.log('Init B');
    
  1385.       return fakeImport(ChildB);
    
  1386.     });
    
  1387.     const LazyChildA2 = lazy(() => {
    
  1388.       Scheduler.log('Init A2');
    
  1389.       return fakeImport(ChildA);
    
  1390.     });
    
  1391.     let resolveB2;
    
  1392.     const LazyChildB2 = lazy(() => {
    
  1393.       Scheduler.log('Init B2');
    
  1394.       return new Promise(r => {
    
  1395.         resolveB2 = r;
    
  1396.       });
    
  1397.     });
    
  1398. 
    
  1399.     function Parent({swap}) {
    
  1400.       return (
    
  1401.         <Suspense fallback={<Text text="Outer..." />}>
    
  1402.           <Suspense fallback={<Text text="Loading..." />}>
    
  1403.             {swap
    
  1404.               ? [
    
  1405.                   <LazyChildB2 key="B" lowerCase={true} />,
    
  1406.                   <LazyChildA2 key="A" lowerCase={true} />,
    
  1407.                 ]
    
  1408.               : [<LazyChildA key="A" />, <LazyChildB key="B" />]}
    
  1409.           </Suspense>
    
  1410.         </Suspense>
    
  1411.       );
    
  1412.     }
    
  1413. 
    
  1414.     const root = ReactTestRenderer.create(<Parent swap={false} />, {
    
  1415.       unstable_isConcurrent: true,
    
  1416.     });
    
  1417. 
    
  1418.     await waitForAll(['Init A', 'Loading...']);
    
  1419.     expect(root).not.toMatchRenderedOutput('AB');
    
  1420. 
    
  1421.     await act(() => resolveFakeImport(ChildA));
    
  1422.     assertLog(['A', 'Init B']);
    
  1423. 
    
  1424.     await act(() => resolveFakeImport(ChildB));
    
  1425.     assertLog(['A', 'B', 'Did mount: A', 'Did mount: B']);
    
  1426.     expect(root).toMatchRenderedOutput('AB');
    
  1427. 
    
  1428.     // Swap the position of A and B
    
  1429.     root.update(<Parent swap={true} />);
    
  1430.     await waitForAll([
    
  1431.       'Init B2',
    
  1432.       'Loading...',
    
  1433.       'Did unmount: A',
    
  1434.       'Did unmount: B',
    
  1435.     ]);
    
  1436. 
    
  1437.     // The suspense boundary should've triggered now.
    
  1438.     expect(root).toMatchRenderedOutput('Loading...');
    
  1439.     await act(() => resolveB2({default: ChildB}));
    
  1440. 
    
  1441.     // We need to flush to trigger the second one to load.
    
  1442.     assertLog(['Init A2', 'b', 'a', 'Did mount: b', 'Did mount: a']);
    
  1443.     expect(root).toMatchRenderedOutput('ba');
    
  1444.   });
    
  1445. 
    
  1446.   it('mount and reorder lazy types (legacy mode)', async () => {
    
  1447.     class Child extends React.Component {
    
  1448.       componentDidMount() {
    
  1449.         Scheduler.log('Did mount: ' + this.props.label);
    
  1450.       }
    
  1451.       componentDidUpdate() {
    
  1452.         Scheduler.log('Did update: ' + this.props.label);
    
  1453.       }
    
  1454.       render() {
    
  1455.         return <Text text={this.props.label} />;
    
  1456.       }
    
  1457.     }
    
  1458. 
    
  1459.     function ChildA({lowerCase}) {
    
  1460.       return <Child label={lowerCase ? 'a' : 'A'} />;
    
  1461.     }
    
  1462. 
    
  1463.     function ChildB({lowerCase}) {
    
  1464.       return <Child label={lowerCase ? 'b' : 'B'} />;
    
  1465.     }
    
  1466. 
    
  1467.     const LazyChildA = lazy(() => {
    
  1468.       Scheduler.log('Init A');
    
  1469.       return fakeImport(ChildA);
    
  1470.     });
    
  1471.     const LazyChildB = lazy(() => {
    
  1472.       Scheduler.log('Init B');
    
  1473.       return fakeImport(ChildB);
    
  1474.     });
    
  1475.     const LazyChildA2 = lazy(() => {
    
  1476.       Scheduler.log('Init A2');
    
  1477.       return fakeImport(ChildA);
    
  1478.     });
    
  1479.     const LazyChildB2 = lazy(() => {
    
  1480.       Scheduler.log('Init B2');
    
  1481.       return fakeImport(ChildB);
    
  1482.     });
    
  1483. 
    
  1484.     function Parent({swap}) {
    
  1485.       return (
    
  1486.         <Suspense fallback={<Text text="Outer..." />}>
    
  1487.           <Suspense fallback={<Text text="Loading..." />}>
    
  1488.             {swap
    
  1489.               ? [
    
  1490.                   <LazyChildB2 key="B" lowerCase={true} />,
    
  1491.                   <LazyChildA2 key="A" lowerCase={true} />,
    
  1492.                 ]
    
  1493.               : [<LazyChildA key="A" />, <LazyChildB key="B" />]}
    
  1494.           </Suspense>
    
  1495.         </Suspense>
    
  1496.       );
    
  1497.     }
    
  1498. 
    
  1499.     const root = ReactTestRenderer.create(<Parent swap={false} />, {
    
  1500.       unstable_isConcurrent: false,
    
  1501.     });
    
  1502. 
    
  1503.     assertLog(['Init A', 'Init B', 'Loading...']);
    
  1504.     expect(root).not.toMatchRenderedOutput('AB');
    
  1505. 
    
  1506.     await resolveFakeImport(ChildA);
    
  1507.     await resolveFakeImport(ChildB);
    
  1508. 
    
  1509.     await waitForAll(['A', 'B', 'Did mount: A', 'Did mount: B']);
    
  1510.     expect(root).toMatchRenderedOutput('AB');
    
  1511. 
    
  1512.     // Swap the position of A and B
    
  1513.     root.update(<Parent swap={true} />);
    
  1514.     assertLog(['Init B2', 'Loading...']);
    
  1515.     await waitForAll(['Init A2', 'b', 'a', 'Did update: b', 'Did update: a']);
    
  1516.     expect(root).toMatchRenderedOutput('ba');
    
  1517.   });
    
  1518. 
    
  1519.   it('mount and reorder lazy elements', async () => {
    
  1520.     class Child extends React.Component {
    
  1521.       componentDidMount() {
    
  1522.         Scheduler.log('Did mount: ' + this.props.label);
    
  1523.       }
    
  1524.       componentDidUpdate() {
    
  1525.         Scheduler.log('Did update: ' + this.props.label);
    
  1526.       }
    
  1527.       render() {
    
  1528.         return <Text text={this.props.label} />;
    
  1529.       }
    
  1530.     }
    
  1531. 
    
  1532.     const ChildA = <Child key="A" label="A" />;
    
  1533.     const lazyChildA = lazy(() => {
    
  1534.       Scheduler.log('Init A');
    
  1535.       return fakeImport(ChildA);
    
  1536.     });
    
  1537.     const ChildB = <Child key="B" label="B" />;
    
  1538.     const lazyChildB = lazy(() => {
    
  1539.       Scheduler.log('Init B');
    
  1540.       return fakeImport(ChildB);
    
  1541.     });
    
  1542.     const ChildA2 = <Child key="A" label="a" />;
    
  1543.     const lazyChildA2 = lazy(() => {
    
  1544.       Scheduler.log('Init A2');
    
  1545.       return fakeImport(ChildA2);
    
  1546.     });
    
  1547.     const ChildB2 = <Child key="B" label="b" />;
    
  1548.     const lazyChildB2 = lazy(() => {
    
  1549.       Scheduler.log('Init B2');
    
  1550.       return fakeImport(ChildB2);
    
  1551.     });
    
  1552. 
    
  1553.     function Parent({swap}) {
    
  1554.       return (
    
  1555.         <Suspense fallback={<Text text="Loading..." />}>
    
  1556.           {swap ? [lazyChildB2, lazyChildA2] : [lazyChildA, lazyChildB]}
    
  1557.         </Suspense>
    
  1558.       );
    
  1559.     }
    
  1560. 
    
  1561.     const root = ReactTestRenderer.create(<Parent swap={false} />, {
    
  1562.       unstable_isConcurrent: true,
    
  1563.     });
    
  1564. 
    
  1565.     await waitForAll(['Init A', 'Loading...']);
    
  1566.     expect(root).not.toMatchRenderedOutput('AB');
    
  1567. 
    
  1568.     await act(() => resolveFakeImport(ChildA));
    
  1569.     // We need to flush to trigger the B to load.
    
  1570.     await assertLog(['Init B']);
    
  1571.     await act(() => resolveFakeImport(ChildB));
    
  1572.     assertLog(['A', 'B', 'Did mount: A', 'Did mount: B']);
    
  1573.     expect(root).toMatchRenderedOutput('AB');
    
  1574. 
    
  1575.     // Swap the position of A and B
    
  1576.     React.startTransition(() => {
    
  1577.       root.update(<Parent swap={true} />);
    
  1578.     });
    
  1579.     await waitForAll(['Init B2', 'Loading...']);
    
  1580.     await act(() => resolveFakeImport(ChildB2));
    
  1581.     // We need to flush to trigger the second one to load.
    
  1582.     assertLog(['Init A2', 'Loading...']);
    
  1583.     await act(() => resolveFakeImport(ChildA2));
    
  1584.     assertLog(['b', 'a', 'Did update: b', 'Did update: a']);
    
  1585.     expect(root).toMatchRenderedOutput('ba');
    
  1586.   });
    
  1587. 
    
  1588.   it('mount and reorder lazy elements (legacy mode)', async () => {
    
  1589.     class Child extends React.Component {
    
  1590.       componentDidMount() {
    
  1591.         Scheduler.log('Did mount: ' + this.props.label);
    
  1592.       }
    
  1593.       componentDidUpdate() {
    
  1594.         Scheduler.log('Did update: ' + this.props.label);
    
  1595.       }
    
  1596.       render() {
    
  1597.         return <Text text={this.props.label} />;
    
  1598.       }
    
  1599.     }
    
  1600. 
    
  1601.     const ChildA = <Child key="A" label="A" />;
    
  1602.     const lazyChildA = lazy(() => {
    
  1603.       Scheduler.log('Init A');
    
  1604.       return fakeImport(ChildA);
    
  1605.     });
    
  1606.     const ChildB = <Child key="B" label="B" />;
    
  1607.     const lazyChildB = lazy(() => {
    
  1608.       Scheduler.log('Init B');
    
  1609.       return fakeImport(ChildB);
    
  1610.     });
    
  1611.     const ChildA2 = <Child key="A" label="a" />;
    
  1612.     const lazyChildA2 = lazy(() => {
    
  1613.       Scheduler.log('Init A2');
    
  1614.       return fakeImport(ChildA2);
    
  1615.     });
    
  1616.     const ChildB2 = <Child key="B" label="b" />;
    
  1617.     const lazyChildB2 = lazy(() => {
    
  1618.       Scheduler.log('Init B2');
    
  1619.       return fakeImport(ChildB2);
    
  1620.     });
    
  1621. 
    
  1622.     function Parent({swap}) {
    
  1623.       return (
    
  1624.         <Suspense fallback={<Text text="Loading..." />}>
    
  1625.           {swap ? [lazyChildB2, lazyChildA2] : [lazyChildA, lazyChildB]}
    
  1626.         </Suspense>
    
  1627.       );
    
  1628.     }
    
  1629. 
    
  1630.     const root = ReactTestRenderer.create(<Parent swap={false} />, {
    
  1631.       unstable_isConcurrent: false,
    
  1632.     });
    
  1633. 
    
  1634.     assertLog(['Init A', 'Loading...']);
    
  1635.     expect(root).not.toMatchRenderedOutput('AB');
    
  1636. 
    
  1637.     await resolveFakeImport(ChildA);
    
  1638.     // We need to flush to trigger the B to load.
    
  1639.     await waitForAll(['Init B']);
    
  1640.     await resolveFakeImport(ChildB);
    
  1641. 
    
  1642.     await waitForAll(['A', 'B', 'Did mount: A', 'Did mount: B']);
    
  1643.     expect(root).toMatchRenderedOutput('AB');
    
  1644. 
    
  1645.     // Swap the position of A and B
    
  1646.     root.update(<Parent swap={true} />);
    
  1647.     assertLog(['Init B2', 'Loading...']);
    
  1648.     await resolveFakeImport(ChildB2);
    
  1649.     // We need to flush to trigger the second one to load.
    
  1650.     await waitForAll(['Init A2']);
    
  1651.     await resolveFakeImport(ChildA2);
    
  1652. 
    
  1653.     await waitForAll(['b', 'a', 'Did update: b', 'Did update: a']);
    
  1654.     expect(root).toMatchRenderedOutput('ba');
    
  1655.   });
    
  1656. });