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. const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');
    
  13. 
    
  14. let React;
    
  15. let ReactDOM;
    
  16. let ReactDOMServer;
    
  17. let ReactTestUtils;
    
  18. 
    
  19. function initModules() {
    
  20.   // Reset warning cache.
    
  21.   jest.resetModules();
    
  22. 
    
  23.   React = require('react');
    
  24.   ReactDOM = require('react-dom');
    
  25.   ReactDOMServer = require('react-dom/server');
    
  26.   ReactTestUtils = require('react-dom/test-utils');
    
  27. 
    
  28.   // Make them available to the helpers.
    
  29.   return {
    
  30.     ReactDOM,
    
  31.     ReactDOMServer,
    
  32.     ReactTestUtils,
    
  33.   };
    
  34. }
    
  35. 
    
  36. const {resetModules, expectMarkupMismatch, expectMarkupMatch} =
    
  37.   ReactDOMServerIntegrationUtils(initModules);
    
  38. 
    
  39. describe('ReactDOMServerIntegration', () => {
    
  40.   beforeEach(() => {
    
  41.     resetModules();
    
  42.   });
    
  43. 
    
  44.   describe('reconnecting to server markup', function () {
    
  45.     let EmptyComponent;
    
  46.     beforeEach(() => {
    
  47.       EmptyComponent = class extends React.Component {
    
  48.         render() {
    
  49.           return null;
    
  50.         }
    
  51.       };
    
  52.     });
    
  53. 
    
  54.     describe('elements', function () {
    
  55.       describe('reconnecting different component implementations', function () {
    
  56.         let ES6ClassComponent, PureComponent, bareElement;
    
  57.         beforeEach(() => {
    
  58.           // try each type of component on client and server.
    
  59.           ES6ClassComponent = class extends React.Component {
    
  60.             render() {
    
  61.               return <div id={this.props.id} />;
    
  62.             }
    
  63.           };
    
  64.           PureComponent = props => <div id={props.id} />;
    
  65.           bareElement = <div id="foobarbaz" />;
    
  66.         });
    
  67. 
    
  68.         it('should reconnect ES6 Class to ES6 Class', () =>
    
  69.           expectMarkupMatch(
    
  70.             <ES6ClassComponent id="foobarbaz" />,
    
  71.             <ES6ClassComponent id="foobarbaz" />,
    
  72.           ));
    
  73. 
    
  74.         it('should reconnect Pure Component to ES6 Class', () =>
    
  75.           expectMarkupMatch(
    
  76.             <ES6ClassComponent id="foobarbaz" />,
    
  77.             <PureComponent id="foobarbaz" />,
    
  78.           ));
    
  79. 
    
  80.         it('should reconnect Bare Element to ES6 Class', () =>
    
  81.           expectMarkupMatch(<ES6ClassComponent id="foobarbaz" />, bareElement));
    
  82. 
    
  83.         it('should reconnect ES6 Class to Pure Component', () =>
    
  84.           expectMarkupMatch(
    
  85.             <PureComponent id="foobarbaz" />,
    
  86.             <ES6ClassComponent id="foobarbaz" />,
    
  87.           ));
    
  88. 
    
  89.         it('should reconnect Pure Component to Pure Component', () =>
    
  90.           expectMarkupMatch(
    
  91.             <PureComponent id="foobarbaz" />,
    
  92.             <PureComponent id="foobarbaz" />,
    
  93.           ));
    
  94. 
    
  95.         it('should reconnect Bare Element to Pure Component', () =>
    
  96.           expectMarkupMatch(<PureComponent id="foobarbaz" />, bareElement));
    
  97. 
    
  98.         it('should reconnect ES6 Class to Bare Element', () =>
    
  99.           expectMarkupMatch(bareElement, <ES6ClassComponent id="foobarbaz" />));
    
  100. 
    
  101.         it('should reconnect Pure Component to Bare Element', () =>
    
  102.           expectMarkupMatch(bareElement, <PureComponent id="foobarbaz" />));
    
  103. 
    
  104.         it('should reconnect Bare Element to Bare Element', () =>
    
  105.           expectMarkupMatch(bareElement, bareElement));
    
  106.       });
    
  107. 
    
  108.       it('should error reconnecting different element types', () =>
    
  109.         expectMarkupMismatch(<div />, <span />));
    
  110. 
    
  111.       it('should error reconnecting fewer root children', () =>
    
  112.         expectMarkupMismatch(<span key="a" />, [
    
  113.           <span key="a" />,
    
  114.           <span key="b" />,
    
  115.         ]));
    
  116. 
    
  117.       it('should error reconnecting missing attributes', () =>
    
  118.         expectMarkupMismatch(<div id="foo" />, <div />));
    
  119. 
    
  120.       it('should error reconnecting added attributes', () =>
    
  121.         expectMarkupMismatch(<div />, <div id="foo" />));
    
  122. 
    
  123.       it('should error reconnecting different attribute values', () =>
    
  124.         expectMarkupMismatch(<div id="foo" />, <div id="bar" />));
    
  125. 
    
  126.       it('can explicitly ignore errors reconnecting different element types of children', () =>
    
  127.         expectMarkupMatch(
    
  128.           <div>
    
  129.             <div />
    
  130.           </div>,
    
  131.           <div suppressHydrationWarning={true}>
    
  132.             <span />
    
  133.           </div>,
    
  134.         ));
    
  135. 
    
  136.       it('can explicitly ignore errors reconnecting missing attributes', () =>
    
  137.         expectMarkupMatch(
    
  138.           <div id="foo" />,
    
  139.           <div suppressHydrationWarning={true} />,
    
  140.         ));
    
  141. 
    
  142.       it('can explicitly ignore errors reconnecting added attributes', () =>
    
  143.         expectMarkupMatch(
    
  144.           <div />,
    
  145.           <div id="foo" suppressHydrationWarning={true} />,
    
  146.         ));
    
  147. 
    
  148.       it('can explicitly ignore errors reconnecting different attribute values', () =>
    
  149.         expectMarkupMatch(
    
  150.           <div id="foo" />,
    
  151.           <div id="bar" suppressHydrationWarning={true} />,
    
  152.         ));
    
  153. 
    
  154.       it('can not deeply ignore errors reconnecting different attribute values', () =>
    
  155.         expectMarkupMismatch(
    
  156.           <div>
    
  157.             <div id="foo" />
    
  158.           </div>,
    
  159.           <div suppressHydrationWarning={true}>
    
  160.             <div id="bar" />
    
  161.           </div>,
    
  162.         ));
    
  163.     });
    
  164. 
    
  165.     describe('inline styles', function () {
    
  166.       it('should error reconnecting missing style attribute', () =>
    
  167.         expectMarkupMismatch(<div style={{width: '1px'}} />, <div />));
    
  168. 
    
  169.       it('should error reconnecting added style attribute', () =>
    
  170.         expectMarkupMismatch(<div />, <div style={{width: '1px'}} />));
    
  171. 
    
  172.       it('should error reconnecting empty style attribute', () =>
    
  173.         expectMarkupMismatch(
    
  174.           <div style={{width: '1px'}} />,
    
  175.           <div style={{}} />,
    
  176.         ));
    
  177. 
    
  178.       it('should error reconnecting added style values', () =>
    
  179.         expectMarkupMismatch(
    
  180.           <div style={{}} />,
    
  181.           <div style={{width: '1px'}} />,
    
  182.         ));
    
  183. 
    
  184.       it('should error reconnecting different style values', () =>
    
  185.         expectMarkupMismatch(
    
  186.           <div style={{width: '1px'}} />,
    
  187.           <div style={{width: '2px'}} />,
    
  188.         ));
    
  189. 
    
  190.       it('should reconnect number and string versions of a number', () =>
    
  191.         expectMarkupMatch(
    
  192.           <div style={{width: '1px', height: 2}} />,
    
  193.           <div style={{width: 1, height: '2px'}} />,
    
  194.         ));
    
  195. 
    
  196.       it('should error reconnecting reordered style values', () =>
    
  197.         expectMarkupMismatch(
    
  198.           <div style={{width: '1px', fontSize: '2px'}} />,
    
  199.           <div style={{fontSize: '2px', width: '1px'}} />,
    
  200.         ));
    
  201. 
    
  202.       it('can explicitly ignore errors reconnecting added style values', () =>
    
  203.         expectMarkupMatch(
    
  204.           <div style={{}} />,
    
  205.           <div style={{width: '1px'}} suppressHydrationWarning={true} />,
    
  206.         ));
    
  207. 
    
  208.       it('can explicitly ignore reconnecting different style values', () =>
    
  209.         expectMarkupMatch(
    
  210.           <div style={{width: '1px'}} />,
    
  211.           <div style={{width: '2px'}} suppressHydrationWarning={true} />,
    
  212.         ));
    
  213.     });
    
  214. 
    
  215.     describe('text nodes', function () {
    
  216.       it('should error reconnecting different text', () =>
    
  217.         expectMarkupMismatch(<div>Text</div>, <div>Other Text</div>));
    
  218. 
    
  219.       it('should reconnect a div with a number and string version of number', () =>
    
  220.         expectMarkupMatch(<div>{2}</div>, <div>2</div>));
    
  221. 
    
  222.       it('should error reconnecting different numbers', () =>
    
  223.         expectMarkupMismatch(<div>{2}</div>, <div>{3}</div>));
    
  224. 
    
  225.       it('should error reconnecting different number from text', () =>
    
  226.         expectMarkupMismatch(<div>{2}</div>, <div>3</div>));
    
  227. 
    
  228.       it('should error reconnecting different text in two code blocks', () =>
    
  229.         expectMarkupMismatch(
    
  230.           <div>
    
  231.             {'Text1'}
    
  232.             {'Text2'}
    
  233.           </div>,
    
  234.           <div>
    
  235.             {'Text1'}
    
  236.             {'Text3'}
    
  237.           </div>,
    
  238.         ));
    
  239. 
    
  240.       it('can explicitly ignore reconnecting different text', () =>
    
  241.         expectMarkupMatch(
    
  242.           <div>Text</div>,
    
  243.           <div suppressHydrationWarning={true}>Other Text</div>,
    
  244.         ));
    
  245. 
    
  246.       it('can explicitly ignore reconnecting different text in two code blocks', () =>
    
  247.         expectMarkupMatch(
    
  248.           <div suppressHydrationWarning={true}>
    
  249.             {'Text1'}
    
  250.             {'Text2'}
    
  251.           </div>,
    
  252.           <div suppressHydrationWarning={true}>
    
  253.             {'Text1'}
    
  254.             {'Text3'}
    
  255.           </div>,
    
  256.         ));
    
  257.     });
    
  258. 
    
  259.     describe('element trees and children', function () {
    
  260.       it('should error reconnecting missing children', () =>
    
  261.         expectMarkupMismatch(
    
  262.           <div>
    
  263.             <div />
    
  264.           </div>,
    
  265.           <div />,
    
  266.         ));
    
  267. 
    
  268.       it('should error reconnecting added children', () =>
    
  269.         expectMarkupMismatch(
    
  270.           <div />,
    
  271.           <div>
    
  272.             <div />
    
  273.           </div>,
    
  274.         ));
    
  275. 
    
  276.       it('should error reconnecting more children', () =>
    
  277.         expectMarkupMismatch(
    
  278.           <div>
    
  279.             <div />
    
  280.           </div>,
    
  281.           <div>
    
  282.             <div />
    
  283.             <div />
    
  284.           </div>,
    
  285.         ));
    
  286. 
    
  287.       it('should error reconnecting fewer children', () =>
    
  288.         expectMarkupMismatch(
    
  289.           <div>
    
  290.             <div />
    
  291.             <div />
    
  292.           </div>,
    
  293.           <div>
    
  294.             <div />
    
  295.           </div>,
    
  296.         ));
    
  297. 
    
  298.       it('should error reconnecting reordered children', () =>
    
  299.         expectMarkupMismatch(
    
  300.           <div>
    
  301.             <div />
    
  302.             <span />
    
  303.           </div>,
    
  304.           <div>
    
  305.             <span />
    
  306.             <div />
    
  307.           </div>,
    
  308.         ));
    
  309. 
    
  310.       it('should error reconnecting a div with children separated by whitespace on the client', () =>
    
  311.         expectMarkupMismatch(
    
  312.           <div id="parent">
    
  313.             <div id="child1" />
    
  314.             <div id="child2" />
    
  315.           </div>,
    
  316.           // prettier-ignore
    
  317.           <div id="parent"><div id="child1" />      <div id="child2" /></div>, // eslint-disable-line no-multi-spaces
    
  318.         ));
    
  319. 
    
  320.       it('should error reconnecting a div with children separated by different whitespace on the server', () =>
    
  321.         expectMarkupMismatch(
    
  322.           // prettier-ignore
    
  323.           <div id="parent"><div id="child1" />      <div id="child2" /></div>, // eslint-disable-line no-multi-spaces
    
  324.           <div id="parent">
    
  325.             <div id="child1" />
    
  326.             <div id="child2" />
    
  327.           </div>,
    
  328.         ));
    
  329. 
    
  330.       it('should error reconnecting a div with children separated by different whitespace', () =>
    
  331.         expectMarkupMismatch(
    
  332.           <div id="parent">
    
  333.             <div id="child1" /> <div id="child2" />
    
  334.           </div>,
    
  335.           // prettier-ignore
    
  336.           <div id="parent"><div id="child1" />      <div id="child2" /></div>, // eslint-disable-line no-multi-spaces
    
  337.         ));
    
  338. 
    
  339.       it('can distinguish an empty component from a dom node', () =>
    
  340.         expectMarkupMismatch(
    
  341.           <div>
    
  342.             <span />
    
  343.           </div>,
    
  344.           <div>
    
  345.             <EmptyComponent />
    
  346.           </div>,
    
  347.         ));
    
  348. 
    
  349.       it('can distinguish an empty component from an empty text component', () =>
    
  350.         expectMarkupMatch(
    
  351.           <div>
    
  352.             <EmptyComponent />
    
  353.           </div>,
    
  354.           <div>{''}</div>,
    
  355.         ));
    
  356. 
    
  357.       it('can explicitly ignore reconnecting more children', () =>
    
  358.         expectMarkupMatch(
    
  359.           <div>
    
  360.             <div />
    
  361.           </div>,
    
  362.           <div suppressHydrationWarning={true}>
    
  363.             <div />
    
  364.             <div />
    
  365.           </div>,
    
  366.         ));
    
  367. 
    
  368.       it('can explicitly ignore reconnecting fewer children', () =>
    
  369.         expectMarkupMatch(
    
  370.           <div>
    
  371.             <div />
    
  372.             <div />
    
  373.           </div>,
    
  374.           <div suppressHydrationWarning={true}>
    
  375.             <div />
    
  376.           </div>,
    
  377.         ));
    
  378. 
    
  379.       it('can explicitly ignore reconnecting reordered children', () =>
    
  380.         expectMarkupMatch(
    
  381.           <div suppressHydrationWarning={true}>
    
  382.             <div />
    
  383.             <span />
    
  384.           </div>,
    
  385.           <div suppressHydrationWarning={true}>
    
  386.             <span />
    
  387.             <div />
    
  388.           </div>,
    
  389.         ));
    
  390. 
    
  391.       it('can not deeply ignore reconnecting reordered children', () =>
    
  392.         expectMarkupMismatch(
    
  393.           <div suppressHydrationWarning={true}>
    
  394.             <div>
    
  395.               <div />
    
  396.               <span />
    
  397.             </div>
    
  398.           </div>,
    
  399.           <div suppressHydrationWarning={true}>
    
  400.             <div>
    
  401.               <span />
    
  402.               <div />
    
  403.             </div>
    
  404.           </div>,
    
  405.         ));
    
  406.     });
    
  407. 
    
  408.     // Markup Mismatches: misc
    
  409.     it('should error reconnecting a div with different dangerouslySetInnerHTML', () =>
    
  410.       expectMarkupMismatch(
    
  411.         <div dangerouslySetInnerHTML={{__html: "<span id='child1'/>"}} />,
    
  412.         <div dangerouslySetInnerHTML={{__html: "<span id='child2'/>"}} />,
    
  413.       ));
    
  414. 
    
  415.     it('should error reconnecting a div with different text dangerouslySetInnerHTML', () =>
    
  416.       expectMarkupMismatch(
    
  417.         <div dangerouslySetInnerHTML={{__html: 'foo'}} />,
    
  418.         <div dangerouslySetInnerHTML={{__html: 'bar'}} />,
    
  419.       ));
    
  420. 
    
  421.     it('should error reconnecting a div with different number dangerouslySetInnerHTML', () =>
    
  422.       expectMarkupMismatch(
    
  423.         <div dangerouslySetInnerHTML={{__html: 10}} />,
    
  424.         <div dangerouslySetInnerHTML={{__html: 20}} />,
    
  425.       ));
    
  426. 
    
  427.     it('should error reconnecting a div with different object dangerouslySetInnerHTML', () =>
    
  428.       expectMarkupMismatch(
    
  429.         <div
    
  430.           dangerouslySetInnerHTML={{
    
  431.             __html: {
    
  432.               toString() {
    
  433.                 return 'hi';
    
  434.               },
    
  435.             },
    
  436.           }}
    
  437.         />,
    
  438.         <div
    
  439.           dangerouslySetInnerHTML={{
    
  440.             __html: {
    
  441.               toString() {
    
  442.                 return 'bye';
    
  443.               },
    
  444.             },
    
  445.           }}
    
  446.         />,
    
  447.       ));
    
  448. 
    
  449.     it('can explicitly ignore reconnecting a div with different dangerouslySetInnerHTML', () =>
    
  450.       expectMarkupMatch(
    
  451.         <div dangerouslySetInnerHTML={{__html: "<span id='child1'/>"}} />,
    
  452.         <div
    
  453.           dangerouslySetInnerHTML={{__html: "<span id='child2'/>"}}
    
  454.           suppressHydrationWarning={true}
    
  455.         />,
    
  456.       ));
    
  457.   });
    
  458. });