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.  * @emails react-core
    
  8.  */
    
  9. 
    
  10. 'use strict';
    
  11. 
    
  12. let React = require('react');
    
  13. let useContext;
    
  14. let ReactNoop;
    
  15. let Scheduler;
    
  16. let gen;
    
  17. let waitForAll;
    
  18. let waitFor;
    
  19. let waitForThrow;
    
  20. 
    
  21. describe('ReactNewContext', () => {
    
  22.   beforeEach(() => {
    
  23.     jest.resetModules();
    
  24. 
    
  25.     React = require('react');
    
  26.     useContext = React.useContext;
    
  27.     ReactNoop = require('react-noop-renderer');
    
  28.     Scheduler = require('scheduler');
    
  29.     gen = require('random-seed');
    
  30. 
    
  31.     const InternalTestUtils = require('internal-test-utils');
    
  32.     waitForAll = InternalTestUtils.waitForAll;
    
  33.     waitFor = InternalTestUtils.waitFor;
    
  34.     waitForThrow = InternalTestUtils.waitForThrow;
    
  35.   });
    
  36. 
    
  37.   afterEach(() => {
    
  38.     jest.restoreAllMocks();
    
  39.   });
    
  40. 
    
  41.   function Text(props) {
    
  42.     Scheduler.log(props.text);
    
  43.     return <span prop={props.text} />;
    
  44.   }
    
  45. 
    
  46.   function span(prop) {
    
  47.     return {type: 'span', children: [], prop, hidden: false};
    
  48.   }
    
  49. 
    
  50.   function readContext(Context) {
    
  51.     const dispatcher =
    
  52.       React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
    
  53.         .ReactCurrentDispatcher.current;
    
  54.     return dispatcher.readContext(Context);
    
  55.   }
    
  56. 
    
  57.   // Note: This is based on a similar component we use in www. We can delete
    
  58.   // once the extra div wrapper is no longer necessary.
    
  59.   function LegacyHiddenDiv({children, mode}) {
    
  60.     return (
    
  61.       <div hidden={mode === 'hidden'}>
    
  62.         <React.unstable_LegacyHidden
    
  63.           mode={mode === 'hidden' ? 'unstable-defer-without-hiding' : mode}>
    
  64.           {children}
    
  65.         </React.unstable_LegacyHidden>
    
  66.       </div>
    
  67.     );
    
  68.   }
    
  69. 
    
  70.   // We have several ways of reading from context. sharedContextTests runs
    
  71.   // a suite of tests for a given context consumer implementation.
    
  72.   sharedContextTests('Context.Consumer', Context => Context.Consumer);
    
  73.   sharedContextTests(
    
  74.     'useContext inside function component',
    
  75.     Context =>
    
  76.       function Consumer(props) {
    
  77.         const contextValue = useContext(Context);
    
  78.         const render = props.children;
    
  79.         return render(contextValue);
    
  80.       },
    
  81.   );
    
  82.   sharedContextTests('useContext inside forwardRef component', Context =>
    
  83.     React.forwardRef(function Consumer(props, ref) {
    
  84.       const contextValue = useContext(Context);
    
  85.       const render = props.children;
    
  86.       return render(contextValue);
    
  87.     }),
    
  88.   );
    
  89.   sharedContextTests('useContext inside memoized function component', Context =>
    
  90.     React.memo(function Consumer(props) {
    
  91.       const contextValue = useContext(Context);
    
  92.       const render = props.children;
    
  93.       return render(contextValue);
    
  94.     }),
    
  95.   );
    
  96.   sharedContextTests(
    
  97.     'readContext(Context) inside class component',
    
  98.     Context =>
    
  99.       class Consumer extends React.Component {
    
  100.         render() {
    
  101.           const contextValue = readContext(Context);
    
  102.           const render = this.props.children;
    
  103.           return render(contextValue);
    
  104.         }
    
  105.       },
    
  106.   );
    
  107.   sharedContextTests(
    
  108.     'readContext(Context) inside pure class component',
    
  109.     Context =>
    
  110.       class Consumer extends React.PureComponent {
    
  111.         render() {
    
  112.           const contextValue = readContext(Context);
    
  113.           const render = this.props.children;
    
  114.           return render(contextValue);
    
  115.         }
    
  116.       },
    
  117.   );
    
  118. 
    
  119.   function sharedContextTests(label, getConsumer) {
    
  120.     describe(`reading context with ${label}`, () => {
    
  121.       it('simple mount and update', async () => {
    
  122.         const Context = React.createContext(1);
    
  123.         const Consumer = getConsumer(Context);
    
  124. 
    
  125.         const Indirection = React.Fragment;
    
  126. 
    
  127.         function App(props) {
    
  128.           return (
    
  129.             <Context.Provider value={props.value}>
    
  130.               <Indirection>
    
  131.                 <Indirection>
    
  132.                   <Consumer>
    
  133.                     {value => <span prop={'Result: ' + value} />}
    
  134.                   </Consumer>
    
  135.                 </Indirection>
    
  136.               </Indirection>
    
  137.             </Context.Provider>
    
  138.           );
    
  139.         }
    
  140. 
    
  141.         ReactNoop.render(<App value={2} />);
    
  142.         await waitForAll([]);
    
  143.         expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 2" />);
    
  144. 
    
  145.         // Update
    
  146.         ReactNoop.render(<App value={3} />);
    
  147.         await waitForAll([]);
    
  148.         expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 3" />);
    
  149.       });
    
  150. 
    
  151.       it('propagates through shouldComponentUpdate false', async () => {
    
  152.         const Context = React.createContext(1);
    
  153.         const ContextConsumer = getConsumer(Context);
    
  154. 
    
  155.         function Provider(props) {
    
  156.           Scheduler.log('Provider');
    
  157.           return (
    
  158.             <Context.Provider value={props.value}>
    
  159.               {props.children}
    
  160.             </Context.Provider>
    
  161.           );
    
  162.         }
    
  163. 
    
  164.         function Consumer(props) {
    
  165.           Scheduler.log('Consumer');
    
  166.           return (
    
  167.             <ContextConsumer>
    
  168.               {value => {
    
  169.                 Scheduler.log('Consumer render prop');
    
  170.                 return <span prop={'Result: ' + value} />;
    
  171.               }}
    
  172.             </ContextConsumer>
    
  173.           );
    
  174.         }
    
  175. 
    
  176.         class Indirection extends React.Component {
    
  177.           shouldComponentUpdate() {
    
  178.             return false;
    
  179.           }
    
  180.           render() {
    
  181.             Scheduler.log('Indirection');
    
  182.             return this.props.children;
    
  183.           }
    
  184.         }
    
  185. 
    
  186.         function App(props) {
    
  187.           Scheduler.log('App');
    
  188.           return (
    
  189.             <Provider value={props.value}>
    
  190.               <Indirection>
    
  191.                 <Indirection>
    
  192.                   <Consumer />
    
  193.                 </Indirection>
    
  194.               </Indirection>
    
  195.             </Provider>
    
  196.           );
    
  197.         }
    
  198. 
    
  199.         ReactNoop.render(<App value={2} />);
    
  200.         await waitForAll([
    
  201.           'App',
    
  202.           'Provider',
    
  203.           'Indirection',
    
  204.           'Indirection',
    
  205.           'Consumer',
    
  206.           'Consumer render prop',
    
  207.         ]);
    
  208.         expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 2" />);
    
  209. 
    
  210.         // Update
    
  211.         ReactNoop.render(<App value={3} />);
    
  212.         await waitForAll(['App', 'Provider', 'Consumer render prop']);
    
  213.         expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 3" />);
    
  214.       });
    
  215. 
    
  216.       it('consumers bail out if context value is the same', async () => {
    
  217.         const Context = React.createContext(1);
    
  218.         const ContextConsumer = getConsumer(Context);
    
  219. 
    
  220.         function Provider(props) {
    
  221.           Scheduler.log('Provider');
    
  222.           return (
    
  223.             <Context.Provider value={props.value}>
    
  224.               {props.children}
    
  225.             </Context.Provider>
    
  226.           );
    
  227.         }
    
  228. 
    
  229.         function Consumer(props) {
    
  230.           Scheduler.log('Consumer');
    
  231.           return (
    
  232.             <ContextConsumer>
    
  233.               {value => {
    
  234.                 Scheduler.log('Consumer render prop');
    
  235.                 return <span prop={'Result: ' + value} />;
    
  236.               }}
    
  237.             </ContextConsumer>
    
  238.           );
    
  239.         }
    
  240. 
    
  241.         class Indirection extends React.Component {
    
  242.           shouldComponentUpdate() {
    
  243.             return false;
    
  244.           }
    
  245.           render() {
    
  246.             Scheduler.log('Indirection');
    
  247.             return this.props.children;
    
  248.           }
    
  249.         }
    
  250. 
    
  251.         function App(props) {
    
  252.           Scheduler.log('App');
    
  253.           return (
    
  254.             <Provider value={props.value}>
    
  255.               <Indirection>
    
  256.                 <Indirection>
    
  257.                   <Consumer />
    
  258.                 </Indirection>
    
  259.               </Indirection>
    
  260.             </Provider>
    
  261.           );
    
  262.         }
    
  263. 
    
  264.         ReactNoop.render(<App value={2} />);
    
  265.         await waitForAll([
    
  266.           'App',
    
  267.           'Provider',
    
  268.           'Indirection',
    
  269.           'Indirection',
    
  270.           'Consumer',
    
  271.           'Consumer render prop',
    
  272.         ]);
    
  273.         expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 2" />);
    
  274. 
    
  275.         // Update with the same context value
    
  276.         ReactNoop.render(<App value={2} />);
    
  277.         await waitForAll([
    
  278.           'App',
    
  279.           'Provider',
    
  280.           // Don't call render prop again
    
  281.         ]);
    
  282.         expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 2" />);
    
  283.       });
    
  284. 
    
  285.       it('nested providers', async () => {
    
  286.         const Context = React.createContext(1);
    
  287.         const Consumer = getConsumer(Context);
    
  288. 
    
  289.         function Provider(props) {
    
  290.           return (
    
  291.             <Consumer>
    
  292.               {contextValue => (
    
  293.                 // Multiply previous context value by 2, unless prop overrides
    
  294.                 <Context.Provider value={props.value || contextValue * 2}>
    
  295.                   {props.children}
    
  296.                 </Context.Provider>
    
  297.               )}
    
  298.             </Consumer>
    
  299.           );
    
  300.         }
    
  301. 
    
  302.         class Indirection extends React.Component {
    
  303.           shouldComponentUpdate() {
    
  304.             return false;
    
  305.           }
    
  306.           render() {
    
  307.             return this.props.children;
    
  308.           }
    
  309.         }
    
  310. 
    
  311.         function App(props) {
    
  312.           return (
    
  313.             <Provider value={props.value}>
    
  314.               <Indirection>
    
  315.                 <Provider>
    
  316.                   <Indirection>
    
  317.                     <Provider>
    
  318.                       <Indirection>
    
  319.                         <Consumer>
    
  320.                           {value => <span prop={'Result: ' + value} />}
    
  321.                         </Consumer>
    
  322.                       </Indirection>
    
  323.                     </Provider>
    
  324.                   </Indirection>
    
  325.                 </Provider>
    
  326.               </Indirection>
    
  327.             </Provider>
    
  328.           );
    
  329.         }
    
  330. 
    
  331.         ReactNoop.render(<App value={2} />);
    
  332.         await waitForAll([]);
    
  333.         expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 8" />);
    
  334. 
    
  335.         // Update
    
  336.         ReactNoop.render(<App value={3} />);
    
  337.         await waitForAll([]);
    
  338.         expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: 12" />);
    
  339.       });
    
  340. 
    
  341.       it('should provide the correct (default) values to consumers outside of a provider', async () => {
    
  342.         const FooContext = React.createContext({value: 'foo-initial'});
    
  343.         const BarContext = React.createContext({value: 'bar-initial'});
    
  344.         const FooConsumer = getConsumer(FooContext);
    
  345.         const BarConsumer = getConsumer(BarContext);
    
  346. 
    
  347.         const Verify = ({actual, expected}) => {
    
  348.           expect(expected).toBe(actual);
    
  349.           return null;
    
  350.         };
    
  351. 
    
  352.         ReactNoop.render(
    
  353.           <>
    
  354.             <BarContext.Provider value={{value: 'bar-updated'}}>
    
  355.               <BarConsumer>
    
  356.                 {({value}) => <Verify actual={value} expected="bar-updated" />}
    
  357.               </BarConsumer>
    
  358. 
    
  359.               <FooContext.Provider value={{value: 'foo-updated'}}>
    
  360.                 <FooConsumer>
    
  361.                   {({value}) => (
    
  362.                     <Verify actual={value} expected="foo-updated" />
    
  363.                   )}
    
  364.                 </FooConsumer>
    
  365.               </FooContext.Provider>
    
  366.             </BarContext.Provider>
    
  367. 
    
  368.             <FooConsumer>
    
  369.               {({value}) => <Verify actual={value} expected="foo-initial" />}
    
  370.             </FooConsumer>
    
  371.             <BarConsumer>
    
  372.               {({value}) => <Verify actual={value} expected="bar-initial" />}
    
  373.             </BarConsumer>
    
  374.           </>,
    
  375.         );
    
  376.         await waitForAll([]);
    
  377.       });
    
  378. 
    
  379.       it('multiple consumers in different branches', async () => {
    
  380.         const Context = React.createContext(1);
    
  381.         const Consumer = getConsumer(Context);
    
  382. 
    
  383.         function Provider(props) {
    
  384.           return (
    
  385.             <Context.Consumer>
    
  386.               {contextValue => (
    
  387.                 // Multiply previous context value by 2, unless prop overrides
    
  388.                 <Context.Provider value={props.value || contextValue * 2}>
    
  389.                   {props.children}
    
  390.                 </Context.Provider>
    
  391.               )}
    
  392.             </Context.Consumer>
    
  393.           );
    
  394.         }
    
  395. 
    
  396.         class Indirection extends React.Component {
    
  397.           shouldComponentUpdate() {
    
  398.             return false;
    
  399.           }
    
  400.           render() {
    
  401.             return this.props.children;
    
  402.           }
    
  403.         }
    
  404. 
    
  405.         function App(props) {
    
  406.           return (
    
  407.             <Provider value={props.value}>
    
  408.               <Indirection>
    
  409.                 <Indirection>
    
  410.                   <Provider>
    
  411.                     <Consumer>
    
  412.                       {value => <span prop={'Result: ' + value} />}
    
  413.                     </Consumer>
    
  414.                   </Provider>
    
  415.                 </Indirection>
    
  416.                 <Indirection>
    
  417.                   <Consumer>
    
  418.                     {value => <span prop={'Result: ' + value} />}
    
  419.                   </Consumer>
    
  420.                 </Indirection>
    
  421.               </Indirection>
    
  422.             </Provider>
    
  423.           );
    
  424.         }
    
  425. 
    
  426.         ReactNoop.render(<App value={2} />);
    
  427.         await waitForAll([]);
    
  428.         expect(ReactNoop).toMatchRenderedOutput(
    
  429.           <>
    
  430.             <span prop="Result: 4" />
    
  431.             <span prop="Result: 2" />
    
  432.           </>,
    
  433.         );
    
  434. 
    
  435.         // Update
    
  436.         ReactNoop.render(<App value={3} />);
    
  437.         await waitForAll([]);
    
  438.         expect(ReactNoop).toMatchRenderedOutput(
    
  439.           <>
    
  440.             <span prop="Result: 6" />
    
  441.             <span prop="Result: 3" />
    
  442.           </>,
    
  443.         );
    
  444. 
    
  445.         // Another update
    
  446.         ReactNoop.render(<App value={4} />);
    
  447.         await waitForAll([]);
    
  448.         expect(ReactNoop).toMatchRenderedOutput(
    
  449.           <>
    
  450.             <span prop="Result: 8" />
    
  451.             <span prop="Result: 4" />
    
  452.           </>,
    
  453.         );
    
  454.       });
    
  455. 
    
  456.       it('compares context values with Object.is semantics', async () => {
    
  457.         const Context = React.createContext(1);
    
  458.         const ContextConsumer = getConsumer(Context);
    
  459. 
    
  460.         function Provider(props) {
    
  461.           Scheduler.log('Provider');
    
  462.           return (
    
  463.             <Context.Provider value={props.value}>
    
  464.               {props.children}
    
  465.             </Context.Provider>
    
  466.           );
    
  467.         }
    
  468. 
    
  469.         function Consumer(props) {
    
  470.           Scheduler.log('Consumer');
    
  471.           return (
    
  472.             <ContextConsumer>
    
  473.               {value => {
    
  474.                 Scheduler.log('Consumer render prop');
    
  475.                 return <span prop={'Result: ' + value} />;
    
  476.               }}
    
  477.             </ContextConsumer>
    
  478.           );
    
  479.         }
    
  480. 
    
  481.         class Indirection extends React.Component {
    
  482.           shouldComponentUpdate() {
    
  483.             return false;
    
  484.           }
    
  485.           render() {
    
  486.             Scheduler.log('Indirection');
    
  487.             return this.props.children;
    
  488.           }
    
  489.         }
    
  490. 
    
  491.         function App(props) {
    
  492.           Scheduler.log('App');
    
  493.           return (
    
  494.             <Provider value={props.value}>
    
  495.               <Indirection>
    
  496.                 <Indirection>
    
  497.                   <Consumer />
    
  498.                 </Indirection>
    
  499.               </Indirection>
    
  500.             </Provider>
    
  501.           );
    
  502.         }
    
  503. 
    
  504.         ReactNoop.render(<App value={NaN} />);
    
  505.         await waitForAll([
    
  506.           'App',
    
  507.           'Provider',
    
  508.           'Indirection',
    
  509.           'Indirection',
    
  510.           'Consumer',
    
  511.           'Consumer render prop',
    
  512.         ]);
    
  513.         expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: NaN" />);
    
  514. 
    
  515.         // Update
    
  516.         ReactNoop.render(<App value={NaN} />);
    
  517.         await waitForAll([
    
  518.           'App',
    
  519.           'Provider',
    
  520.           // Consumer should not re-render again
    
  521.           // 'Consumer render prop',
    
  522.         ]);
    
  523.         expect(ReactNoop).toMatchRenderedOutput(<span prop="Result: NaN" />);
    
  524.       });
    
  525. 
    
  526.       it('context unwinds when interrupted', async () => {
    
  527.         const Context = React.createContext('Default');
    
  528.         const ContextConsumer = getConsumer(Context);
    
  529. 
    
  530.         function Consumer(props) {
    
  531.           return (
    
  532.             <ContextConsumer>
    
  533.               {value => <span prop={'Result: ' + value} />}
    
  534.             </ContextConsumer>
    
  535.           );
    
  536.         }
    
  537. 
    
  538.         function BadRender() {
    
  539.           throw new Error('Bad render');
    
  540.         }
    
  541. 
    
  542.         class ErrorBoundary extends React.Component {
    
  543.           state = {error: null};
    
  544.           componentDidCatch(error) {
    
  545.             this.setState({error});
    
  546.           }
    
  547.           render() {
    
  548.             if (this.state.error) {
    
  549.               return null;
    
  550.             }
    
  551.             return this.props.children;
    
  552.           }
    
  553.         }
    
  554. 
    
  555.         function App(props) {
    
  556.           return (
    
  557.             <>
    
  558.               <Context.Provider value="Does not unwind">
    
  559.                 <ErrorBoundary>
    
  560.                   <Context.Provider value="Unwinds after BadRender throws">
    
  561.                     <BadRender />
    
  562.                   </Context.Provider>
    
  563.                 </ErrorBoundary>
    
  564.                 <Consumer />
    
  565.               </Context.Provider>
    
  566.             </>
    
  567.           );
    
  568.         }
    
  569. 
    
  570.         ReactNoop.render(<App value="A" />);
    
  571.         await waitForAll([]);
    
  572.         expect(ReactNoop).toMatchRenderedOutput(
    
  573.           // The second provider should use the default value.
    
  574.           <span prop="Result: Does not unwind" />,
    
  575.         );
    
  576.       });
    
  577. 
    
  578.       it("does not re-render if there's an update in a child", async () => {
    
  579.         const Context = React.createContext(0);
    
  580.         const Consumer = getConsumer(Context);
    
  581. 
    
  582.         let child;
    
  583.         class Child extends React.Component {
    
  584.           state = {step: 0};
    
  585.           render() {
    
  586.             Scheduler.log('Child');
    
  587.             return (
    
  588.               <span
    
  589.                 prop={`Context: ${this.props.context}, Step: ${this.state.step}`}
    
  590.               />
    
  591.             );
    
  592.           }
    
  593.         }
    
  594. 
    
  595.         function App(props) {
    
  596.           return (
    
  597.             <Context.Provider value={props.value}>
    
  598.               <Consumer>
    
  599.                 {value => {
    
  600.                   Scheduler.log('Consumer render prop');
    
  601.                   return <Child ref={inst => (child = inst)} context={value} />;
    
  602.                 }}
    
  603.               </Consumer>
    
  604.             </Context.Provider>
    
  605.           );
    
  606.         }
    
  607. 
    
  608.         // Initial mount
    
  609.         ReactNoop.render(<App value={1} />);
    
  610.         await waitForAll(['Consumer render prop', 'Child']);
    
  611.         expect(ReactNoop).toMatchRenderedOutput(
    
  612.           <span prop="Context: 1, Step: 0" />,
    
  613.         );
    
  614. 
    
  615.         child.setState({step: 1});
    
  616.         await waitForAll(['Child']);
    
  617.         expect(ReactNoop).toMatchRenderedOutput(
    
  618.           <span prop="Context: 1, Step: 1" />,
    
  619.         );
    
  620.       });
    
  621. 
    
  622.       it('consumer bails out if value is unchanged and something above bailed out', async () => {
    
  623.         const Context = React.createContext(0);
    
  624.         const Consumer = getConsumer(Context);
    
  625. 
    
  626.         function renderChildValue(value) {
    
  627.           Scheduler.log('Consumer');
    
  628.           return <span prop={value} />;
    
  629.         }
    
  630. 
    
  631.         function ChildWithInlineRenderCallback() {
    
  632.           Scheduler.log('ChildWithInlineRenderCallback');
    
  633.           // Note: we are intentionally passing an inline arrow. Don't refactor.
    
  634.           return <Consumer>{value => renderChildValue(value)}</Consumer>;
    
  635.         }
    
  636. 
    
  637.         function ChildWithCachedRenderCallback() {
    
  638.           Scheduler.log('ChildWithCachedRenderCallback');
    
  639.           return <Consumer>{renderChildValue}</Consumer>;
    
  640.         }
    
  641. 
    
  642.         class PureIndirection extends React.PureComponent {
    
  643.           render() {
    
  644.             Scheduler.log('PureIndirection');
    
  645.             return (
    
  646.               <>
    
  647.                 <ChildWithInlineRenderCallback />
    
  648.                 <ChildWithCachedRenderCallback />
    
  649.               </>
    
  650.             );
    
  651.           }
    
  652.         }
    
  653. 
    
  654.         class App extends React.Component {
    
  655.           render() {
    
  656.             Scheduler.log('App');
    
  657.             return (
    
  658.               <Context.Provider value={this.props.value}>
    
  659.                 <PureIndirection />
    
  660.               </Context.Provider>
    
  661.             );
    
  662.           }
    
  663.         }
    
  664. 
    
  665.         // Initial mount
    
  666.         ReactNoop.render(<App value={1} />);
    
  667.         await waitForAll([
    
  668.           'App',
    
  669.           'PureIndirection',
    
  670.           'ChildWithInlineRenderCallback',
    
  671.           'Consumer',
    
  672.           'ChildWithCachedRenderCallback',
    
  673.           'Consumer',
    
  674.         ]);
    
  675.         expect(ReactNoop).toMatchRenderedOutput(
    
  676.           <>
    
  677.             <span prop={1} />
    
  678.             <span prop={1} />
    
  679.           </>,
    
  680.         );
    
  681. 
    
  682.         // Update (bailout)
    
  683.         ReactNoop.render(<App value={1} />);
    
  684.         await waitForAll(['App']);
    
  685.         expect(ReactNoop).toMatchRenderedOutput(
    
  686.           <>
    
  687.             <span prop={1} />
    
  688.             <span prop={1} />
    
  689.           </>,
    
  690.         );
    
  691. 
    
  692.         // Update (no bailout)
    
  693.         ReactNoop.render(<App value={2} />);
    
  694.         await waitForAll(['App', 'Consumer', 'Consumer']);
    
  695.         expect(ReactNoop).toMatchRenderedOutput(
    
  696.           <>
    
  697.             <span prop={2} />
    
  698.             <span prop={2} />
    
  699.           </>,
    
  700.         );
    
  701.       });
    
  702. 
    
  703.       // @gate www
    
  704.       it("context consumer doesn't bail out inside hidden subtree", async () => {
    
  705.         const Context = React.createContext('dark');
    
  706.         const Consumer = getConsumer(Context);
    
  707. 
    
  708.         function App({theme}) {
    
  709.           return (
    
  710.             <Context.Provider value={theme}>
    
  711.               <LegacyHiddenDiv mode="hidden">
    
  712.                 <Consumer>{value => <Text text={value} />}</Consumer>
    
  713.               </LegacyHiddenDiv>
    
  714.             </Context.Provider>
    
  715.           );
    
  716.         }
    
  717. 
    
  718.         ReactNoop.render(<App theme="dark" />);
    
  719.         await waitForAll(['dark']);
    
  720.         expect(ReactNoop.getChildrenAsJSX()).toEqual(
    
  721.           <div hidden={true}>
    
  722.             <span prop="dark" />
    
  723.           </div>,
    
  724.         );
    
  725. 
    
  726.         ReactNoop.render(<App theme="light" />);
    
  727.         await waitForAll(['light']);
    
  728.         expect(ReactNoop.getChildrenAsJSX()).toEqual(
    
  729.           <div hidden={true}>
    
  730.             <span prop="light" />
    
  731.           </div>,
    
  732.         );
    
  733.       });
    
  734. 
    
  735.       // This is a regression case for https://github.com/facebook/react/issues/12389.
    
  736.       it('does not run into an infinite loop', async () => {
    
  737.         const Context = React.createContext(null);
    
  738.         const Consumer = getConsumer(Context);
    
  739. 
    
  740.         class App extends React.Component {
    
  741.           renderItem(id) {
    
  742.             return (
    
  743.               <span key={id}>
    
  744.                 <Consumer>{() => <span>inner</span>}</Consumer>
    
  745.                 <span>outer</span>
    
  746.               </span>
    
  747.             );
    
  748.           }
    
  749.           renderList() {
    
  750.             const list = [1, 2].map(id => this.renderItem(id));
    
  751.             if (this.props.reverse) {
    
  752.               list.reverse();
    
  753.             }
    
  754.             return list;
    
  755.           }
    
  756.           render() {
    
  757.             return (
    
  758.               <Context.Provider value={{}}>
    
  759.                 {this.renderList()}
    
  760.               </Context.Provider>
    
  761.             );
    
  762.           }
    
  763.         }
    
  764. 
    
  765.         ReactNoop.render(<App reverse={false} />);
    
  766.         await waitForAll([]);
    
  767.         ReactNoop.render(<App reverse={true} />);
    
  768.         await waitForAll([]);
    
  769.         ReactNoop.render(<App reverse={false} />);
    
  770.         await waitForAll([]);
    
  771.       });
    
  772. 
    
  773.       // This is a regression case for https://github.com/facebook/react/issues/12686
    
  774.       it('does not skip some siblings', async () => {
    
  775.         const Context = React.createContext(0);
    
  776.         const ContextConsumer = getConsumer(Context);
    
  777. 
    
  778.         class App extends React.Component {
    
  779.           state = {
    
  780.             step: 0,
    
  781.           };
    
  782. 
    
  783.           render() {
    
  784.             Scheduler.log('App');
    
  785.             return (
    
  786.               <Context.Provider value={this.state.step}>
    
  787.                 <StaticContent />
    
  788.                 {this.state.step > 0 && <Indirection />}
    
  789.               </Context.Provider>
    
  790.             );
    
  791.           }
    
  792.         }
    
  793. 
    
  794.         class StaticContent extends React.PureComponent {
    
  795.           render() {
    
  796.             return (
    
  797.               <>
    
  798.                 <>
    
  799.                   <span prop="static 1" />
    
  800.                   <span prop="static 2" />
    
  801.                 </>
    
  802.               </>
    
  803.             );
    
  804.           }
    
  805.         }
    
  806. 
    
  807.         class Indirection extends React.PureComponent {
    
  808.           render() {
    
  809.             return (
    
  810.               <ContextConsumer>
    
  811.                 {value => {
    
  812.                   Scheduler.log('Consumer');
    
  813.                   return <span prop={value} />;
    
  814.                 }}
    
  815.               </ContextConsumer>
    
  816.             );
    
  817.           }
    
  818.         }
    
  819. 
    
  820.         // Initial mount
    
  821.         let inst;
    
  822.         ReactNoop.render(<App ref={ref => (inst = ref)} />);
    
  823.         await waitForAll(['App']);
    
  824.         expect(ReactNoop).toMatchRenderedOutput(
    
  825.           <>
    
  826.             <span prop="static 1" />
    
  827.             <span prop="static 2" />
    
  828.           </>,
    
  829.         );
    
  830.         // Update the first time
    
  831.         inst.setState({step: 1});
    
  832.         await waitForAll(['App', 'Consumer']);
    
  833.         expect(ReactNoop).toMatchRenderedOutput(
    
  834.           <>
    
  835.             <span prop="static 1" />
    
  836.             <span prop="static 2" />
    
  837.             <span prop={1} />
    
  838.           </>,
    
  839.         );
    
  840.         // Update the second time
    
  841.         inst.setState({step: 2});
    
  842.         await waitForAll(['App', 'Consumer']);
    
  843.         expect(ReactNoop).toMatchRenderedOutput(
    
  844.           <>
    
  845.             <span prop="static 1" />
    
  846.             <span prop="static 2" />
    
  847.             <span prop={2} />
    
  848.           </>,
    
  849.         );
    
  850.       });
    
  851.     });
    
  852.   }
    
  853. 
    
  854.   describe('Context.Provider', () => {
    
  855.     it('warns if no value prop provided', async () => {
    
  856.       const Context = React.createContext();
    
  857. 
    
  858.       ReactNoop.render(
    
  859.         <Context.Provider anyPropNameOtherThanValue="value could be anything" />,
    
  860.       );
    
  861. 
    
  862.       await expect(async () => await waitForAll([])).toErrorDev(
    
  863.         'The `value` prop is required for the `<Context.Provider>`. Did you misspell it or forget to pass it?',
    
  864.         {
    
  865.           withoutStack: true,
    
  866.         },
    
  867.       );
    
  868.     });
    
  869. 
    
  870.     it('warns if multiple renderers concurrently render the same context', async () => {
    
  871.       spyOnDev(console, 'error').mockImplementation(() => {});
    
  872.       const Context = React.createContext(0);
    
  873. 
    
  874.       function Foo(props) {
    
  875.         Scheduler.log('Foo');
    
  876.         return null;
    
  877.       }
    
  878. 
    
  879.       function App(props) {
    
  880.         return (
    
  881.           <Context.Provider value={props.value}>
    
  882.             <Foo />
    
  883.             <Foo />
    
  884.           </Context.Provider>
    
  885.         );
    
  886.       }
    
  887. 
    
  888.       React.startTransition(() => {
    
  889.         ReactNoop.render(<App value={1} />);
    
  890.       });
    
  891.       // Render past the Provider, but don't commit yet
    
  892.       await waitFor(['Foo']);
    
  893. 
    
  894.       // Get a new copy of ReactNoop
    
  895.       jest.resetModules();
    
  896.       React = require('react');
    
  897.       ReactNoop = require('react-noop-renderer');
    
  898.       Scheduler = require('scheduler');
    
  899.       const InternalTestUtils = require('internal-test-utils');
    
  900.       waitForAll = InternalTestUtils.waitForAll;
    
  901.       waitFor = InternalTestUtils.waitFor;
    
  902. 
    
  903.       // Render the provider again using a different renderer
    
  904.       ReactNoop.render(<App value={1} />);
    
  905.       await waitForAll(['Foo', 'Foo']);
    
  906. 
    
  907.       if (__DEV__) {
    
  908.         expect(console.error.mock.calls[0][0]).toContain(
    
  909.           'Detected multiple renderers concurrently rendering the same ' +
    
  910.             'context provider. This is currently unsupported',
    
  911.         );
    
  912.       }
    
  913.     });
    
  914. 
    
  915.     it('does not warn if multiple renderers use the same context sequentially', async () => {
    
  916.       spyOnDev(console, 'error');
    
  917.       const Context = React.createContext(0);
    
  918. 
    
  919.       function Foo(props) {
    
  920.         Scheduler.log('Foo');
    
  921.         return null;
    
  922.       }
    
  923. 
    
  924.       function App(props) {
    
  925.         return (
    
  926.           <Context.Provider value={props.value}>
    
  927.             <Foo />
    
  928.             <Foo />
    
  929.           </Context.Provider>
    
  930.         );
    
  931.       }
    
  932. 
    
  933.       React.startTransition(() => {
    
  934.         ReactNoop.render(<App value={1} />);
    
  935.       });
    
  936.       await waitForAll(['Foo', 'Foo']);
    
  937. 
    
  938.       // Get a new copy of ReactNoop
    
  939.       jest.resetModules();
    
  940.       React = require('react');
    
  941.       ReactNoop = require('react-noop-renderer');
    
  942.       Scheduler = require('scheduler');
    
  943.       const InternalTestUtils = require('internal-test-utils');
    
  944.       waitForAll = InternalTestUtils.waitForAll;
    
  945.       waitFor = InternalTestUtils.waitFor;
    
  946. 
    
  947.       // Render the provider again using a different renderer
    
  948.       ReactNoop.render(<App value={1} />);
    
  949.       await waitForAll(['Foo', 'Foo']);
    
  950. 
    
  951.       if (__DEV__) {
    
  952.         expect(console.error).not.toHaveBeenCalled();
    
  953.       }
    
  954.     });
    
  955. 
    
  956.     it('provider bails out if children and value are unchanged (like sCU)', async () => {
    
  957.       const Context = React.createContext(0);
    
  958. 
    
  959.       function Child() {
    
  960.         Scheduler.log('Child');
    
  961.         return <span prop="Child" />;
    
  962.       }
    
  963. 
    
  964.       const children = <Child />;
    
  965. 
    
  966.       function App(props) {
    
  967.         Scheduler.log('App');
    
  968.         return (
    
  969.           <Context.Provider value={props.value}>{children}</Context.Provider>
    
  970.         );
    
  971.       }
    
  972. 
    
  973.       // Initial mount
    
  974.       ReactNoop.render(<App value={1} />);
    
  975.       await waitForAll(['App', 'Child']);
    
  976.       expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
    
  977. 
    
  978.       // Update
    
  979.       ReactNoop.render(<App value={1} />);
    
  980.       await waitForAll([
    
  981.         'App',
    
  982.         // Child does not re-render
    
  983.       ]);
    
  984.       expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
    
  985.     });
    
  986. 
    
  987.     // @gate !disableLegacyContext
    
  988.     it('provider does not bail out if legacy context changed above', async () => {
    
  989.       const Context = React.createContext(0);
    
  990. 
    
  991.       function Child() {
    
  992.         Scheduler.log('Child');
    
  993.         return <span prop="Child" />;
    
  994.       }
    
  995. 
    
  996.       const children = <Child />;
    
  997. 
    
  998.       class LegacyProvider extends React.Component {
    
  999.         static childContextTypes = {
    
  1000.           legacyValue: () => {},
    
  1001.         };
    
  1002.         state = {legacyValue: 1};
    
  1003.         getChildContext() {
    
  1004.           return {legacyValue: this.state.legacyValue};
    
  1005.         }
    
  1006.         render() {
    
  1007.           Scheduler.log('LegacyProvider');
    
  1008.           return this.props.children;
    
  1009.         }
    
  1010.       }
    
  1011. 
    
  1012.       class App extends React.Component {
    
  1013.         state = {value: 1};
    
  1014.         render() {
    
  1015.           Scheduler.log('App');
    
  1016.           return (
    
  1017.             <Context.Provider value={this.state.value}>
    
  1018.               {this.props.children}
    
  1019.             </Context.Provider>
    
  1020.           );
    
  1021.         }
    
  1022.       }
    
  1023. 
    
  1024.       const legacyProviderRef = React.createRef();
    
  1025.       const appRef = React.createRef();
    
  1026. 
    
  1027.       // Initial mount
    
  1028.       ReactNoop.render(
    
  1029.         <LegacyProvider ref={legacyProviderRef}>
    
  1030.           <App ref={appRef} value={1}>
    
  1031.             {children}
    
  1032.           </App>
    
  1033.         </LegacyProvider>,
    
  1034.       );
    
  1035.       await waitForAll(['LegacyProvider', 'App', 'Child']);
    
  1036.       expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
    
  1037. 
    
  1038.       // Update App with same value (should bail out)
    
  1039.       appRef.current.setState({value: 1});
    
  1040.       await waitForAll(['App']);
    
  1041.       expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
    
  1042. 
    
  1043.       // Update LegacyProvider (should not bail out)
    
  1044.       legacyProviderRef.current.setState({value: 1});
    
  1045.       await waitForAll(['LegacyProvider', 'App', 'Child']);
    
  1046.       expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
    
  1047. 
    
  1048.       // Update App with same value (should bail out)
    
  1049.       appRef.current.setState({value: 1});
    
  1050.       await waitForAll(['App']);
    
  1051.       expect(ReactNoop).toMatchRenderedOutput(<span prop="Child" />);
    
  1052.     });
    
  1053.   });
    
  1054. 
    
  1055.   describe('Context.Consumer', () => {
    
  1056.     it('warns if child is not a function', async () => {
    
  1057.       spyOnDev(console, 'error').mockImplementation(() => {});
    
  1058.       const Context = React.createContext(0);
    
  1059.       ReactNoop.render(<Context.Consumer />);
    
  1060.       await waitForThrow('is not a function');
    
  1061.       if (__DEV__) {
    
  1062.         expect(console.error.mock.calls[0][0]).toContain(
    
  1063.           'A context consumer was rendered with multiple children, or a child ' +
    
  1064.             "that isn't a function",
    
  1065.         );
    
  1066.       }
    
  1067.     });
    
  1068. 
    
  1069.     it('can read other contexts inside consumer render prop', async () => {
    
  1070.       const FooContext = React.createContext(0);
    
  1071.       const BarContext = React.createContext(0);
    
  1072. 
    
  1073.       function FooAndBar() {
    
  1074.         return (
    
  1075.           <FooContext.Consumer>
    
  1076.             {foo => {
    
  1077.               const bar = readContext(BarContext);
    
  1078.               return <Text text={`Foo: ${foo}, Bar: ${bar}`} />;
    
  1079.             }}
    
  1080.           </FooContext.Consumer>
    
  1081.         );
    
  1082.       }
    
  1083. 
    
  1084.       class Indirection extends React.Component {
    
  1085.         shouldComponentUpdate() {
    
  1086.           return false;
    
  1087.         }
    
  1088.         render() {
    
  1089.           return this.props.children;
    
  1090.         }
    
  1091.       }
    
  1092. 
    
  1093.       function App(props) {
    
  1094.         return (
    
  1095.           <FooContext.Provider value={props.foo}>
    
  1096.             <BarContext.Provider value={props.bar}>
    
  1097.               <Indirection>
    
  1098.                 <FooAndBar />
    
  1099.               </Indirection>
    
  1100.             </BarContext.Provider>
    
  1101.           </FooContext.Provider>
    
  1102.         );
    
  1103.       }
    
  1104. 
    
  1105.       ReactNoop.render(<App foo={1} bar={1} />);
    
  1106.       await waitForAll(['Foo: 1, Bar: 1']);
    
  1107.       expect(ReactNoop).toMatchRenderedOutput(<span prop="Foo: 1, Bar: 1" />);
    
  1108. 
    
  1109.       // Update foo
    
  1110.       ReactNoop.render(<App foo={2} bar={1} />);
    
  1111.       await waitForAll(['Foo: 2, Bar: 1']);
    
  1112.       expect(ReactNoop).toMatchRenderedOutput(<span prop="Foo: 2, Bar: 1" />);
    
  1113. 
    
  1114.       // Update bar
    
  1115.       ReactNoop.render(<App foo={2} bar={2} />);
    
  1116.       await waitForAll(['Foo: 2, Bar: 2']);
    
  1117.       expect(ReactNoop).toMatchRenderedOutput(<span prop="Foo: 2, Bar: 2" />);
    
  1118.     });
    
  1119. 
    
  1120.     // Context consumer bails out on propagating "deep" updates when `value` hasn't changed.
    
  1121.     // However, it doesn't bail out from rendering if the component above it re-rendered anyway.
    
  1122.     // If we bailed out on referential equality, it would be confusing that you
    
  1123.     // can call this.setState(), but an autobound render callback "blocked" the update.
    
  1124.     // https://github.com/facebook/react/pull/12470#issuecomment-376917711
    
  1125.     it('consumer does not bail out if there were no bailouts above it', async () => {
    
  1126.       const Context = React.createContext(0);
    
  1127.       const Consumer = Context.Consumer;
    
  1128. 
    
  1129.       class App extends React.Component {
    
  1130.         state = {
    
  1131.           text: 'hello',
    
  1132.         };
    
  1133. 
    
  1134.         renderConsumer = context => {
    
  1135.           Scheduler.log('App#renderConsumer');
    
  1136.           return <span prop={this.state.text} />;
    
  1137.         };
    
  1138. 
    
  1139.         render() {
    
  1140.           Scheduler.log('App');
    
  1141.           return (
    
  1142.             <Context.Provider value={this.props.value}>
    
  1143.               <Consumer>{this.renderConsumer}</Consumer>
    
  1144.             </Context.Provider>
    
  1145.           );
    
  1146.         }
    
  1147.       }
    
  1148. 
    
  1149.       // Initial mount
    
  1150.       let inst;
    
  1151.       ReactNoop.render(<App value={1} ref={ref => (inst = ref)} />);
    
  1152.       await waitForAll(['App', 'App#renderConsumer']);
    
  1153.       expect(ReactNoop).toMatchRenderedOutput(<span prop="hello" />);
    
  1154. 
    
  1155.       // Update
    
  1156.       inst.setState({text: 'goodbye'});
    
  1157.       await waitForAll(['App', 'App#renderConsumer']);
    
  1158.       expect(ReactNoop).toMatchRenderedOutput(<span prop="goodbye" />);
    
  1159.     });
    
  1160.   });
    
  1161. 
    
  1162.   describe('readContext', () => {
    
  1163.     // Unstable changedBits API was removed. Port this test to context selectors
    
  1164.     // once that exists.
    
  1165.     // @gate FIXME
    
  1166.     it('can read the same context multiple times in the same function', async () => {
    
  1167.       const Context = React.createContext({foo: 0, bar: 0, baz: 0}, (a, b) => {
    
  1168.         let result = 0;
    
  1169.         if (a.foo !== b.foo) {
    
  1170.           result |= 0b001;
    
  1171.         }
    
  1172.         if (a.bar !== b.bar) {
    
  1173.           result |= 0b010;
    
  1174.         }
    
  1175.         if (a.baz !== b.baz) {
    
  1176.           result |= 0b100;
    
  1177.         }
    
  1178.         return result;
    
  1179.       });
    
  1180. 
    
  1181.       function Provider(props) {
    
  1182.         return (
    
  1183.           <Context.Provider
    
  1184.             value={{foo: props.foo, bar: props.bar, baz: props.baz}}>
    
  1185.             {props.children}
    
  1186.           </Context.Provider>
    
  1187.         );
    
  1188.       }
    
  1189. 
    
  1190.       function FooAndBar() {
    
  1191.         const {foo} = readContext(Context, 0b001);
    
  1192.         const {bar} = readContext(Context, 0b010);
    
  1193.         return <Text text={`Foo: ${foo}, Bar: ${bar}`} />;
    
  1194.       }
    
  1195. 
    
  1196.       function Baz() {
    
  1197.         const {baz} = readContext(Context, 0b100);
    
  1198.         return <Text text={'Baz: ' + baz} />;
    
  1199.       }
    
  1200. 
    
  1201.       class Indirection extends React.Component {
    
  1202.         shouldComponentUpdate() {
    
  1203.           return false;
    
  1204.         }
    
  1205.         render() {
    
  1206.           return this.props.children;
    
  1207.         }
    
  1208.       }
    
  1209. 
    
  1210.       function App(props) {
    
  1211.         return (
    
  1212.           <Provider foo={props.foo} bar={props.bar} baz={props.baz}>
    
  1213.             <Indirection>
    
  1214.               <Indirection>
    
  1215.                 <FooAndBar />
    
  1216.               </Indirection>
    
  1217.               <Indirection>
    
  1218.                 <Baz />
    
  1219.               </Indirection>
    
  1220.             </Indirection>
    
  1221.           </Provider>
    
  1222.         );
    
  1223.       }
    
  1224. 
    
  1225.       ReactNoop.render(<App foo={1} bar={1} baz={1} />);
    
  1226.       await waitForAll(['Foo: 1, Bar: 1', 'Baz: 1']);
    
  1227.       expect(ReactNoop).toMatchRenderedOutput([
    
  1228.         <span prop="Foo: 1, Bar: 1" />,
    
  1229.         <span prop="Baz: 1" />,
    
  1230.       ]);
    
  1231. 
    
  1232.       // Update only foo
    
  1233.       ReactNoop.render(<App foo={2} bar={1} baz={1} />);
    
  1234.       await waitForAll(['Foo: 2, Bar: 1']);
    
  1235.       expect(ReactNoop).toMatchRenderedOutput([
    
  1236.         <span prop="Foo: 2, Bar: 1" />,
    
  1237.         <span prop="Baz: 1" />,
    
  1238.       ]);
    
  1239. 
    
  1240.       // Update only bar
    
  1241.       ReactNoop.render(<App foo={2} bar={2} baz={1} />);
    
  1242.       await waitForAll(['Foo: 2, Bar: 2']);
    
  1243.       expect(ReactNoop).toMatchRenderedOutput([
    
  1244.         <span prop="Foo: 2, Bar: 2" />,
    
  1245.         <span prop="Baz: 1" />,
    
  1246.       ]);
    
  1247. 
    
  1248.       // Update only baz
    
  1249.       ReactNoop.render(<App foo={2} bar={2} baz={2} />);
    
  1250.       await waitForAll(['Baz: 2']);
    
  1251.       expect(ReactNoop).toMatchRenderedOutput([
    
  1252.         <span prop="Foo: 2, Bar: 2" />,
    
  1253.         <span prop="Baz: 2" />,
    
  1254.       ]);
    
  1255.     });
    
  1256. 
    
  1257.     // Context consumer bails out on propagating "deep" updates when `value` hasn't changed.
    
  1258.     // However, it doesn't bail out from rendering if the component above it re-rendered anyway.
    
  1259.     // If we bailed out on referential equality, it would be confusing that you
    
  1260.     // can call this.setState(), but an autobound render callback "blocked" the update.
    
  1261.     // https://github.com/facebook/react/pull/12470#issuecomment-376917711
    
  1262.     it('does not bail out if there were no bailouts above it', async () => {
    
  1263.       const Context = React.createContext(0);
    
  1264. 
    
  1265.       class Consumer extends React.Component {
    
  1266.         render() {
    
  1267.           const contextValue = readContext(Context);
    
  1268.           return this.props.children(contextValue);
    
  1269.         }
    
  1270.       }
    
  1271. 
    
  1272.       class App extends React.Component {
    
  1273.         state = {
    
  1274.           text: 'hello',
    
  1275.         };
    
  1276. 
    
  1277.         renderConsumer = context => {
    
  1278.           Scheduler.log('App#renderConsumer');
    
  1279.           return <span prop={this.state.text} />;
    
  1280.         };
    
  1281. 
    
  1282.         render() {
    
  1283.           Scheduler.log('App');
    
  1284.           return (
    
  1285.             <Context.Provider value={this.props.value}>
    
  1286.               <Consumer>{this.renderConsumer}</Consumer>
    
  1287.             </Context.Provider>
    
  1288.           );
    
  1289.         }
    
  1290.       }
    
  1291. 
    
  1292.       // Initial mount
    
  1293.       let inst;
    
  1294.       ReactNoop.render(<App value={1} ref={ref => (inst = ref)} />);
    
  1295.       await waitForAll(['App', 'App#renderConsumer']);
    
  1296.       expect(ReactNoop).toMatchRenderedOutput(<span prop="hello" />);
    
  1297. 
    
  1298.       // Update
    
  1299.       inst.setState({text: 'goodbye'});
    
  1300.       await waitForAll(['App', 'App#renderConsumer']);
    
  1301.       expect(ReactNoop).toMatchRenderedOutput(<span prop="goodbye" />);
    
  1302.     });
    
  1303. 
    
  1304.     it('warns when reading context inside render phase class setState updater', async () => {
    
  1305.       const ThemeContext = React.createContext('light');
    
  1306. 
    
  1307.       class Cls extends React.Component {
    
  1308.         state = {};
    
  1309.         render() {
    
  1310.           this.setState(() => {
    
  1311.             readContext(ThemeContext);
    
  1312.           });
    
  1313.           return null;
    
  1314.         }
    
  1315.       }
    
  1316. 
    
  1317.       ReactNoop.render(<Cls />);
    
  1318.       await expect(async () => await waitForAll([])).toErrorDev([
    
  1319.         'Context can only be read while React is rendering',
    
  1320.         'Cannot update during an existing state transition',
    
  1321.       ]);
    
  1322.     });
    
  1323.   });
    
  1324. 
    
  1325.   describe('useContext', () => {
    
  1326.     it('throws when used in a class component', async () => {
    
  1327.       const Context = React.createContext(0);
    
  1328.       class Foo extends React.Component {
    
  1329.         render() {
    
  1330.           return useContext(Context);
    
  1331.         }
    
  1332.       }
    
  1333.       ReactNoop.render(<Foo />);
    
  1334.       await waitForThrow(
    
  1335.         'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen' +
    
  1336.           ' for one of the following reasons:\n' +
    
  1337.           '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
    
  1338.           '2. You might be breaking the Rules of Hooks\n' +
    
  1339.           '3. You might have more than one copy of React in the same app\n' +
    
  1340.           'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.',
    
  1341.       );
    
  1342.     });
    
  1343. 
    
  1344.     it('warns when passed a consumer', async () => {
    
  1345.       const Context = React.createContext(0);
    
  1346.       function Foo() {
    
  1347.         return useContext(Context.Consumer);
    
  1348.       }
    
  1349.       ReactNoop.render(<Foo />);
    
  1350.       await expect(async () => await waitForAll([])).toErrorDev(
    
  1351.         'Calling useContext(Context.Consumer) is not supported, may cause bugs, ' +
    
  1352.           'and will be removed in a future major release. ' +
    
  1353.           'Did you mean to call useContext(Context) instead?',
    
  1354.       );
    
  1355.     });
    
  1356. 
    
  1357.     it('warns when passed a provider', async () => {
    
  1358.       const Context = React.createContext(0);
    
  1359.       function Foo() {
    
  1360.         useContext(Context.Provider);
    
  1361.         return null;
    
  1362.       }
    
  1363.       ReactNoop.render(<Foo />);
    
  1364.       await expect(async () => await waitForAll([])).toErrorDev(
    
  1365.         'Calling useContext(Context.Provider) is not supported. ' +
    
  1366.           'Did you mean to call useContext(Context) instead?',
    
  1367.       );
    
  1368.     });
    
  1369. 
    
  1370.     // Context consumer bails out on propagating "deep" updates when `value` hasn't changed.
    
  1371.     // However, it doesn't bail out from rendering if the component above it re-rendered anyway.
    
  1372.     // If we bailed out on referential equality, it would be confusing that you
    
  1373.     // can call this.setState(), but an autobound render callback "blocked" the update.
    
  1374.     // https://github.com/facebook/react/pull/12470#issuecomment-376917711
    
  1375.     it('does not bail out if there were no bailouts above it', async () => {
    
  1376.       const Context = React.createContext(0);
    
  1377. 
    
  1378.       function Consumer({children}) {
    
  1379.         const contextValue = useContext(Context);
    
  1380.         return children(contextValue);
    
  1381.       }
    
  1382. 
    
  1383.       class App extends React.Component {
    
  1384.         state = {
    
  1385.           text: 'hello',
    
  1386.         };
    
  1387. 
    
  1388.         renderConsumer = context => {
    
  1389.           Scheduler.log('App#renderConsumer');
    
  1390.           return <span prop={this.state.text} />;
    
  1391.         };
    
  1392. 
    
  1393.         render() {
    
  1394.           Scheduler.log('App');
    
  1395.           return (
    
  1396.             <Context.Provider value={this.props.value}>
    
  1397.               <Consumer>{this.renderConsumer}</Consumer>
    
  1398.             </Context.Provider>
    
  1399.           );
    
  1400.         }
    
  1401.       }
    
  1402. 
    
  1403.       // Initial mount
    
  1404.       let inst;
    
  1405.       ReactNoop.render(<App value={1} ref={ref => (inst = ref)} />);
    
  1406.       await waitForAll(['App', 'App#renderConsumer']);
    
  1407.       expect(ReactNoop).toMatchRenderedOutput(<span prop="hello" />);
    
  1408. 
    
  1409.       // Update
    
  1410.       inst.setState({text: 'goodbye'});
    
  1411.       await waitForAll(['App', 'App#renderConsumer']);
    
  1412.       expect(ReactNoop).toMatchRenderedOutput(<span prop="goodbye" />);
    
  1413.     });
    
  1414.   });
    
  1415. 
    
  1416.   it('unwinds after errors in complete phase', async () => {
    
  1417.     const Context = React.createContext(0);
    
  1418. 
    
  1419.     // This is a regression test for stack misalignment
    
  1420.     // caused by unwinding the context from wrong point.
    
  1421.     ReactNoop.render(
    
  1422.       <errorInCompletePhase>
    
  1423.         <Context.Provider value={null} />
    
  1424.       </errorInCompletePhase>,
    
  1425.     );
    
  1426.     await waitForThrow('Error in host config.');
    
  1427. 
    
  1428.     ReactNoop.render(
    
  1429.       <Context.Provider value={10}>
    
  1430.         <Context.Consumer>{value => <span prop={value} />}</Context.Consumer>
    
  1431.       </Context.Provider>,
    
  1432.     );
    
  1433.     await waitForAll([]);
    
  1434.     expect(ReactNoop).toMatchRenderedOutput(<span prop={10} />);
    
  1435.   });
    
  1436. 
    
  1437.   describe('fuzz test', () => {
    
  1438.     const contextKeys = ['A', 'B', 'C', 'D', 'E', 'F', 'G'];
    
  1439. 
    
  1440.     const FLUSH_ALL = 'FLUSH_ALL';
    
  1441.     function flushAll() {
    
  1442.       return {
    
  1443.         type: FLUSH_ALL,
    
  1444.         toString() {
    
  1445.           return `flushAll()`;
    
  1446.         },
    
  1447.       };
    
  1448.     }
    
  1449. 
    
  1450.     const FLUSH = 'FLUSH';
    
  1451.     function flush(unitsOfWork) {
    
  1452.       return {
    
  1453.         type: FLUSH,
    
  1454.         unitsOfWork,
    
  1455.         toString() {
    
  1456.           return `flush(${unitsOfWork})`;
    
  1457.         },
    
  1458.       };
    
  1459.     }
    
  1460. 
    
  1461.     const UPDATE = 'UPDATE';
    
  1462.     function update(key, value) {
    
  1463.       return {
    
  1464.         type: UPDATE,
    
  1465.         key,
    
  1466.         value,
    
  1467.         toString() {
    
  1468.           return `update('${key}', ${value})`;
    
  1469.         },
    
  1470.       };
    
  1471.     }
    
  1472. 
    
  1473.     function randomInteger(min, max) {
    
  1474.       min = Math.ceil(min);
    
  1475.       max = Math.floor(max);
    
  1476.       return Math.floor(Math.random() * (max - min)) + min;
    
  1477.     }
    
  1478. 
    
  1479.     function randomAction() {
    
  1480.       switch (randomInteger(0, 3)) {
    
  1481.         case 0:
    
  1482.           return flushAll();
    
  1483.         case 1:
    
  1484.           return flush(randomInteger(0, 500));
    
  1485.         case 2:
    
  1486.           const key = contextKeys[randomInteger(0, contextKeys.length)];
    
  1487.           const value = randomInteger(1, 10);
    
  1488.           return update(key, value);
    
  1489.         default:
    
  1490.           throw new Error('Switch statement should be exhaustive');
    
  1491.       }
    
  1492.     }
    
  1493. 
    
  1494.     function randomActions(n) {
    
  1495.       const actions = [];
    
  1496.       for (let i = 0; i < n; i++) {
    
  1497.         actions.push(randomAction());
    
  1498.       }
    
  1499.       return actions;
    
  1500.     }
    
  1501. 
    
  1502.     function ContextSimulator(maxDepth) {
    
  1503.       const contexts = new Map(
    
  1504.         contextKeys.map(key => {
    
  1505.           const Context = React.createContext(0);
    
  1506.           Context.displayName = 'Context' + key;
    
  1507.           return [key, Context];
    
  1508.         }),
    
  1509.       );
    
  1510. 
    
  1511.       class ConsumerTree extends React.Component {
    
  1512.         shouldComponentUpdate() {
    
  1513.           return false;
    
  1514.         }
    
  1515.         render() {
    
  1516.           Scheduler.log();
    
  1517.           if (this.props.depth >= this.props.maxDepth) {
    
  1518.             return null;
    
  1519.           }
    
  1520.           const consumers = [0, 1, 2].map(i => {
    
  1521.             const randomKey =
    
  1522.               contextKeys[
    
  1523.                 this.props.rand.intBetween(0, contextKeys.length - 1)
    
  1524.               ];
    
  1525.             const Context = contexts.get(randomKey);
    
  1526.             return (
    
  1527.               <Context.Consumer key={i}>
    
  1528.                 {value => (
    
  1529.                   <>
    
  1530.                     <span prop={`${randomKey}:${value}`} />
    
  1531.                     <ConsumerTree
    
  1532.                       rand={this.props.rand}
    
  1533.                       depth={this.props.depth + 1}
    
  1534.                       maxDepth={this.props.maxDepth}
    
  1535.                     />
    
  1536.                   </>
    
  1537.                 )}
    
  1538.               </Context.Consumer>
    
  1539.             );
    
  1540.           });
    
  1541.           return consumers;
    
  1542.         }
    
  1543.       }
    
  1544. 
    
  1545.       function Root(props) {
    
  1546.         return contextKeys.reduceRight(
    
  1547.           (children, key) => {
    
  1548.             const Context = contexts.get(key);
    
  1549.             const value = props.values[key];
    
  1550.             return (
    
  1551.               <Context.Provider value={value}>{children}</Context.Provider>
    
  1552.             );
    
  1553.           },
    
  1554.           <ConsumerTree
    
  1555.             rand={props.rand}
    
  1556.             depth={0}
    
  1557.             maxDepth={props.maxDepth}
    
  1558.           />,
    
  1559.         );
    
  1560.       }
    
  1561. 
    
  1562.       const initialValues = contextKeys.reduce(
    
  1563.         (result, key, i) => ({...result, [key]: i + 1}),
    
  1564.         {},
    
  1565.       );
    
  1566. 
    
  1567.       function assertConsistentTree(expectedValues = {}) {
    
  1568.         const jsx = ReactNoop.getChildrenAsJSX();
    
  1569.         const children = jsx === null ? [] : jsx.props.children;
    
  1570.         children.forEach(child => {
    
  1571.           const text = child.props.prop;
    
  1572.           const key = text[0];
    
  1573.           const value = parseInt(text[2], 10);
    
  1574.           const expectedValue = expectedValues[key];
    
  1575.           if (expectedValue === undefined) {
    
  1576.             // If an expected value was not explicitly passed to this function,
    
  1577.             // use the first occurrence.
    
  1578.             expectedValues[key] = value;
    
  1579.           } else if (value !== expectedValue) {
    
  1580.             throw new Error(
    
  1581.               `Inconsistent value! Expected: ${key}:${expectedValue}. Actual: ${text}`,
    
  1582.             );
    
  1583.           }
    
  1584.         });
    
  1585.       }
    
  1586. 
    
  1587.       function simulate(seed, actions) {
    
  1588.         const rand = gen.create(seed);
    
  1589.         let finalExpectedValues = initialValues;
    
  1590.         function updateRoot() {
    
  1591.           ReactNoop.render(
    
  1592.             <Root
    
  1593.               maxDepth={maxDepth}
    
  1594.               rand={rand}
    
  1595.               values={finalExpectedValues}
    
  1596.             />,
    
  1597.           );
    
  1598.         }
    
  1599.         updateRoot();
    
  1600. 
    
  1601.         actions.forEach(action => {
    
  1602.           switch (action.type) {
    
  1603.             case FLUSH_ALL:
    
  1604.               Scheduler.unstable_flushAllWithoutAsserting();
    
  1605.               break;
    
  1606.             case FLUSH:
    
  1607.               Scheduler.unstable_flushNumberOfYields(action.unitsOfWork);
    
  1608.               break;
    
  1609.             case UPDATE:
    
  1610.               finalExpectedValues = {
    
  1611.                 ...finalExpectedValues,
    
  1612.                 [action.key]: action.value,
    
  1613.               };
    
  1614.               updateRoot();
    
  1615.               break;
    
  1616.             default:
    
  1617.               throw new Error('Switch statement should be exhaustive');
    
  1618.           }
    
  1619.           assertConsistentTree();
    
  1620.         });
    
  1621. 
    
  1622.         Scheduler.unstable_flushAllWithoutAsserting();
    
  1623.         assertConsistentTree(finalExpectedValues);
    
  1624.       }
    
  1625. 
    
  1626.       return {simulate};
    
  1627.     }
    
  1628. 
    
  1629.     it('hard-coded tests', () => {
    
  1630.       const {simulate} = ContextSimulator(5);
    
  1631.       simulate('randomSeed', [flush(3), update('A', 4)]);
    
  1632.     });
    
  1633. 
    
  1634.     it('generated tests', () => {
    
  1635.       const {simulate} = ContextSimulator(5);
    
  1636. 
    
  1637.       const LIMIT = 100;
    
  1638.       for (let i = 0; i < LIMIT; i++) {
    
  1639.         const seed = Math.random().toString(36).slice(2, 7);
    
  1640.         const actions = randomActions(5);
    
  1641.         try {
    
  1642.           simulate(seed, actions);
    
  1643.         } catch (error) {
    
  1644.           console.error(`
    
  1645. Context fuzz tester error! Copy and paste the following line into the test suite:
    
  1646.   simulate('${seed}', ${actions.join(', ')});
    
  1647. `);
    
  1648.           throw error;
    
  1649.         }
    
  1650.       }
    
  1651.     });
    
  1652.   });
    
  1653. 
    
  1654.   it('should warn with an error message when using context as a consumer in DEV', async () => {
    
  1655.     const BarContext = React.createContext({value: 'bar-initial'});
    
  1656.     const BarConsumer = BarContext;
    
  1657. 
    
  1658.     function Component() {
    
  1659.       return (
    
  1660.         <>
    
  1661.           <BarContext.Provider value={{value: 'bar-updated'}}>
    
  1662.             <BarConsumer>
    
  1663.               {({value}) => <div actual={value} expected="bar-updated" />}
    
  1664.             </BarConsumer>
    
  1665.           </BarContext.Provider>
    
  1666.         </>
    
  1667.       );
    
  1668.     }
    
  1669. 
    
  1670.     await expect(async () => {
    
  1671.       ReactNoop.render(<Component />);
    
  1672.       await waitForAll([]);
    
  1673.     }).toErrorDev(
    
  1674.       'Rendering <Context> directly is not supported and will be removed in ' +
    
  1675.         'a future major release. Did you mean to render <Context.Consumer> instead?',
    
  1676.     );
    
  1677.   });
    
  1678. 
    
  1679.   // False positive regression test.
    
  1680.   it('should not warn when using Consumer from React < 16.6 with newer renderer', async () => {
    
  1681.     const BarContext = React.createContext({value: 'bar-initial'});
    
  1682.     // React 16.5 and earlier didn't have a separate object.
    
  1683.     BarContext.Consumer = BarContext;
    
  1684. 
    
  1685.     function Component() {
    
  1686.       return (
    
  1687.         <>
    
  1688.           <BarContext.Provider value={{value: 'bar-updated'}}>
    
  1689.             <BarContext.Consumer>
    
  1690.               {({value}) => <div actual={value} expected="bar-updated" />}
    
  1691.             </BarContext.Consumer>
    
  1692.           </BarContext.Provider>
    
  1693.         </>
    
  1694.       );
    
  1695.     }
    
  1696. 
    
  1697.     ReactNoop.render(<Component />);
    
  1698.     await waitForAll([]);
    
  1699.   });
    
  1700. 
    
  1701.   it('should warn with an error message when using nested context consumers in DEV', async () => {
    
  1702.     const BarContext = React.createContext({value: 'bar-initial'});
    
  1703.     const BarConsumer = BarContext;
    
  1704. 
    
  1705.     function Component() {
    
  1706.       return (
    
  1707.         <>
    
  1708.           <BarContext.Provider value={{value: 'bar-updated'}}>
    
  1709.             <BarConsumer.Consumer.Consumer>
    
  1710.               {({value}) => <div actual={value} expected="bar-updated" />}
    
  1711.             </BarConsumer.Consumer.Consumer>
    
  1712.           </BarContext.Provider>
    
  1713.         </>
    
  1714.       );
    
  1715.     }
    
  1716. 
    
  1717.     await expect(async () => {
    
  1718.       ReactNoop.render(<Component />);
    
  1719.       await waitForAll([]);
    
  1720.     }).toErrorDev(
    
  1721.       'Rendering <Context.Consumer.Consumer> is not supported and will be removed in ' +
    
  1722.         'a future major release. Did you mean to render <Context.Consumer> instead?',
    
  1723.     );
    
  1724.   });
    
  1725. 
    
  1726.   it('should warn with an error message when using Context.Consumer.Provider DEV', async () => {
    
  1727.     const BarContext = React.createContext({value: 'bar-initial'});
    
  1728. 
    
  1729.     function Component() {
    
  1730.       return (
    
  1731.         <>
    
  1732.           <BarContext.Consumer.Provider value={{value: 'bar-updated'}}>
    
  1733.             <BarContext.Consumer>
    
  1734.               {({value}) => <div actual={value} expected="bar-updated" />}
    
  1735.             </BarContext.Consumer>
    
  1736.           </BarContext.Consumer.Provider>
    
  1737.         </>
    
  1738.       );
    
  1739.     }
    
  1740. 
    
  1741.     await expect(async () => {
    
  1742.       ReactNoop.render(<Component />);
    
  1743.       await waitForAll([]);
    
  1744.     }).toErrorDev(
    
  1745.       'Rendering <Context.Consumer.Provider> is not supported and will be removed in ' +
    
  1746.         'a future major release. Did you mean to render <Context.Provider> instead?',
    
  1747.     );
    
  1748.   });
    
  1749. });