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.  * @jest-environment ./scripts/jest/ReactDOMServerIntegrationEnvironment
    
  9.  */
    
  10. 
    
  11. 'use strict';
    
  12. 
    
  13. const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');
    
  14. 
    
  15. let PropTypes;
    
  16. let React;
    
  17. let ReactDOM;
    
  18. let ReactDOMServer;
    
  19. let ReactTestUtils;
    
  20. 
    
  21. function initModules() {
    
  22.   // Reset warning cache.
    
  23.   jest.resetModules();
    
  24.   PropTypes = require('prop-types');
    
  25.   React = require('react');
    
  26.   ReactDOM = require('react-dom');
    
  27.   ReactDOMServer = require('react-dom/server');
    
  28.   ReactTestUtils = require('react-dom/test-utils');
    
  29. 
    
  30.   // Make them available to the helpers.
    
  31.   return {
    
  32.     ReactDOM,
    
  33.     ReactDOMServer,
    
  34.     ReactTestUtils,
    
  35.   };
    
  36. }
    
  37. 
    
  38. const {resetModules, itRenders, itThrowsWhenRendering} =
    
  39.   ReactDOMServerIntegrationUtils(initModules);
    
  40. 
    
  41. describe('ReactDOMServerIntegration', () => {
    
  42.   beforeEach(() => {
    
  43.     resetModules();
    
  44.   });
    
  45. 
    
  46.   describe('legacy context', function () {
    
  47.     // The `itRenders` test abstraction doesn't work with @gate so we have
    
  48.     // to do this instead.
    
  49.     if (gate(flags => flags.disableLegacyContext)) {
    
  50.       test('empty test to stop Jest from being a complainy complainer', () => {});
    
  51.       return;
    
  52.     }
    
  53. 
    
  54.     let PurpleContext, RedContext;
    
  55.     beforeEach(() => {
    
  56.       class Parent extends React.Component {
    
  57.         getChildContext() {
    
  58.           return {text: this.props.text};
    
  59.         }
    
  60.         render() {
    
  61.           return this.props.children;
    
  62.         }
    
  63.       }
    
  64.       Parent.childContextTypes = {text: PropTypes.string};
    
  65. 
    
  66.       PurpleContext = props => <Parent text="purple">{props.children}</Parent>;
    
  67.       RedContext = props => <Parent text="red">{props.children}</Parent>;
    
  68.     });
    
  69. 
    
  70.     itRenders('class child with context', async render => {
    
  71.       class ClassChildWithContext extends React.Component {
    
  72.         render() {
    
  73.           return <div>{this.context.text}</div>;
    
  74.         }
    
  75.       }
    
  76.       ClassChildWithContext.contextTypes = {text: PropTypes.string};
    
  77. 
    
  78.       const e = await render(
    
  79.         <PurpleContext>
    
  80.           <ClassChildWithContext />
    
  81.         </PurpleContext>,
    
  82.       );
    
  83.       expect(e.textContent).toBe('purple');
    
  84.     });
    
  85. 
    
  86.     itRenders('stateless child with context', async render => {
    
  87.       function FunctionChildWithContext(props, context) {
    
  88.         return <div>{context.text}</div>;
    
  89.       }
    
  90.       FunctionChildWithContext.contextTypes = {text: PropTypes.string};
    
  91. 
    
  92.       const e = await render(
    
  93.         <PurpleContext>
    
  94.           <FunctionChildWithContext />
    
  95.         </PurpleContext>,
    
  96.       );
    
  97.       expect(e.textContent).toBe('purple');
    
  98.     });
    
  99. 
    
  100.     itRenders('class child without context', async render => {
    
  101.       class ClassChildWithoutContext extends React.Component {
    
  102.         render() {
    
  103.           // this should render blank; context isn't passed to this component.
    
  104.           return <div>{this.context.text}</div>;
    
  105.         }
    
  106.       }
    
  107. 
    
  108.       const e = await render(
    
  109.         <PurpleContext>
    
  110.           <ClassChildWithoutContext />
    
  111.         </PurpleContext>,
    
  112.       );
    
  113.       expect(e.textContent).toBe('');
    
  114.     });
    
  115. 
    
  116.     itRenders('stateless child without context', async render => {
    
  117.       function FunctionChildWithoutContext(props, context) {
    
  118.         // this should render blank; context isn't passed to this component.
    
  119.         return <div>{context.text}</div>;
    
  120.       }
    
  121. 
    
  122.       const e = await render(
    
  123.         <PurpleContext>
    
  124.           <FunctionChildWithoutContext />
    
  125.         </PurpleContext>,
    
  126.       );
    
  127.       expect(e.textContent).toBe('');
    
  128.     });
    
  129. 
    
  130.     itRenders('class child with wrong context', async render => {
    
  131.       class ClassChildWithWrongContext extends React.Component {
    
  132.         render() {
    
  133.           // this should render blank; context.text isn't passed to this component.
    
  134.           return <div id="classWrongChild">{this.context.text}</div>;
    
  135.         }
    
  136.       }
    
  137.       ClassChildWithWrongContext.contextTypes = {foo: PropTypes.string};
    
  138. 
    
  139.       const e = await render(
    
  140.         <PurpleContext>
    
  141.           <ClassChildWithWrongContext />
    
  142.         </PurpleContext>,
    
  143.       );
    
  144.       expect(e.textContent).toBe('');
    
  145.     });
    
  146. 
    
  147.     itRenders('stateless child with wrong context', async render => {
    
  148.       function FunctionChildWithWrongContext(props, context) {
    
  149.         // this should render blank; context.text isn't passed to this component.
    
  150.         return <div id="statelessWrongChild">{context.text}</div>;
    
  151.       }
    
  152.       FunctionChildWithWrongContext.contextTypes = {
    
  153.         foo: PropTypes.string,
    
  154.       };
    
  155. 
    
  156.       const e = await render(
    
  157.         <PurpleContext>
    
  158.           <FunctionChildWithWrongContext />
    
  159.         </PurpleContext>,
    
  160.       );
    
  161.       expect(e.textContent).toBe('');
    
  162.     });
    
  163. 
    
  164.     itRenders('with context passed through to a grandchild', async render => {
    
  165.       function Grandchild(props, context) {
    
  166.         return <div>{context.text}</div>;
    
  167.       }
    
  168.       Grandchild.contextTypes = {text: PropTypes.string};
    
  169. 
    
  170.       const Child = props => <Grandchild />;
    
  171. 
    
  172.       const e = await render(
    
  173.         <PurpleContext>
    
  174.           <Child />
    
  175.         </PurpleContext>,
    
  176.       );
    
  177.       expect(e.textContent).toBe('purple');
    
  178.     });
    
  179. 
    
  180.     itRenders('a child context overriding a parent context', async render => {
    
  181.       const Grandchild = (props, context) => {
    
  182.         return <div>{context.text}</div>;
    
  183.       };
    
  184.       Grandchild.contextTypes = {text: PropTypes.string};
    
  185. 
    
  186.       const e = await render(
    
  187.         <PurpleContext>
    
  188.           <RedContext>
    
  189.             <Grandchild />
    
  190.           </RedContext>
    
  191.         </PurpleContext>,
    
  192.       );
    
  193.       expect(e.textContent).toBe('red');
    
  194.     });
    
  195. 
    
  196.     itRenders('a child context merged with a parent context', async render => {
    
  197.       class Parent extends React.Component {
    
  198.         getChildContext() {
    
  199.           return {text1: 'purple'};
    
  200.         }
    
  201.         render() {
    
  202.           return <Child />;
    
  203.         }
    
  204.       }
    
  205.       Parent.childContextTypes = {text1: PropTypes.string};
    
  206. 
    
  207.       class Child extends React.Component {
    
  208.         getChildContext() {
    
  209.           return {text2: 'red'};
    
  210.         }
    
  211.         render() {
    
  212.           return <Grandchild />;
    
  213.         }
    
  214.       }
    
  215.       Child.childContextTypes = {text2: PropTypes.string};
    
  216. 
    
  217.       const Grandchild = (props, context) => {
    
  218.         return (
    
  219.           <div>
    
  220.             <div id="first">{context.text1}</div>
    
  221.             <div id="second">{context.text2}</div>
    
  222.           </div>
    
  223.         );
    
  224.       };
    
  225.       Grandchild.contextTypes = {
    
  226.         text1: PropTypes.string,
    
  227.         text2: PropTypes.string,
    
  228.       };
    
  229. 
    
  230.       const e = await render(<Parent />);
    
  231.       expect(e.querySelector('#first').textContent).toBe('purple');
    
  232.       expect(e.querySelector('#second').textContent).toBe('red');
    
  233.     });
    
  234. 
    
  235.     itRenders(
    
  236.       'with a call to componentWillMount before getChildContext',
    
  237.       async render => {
    
  238.         class WillMountContext extends React.Component {
    
  239.           getChildContext() {
    
  240.             return {text: this.state.text};
    
  241.           }
    
  242.           UNSAFE_componentWillMount() {
    
  243.             this.setState({text: 'foo'});
    
  244.           }
    
  245.           render() {
    
  246.             return <Child />;
    
  247.           }
    
  248.         }
    
  249.         WillMountContext.childContextTypes = {text: PropTypes.string};
    
  250. 
    
  251.         const Child = (props, context) => {
    
  252.           return <div>{context.text}</div>;
    
  253.         };
    
  254.         Child.contextTypes = {text: PropTypes.string};
    
  255. 
    
  256.         const e = await render(<WillMountContext />);
    
  257.         expect(e.textContent).toBe('foo');
    
  258.       },
    
  259.     );
    
  260. 
    
  261.     itRenders(
    
  262.       'if getChildContext exists but childContextTypes is missing with a warning',
    
  263.       async render => {
    
  264.         function HopefulChild(props, context) {
    
  265.           return context.foo || 'nope';
    
  266.         }
    
  267.         HopefulChild.contextTypes = {
    
  268.           foo: PropTypes.string,
    
  269.         };
    
  270.         class ForgetfulParent extends React.Component {
    
  271.           render() {
    
  272.             return <HopefulChild />;
    
  273.           }
    
  274.           getChildContext() {
    
  275.             return {foo: 'bar'};
    
  276.           }
    
  277.         }
    
  278.         const e = await render(<ForgetfulParent />, 1);
    
  279.         expect(e.textContent).toBe('nope');
    
  280.       },
    
  281.     );
    
  282. 
    
  283.     itThrowsWhenRendering(
    
  284.       'if getChildContext returns a value not in childContextTypes',
    
  285.       render => {
    
  286.         class MyComponent extends React.Component {
    
  287.           render() {
    
  288.             return <div />;
    
  289.           }
    
  290.           getChildContext() {
    
  291.             return {value1: 'foo', value2: 'bar'};
    
  292.           }
    
  293.         }
    
  294.         MyComponent.childContextTypes = {value1: PropTypes.string};
    
  295.         return render(<MyComponent />);
    
  296.       },
    
  297.       'MyComponent.getChildContext(): key "value2" is not defined in childContextTypes.',
    
  298.     );
    
  299. 
    
  300.     it('warns when childContextTypes is not defined', () => {
    
  301.       class MyComponent extends React.Component {
    
  302.         render() {
    
  303.           return <div />;
    
  304.         }
    
  305.         getChildContext() {
    
  306.           return {value1: 'foo', value2: 'bar'};
    
  307.         }
    
  308.       }
    
  309. 
    
  310.       expect(() => {
    
  311.         ReactDOMServer.renderToString(<MyComponent />);
    
  312.       }).toErrorDev(
    
  313.         'Warning: MyComponent.getChildContext(): childContextTypes must be defined in order to use getChildContext().\n' +
    
  314.           '    in MyComponent (at **)',
    
  315.       );
    
  316.     });
    
  317.   });
    
  318. });