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. describe('ReactDOMComponent', () => {
    
  13.   let React;
    
  14.   let ReactTestUtils;
    
  15.   let ReactDOM;
    
  16.   let ReactDOMServer;
    
  17.   const ReactFeatureFlags = require('shared/ReactFeatureFlags');
    
  18. 
    
  19.   beforeEach(() => {
    
  20.     jest.resetModules();
    
  21.     React = require('react');
    
  22.     ReactDOM = require('react-dom');
    
  23.     ReactDOMServer = require('react-dom/server');
    
  24.     ReactTestUtils = require('react-dom/test-utils');
    
  25.   });
    
  26. 
    
  27.   afterEach(() => {
    
  28.     jest.restoreAllMocks();
    
  29.   });
    
  30. 
    
  31.   describe('updateDOM', () => {
    
  32.     it('should handle className', () => {
    
  33.       const container = document.createElement('div');
    
  34.       ReactDOM.render(<div style={{}} />, container);
    
  35. 
    
  36.       ReactDOM.render(<div className={'foo'} />, container);
    
  37.       expect(container.firstChild.className).toEqual('foo');
    
  38.       ReactDOM.render(<div className={'bar'} />, container);
    
  39.       expect(container.firstChild.className).toEqual('bar');
    
  40.       ReactDOM.render(<div className={null} />, container);
    
  41.       expect(container.firstChild.className).toEqual('');
    
  42.     });
    
  43. 
    
  44.     it('should gracefully handle various style value types', () => {
    
  45.       const container = document.createElement('div');
    
  46.       ReactDOM.render(<div style={{}} />, container);
    
  47.       const stubStyle = container.firstChild.style;
    
  48. 
    
  49.       // set initial style
    
  50.       const setup = {
    
  51.         display: 'block',
    
  52.         left: '1px',
    
  53.         top: 2,
    
  54.         fontFamily: 'Arial',
    
  55.       };
    
  56.       ReactDOM.render(<div style={setup} />, container);
    
  57.       expect(stubStyle.display).toEqual('block');
    
  58.       expect(stubStyle.left).toEqual('1px');
    
  59.       expect(stubStyle.top).toEqual('2px');
    
  60.       expect(stubStyle.fontFamily).toEqual('Arial');
    
  61. 
    
  62.       // reset the style to their default state
    
  63.       const reset = {display: '', left: null, top: false, fontFamily: true};
    
  64.       ReactDOM.render(<div style={reset} />, container);
    
  65.       expect(stubStyle.display).toEqual('');
    
  66.       expect(stubStyle.left).toEqual('');
    
  67.       expect(stubStyle.top).toEqual('');
    
  68.       expect(stubStyle.fontFamily).toEqual('');
    
  69.     });
    
  70. 
    
  71.     it('should not update styles when mutating a proxy style object', () => {
    
  72.       const styleStore = {
    
  73.         display: 'none',
    
  74.         fontFamily: 'Arial',
    
  75.         lineHeight: 1.2,
    
  76.       };
    
  77.       // We use a proxy style object so that we can mutate it even if it is
    
  78.       // frozen in DEV.
    
  79.       const styles = {
    
  80.         get display() {
    
  81.           return styleStore.display;
    
  82.         },
    
  83.         set display(v) {
    
  84.           styleStore.display = v;
    
  85.         },
    
  86.         get fontFamily() {
    
  87.           return styleStore.fontFamily;
    
  88.         },
    
  89.         set fontFamily(v) {
    
  90.           styleStore.fontFamily = v;
    
  91.         },
    
  92.         get lineHeight() {
    
  93.           return styleStore.lineHeight;
    
  94.         },
    
  95.         set lineHeight(v) {
    
  96.           styleStore.lineHeight = v;
    
  97.         },
    
  98.       };
    
  99.       const container = document.createElement('div');
    
  100.       ReactDOM.render(<div style={styles} />, container);
    
  101. 
    
  102.       const stubStyle = container.firstChild.style;
    
  103.       stubStyle.display = styles.display;
    
  104.       stubStyle.fontFamily = styles.fontFamily;
    
  105. 
    
  106.       styles.display = 'block';
    
  107. 
    
  108.       ReactDOM.render(<div style={styles} />, container);
    
  109.       expect(stubStyle.display).toEqual('none');
    
  110.       expect(stubStyle.fontFamily).toEqual('Arial');
    
  111.       expect(stubStyle.lineHeight).toEqual('1.2');
    
  112. 
    
  113.       styles.fontFamily = 'Helvetica';
    
  114. 
    
  115.       ReactDOM.render(<div style={styles} />, container);
    
  116.       expect(stubStyle.display).toEqual('none');
    
  117.       expect(stubStyle.fontFamily).toEqual('Arial');
    
  118.       expect(stubStyle.lineHeight).toEqual('1.2');
    
  119. 
    
  120.       styles.lineHeight = 0.5;
    
  121. 
    
  122.       ReactDOM.render(<div style={styles} />, container);
    
  123.       expect(stubStyle.display).toEqual('none');
    
  124.       expect(stubStyle.fontFamily).toEqual('Arial');
    
  125.       expect(stubStyle.lineHeight).toEqual('1.2');
    
  126. 
    
  127.       ReactDOM.render(<div style={undefined} />, container);
    
  128.       expect(stubStyle.display).toBe('');
    
  129.       expect(stubStyle.fontFamily).toBe('');
    
  130.       expect(stubStyle.lineHeight).toBe('');
    
  131.     });
    
  132. 
    
  133.     it('should throw when mutating style objects', () => {
    
  134.       const style = {border: '1px solid black'};
    
  135. 
    
  136.       class App extends React.Component {
    
  137.         state = {style: style};
    
  138. 
    
  139.         render() {
    
  140.           return <div style={this.state.style}>asd</div>;
    
  141.         }
    
  142.       }
    
  143. 
    
  144.       ReactTestUtils.renderIntoDocument(<App />);
    
  145.       if (__DEV__) {
    
  146.         expect(() => (style.position = 'absolute')).toThrow();
    
  147.       }
    
  148.     });
    
  149. 
    
  150.     it('should warn for unknown prop', () => {
    
  151.       const container = document.createElement('div');
    
  152.       expect(() =>
    
  153.         ReactDOM.render(<div foo={() => {}} />, container),
    
  154.       ).toErrorDev(
    
  155.         'Warning: Invalid value for prop `foo` on <div> tag. Either remove it ' +
    
  156.           'from the element, or pass a string or number value to keep ' +
    
  157.           'it in the DOM. For details, see https://reactjs.org/link/attribute-behavior ' +
    
  158.           '\n    in div (at **)',
    
  159.       );
    
  160.     });
    
  161. 
    
  162.     it('should group multiple unknown prop warnings together', () => {
    
  163.       const container = document.createElement('div');
    
  164.       expect(() =>
    
  165.         ReactDOM.render(<div foo={() => {}} baz={() => {}} />, container),
    
  166.       ).toErrorDev(
    
  167.         'Warning: Invalid values for props `foo`, `baz` on <div> tag. Either remove ' +
    
  168.           'them from the element, or pass a string or number value to keep ' +
    
  169.           'them in the DOM. For details, see https://reactjs.org/link/attribute-behavior ' +
    
  170.           '\n    in div (at **)',
    
  171.       );
    
  172.     });
    
  173. 
    
  174.     it('should warn for onDblClick prop', () => {
    
  175.       const container = document.createElement('div');
    
  176.       expect(() =>
    
  177.         ReactDOM.render(<div onDblClick={() => {}} />, container),
    
  178.       ).toErrorDev(
    
  179.         'Warning: Invalid event handler property `onDblClick`. Did you mean `onDoubleClick`?\n    in div (at **)',
    
  180.       );
    
  181.     });
    
  182. 
    
  183.     it('should warn for unknown string event handlers', () => {
    
  184.       const container = document.createElement('div');
    
  185.       expect(() =>
    
  186.         ReactDOM.render(<div onUnknown='alert("hack")' />, container),
    
  187.       ).toErrorDev(
    
  188.         'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n    in div (at **)',
    
  189.       );
    
  190.       expect(container.firstChild.hasAttribute('onUnknown')).toBe(false);
    
  191.       expect(container.firstChild.onUnknown).toBe(undefined);
    
  192.       expect(() =>
    
  193.         ReactDOM.render(<div onunknown='alert("hack")' />, container),
    
  194.       ).toErrorDev(
    
  195.         'Warning: Unknown event handler property `onunknown`. It will be ignored.\n    in div (at **)',
    
  196.       );
    
  197.       expect(container.firstChild.hasAttribute('onunknown')).toBe(false);
    
  198.       expect(container.firstChild.onunknown).toBe(undefined);
    
  199.       expect(() =>
    
  200.         ReactDOM.render(<div on-unknown='alert("hack")' />, container),
    
  201.       ).toErrorDev(
    
  202.         'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n    in div (at **)',
    
  203.       );
    
  204.       expect(container.firstChild.hasAttribute('on-unknown')).toBe(false);
    
  205.       expect(container.firstChild['on-unknown']).toBe(undefined);
    
  206.     });
    
  207. 
    
  208.     it('should warn for unknown function event handlers', () => {
    
  209.       const container = document.createElement('div');
    
  210.       expect(() =>
    
  211.         ReactDOM.render(<div onUnknown={function () {}} />, container),
    
  212.       ).toErrorDev(
    
  213.         'Warning: Unknown event handler property `onUnknown`. It will be ignored.\n    in div (at **)',
    
  214.       );
    
  215.       expect(container.firstChild.hasAttribute('onUnknown')).toBe(false);
    
  216.       expect(container.firstChild.onUnknown).toBe(undefined);
    
  217.       expect(() =>
    
  218.         ReactDOM.render(<div onunknown={function () {}} />, container),
    
  219.       ).toErrorDev(
    
  220.         'Warning: Unknown event handler property `onunknown`. It will be ignored.\n    in div (at **)',
    
  221.       );
    
  222.       expect(container.firstChild.hasAttribute('onunknown')).toBe(false);
    
  223.       expect(container.firstChild.onunknown).toBe(undefined);
    
  224.       expect(() =>
    
  225.         ReactDOM.render(<div on-unknown={function () {}} />, container),
    
  226.       ).toErrorDev(
    
  227.         'Warning: Unknown event handler property `on-unknown`. It will be ignored.\n    in div (at **)',
    
  228.       );
    
  229.       expect(container.firstChild.hasAttribute('on-unknown')).toBe(false);
    
  230.       expect(container.firstChild['on-unknown']).toBe(undefined);
    
  231.     });
    
  232. 
    
  233.     it('should warn for badly cased React attributes', () => {
    
  234.       const container = document.createElement('div');
    
  235.       expect(() => ReactDOM.render(<div CHILDREN="5" />, container)).toErrorDev(
    
  236.         'Warning: Invalid DOM property `CHILDREN`. Did you mean `children`?\n    in div (at **)',
    
  237.       );
    
  238.       expect(container.firstChild.getAttribute('CHILDREN')).toBe('5');
    
  239.     });
    
  240. 
    
  241.     it('should not warn for "0" as a unitless style value', () => {
    
  242.       class Component extends React.Component {
    
  243.         render() {
    
  244.           return <div style={{margin: '0'}} />;
    
  245.         }
    
  246.       }
    
  247. 
    
  248.       ReactTestUtils.renderIntoDocument(<Component />);
    
  249.     });
    
  250. 
    
  251.     it('should warn nicely about NaN in style', () => {
    
  252.       const style = {fontSize: NaN};
    
  253.       const div = document.createElement('div');
    
  254.       expect(() => ReactDOM.render(<span style={style} />, div)).toErrorDev(
    
  255.         'Warning: `NaN` is an invalid value for the `fontSize` css style property.' +
    
  256.           '\n    in span (at **)',
    
  257.       );
    
  258.       ReactDOM.render(<span style={style} />, div);
    
  259.     });
    
  260. 
    
  261.     it('throws with Temporal-like objects as style values', () => {
    
  262.       class TemporalLike {
    
  263.         valueOf() {
    
  264.           // Throwing here is the behavior of ECMAScript "Temporal" date/time API.
    
  265.           // See https://tc39.es/proposal-temporal/docs/plaindate.html#valueOf
    
  266.           throw new TypeError('prod message');
    
  267.         }
    
  268.         toString() {
    
  269.           return '2020-01-01';
    
  270.         }
    
  271.       }
    
  272.       const style = {fontSize: new TemporalLike()};
    
  273.       const div = document.createElement('div');
    
  274.       const test = () => ReactDOM.render(<span style={style} />, div);
    
  275.       expect(() =>
    
  276.         expect(test).toThrowError(new TypeError('prod message')),
    
  277.       ).toErrorDev(
    
  278.         'Warning: The provided `fontSize` CSS property is an unsupported type TemporalLike.' +
    
  279.           ' This value must be coerced to a string before using it here.',
    
  280.       );
    
  281.     });
    
  282. 
    
  283.     it('should update styles if initially null', () => {
    
  284.       let styles = null;
    
  285.       const container = document.createElement('div');
    
  286.       ReactDOM.render(<div style={styles} />, container);
    
  287. 
    
  288.       const stubStyle = container.firstChild.style;
    
  289. 
    
  290.       styles = {display: 'block'};
    
  291. 
    
  292.       ReactDOM.render(<div style={styles} />, container);
    
  293.       expect(stubStyle.display).toEqual('block');
    
  294.     });
    
  295. 
    
  296.     it('should update styles if updated to null multiple times', () => {
    
  297.       let styles = null;
    
  298.       const container = document.createElement('div');
    
  299.       ReactDOM.render(<div style={styles} />, container);
    
  300. 
    
  301.       styles = {display: 'block'};
    
  302.       const stubStyle = container.firstChild.style;
    
  303. 
    
  304.       ReactDOM.render(<div style={styles} />, container);
    
  305.       expect(stubStyle.display).toEqual('block');
    
  306. 
    
  307.       ReactDOM.render(<div style={null} />, container);
    
  308.       expect(stubStyle.display).toEqual('');
    
  309. 
    
  310.       ReactDOM.render(<div style={styles} />, container);
    
  311.       expect(stubStyle.display).toEqual('block');
    
  312. 
    
  313.       ReactDOM.render(<div style={null} />, container);
    
  314.       expect(stubStyle.display).toEqual('');
    
  315.     });
    
  316. 
    
  317.     it('should allow named slot projection on both web components and regular DOM elements', () => {
    
  318.       const container = document.createElement('div');
    
  319. 
    
  320.       ReactDOM.render(
    
  321.         <my-component>
    
  322.           <my-second-component slot="first" />
    
  323.           <button slot="second">Hello</button>
    
  324.         </my-component>,
    
  325.         container,
    
  326.       );
    
  327. 
    
  328.       const lightDOM = container.firstChild.childNodes;
    
  329. 
    
  330.       expect(lightDOM[0].getAttribute('slot')).toBe('first');
    
  331.       expect(lightDOM[1].getAttribute('slot')).toBe('second');
    
  332.     });
    
  333. 
    
  334.     it('should skip reserved props on web components', () => {
    
  335.       const container = document.createElement('div');
    
  336. 
    
  337.       ReactDOM.render(
    
  338.         <my-component
    
  339.           children={['foo']}
    
  340.           suppressContentEditableWarning={true}
    
  341.           suppressHydrationWarning={true}
    
  342.         />,
    
  343.         container,
    
  344.       );
    
  345.       expect(container.firstChild.hasAttribute('children')).toBe(false);
    
  346.       expect(
    
  347.         container.firstChild.hasAttribute('suppressContentEditableWarning'),
    
  348.       ).toBe(false);
    
  349.       expect(
    
  350.         container.firstChild.hasAttribute('suppressHydrationWarning'),
    
  351.       ).toBe(false);
    
  352. 
    
  353.       ReactDOM.render(
    
  354.         <my-component
    
  355.           children={['bar']}
    
  356.           suppressContentEditableWarning={false}
    
  357.           suppressHydrationWarning={false}
    
  358.         />,
    
  359.         container,
    
  360.       );
    
  361.       expect(container.firstChild.hasAttribute('children')).toBe(false);
    
  362.       expect(
    
  363.         container.firstChild.hasAttribute('suppressContentEditableWarning'),
    
  364.       ).toBe(false);
    
  365.       expect(
    
  366.         container.firstChild.hasAttribute('suppressHydrationWarning'),
    
  367.       ).toBe(false);
    
  368.     });
    
  369. 
    
  370.     it('should skip dangerouslySetInnerHTML on web components', () => {
    
  371.       const container = document.createElement('div');
    
  372. 
    
  373.       ReactDOM.render(
    
  374.         <my-component dangerouslySetInnerHTML={{__html: 'hi'}} />,
    
  375.         container,
    
  376.       );
    
  377.       expect(container.firstChild.hasAttribute('dangerouslySetInnerHTML')).toBe(
    
  378.         false,
    
  379.       );
    
  380. 
    
  381.       ReactDOM.render(
    
  382.         <my-component dangerouslySetInnerHTML={{__html: 'bye'}} />,
    
  383.         container,
    
  384.       );
    
  385.       expect(container.firstChild.hasAttribute('dangerouslySetInnerHTML')).toBe(
    
  386.         false,
    
  387.       );
    
  388.     });
    
  389. 
    
  390.     it('should render null and undefined as empty but print other falsy values', () => {
    
  391.       const container = document.createElement('div');
    
  392. 
    
  393.       ReactDOM.render(
    
  394.         <div dangerouslySetInnerHTML={{__html: 'textContent'}} />,
    
  395.         container,
    
  396.       );
    
  397.       expect(container.textContent).toEqual('textContent');
    
  398. 
    
  399.       ReactDOM.render(<div dangerouslySetInnerHTML={{__html: 0}} />, container);
    
  400.       expect(container.textContent).toEqual('0');
    
  401. 
    
  402.       ReactDOM.render(
    
  403.         <div dangerouslySetInnerHTML={{__html: false}} />,
    
  404.         container,
    
  405.       );
    
  406.       expect(container.textContent).toEqual('false');
    
  407. 
    
  408.       ReactDOM.render(
    
  409.         <div dangerouslySetInnerHTML={{__html: ''}} />,
    
  410.         container,
    
  411.       );
    
  412.       expect(container.textContent).toEqual('');
    
  413. 
    
  414.       ReactDOM.render(
    
  415.         <div dangerouslySetInnerHTML={{__html: null}} />,
    
  416.         container,
    
  417.       );
    
  418.       expect(container.textContent).toEqual('');
    
  419. 
    
  420.       ReactDOM.render(
    
  421.         <div dangerouslySetInnerHTML={{__html: undefined}} />,
    
  422.         container,
    
  423.       );
    
  424.       expect(container.textContent).toEqual('');
    
  425.     });
    
  426. 
    
  427.     it('should remove attributes', () => {
    
  428.       const container = document.createElement('div');
    
  429.       ReactDOM.render(<img height="17" />, container);
    
  430. 
    
  431.       expect(container.firstChild.hasAttribute('height')).toBe(true);
    
  432.       ReactDOM.render(<img />, container);
    
  433.       expect(container.firstChild.hasAttribute('height')).toBe(false);
    
  434.     });
    
  435. 
    
  436.     it('should remove properties', () => {
    
  437.       const container = document.createElement('div');
    
  438.       ReactDOM.render(<div className="monkey" />, container);
    
  439. 
    
  440.       expect(container.firstChild.className).toEqual('monkey');
    
  441.       ReactDOM.render(<div />, container);
    
  442.       expect(container.firstChild.className).toEqual('');
    
  443.     });
    
  444. 
    
  445.     it('should not set null/undefined attributes', () => {
    
  446.       const container = document.createElement('div');
    
  447.       // Initial render.
    
  448.       ReactDOM.render(<img src={null} data-foo={undefined} />, container);
    
  449.       const node = container.firstChild;
    
  450.       expect(node.hasAttribute('src')).toBe(false);
    
  451.       expect(node.hasAttribute('data-foo')).toBe(false);
    
  452.       // Update in one direction.
    
  453.       ReactDOM.render(<img src={undefined} data-foo={null} />, container);
    
  454.       expect(node.hasAttribute('src')).toBe(false);
    
  455.       expect(node.hasAttribute('data-foo')).toBe(false);
    
  456.       // Update in another direction.
    
  457.       ReactDOM.render(<img src={null} data-foo={undefined} />, container);
    
  458.       expect(node.hasAttribute('src')).toBe(false);
    
  459.       expect(node.hasAttribute('data-foo')).toBe(false);
    
  460.       // Removal.
    
  461.       ReactDOM.render(<img />, container);
    
  462.       expect(node.hasAttribute('src')).toBe(false);
    
  463.       expect(node.hasAttribute('data-foo')).toBe(false);
    
  464.       // Addition.
    
  465.       ReactDOM.render(<img src={undefined} data-foo={null} />, container);
    
  466.       expect(node.hasAttribute('src')).toBe(false);
    
  467.       expect(node.hasAttribute('data-foo')).toBe(false);
    
  468.     });
    
  469. 
    
  470.     if (ReactFeatureFlags.enableFilterEmptyStringAttributesDOM) {
    
  471.       it('should not add an empty src attribute', () => {
    
  472.         const container = document.createElement('div');
    
  473.         expect(() => ReactDOM.render(<img src="" />, container)).toErrorDev(
    
  474.           'An empty string ("") was passed to the src attribute. ' +
    
  475.             'This may cause the browser to download the whole page again over the network. ' +
    
  476.             'To fix this, either do not render the element at all ' +
    
  477.             'or pass null to src instead of an empty string.',
    
  478.         );
    
  479.         const node = container.firstChild;
    
  480.         expect(node.hasAttribute('src')).toBe(false);
    
  481. 
    
  482.         ReactDOM.render(<img src="abc" />, container);
    
  483.         expect(node.hasAttribute('src')).toBe(true);
    
  484. 
    
  485.         expect(() => ReactDOM.render(<img src="" />, container)).toErrorDev(
    
  486.           'An empty string ("") was passed to the src attribute. ' +
    
  487.             'This may cause the browser to download the whole page again over the network. ' +
    
  488.             'To fix this, either do not render the element at all ' +
    
  489.             'or pass null to src instead of an empty string.',
    
  490.         );
    
  491.         expect(node.hasAttribute('src')).toBe(false);
    
  492.       });
    
  493. 
    
  494.       it('should not add an empty href attribute', () => {
    
  495.         const container = document.createElement('div');
    
  496.         expect(() => ReactDOM.render(<link href="" />, container)).toErrorDev(
    
  497.           'An empty string ("") was passed to the href attribute. ' +
    
  498.             'To fix this, either do not render the element at all ' +
    
  499.             'or pass null to href instead of an empty string.',
    
  500.         );
    
  501.         const node = container.firstChild;
    
  502.         expect(node.hasAttribute('href')).toBe(false);
    
  503. 
    
  504.         ReactDOM.render(<link href="abc" />, container);
    
  505.         expect(node.hasAttribute('href')).toBe(true);
    
  506. 
    
  507.         expect(() => ReactDOM.render(<link href="" />, container)).toErrorDev(
    
  508.           'An empty string ("") was passed to the href attribute. ' +
    
  509.             'To fix this, either do not render the element at all ' +
    
  510.             'or pass null to href instead of an empty string.',
    
  511.         );
    
  512.         expect(node.hasAttribute('href')).toBe(false);
    
  513.       });
    
  514. 
    
  515.       it('should allow an empty action attribute', () => {
    
  516.         const container = document.createElement('div');
    
  517.         ReactDOM.render(<form action="" />, container);
    
  518.         const node = container.firstChild;
    
  519.         expect(node.getAttribute('action')).toBe('');
    
  520. 
    
  521.         ReactDOM.render(<form action="abc" />, container);
    
  522.         expect(node.hasAttribute('action')).toBe(true);
    
  523. 
    
  524.         ReactDOM.render(<form action="" />, container);
    
  525.         expect(node.getAttribute('action')).toBe('');
    
  526.       });
    
  527. 
    
  528.       it('allows empty string of a formAction to override the default of a parent', () => {
    
  529.         const container = document.createElement('div');
    
  530.         ReactDOM.render(
    
  531.           <form action="hello">
    
  532.             <button formAction="" />,
    
  533.           </form>,
    
  534.           container,
    
  535.         );
    
  536.         const node = container.firstChild.firstChild;
    
  537.         expect(node.hasAttribute('formaction')).toBe(true);
    
  538.         expect(node.getAttribute('formaction')).toBe('');
    
  539.       });
    
  540. 
    
  541.       it('should not filter attributes for custom elements', () => {
    
  542.         const container = document.createElement('div');
    
  543.         ReactDOM.render(
    
  544.           <some-custom-element action="" formAction="" href="" src="" />,
    
  545.           container,
    
  546.         );
    
  547.         const node = container.firstChild;
    
  548.         expect(node.hasAttribute('action')).toBe(true);
    
  549.         expect(node.hasAttribute('formAction')).toBe(true);
    
  550.         expect(node.hasAttribute('href')).toBe(true);
    
  551.         expect(node.hasAttribute('src')).toBe(true);
    
  552.       });
    
  553.     }
    
  554. 
    
  555.     it('should apply React-specific aliases to HTML elements', () => {
    
  556.       const container = document.createElement('div');
    
  557.       ReactDOM.render(<form acceptCharset="foo" />, container);
    
  558.       const node = container.firstChild;
    
  559.       // Test attribute initialization.
    
  560.       expect(node.getAttribute('accept-charset')).toBe('foo');
    
  561.       expect(node.hasAttribute('acceptCharset')).toBe(false);
    
  562.       // Test attribute update.
    
  563.       ReactDOM.render(<form acceptCharset="boo" />, container);
    
  564.       expect(node.getAttribute('accept-charset')).toBe('boo');
    
  565.       expect(node.hasAttribute('acceptCharset')).toBe(false);
    
  566.       // Test attribute removal by setting to null.
    
  567.       ReactDOM.render(<form acceptCharset={null} />, container);
    
  568.       expect(node.hasAttribute('accept-charset')).toBe(false);
    
  569.       expect(node.hasAttribute('acceptCharset')).toBe(false);
    
  570.       // Restore.
    
  571.       ReactDOM.render(<form acceptCharset="foo" />, container);
    
  572.       expect(node.getAttribute('accept-charset')).toBe('foo');
    
  573.       expect(node.hasAttribute('acceptCharset')).toBe(false);
    
  574.       // Test attribute removal by setting to undefined.
    
  575.       ReactDOM.render(<form acceptCharset={undefined} />, container);
    
  576.       expect(node.hasAttribute('accept-charset')).toBe(false);
    
  577.       expect(node.hasAttribute('acceptCharset')).toBe(false);
    
  578.       // Restore.
    
  579.       ReactDOM.render(<form acceptCharset="foo" />, container);
    
  580.       expect(node.getAttribute('accept-charset')).toBe('foo');
    
  581.       expect(node.hasAttribute('acceptCharset')).toBe(false);
    
  582.       // Test attribute removal.
    
  583.       ReactDOM.render(<form />, container);
    
  584.       expect(node.hasAttribute('accept-charset')).toBe(false);
    
  585.       expect(node.hasAttribute('acceptCharset')).toBe(false);
    
  586.     });
    
  587. 
    
  588.     it('should apply React-specific aliases to SVG elements', () => {
    
  589.       const container = document.createElement('div');
    
  590.       ReactDOM.render(<svg arabicForm="foo" />, container);
    
  591.       const node = container.firstChild;
    
  592.       // Test attribute initialization.
    
  593.       expect(node.getAttribute('arabic-form')).toBe('foo');
    
  594.       expect(node.hasAttribute('arabicForm')).toBe(false);
    
  595.       // Test attribute update.
    
  596.       ReactDOM.render(<svg arabicForm="boo" />, container);
    
  597.       expect(node.getAttribute('arabic-form')).toBe('boo');
    
  598.       expect(node.hasAttribute('arabicForm')).toBe(false);
    
  599.       // Test attribute removal by setting to null.
    
  600.       ReactDOM.render(<svg arabicForm={null} />, container);
    
  601.       expect(node.hasAttribute('arabic-form')).toBe(false);
    
  602.       expect(node.hasAttribute('arabicForm')).toBe(false);
    
  603.       // Restore.
    
  604.       ReactDOM.render(<svg arabicForm="foo" />, container);
    
  605.       expect(node.getAttribute('arabic-form')).toBe('foo');
    
  606.       expect(node.hasAttribute('arabicForm')).toBe(false);
    
  607.       // Test attribute removal by setting to undefined.
    
  608.       ReactDOM.render(<svg arabicForm={undefined} />, container);
    
  609.       expect(node.hasAttribute('arabic-form')).toBe(false);
    
  610.       expect(node.hasAttribute('arabicForm')).toBe(false);
    
  611.       // Restore.
    
  612.       ReactDOM.render(<svg arabicForm="foo" />, container);
    
  613.       expect(node.getAttribute('arabic-form')).toBe('foo');
    
  614.       expect(node.hasAttribute('arabicForm')).toBe(false);
    
  615.       // Test attribute removal.
    
  616.       ReactDOM.render(<svg />, container);
    
  617.       expect(node.hasAttribute('arabic-form')).toBe(false);
    
  618.       expect(node.hasAttribute('arabicForm')).toBe(false);
    
  619.     });
    
  620. 
    
  621.     it('should properly update custom attributes on custom elements', () => {
    
  622.       const container = document.createElement('div');
    
  623.       ReactDOM.render(<some-custom-element foo="bar" />, container);
    
  624.       ReactDOM.render(<some-custom-element bar="buzz" />, container);
    
  625.       const node = container.firstChild;
    
  626.       expect(node.hasAttribute('foo')).toBe(false);
    
  627.       expect(node.getAttribute('bar')).toBe('buzz');
    
  628.     });
    
  629. 
    
  630.     it('should not apply React-specific aliases to custom elements', () => {
    
  631.       const container = document.createElement('div');
    
  632.       ReactDOM.render(<some-custom-element arabicForm="foo" />, container);
    
  633.       const node = container.firstChild;
    
  634.       // Should not get transformed to arabic-form as SVG would be.
    
  635.       expect(node.getAttribute('arabicForm')).toBe('foo');
    
  636.       expect(node.hasAttribute('arabic-form')).toBe(false);
    
  637.       // Test attribute update.
    
  638.       ReactDOM.render(<some-custom-element arabicForm="boo" />, container);
    
  639.       expect(node.getAttribute('arabicForm')).toBe('boo');
    
  640.       // Test attribute removal and addition.
    
  641.       ReactDOM.render(<some-custom-element acceptCharset="buzz" />, container);
    
  642.       // Verify the previous attribute was removed.
    
  643.       expect(node.hasAttribute('arabicForm')).toBe(false);
    
  644.       // Should not get transformed to accept-charset as HTML would be.
    
  645.       expect(node.getAttribute('acceptCharset')).toBe('buzz');
    
  646.       expect(node.hasAttribute('accept-charset')).toBe(false);
    
  647.     });
    
  648. 
    
  649.     it('should clear a single style prop when changing `style`', () => {
    
  650.       let styles = {display: 'none', color: 'red'};
    
  651.       const container = document.createElement('div');
    
  652.       ReactDOM.render(<div style={styles} />, container);
    
  653. 
    
  654.       const stubStyle = container.firstChild.style;
    
  655. 
    
  656.       styles = {color: 'green'};
    
  657.       ReactDOM.render(<div style={styles} />, container);
    
  658.       expect(stubStyle.display).toEqual('');
    
  659.       expect(stubStyle.color).toEqual('green');
    
  660.     });
    
  661. 
    
  662.     it('should reject attribute key injection attack on markup for regular DOM (SSR)', () => {
    
  663.       expect(() => {
    
  664.         for (let i = 0; i < 3; i++) {
    
  665.           const element1 = React.createElement(
    
  666.             'div',
    
  667.             {'blah" onclick="beevil" noise="hi': 'selected'},
    
  668.             null,
    
  669.           );
    
  670.           const element2 = React.createElement(
    
  671.             'div',
    
  672.             {'></div><script>alert("hi")</script>': 'selected'},
    
  673.             null,
    
  674.           );
    
  675.           const result1 = ReactDOMServer.renderToString(element1);
    
  676.           const result2 = ReactDOMServer.renderToString(element2);
    
  677.           expect(result1.toLowerCase()).not.toContain('onclick');
    
  678.           expect(result2.toLowerCase()).not.toContain('script');
    
  679.         }
    
  680.       }).toErrorDev([
    
  681.         'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
    
  682.         'Warning: Invalid attribute name: `></div><script>alert("hi")</script>`',
    
  683.       ]);
    
  684.     });
    
  685. 
    
  686.     it('should reject attribute key injection attack on markup for custom elements (SSR)', () => {
    
  687.       expect(() => {
    
  688.         for (let i = 0; i < 3; i++) {
    
  689.           const element1 = React.createElement(
    
  690.             'x-foo-component',
    
  691.             {'blah" onclick="beevil" noise="hi': 'selected'},
    
  692.             null,
    
  693.           );
    
  694.           const element2 = React.createElement(
    
  695.             'x-foo-component',
    
  696.             {'></x-foo-component><script>alert("hi")</script>': 'selected'},
    
  697.             null,
    
  698.           );
    
  699.           const result1 = ReactDOMServer.renderToString(element1);
    
  700.           const result2 = ReactDOMServer.renderToString(element2);
    
  701.           expect(result1.toLowerCase()).not.toContain('onclick');
    
  702.           expect(result2.toLowerCase()).not.toContain('script');
    
  703.         }
    
  704.       }).toErrorDev([
    
  705.         'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
    
  706.         'Warning: Invalid attribute name: `></x-foo-component><script>alert("hi")</script>`',
    
  707.       ]);
    
  708.     });
    
  709. 
    
  710.     it('should reject attribute key injection attack on mount for regular DOM', () => {
    
  711.       expect(() => {
    
  712.         for (let i = 0; i < 3; i++) {
    
  713.           const container = document.createElement('div');
    
  714.           ReactDOM.render(
    
  715.             React.createElement(
    
  716.               'div',
    
  717.               {'blah" onclick="beevil" noise="hi': 'selected'},
    
  718.               null,
    
  719.             ),
    
  720.             container,
    
  721.           );
    
  722.           expect(container.firstChild.attributes.length).toBe(0);
    
  723.           ReactDOM.unmountComponentAtNode(container);
    
  724.           ReactDOM.render(
    
  725.             React.createElement(
    
  726.               'div',
    
  727.               {'></div><script>alert("hi")</script>': 'selected'},
    
  728.               null,
    
  729.             ),
    
  730.             container,
    
  731.           );
    
  732.           expect(container.firstChild.attributes.length).toBe(0);
    
  733.         }
    
  734.       }).toErrorDev([
    
  735.         'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
    
  736.         'Warning: Invalid attribute name: `></div><script>alert("hi")</script>`',
    
  737.       ]);
    
  738.     });
    
  739. 
    
  740.     it('should reject attribute key injection attack on mount for custom elements', () => {
    
  741.       expect(() => {
    
  742.         for (let i = 0; i < 3; i++) {
    
  743.           const container = document.createElement('div');
    
  744.           ReactDOM.render(
    
  745.             React.createElement(
    
  746.               'x-foo-component',
    
  747.               {'blah" onclick="beevil" noise="hi': 'selected'},
    
  748.               null,
    
  749.             ),
    
  750.             container,
    
  751.           );
    
  752.           expect(container.firstChild.attributes.length).toBe(0);
    
  753.           ReactDOM.unmountComponentAtNode(container);
    
  754.           ReactDOM.render(
    
  755.             React.createElement(
    
  756.               'x-foo-component',
    
  757.               {'></x-foo-component><script>alert("hi")</script>': 'selected'},
    
  758.               null,
    
  759.             ),
    
  760.             container,
    
  761.           );
    
  762.           expect(container.firstChild.attributes.length).toBe(0);
    
  763.         }
    
  764.       }).toErrorDev([
    
  765.         'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
    
  766.         'Warning: Invalid attribute name: `></x-foo-component><script>alert("hi")</script>`',
    
  767.       ]);
    
  768.     });
    
  769. 
    
  770.     it('should reject attribute key injection attack on update for regular DOM', () => {
    
  771.       expect(() => {
    
  772.         for (let i = 0; i < 3; i++) {
    
  773.           const container = document.createElement('div');
    
  774.           const beforeUpdate = React.createElement('div', {}, null);
    
  775.           ReactDOM.render(beforeUpdate, container);
    
  776.           ReactDOM.render(
    
  777.             React.createElement(
    
  778.               'div',
    
  779.               {'blah" onclick="beevil" noise="hi': 'selected'},
    
  780.               null,
    
  781.             ),
    
  782.             container,
    
  783.           );
    
  784.           expect(container.firstChild.attributes.length).toBe(0);
    
  785.           ReactDOM.render(
    
  786.             React.createElement(
    
  787.               'div',
    
  788.               {'></div><script>alert("hi")</script>': 'selected'},
    
  789.               null,
    
  790.             ),
    
  791.             container,
    
  792.           );
    
  793.           expect(container.firstChild.attributes.length).toBe(0);
    
  794.         }
    
  795.       }).toErrorDev([
    
  796.         'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
    
  797.         'Warning: Invalid attribute name: `></div><script>alert("hi")</script>`',
    
  798.       ]);
    
  799.     });
    
  800. 
    
  801.     it('should reject attribute key injection attack on update for custom elements', () => {
    
  802.       expect(() => {
    
  803.         for (let i = 0; i < 3; i++) {
    
  804.           const container = document.createElement('div');
    
  805.           const beforeUpdate = React.createElement('x-foo-component', {}, null);
    
  806.           ReactDOM.render(beforeUpdate, container);
    
  807.           ReactDOM.render(
    
  808.             React.createElement(
    
  809.               'x-foo-component',
    
  810.               {'blah" onclick="beevil" noise="hi': 'selected'},
    
  811.               null,
    
  812.             ),
    
  813.             container,
    
  814.           );
    
  815.           expect(container.firstChild.attributes.length).toBe(0);
    
  816.           ReactDOM.render(
    
  817.             React.createElement(
    
  818.               'x-foo-component',
    
  819.               {'></x-foo-component><script>alert("hi")</script>': 'selected'},
    
  820.               null,
    
  821.             ),
    
  822.             container,
    
  823.           );
    
  824.           expect(container.firstChild.attributes.length).toBe(0);
    
  825.         }
    
  826.       }).toErrorDev([
    
  827.         'Warning: Invalid attribute name: `blah" onclick="beevil" noise="hi`',
    
  828.         'Warning: Invalid attribute name: `></x-foo-component><script>alert("hi")</script>`',
    
  829.       ]);
    
  830.     });
    
  831. 
    
  832.     it('should update arbitrary attributes for tags containing dashes', () => {
    
  833.       const container = document.createElement('div');
    
  834. 
    
  835.       const beforeUpdate = React.createElement('x-foo-component', {}, null);
    
  836.       ReactDOM.render(beforeUpdate, container);
    
  837. 
    
  838.       const afterUpdate = <x-foo-component myattr="myval" />;
    
  839.       ReactDOM.render(afterUpdate, container);
    
  840. 
    
  841.       expect(container.childNodes[0].getAttribute('myattr')).toBe('myval');
    
  842.     });
    
  843. 
    
  844.     it('should clear all the styles when removing `style`', () => {
    
  845.       const styles = {display: 'none', color: 'red'};
    
  846.       const container = document.createElement('div');
    
  847.       ReactDOM.render(<div style={styles} />, container);
    
  848. 
    
  849.       const stubStyle = container.firstChild.style;
    
  850. 
    
  851.       ReactDOM.render(<div />, container);
    
  852.       expect(stubStyle.display).toEqual('');
    
  853.       expect(stubStyle.color).toEqual('');
    
  854.     });
    
  855. 
    
  856.     it('should update styles when `style` changes from null to object', () => {
    
  857.       const container = document.createElement('div');
    
  858.       const styles = {color: 'red'};
    
  859.       ReactDOM.render(<div style={styles} />, container);
    
  860.       ReactDOM.render(<div />, container);
    
  861.       ReactDOM.render(<div style={styles} />, container);
    
  862. 
    
  863.       const stubStyle = container.firstChild.style;
    
  864.       expect(stubStyle.color).toEqual('red');
    
  865.     });
    
  866. 
    
  867.     it('should not reset innerHTML for when children is null', () => {
    
  868.       const container = document.createElement('div');
    
  869.       ReactDOM.render(<div />, container);
    
  870.       container.firstChild.innerHTML = 'bonjour';
    
  871.       expect(container.firstChild.innerHTML).toEqual('bonjour');
    
  872. 
    
  873.       ReactDOM.render(<div />, container);
    
  874.       expect(container.firstChild.innerHTML).toEqual('bonjour');
    
  875.     });
    
  876. 
    
  877.     it('should reset innerHTML when switching from a direct text child to an empty child', () => {
    
  878.       const transitionToValues = [null, undefined, false];
    
  879.       transitionToValues.forEach(transitionToValue => {
    
  880.         const container = document.createElement('div');
    
  881.         ReactDOM.render(<div>bonjour</div>, container);
    
  882.         expect(container.firstChild.innerHTML).toEqual('bonjour');
    
  883. 
    
  884.         ReactDOM.render(<div>{transitionToValue}</div>, container);
    
  885.         expect(container.firstChild.innerHTML).toEqual('');
    
  886.       });
    
  887.     });
    
  888. 
    
  889.     it('should empty element when removing innerHTML', () => {
    
  890.       const container = document.createElement('div');
    
  891.       ReactDOM.render(
    
  892.         <div dangerouslySetInnerHTML={{__html: ':)'}} />,
    
  893.         container,
    
  894.       );
    
  895. 
    
  896.       expect(container.firstChild.innerHTML).toEqual(':)');
    
  897.       ReactDOM.render(<div />, container);
    
  898.       expect(container.firstChild.innerHTML).toEqual('');
    
  899.     });
    
  900. 
    
  901.     it('should transition from string content to innerHTML', () => {
    
  902.       const container = document.createElement('div');
    
  903.       ReactDOM.render(<div>hello</div>, container);
    
  904. 
    
  905.       expect(container.firstChild.innerHTML).toEqual('hello');
    
  906.       ReactDOM.render(
    
  907.         <div dangerouslySetInnerHTML={{__html: 'goodbye'}} />,
    
  908.         container,
    
  909.       );
    
  910.       expect(container.firstChild.innerHTML).toEqual('goodbye');
    
  911.     });
    
  912. 
    
  913.     it('should transition from innerHTML to string content', () => {
    
  914.       const container = document.createElement('div');
    
  915.       ReactDOM.render(
    
  916.         <div dangerouslySetInnerHTML={{__html: 'bonjour'}} />,
    
  917.         container,
    
  918.       );
    
  919. 
    
  920.       expect(container.firstChild.innerHTML).toEqual('bonjour');
    
  921.       ReactDOM.render(<div>adieu</div>, container);
    
  922.       expect(container.firstChild.innerHTML).toEqual('adieu');
    
  923.     });
    
  924. 
    
  925.     it('should transition from innerHTML to children in nested el', () => {
    
  926.       const container = document.createElement('div');
    
  927.       ReactDOM.render(
    
  928.         <div>
    
  929.           <div dangerouslySetInnerHTML={{__html: 'bonjour'}} />
    
  930.         </div>,
    
  931.         container,
    
  932.       );
    
  933. 
    
  934.       expect(container.textContent).toEqual('bonjour');
    
  935.       ReactDOM.render(
    
  936.         <div>
    
  937.           <div>
    
  938.             <span>adieu</span>
    
  939.           </div>
    
  940.         </div>,
    
  941.         container,
    
  942.       );
    
  943.       expect(container.textContent).toEqual('adieu');
    
  944.     });
    
  945. 
    
  946.     it('should transition from children to innerHTML in nested el', () => {
    
  947.       const container = document.createElement('div');
    
  948.       ReactDOM.render(
    
  949.         <div>
    
  950.           <div>
    
  951.             <span>adieu</span>
    
  952.           </div>
    
  953.         </div>,
    
  954.         container,
    
  955.       );
    
  956. 
    
  957.       expect(container.textContent).toEqual('adieu');
    
  958.       ReactDOM.render(
    
  959.         <div>
    
  960.           <div dangerouslySetInnerHTML={{__html: 'bonjour'}} />
    
  961.         </div>,
    
  962.         container,
    
  963.       );
    
  964.       expect(container.textContent).toEqual('bonjour');
    
  965.     });
    
  966. 
    
  967.     it('should not incur unnecessary DOM mutations for attributes', () => {
    
  968.       const container = document.createElement('div');
    
  969.       ReactDOM.render(<div id="" />, container);
    
  970. 
    
  971.       const node = container.firstChild;
    
  972.       const nodeSetAttribute = node.setAttribute;
    
  973.       node.setAttribute = jest.fn();
    
  974.       node.setAttribute.mockImplementation(nodeSetAttribute);
    
  975. 
    
  976.       const nodeRemoveAttribute = node.removeAttribute;
    
  977.       node.removeAttribute = jest.fn();
    
  978.       node.removeAttribute.mockImplementation(nodeRemoveAttribute);
    
  979. 
    
  980.       ReactDOM.render(<div id="" />, container);
    
  981.       expect(node.setAttribute).toHaveBeenCalledTimes(0);
    
  982.       expect(node.removeAttribute).toHaveBeenCalledTimes(0);
    
  983. 
    
  984.       ReactDOM.render(<div id="foo" />, container);
    
  985.       expect(node.setAttribute).toHaveBeenCalledTimes(1);
    
  986.       expect(node.removeAttribute).toHaveBeenCalledTimes(0);
    
  987. 
    
  988.       ReactDOM.render(<div id="foo" />, container);
    
  989.       expect(node.setAttribute).toHaveBeenCalledTimes(1);
    
  990.       expect(node.removeAttribute).toHaveBeenCalledTimes(0);
    
  991. 
    
  992.       ReactDOM.render(<div />, container);
    
  993.       expect(node.setAttribute).toHaveBeenCalledTimes(1);
    
  994.       expect(node.removeAttribute).toHaveBeenCalledTimes(1);
    
  995. 
    
  996.       ReactDOM.render(<div id="" />, container);
    
  997.       expect(node.setAttribute).toHaveBeenCalledTimes(2);
    
  998.       expect(node.removeAttribute).toHaveBeenCalledTimes(1);
    
  999. 
    
  1000.       ReactDOM.render(<div />, container);
    
  1001.       expect(node.setAttribute).toHaveBeenCalledTimes(2);
    
  1002.       expect(node.removeAttribute).toHaveBeenCalledTimes(2);
    
  1003.     });
    
  1004. 
    
  1005.     it('should not incur unnecessary DOM mutations for string properties', () => {
    
  1006.       const container = document.createElement('div');
    
  1007.       ReactDOM.render(<div value="" />, container);
    
  1008. 
    
  1009.       const node = container.firstChild;
    
  1010. 
    
  1011.       const nodeValueSetter = jest.fn();
    
  1012. 
    
  1013.       const oldSetAttribute = node.setAttribute.bind(node);
    
  1014.       node.setAttribute = function (key, value) {
    
  1015.         oldSetAttribute(key, value);
    
  1016.         nodeValueSetter(key, value);
    
  1017.       };
    
  1018. 
    
  1019.       ReactDOM.render(<div value="foo" />, container);
    
  1020.       expect(nodeValueSetter).toHaveBeenCalledTimes(1);
    
  1021. 
    
  1022.       ReactDOM.render(<div value="foo" />, container);
    
  1023.       expect(nodeValueSetter).toHaveBeenCalledTimes(1);
    
  1024. 
    
  1025.       ReactDOM.render(<div />, container);
    
  1026.       expect(nodeValueSetter).toHaveBeenCalledTimes(1);
    
  1027. 
    
  1028.       ReactDOM.render(<div value={null} />, container);
    
  1029.       expect(nodeValueSetter).toHaveBeenCalledTimes(1);
    
  1030. 
    
  1031.       ReactDOM.render(<div value="" />, container);
    
  1032.       expect(nodeValueSetter).toHaveBeenCalledTimes(2);
    
  1033. 
    
  1034.       ReactDOM.render(<div />, container);
    
  1035.       expect(nodeValueSetter).toHaveBeenCalledTimes(2);
    
  1036.     });
    
  1037. 
    
  1038.     it('should not incur unnecessary DOM mutations for controlled string properties', () => {
    
  1039.       function onChange() {}
    
  1040.       const container = document.createElement('div');
    
  1041.       ReactDOM.render(<input value="" onChange={onChange} />, container);
    
  1042. 
    
  1043.       const node = container.firstChild;
    
  1044. 
    
  1045.       let nodeValue = '';
    
  1046.       const nodeValueSetter = jest.fn();
    
  1047.       Object.defineProperty(node, 'value', {
    
  1048.         get: function () {
    
  1049.           return nodeValue;
    
  1050.         },
    
  1051.         set: nodeValueSetter.mockImplementation(function (newValue) {
    
  1052.           nodeValue = newValue;
    
  1053.         }),
    
  1054.       });
    
  1055. 
    
  1056.       ReactDOM.render(<input value="foo" onChange={onChange} />, container);
    
  1057.       expect(nodeValueSetter).toHaveBeenCalledTimes(1);
    
  1058. 
    
  1059.       ReactDOM.render(
    
  1060.         <input value="foo" data-unrelated={true} onChange={onChange} />,
    
  1061.         container,
    
  1062.       );
    
  1063.       expect(nodeValueSetter).toHaveBeenCalledTimes(1);
    
  1064. 
    
  1065.       expect(() => {
    
  1066.         ReactDOM.render(<input onChange={onChange} />, container);
    
  1067.       }).toErrorDev(
    
  1068.         'A component is changing a controlled input to be uncontrolled. This is likely caused by ' +
    
  1069.           'the value changing from a defined to undefined, which should not happen. Decide between ' +
    
  1070.           'using a controlled or uncontrolled input element for the lifetime of the component.',
    
  1071.       );
    
  1072.       expect(nodeValueSetter).toHaveBeenCalledTimes(1);
    
  1073. 
    
  1074.       expect(() => {
    
  1075.         ReactDOM.render(<input value={null} onChange={onChange} />, container);
    
  1076.       }).toErrorDev(
    
  1077.         'value` prop on `input` should not be null. Consider using an empty string to clear the ' +
    
  1078.           'component or `undefined` for uncontrolled components.',
    
  1079.       );
    
  1080.       expect(nodeValueSetter).toHaveBeenCalledTimes(1);
    
  1081. 
    
  1082.       expect(() => {
    
  1083.         ReactDOM.render(<input value="" onChange={onChange} />, container);
    
  1084.       }).toErrorDev(
    
  1085.         ' A component is changing an uncontrolled input to be controlled. This is likely caused by ' +
    
  1086.           'the value changing from undefined to a defined value, which should not happen. Decide between ' +
    
  1087.           'using a controlled or uncontrolled input element for the lifetime of the component.',
    
  1088.       );
    
  1089.       expect(nodeValueSetter).toHaveBeenCalledTimes(2);
    
  1090. 
    
  1091.       ReactDOM.render(<input onChange={onChange} />, container);
    
  1092.       expect(nodeValueSetter).toHaveBeenCalledTimes(2);
    
  1093.     });
    
  1094. 
    
  1095.     it('should not incur unnecessary DOM mutations for boolean properties', () => {
    
  1096.       const container = document.createElement('div');
    
  1097.       ReactDOM.render(<audio muted={true} />, container);
    
  1098. 
    
  1099.       const node = container.firstChild;
    
  1100.       let nodeValue = true;
    
  1101.       const nodeValueSetter = jest.fn();
    
  1102.       Object.defineProperty(node, 'muted', {
    
  1103.         get: function () {
    
  1104.           return nodeValue;
    
  1105.         },
    
  1106.         set: nodeValueSetter.mockImplementation(function (newValue) {
    
  1107.           nodeValue = newValue;
    
  1108.         }),
    
  1109.       });
    
  1110. 
    
  1111.       ReactDOM.render(<audio muted={true} data-unrelated="yes" />, container);
    
  1112.       expect(nodeValueSetter).toHaveBeenCalledTimes(0);
    
  1113. 
    
  1114.       ReactDOM.render(<audio muted={false} data-unrelated="ok" />, container);
    
  1115.       expect(nodeValueSetter).toHaveBeenCalledTimes(1);
    
  1116.     });
    
  1117. 
    
  1118.     it('should ignore attribute list for elements with the "is" attribute', () => {
    
  1119.       const container = document.createElement('div');
    
  1120.       ReactDOM.render(<button is="test" cowabunga="chevynova" />, container);
    
  1121.       expect(container.firstChild.hasAttribute('cowabunga')).toBe(true);
    
  1122.     });
    
  1123. 
    
  1124.     it('should warn about non-string "is" attribute', () => {
    
  1125.       const container = document.createElement('div');
    
  1126.       expect(() =>
    
  1127.         ReactDOM.render(<button is={function () {}} />, container),
    
  1128.       ).toErrorDev(
    
  1129.         'Received a `function` for a string attribute `is`. If this is expected, cast ' +
    
  1130.           'the value to a string.',
    
  1131.       );
    
  1132.     });
    
  1133. 
    
  1134.     it('should not update when switching between null/undefined', () => {
    
  1135.       const container = document.createElement('div');
    
  1136.       const node = ReactDOM.render(<div />, container);
    
  1137. 
    
  1138.       const setter = jest.fn();
    
  1139.       node.setAttribute = setter;
    
  1140. 
    
  1141.       ReactDOM.render(<div dir={null} />, container);
    
  1142.       ReactDOM.render(<div dir={undefined} />, container);
    
  1143.       ReactDOM.render(<div />, container);
    
  1144.       expect(setter).toHaveBeenCalledTimes(0);
    
  1145.       ReactDOM.render(<div dir="ltr" />, container);
    
  1146.       expect(setter).toHaveBeenCalledTimes(1);
    
  1147.     });
    
  1148. 
    
  1149.     it('handles multiple child updates without interference', () => {
    
  1150.       // This test might look like it's just testing ReactMultiChild but the
    
  1151.       // last bug in this was actually in DOMChildrenOperations so this test
    
  1152.       // needs to be in some DOM-specific test file.
    
  1153.       const container = document.createElement('div');
    
  1154. 
    
  1155.       // ABCD
    
  1156.       ReactDOM.render(
    
  1157.         <div>
    
  1158.           <div key="one">
    
  1159.             <div key="A">A</div>
    
  1160.             <div key="B">B</div>
    
  1161.           </div>
    
  1162.           <div key="two">
    
  1163.             <div key="C">C</div>
    
  1164.             <div key="D">D</div>
    
  1165.           </div>
    
  1166.         </div>,
    
  1167.         container,
    
  1168.       );
    
  1169.       // BADC
    
  1170.       ReactDOM.render(
    
  1171.         <div>
    
  1172.           <div key="one">
    
  1173.             <div key="B">B</div>
    
  1174.             <div key="A">A</div>
    
  1175.           </div>
    
  1176.           <div key="two">
    
  1177.             <div key="D">D</div>
    
  1178.             <div key="C">C</div>
    
  1179.           </div>
    
  1180.         </div>,
    
  1181.         container,
    
  1182.       );
    
  1183. 
    
  1184.       expect(container.textContent).toBe('BADC');
    
  1185.     });
    
  1186.   });
    
  1187. 
    
  1188.   describe('createOpenTagMarkup', () => {
    
  1189.     function quoteRegexp(str) {
    
  1190.       return String(str).replace(/([.?*+\^$\[\]\\(){}|-])/g, '\\$1');
    
  1191.     }
    
  1192. 
    
  1193.     function expectToHaveAttribute(actual, expected) {
    
  1194.       const [attr, value] = expected;
    
  1195.       let re = '(?:^|\\s)' + attr + '=[\\\'"]';
    
  1196.       if (typeof value !== 'undefined') {
    
  1197.         re += quoteRegexp(value) + '[\\\'"]';
    
  1198.       }
    
  1199.       expect(actual).toMatch(new RegExp(re));
    
  1200.     }
    
  1201. 
    
  1202.     function genMarkup(props) {
    
  1203.       return ReactDOMServer.renderToString(<div {...props} />);
    
  1204.     }
    
  1205. 
    
  1206.     it('should generate the correct markup with className', () => {
    
  1207.       expectToHaveAttribute(genMarkup({className: 'a'}), ['class', 'a']);
    
  1208.       expectToHaveAttribute(genMarkup({className: 'a b'}), ['class', 'a b']);
    
  1209.       expectToHaveAttribute(genMarkup({className: ''}), ['class', '']);
    
  1210.     });
    
  1211. 
    
  1212.     it('should escape style names and values', () => {
    
  1213.       expectToHaveAttribute(
    
  1214.         genMarkup({
    
  1215.           style: {'b&ckground': '<3'},
    
  1216.         }),
    
  1217.         ['style', 'b&amp;ckground:&lt;3'],
    
  1218.       );
    
  1219.     });
    
  1220.   });
    
  1221. 
    
  1222.   describe('createContentMarkup', () => {
    
  1223.     function quoteRegexp(str) {
    
  1224.       return String(str).replace(/([.?*+\^$\[\]\\(){}|-])/g, '\\$1');
    
  1225.     }
    
  1226. 
    
  1227.     function genMarkup(props) {
    
  1228.       return ReactDOMServer.renderToString(<div {...props} />);
    
  1229.     }
    
  1230. 
    
  1231.     function toHaveInnerhtml(actual, expected) {
    
  1232.       const re = quoteRegexp(expected);
    
  1233.       return new RegExp(re).test(actual);
    
  1234.     }
    
  1235. 
    
  1236.     it('should handle dangerouslySetInnerHTML', () => {
    
  1237.       const innerHTML = {__html: 'testContent'};
    
  1238.       expect(
    
  1239.         toHaveInnerhtml(
    
  1240.           genMarkup({dangerouslySetInnerHTML: innerHTML}),
    
  1241.           'testContent',
    
  1242.         ),
    
  1243.       ).toBe(true);
    
  1244.     });
    
  1245.   });
    
  1246. 
    
  1247.   describe('mountComponent', () => {
    
  1248.     let mountComponent;
    
  1249. 
    
  1250.     beforeEach(() => {
    
  1251.       mountComponent = function (props) {
    
  1252.         const container = document.createElement('div');
    
  1253.         ReactDOM.render(<div {...props} />, container);
    
  1254.       };
    
  1255.     });
    
  1256. 
    
  1257.     it('should work error event on <source> element', () => {
    
  1258.       spyOnDevAndProd(console, 'log');
    
  1259.       const container = document.createElement('div');
    
  1260.       ReactDOM.render(
    
  1261.         <video>
    
  1262.           <source
    
  1263.             src="http://example.org/video"
    
  1264.             type="video/mp4"
    
  1265.             onError={e => console.log('onError called')}
    
  1266.           />
    
  1267.         </video>,
    
  1268.         container,
    
  1269.       );
    
  1270. 
    
  1271.       const errorEvent = document.createEvent('Event');
    
  1272.       errorEvent.initEvent('error', false, false);
    
  1273.       container.getElementsByTagName('source')[0].dispatchEvent(errorEvent);
    
  1274. 
    
  1275.       if (__DEV__) {
    
  1276.         expect(console.log).toHaveBeenCalledTimes(1);
    
  1277.         expect(console.log.mock.calls[0][0]).toContain('onError called');
    
  1278.       }
    
  1279.     });
    
  1280. 
    
  1281.     it('should warn for uppercased selfclosing tags', () => {
    
  1282.       class Container extends React.Component {
    
  1283.         render() {
    
  1284.           return React.createElement('BR', null);
    
  1285.         }
    
  1286.       }
    
  1287. 
    
  1288.       let returnedValue;
    
  1289. 
    
  1290.       expect(() => {
    
  1291.         returnedValue = ReactDOMServer.renderToString(<Container />);
    
  1292.       }).toErrorDev(
    
  1293.         '<BR /> is using incorrect casing. ' +
    
  1294.           'Use PascalCase for React components, ' +
    
  1295.           'or lowercase for HTML elements.',
    
  1296.       );
    
  1297.       // This includes a duplicate tag because we didn't treat this as self-closing.
    
  1298.       expect(returnedValue).toContain('</BR>');
    
  1299.     });
    
  1300. 
    
  1301.     it('should warn on upper case HTML tags, not SVG nor custom tags', () => {
    
  1302.       ReactTestUtils.renderIntoDocument(
    
  1303.         React.createElement('svg', null, React.createElement('PATH')),
    
  1304.       );
    
  1305.       ReactTestUtils.renderIntoDocument(React.createElement('CUSTOM-TAG'));
    
  1306. 
    
  1307.       expect(() =>
    
  1308.         ReactTestUtils.renderIntoDocument(React.createElement('IMG')),
    
  1309.       ).toErrorDev(
    
  1310.         '<IMG /> is using incorrect casing. ' +
    
  1311.           'Use PascalCase for React components, ' +
    
  1312.           'or lowercase for HTML elements.',
    
  1313.       );
    
  1314.     });
    
  1315. 
    
  1316.     it('should warn on props reserved for future use', () => {
    
  1317.       expect(() =>
    
  1318.         ReactTestUtils.renderIntoDocument(<div aria="hello" />),
    
  1319.       ).toErrorDev(
    
  1320.         'The `aria` attribute is reserved for future use in React. ' +
    
  1321.           'Pass individual `aria-` attributes instead.',
    
  1322.       );
    
  1323.     });
    
  1324. 
    
  1325.     it('should warn if the tag is unrecognized', () => {
    
  1326.       let realToString;
    
  1327.       try {
    
  1328.         realToString = Object.prototype.toString;
    
  1329.         const wrappedToString = function () {
    
  1330.           // Emulate browser behavior which is missing in jsdom
    
  1331.           if (this instanceof window.HTMLUnknownElement) {
    
  1332.             return '[object HTMLUnknownElement]';
    
  1333.           }
    
  1334.           return realToString.apply(this, arguments);
    
  1335.         };
    
  1336.         Object.prototype.toString = wrappedToString; // eslint-disable-line no-extend-native
    
  1337. 
    
  1338.         expect(() => ReactTestUtils.renderIntoDocument(<bar />)).toErrorDev(
    
  1339.           'The tag <bar> is unrecognized in this browser',
    
  1340.         );
    
  1341.         // Test deduplication
    
  1342.         expect(() => ReactTestUtils.renderIntoDocument(<foo />)).toErrorDev(
    
  1343.           'The tag <foo> is unrecognized in this browser',
    
  1344.         );
    
  1345.         ReactTestUtils.renderIntoDocument(<foo />);
    
  1346.         ReactTestUtils.renderIntoDocument(<time />);
    
  1347.         // Corner case. Make sure out deduplication logic doesn't break with weird tag.
    
  1348.         expect(() =>
    
  1349.           ReactTestUtils.renderIntoDocument(<hasOwnProperty />),
    
  1350.         ).toErrorDev([
    
  1351.           '<hasOwnProperty /> is using incorrect casing. ' +
    
  1352.             'Use PascalCase for React components, ' +
    
  1353.             'or lowercase for HTML elements.',
    
  1354.           'The tag <hasOwnProperty> is unrecognized in this browser',
    
  1355.         ]);
    
  1356.       } finally {
    
  1357.         Object.prototype.toString = realToString; // eslint-disable-line no-extend-native
    
  1358.       }
    
  1359.     });
    
  1360. 
    
  1361.     it('should throw on children for void elements', () => {
    
  1362.       const container = document.createElement('div');
    
  1363.       expect(() => {
    
  1364.         ReactDOM.render(<input>children</input>, container);
    
  1365.       }).toThrowError(
    
  1366.         'input is a void element tag and must neither have `children` nor ' +
    
  1367.           'use `dangerouslySetInnerHTML`.',
    
  1368.       );
    
  1369.     });
    
  1370. 
    
  1371.     it('should throw on dangerouslySetInnerHTML for void elements', () => {
    
  1372.       const container = document.createElement('div');
    
  1373.       expect(() => {
    
  1374.         ReactDOM.render(
    
  1375.           <input dangerouslySetInnerHTML={{__html: 'content'}} />,
    
  1376.           container,
    
  1377.         );
    
  1378.       }).toThrowError(
    
  1379.         'input is a void element tag and must neither have `children` nor ' +
    
  1380.           'use `dangerouslySetInnerHTML`.',
    
  1381.       );
    
  1382.     });
    
  1383. 
    
  1384.     it('should treat menuitem as a void element but still create the closing tag', () => {
    
  1385.       // menuitem is not implemented in jsdom, so this triggers the unknown warning error
    
  1386.       const container = document.createElement('div');
    
  1387. 
    
  1388.       const returnedValue = ReactDOMServer.renderToString(
    
  1389.         <menu>
    
  1390.           <menuitem />
    
  1391.         </menu>,
    
  1392.       );
    
  1393. 
    
  1394.       expect(returnedValue).toContain('</menuitem>');
    
  1395. 
    
  1396.       expect(function () {
    
  1397.         expect(() => {
    
  1398.           ReactDOM.render(
    
  1399.             <menu>
    
  1400.               <menuitem>children</menuitem>
    
  1401.             </menu>,
    
  1402.             container,
    
  1403.           );
    
  1404.         }).toErrorDev('The tag <menuitem> is unrecognized in this browser.');
    
  1405.       }).toThrowError(
    
  1406.         'menuitem is a void element tag and must neither have `children` nor use ' +
    
  1407.           '`dangerouslySetInnerHTML`.',
    
  1408.       );
    
  1409.     });
    
  1410. 
    
  1411.     it('should validate against multiple children props', () => {
    
  1412.       expect(function () {
    
  1413.         mountComponent({children: '', dangerouslySetInnerHTML: ''});
    
  1414.       }).toThrowError(
    
  1415.         '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
    
  1416.           'Please visit https://reactjs.org/link/dangerously-set-inner-html for more information.',
    
  1417.       );
    
  1418.     });
    
  1419. 
    
  1420.     it('should validate against use of innerHTML', () => {
    
  1421.       expect(() =>
    
  1422.         mountComponent({innerHTML: '<span>Hi Jim!</span>'}),
    
  1423.       ).toErrorDev('Directly setting property `innerHTML` is not permitted. ');
    
  1424.     });
    
  1425. 
    
  1426.     it('should validate against use of innerHTML without case sensitivity', () => {
    
  1427.       expect(() =>
    
  1428.         mountComponent({innerhtml: '<span>Hi Jim!</span>'}),
    
  1429.       ).toErrorDev('Directly setting property `innerHTML` is not permitted. ');
    
  1430.     });
    
  1431. 
    
  1432.     it('should validate use of dangerouslySetInnerHTML', () => {
    
  1433.       expect(function () {
    
  1434.         mountComponent({dangerouslySetInnerHTML: '<span>Hi Jim!</span>'});
    
  1435.       }).toThrowError(
    
  1436.         '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
    
  1437.           'Please visit https://reactjs.org/link/dangerously-set-inner-html for more information.',
    
  1438.       );
    
  1439.     });
    
  1440. 
    
  1441.     it('should validate use of dangerouslySetInnerHTML', () => {
    
  1442.       expect(function () {
    
  1443.         mountComponent({dangerouslySetInnerHTML: {foo: 'bar'}});
    
  1444.       }).toThrowError(
    
  1445.         '`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
    
  1446.           'Please visit https://reactjs.org/link/dangerously-set-inner-html for more information.',
    
  1447.       );
    
  1448.     });
    
  1449. 
    
  1450.     it('should allow {__html: null}', () => {
    
  1451.       expect(function () {
    
  1452.         mountComponent({dangerouslySetInnerHTML: {__html: null}});
    
  1453.       }).not.toThrow();
    
  1454.     });
    
  1455. 
    
  1456.     it('should warn about contentEditable and children', () => {
    
  1457.       expect(() =>
    
  1458.         mountComponent({contentEditable: true, children: ''}),
    
  1459.       ).toErrorDev(
    
  1460.         'Warning: A component is `contentEditable` and contains `children` ' +
    
  1461.           'managed by React. It is now your responsibility to guarantee that ' +
    
  1462.           'none of those nodes are unexpectedly modified or duplicated. This ' +
    
  1463.           'is probably not intentional.\n    in div (at **)',
    
  1464.       );
    
  1465.     });
    
  1466. 
    
  1467.     it('should respect suppressContentEditableWarning', () => {
    
  1468.       mountComponent({
    
  1469.         contentEditable: true,
    
  1470.         children: '',
    
  1471.         suppressContentEditableWarning: true,
    
  1472.       });
    
  1473.     });
    
  1474. 
    
  1475.     it('should validate against invalid styles', () => {
    
  1476.       expect(function () {
    
  1477.         mountComponent({style: 'display: none'});
    
  1478.       }).toThrowError(
    
  1479.         'The `style` prop expects a mapping from style properties to values, ' +
    
  1480.           "not a string. For example, style={{marginRight: spacing + 'em'}} " +
    
  1481.           'when using JSX.',
    
  1482.       );
    
  1483.     });
    
  1484. 
    
  1485.     it('should throw for children on void elements', () => {
    
  1486.       class X extends React.Component {
    
  1487.         render() {
    
  1488.           return <input>moo</input>;
    
  1489.         }
    
  1490.       }
    
  1491. 
    
  1492.       const container = document.createElement('div');
    
  1493.       expect(() => {
    
  1494.         ReactDOM.render(<X />, container);
    
  1495.       }).toThrowError(
    
  1496.         'input is a void element tag and must neither have `children` ' +
    
  1497.           'nor use `dangerouslySetInnerHTML`.',
    
  1498.       );
    
  1499.     });
    
  1500. 
    
  1501.     it('should support custom elements which extend native elements', () => {
    
  1502.       const container = document.createElement('div');
    
  1503.       spyOnDevAndProd(document, 'createElement');
    
  1504.       ReactDOM.render(<div is="custom-div" />, container);
    
  1505.       expect(document.createElement).toHaveBeenCalledWith('div', {
    
  1506.         is: 'custom-div',
    
  1507.       });
    
  1508.     });
    
  1509. 
    
  1510.     it('should work load and error events on <image> element in SVG', () => {
    
  1511.       spyOnDevAndProd(console, 'log');
    
  1512.       const container = document.createElement('div');
    
  1513.       ReactDOM.render(
    
  1514.         <svg>
    
  1515.           <image
    
  1516.             xlinkHref="http://example.org/image"
    
  1517.             onError={e => console.log('onError called')}
    
  1518.             onLoad={e => console.log('onLoad called')}
    
  1519.           />
    
  1520.         </svg>,
    
  1521.         container,
    
  1522.       );
    
  1523. 
    
  1524.       const loadEvent = document.createEvent('Event');
    
  1525.       const errorEvent = document.createEvent('Event');
    
  1526. 
    
  1527.       loadEvent.initEvent('load', false, false);
    
  1528.       errorEvent.initEvent('error', false, false);
    
  1529. 
    
  1530.       container.getElementsByTagName('image')[0].dispatchEvent(errorEvent);
    
  1531.       container.getElementsByTagName('image')[0].dispatchEvent(loadEvent);
    
  1532. 
    
  1533.       if (__DEV__) {
    
  1534.         expect(console.log).toHaveBeenCalledTimes(2);
    
  1535.         expect(console.log.mock.calls[0][0]).toContain('onError called');
    
  1536.         expect(console.log.mock.calls[1][0]).toContain('onLoad called');
    
  1537.       }
    
  1538.     });
    
  1539. 
    
  1540.     it('should receive a load event on <link> elements', () => {
    
  1541.       const container = document.createElement('div');
    
  1542.       const onLoad = jest.fn();
    
  1543. 
    
  1544.       ReactDOM.render(
    
  1545.         <link href="http://example.org/link" onLoad={onLoad} />,
    
  1546.         container,
    
  1547.       );
    
  1548. 
    
  1549.       const loadEvent = document.createEvent('Event');
    
  1550.       const link = container.getElementsByTagName('link')[0];
    
  1551. 
    
  1552.       loadEvent.initEvent('load', false, false);
    
  1553.       link.dispatchEvent(loadEvent);
    
  1554. 
    
  1555.       expect(onLoad).toHaveBeenCalledTimes(1);
    
  1556.     });
    
  1557. 
    
  1558.     it('should receive an error event on <link> elements', () => {
    
  1559.       const container = document.createElement('div');
    
  1560.       const onError = jest.fn();
    
  1561. 
    
  1562.       ReactDOM.render(
    
  1563.         <link href="http://example.org/link" onError={onError} />,
    
  1564.         container,
    
  1565.       );
    
  1566. 
    
  1567.       const errorEvent = document.createEvent('Event');
    
  1568.       const link = container.getElementsByTagName('link')[0];
    
  1569. 
    
  1570.       errorEvent.initEvent('error', false, false);
    
  1571.       link.dispatchEvent(errorEvent);
    
  1572. 
    
  1573.       expect(onError).toHaveBeenCalledTimes(1);
    
  1574.     });
    
  1575.   });
    
  1576. 
    
  1577.   describe('updateComponent', () => {
    
  1578.     let container;
    
  1579. 
    
  1580.     beforeEach(() => {
    
  1581.       container = document.createElement('div');
    
  1582.     });
    
  1583. 
    
  1584.     it('should warn against children for void elements', () => {
    
  1585.       ReactDOM.render(<input />, container);
    
  1586. 
    
  1587.       expect(function () {
    
  1588.         ReactDOM.render(<input>children</input>, container);
    
  1589.       }).toThrowError(
    
  1590.         'input is a void element tag and must neither have `children` nor use ' +
    
  1591.           '`dangerouslySetInnerHTML`.',
    
  1592.       );
    
  1593.     });
    
  1594. 
    
  1595.     it('should warn against dangerouslySetInnerHTML for void elements', () => {
    
  1596.       ReactDOM.render(<input />, container);
    
  1597. 
    
  1598.       expect(function () {
    
  1599.         ReactDOM.render(
    
  1600.           <input dangerouslySetInnerHTML={{__html: 'content'}} />,
    
  1601.           container,
    
  1602.         );
    
  1603.       }).toThrowError(
    
  1604.         'input is a void element tag and must neither have `children` nor use ' +
    
  1605.           '`dangerouslySetInnerHTML`.',
    
  1606.       );
    
  1607.     });
    
  1608. 
    
  1609.     it('should validate against multiple children props', () => {
    
  1610.       ReactDOM.render(<div />, container);
    
  1611. 
    
  1612.       expect(function () {
    
  1613.         ReactDOM.render(
    
  1614.           <div children="" dangerouslySetInnerHTML={{__html: ''}} />,
    
  1615.           container,
    
  1616.         );
    
  1617.       }).toThrowError(
    
  1618.         'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
    
  1619.       );
    
  1620.     });
    
  1621. 
    
  1622.     it('should warn about contentEditable and children', () => {
    
  1623.       expect(() => {
    
  1624.         ReactDOM.render(
    
  1625.           <div contentEditable={true}>
    
  1626.             <div />
    
  1627.           </div>,
    
  1628.           container,
    
  1629.         );
    
  1630.       }).toErrorDev('contentEditable');
    
  1631.     });
    
  1632. 
    
  1633.     it('should validate against invalid styles', () => {
    
  1634.       ReactDOM.render(<div />, container);
    
  1635. 
    
  1636.       expect(function () {
    
  1637.         ReactDOM.render(<div style={1} />, container);
    
  1638.       }).toThrowError(
    
  1639.         'The `style` prop expects a mapping from style properties to values, ' +
    
  1640.           "not a string. For example, style={{marginRight: spacing + 'em'}} " +
    
  1641.           'when using JSX.',
    
  1642.       );
    
  1643.     });
    
  1644. 
    
  1645.     it('should report component containing invalid styles', () => {
    
  1646.       class Animal extends React.Component {
    
  1647.         render() {
    
  1648.           return <div style={1} />;
    
  1649.         }
    
  1650.       }
    
  1651. 
    
  1652.       expect(() => {
    
  1653.         ReactDOM.render(<Animal />, container);
    
  1654.       }).toThrowError(
    
  1655.         'The `style` prop expects a mapping from style properties to values, ' +
    
  1656.           "not a string. For example, style={{marginRight: spacing + 'em'}} " +
    
  1657.           'when using JSX.',
    
  1658.       );
    
  1659.     });
    
  1660. 
    
  1661.     it('should properly escape text content and attributes values', () => {
    
  1662.       expect(
    
  1663.         ReactDOMServer.renderToStaticMarkup(
    
  1664.           React.createElement(
    
  1665.             'div',
    
  1666.             {
    
  1667.               title: '\'"<>&',
    
  1668.               style: {
    
  1669.                 textAlign: '\'"<>&',
    
  1670.               },
    
  1671.             },
    
  1672.             '\'"<>&',
    
  1673.           ),
    
  1674.         ),
    
  1675.       ).toBe(
    
  1676.         '<div title="&#x27;&quot;&lt;&gt;&amp;" style="text-align:&#x27;&quot;&lt;&gt;&amp;">' +
    
  1677.           '&#x27;&quot;&lt;&gt;&amp;' +
    
  1678.           '</div>',
    
  1679.       );
    
  1680.     });
    
  1681.   });
    
  1682. 
    
  1683.   describe('unmountComponent', () => {
    
  1684.     it('unmounts children before unsetting DOM node info', () => {
    
  1685.       class Inner extends React.Component {
    
  1686.         render() {
    
  1687.           return <span />;
    
  1688.         }
    
  1689. 
    
  1690.         componentWillUnmount() {
    
  1691.           // Should not throw
    
  1692.           expect(ReactDOM.findDOMNode(this).nodeName).toBe('SPAN');
    
  1693.         }
    
  1694.       }
    
  1695. 
    
  1696.       const container = document.createElement('div');
    
  1697.       ReactDOM.render(
    
  1698.         <div>
    
  1699.           <Inner />
    
  1700.         </div>,
    
  1701.         container,
    
  1702.       );
    
  1703.       ReactDOM.unmountComponentAtNode(container);
    
  1704.     });
    
  1705.   });
    
  1706. 
    
  1707.   describe('tag sanitization', () => {
    
  1708.     it('should throw when an invalid tag name is used server-side', () => {
    
  1709.       const hackzor = React.createElement('script tag');
    
  1710.       expect(() => ReactDOMServer.renderToString(hackzor)).toThrowError(
    
  1711.         'Invalid tag: script tag',
    
  1712.       );
    
  1713.     });
    
  1714. 
    
  1715.     it('should throw when an attack vector is used server-side', () => {
    
  1716.       const hackzor = React.createElement('div><img /><div');
    
  1717.       expect(() => ReactDOMServer.renderToString(hackzor)).toThrowError(
    
  1718.         'Invalid tag: div><img /><div',
    
  1719.       );
    
  1720.     });
    
  1721. 
    
  1722.     it('should throw when an invalid tag name is used', () => {
    
  1723.       const hackzor = React.createElement('script tag');
    
  1724.       expect(() => ReactTestUtils.renderIntoDocument(hackzor)).toThrow();
    
  1725.     });
    
  1726. 
    
  1727.     it('should throw when an attack vector is used', () => {
    
  1728.       const hackzor = React.createElement('div><img /><div');
    
  1729.       expect(() => ReactTestUtils.renderIntoDocument(hackzor)).toThrow();
    
  1730.     });
    
  1731.   });
    
  1732. 
    
  1733.   describe('nesting validation', () => {
    
  1734.     it('warns on invalid nesting', () => {
    
  1735.       expect(() => {
    
  1736.         ReactTestUtils.renderIntoDocument(
    
  1737.           <div>
    
  1738.             <tr />
    
  1739.             <tr />
    
  1740.           </div>,
    
  1741.         );
    
  1742.       }).toErrorDev([
    
  1743.         'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
    
  1744.           '<div>.' +
    
  1745.           '\n    in tr (at **)' +
    
  1746.           '\n    in div (at **)',
    
  1747.       ]);
    
  1748.     });
    
  1749. 
    
  1750.     it('warns on invalid nesting at root', () => {
    
  1751.       const p = document.createElement('p');
    
  1752. 
    
  1753.       expect(() => {
    
  1754.         ReactDOM.render(
    
  1755.           <span>
    
  1756.             <p />
    
  1757.           </span>,
    
  1758.           p,
    
  1759.         );
    
  1760.       }).toErrorDev(
    
  1761.         'Warning: validateDOMNesting(...): <p> cannot appear as a descendant ' +
    
  1762.           'of <p>.' +
    
  1763.           // There is no outer `p` here because root container is not part of the stack.
    
  1764.           '\n    in p (at **)' +
    
  1765.           '\n    in span (at **)',
    
  1766.       );
    
  1767.     });
    
  1768. 
    
  1769.     it('warns nicely for table rows', () => {
    
  1770.       class Row extends React.Component {
    
  1771.         render() {
    
  1772.           return <tr>x</tr>;
    
  1773.         }
    
  1774.       }
    
  1775. 
    
  1776.       class Foo extends React.Component {
    
  1777.         render() {
    
  1778.           return (
    
  1779.             <table>
    
  1780.               <Row />{' '}
    
  1781.             </table>
    
  1782.           );
    
  1783.         }
    
  1784.       }
    
  1785. 
    
  1786.       expect(() => ReactTestUtils.renderIntoDocument(<Foo />)).toErrorDev([
    
  1787.         'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
    
  1788.           '<table>. Add a <tbody>, <thead> or <tfoot> to your code to match the DOM tree generated ' +
    
  1789.           'by the browser.' +
    
  1790.           '\n    in tr (at **)' +
    
  1791.           '\n    in Row (at **)' +
    
  1792.           '\n    in table (at **)' +
    
  1793.           '\n    in Foo (at **)',
    
  1794.         'Warning: validateDOMNesting(...): Text nodes cannot appear as a ' +
    
  1795.           'child of <tr>.' +
    
  1796.           '\n    in tr (at **)' +
    
  1797.           '\n    in Row (at **)' +
    
  1798.           '\n    in table (at **)' +
    
  1799.           '\n    in Foo (at **)',
    
  1800.         'Warning: validateDOMNesting(...): Whitespace text nodes cannot ' +
    
  1801.           "appear as a child of <table>. Make sure you don't have any extra " +
    
  1802.           'whitespace between tags on each line of your source code.' +
    
  1803.           '\n    in table (at **)' +
    
  1804.           '\n    in Foo (at **)',
    
  1805.       ]);
    
  1806.     });
    
  1807. 
    
  1808.     it('warns nicely for updating table rows to use text', () => {
    
  1809.       const container = document.createElement('div');
    
  1810. 
    
  1811.       function Row({children}) {
    
  1812.         return <tr>{children}</tr>;
    
  1813.       }
    
  1814. 
    
  1815.       function Foo({children}) {
    
  1816.         return <table>{children}</table>;
    
  1817.       }
    
  1818. 
    
  1819.       // First is fine.
    
  1820.       ReactDOM.render(<Foo />, container);
    
  1821. 
    
  1822.       expect(() => ReactDOM.render(<Foo> </Foo>, container)).toErrorDev([
    
  1823.         'Warning: validateDOMNesting(...): Whitespace text nodes cannot ' +
    
  1824.           "appear as a child of <table>. Make sure you don't have any extra " +
    
  1825.           'whitespace between tags on each line of your source code.' +
    
  1826.           '\n    in table (at **)' +
    
  1827.           '\n    in Foo (at **)',
    
  1828.       ]);
    
  1829. 
    
  1830.       ReactDOM.render(
    
  1831.         <Foo>
    
  1832.           <tbody>
    
  1833.             <Row />
    
  1834.           </tbody>
    
  1835.         </Foo>,
    
  1836.         container,
    
  1837.       );
    
  1838. 
    
  1839.       expect(() =>
    
  1840.         ReactDOM.render(
    
  1841.           <Foo>
    
  1842.             <tbody>
    
  1843.               <Row>text</Row>
    
  1844.             </tbody>
    
  1845.           </Foo>,
    
  1846.           container,
    
  1847.         ),
    
  1848.       ).toErrorDev([
    
  1849.         'Warning: validateDOMNesting(...): Text nodes cannot appear as a ' +
    
  1850.           'child of <tr>.' +
    
  1851.           '\n    in tr (at **)' +
    
  1852.           '\n    in Row (at **)' +
    
  1853.           '\n    in tbody (at **)' +
    
  1854.           '\n    in table (at **)' +
    
  1855.           '\n    in Foo (at **)',
    
  1856.       ]);
    
  1857.     });
    
  1858. 
    
  1859.     it('gives useful context in warnings', () => {
    
  1860.       function Row() {
    
  1861.         return <tr />;
    
  1862.       }
    
  1863.       function FancyRow() {
    
  1864.         return <Row />;
    
  1865.       }
    
  1866. 
    
  1867.       function Viz1() {
    
  1868.         return (
    
  1869.           <table>
    
  1870.             <FancyRow />
    
  1871.           </table>
    
  1872.         );
    
  1873.       }
    
  1874.       function App1() {
    
  1875.         return <Viz1 />;
    
  1876.       }
    
  1877.       expect(() => ReactTestUtils.renderIntoDocument(<App1 />)).toErrorDev(
    
  1878.         '\n    in tr (at **)' +
    
  1879.           '\n    in Row (at **)' +
    
  1880.           '\n    in FancyRow (at **)' +
    
  1881.           '\n    in table (at **)' +
    
  1882.           '\n    in Viz1 (at **)',
    
  1883.       );
    
  1884.     });
    
  1885. 
    
  1886.     it('gives useful context in warnings 2', () => {
    
  1887.       function Row() {
    
  1888.         return <tr />;
    
  1889.       }
    
  1890.       function FancyRow() {
    
  1891.         return <Row />;
    
  1892.       }
    
  1893. 
    
  1894.       class Table extends React.Component {
    
  1895.         render() {
    
  1896.           return <table>{this.props.children}</table>;
    
  1897.         }
    
  1898.       }
    
  1899. 
    
  1900.       class FancyTable extends React.Component {
    
  1901.         render() {
    
  1902.           return <Table>{this.props.children}</Table>;
    
  1903.         }
    
  1904.       }
    
  1905. 
    
  1906.       function Viz2() {
    
  1907.         return (
    
  1908.           <FancyTable>
    
  1909.             <FancyRow />
    
  1910.           </FancyTable>
    
  1911.         );
    
  1912.       }
    
  1913.       function App2() {
    
  1914.         return <Viz2 />;
    
  1915.       }
    
  1916.       expect(() => ReactTestUtils.renderIntoDocument(<App2 />)).toErrorDev(
    
  1917.         '\n    in tr (at **)' +
    
  1918.           '\n    in Row (at **)' +
    
  1919.           '\n    in FancyRow (at **)' +
    
  1920.           '\n    in table (at **)' +
    
  1921.           '\n    in Table (at **)' +
    
  1922.           '\n    in FancyTable (at **)' +
    
  1923.           '\n    in Viz2 (at **)',
    
  1924.       );
    
  1925.     });
    
  1926. 
    
  1927.     it('gives useful context in warnings 3', () => {
    
  1928.       function Row() {
    
  1929.         return <tr />;
    
  1930.       }
    
  1931.       function FancyRow() {
    
  1932.         return <Row />;
    
  1933.       }
    
  1934. 
    
  1935.       class Table extends React.Component {
    
  1936.         render() {
    
  1937.           return <table>{this.props.children}</table>;
    
  1938.         }
    
  1939.       }
    
  1940. 
    
  1941.       class FancyTable extends React.Component {
    
  1942.         render() {
    
  1943.           return <Table>{this.props.children}</Table>;
    
  1944.         }
    
  1945.       }
    
  1946.       expect(() => {
    
  1947.         ReactTestUtils.renderIntoDocument(
    
  1948.           <FancyTable>
    
  1949.             <FancyRow />
    
  1950.           </FancyTable>,
    
  1951.         );
    
  1952.       }).toErrorDev(
    
  1953.         '\n    in tr (at **)' +
    
  1954.           '\n    in Row (at **)' +
    
  1955.           '\n    in FancyRow (at **)' +
    
  1956.           '\n    in table (at **)' +
    
  1957.           '\n    in Table (at **)' +
    
  1958.           '\n    in FancyTable (at **)',
    
  1959.       );
    
  1960.     });
    
  1961. 
    
  1962.     it('gives useful context in warnings 4', () => {
    
  1963.       function Row() {
    
  1964.         return <tr />;
    
  1965.       }
    
  1966.       function FancyRow() {
    
  1967.         return <Row />;
    
  1968.       }
    
  1969. 
    
  1970.       expect(() => {
    
  1971.         ReactTestUtils.renderIntoDocument(
    
  1972.           <table>
    
  1973.             <FancyRow />
    
  1974.           </table>,
    
  1975.         );
    
  1976.       }).toErrorDev(
    
  1977.         '\n    in tr (at **)' +
    
  1978.           '\n    in Row (at **)' +
    
  1979.           '\n    in FancyRow (at **)' +
    
  1980.           '\n    in table (at **)',
    
  1981.       );
    
  1982.     });
    
  1983. 
    
  1984.     it('gives useful context in warnings 5', () => {
    
  1985.       class Table extends React.Component {
    
  1986.         render() {
    
  1987.           return <table>{this.props.children}</table>;
    
  1988.         }
    
  1989.       }
    
  1990. 
    
  1991.       class FancyTable extends React.Component {
    
  1992.         render() {
    
  1993.           return <Table>{this.props.children}</Table>;
    
  1994.         }
    
  1995.       }
    
  1996. 
    
  1997.       expect(() => {
    
  1998.         ReactTestUtils.renderIntoDocument(
    
  1999.           <FancyTable>
    
  2000.             <tr />
    
  2001.           </FancyTable>,
    
  2002.         );
    
  2003.       }).toErrorDev(
    
  2004.         '\n    in tr (at **)' +
    
  2005.           '\n    in table (at **)' +
    
  2006.           '\n    in Table (at **)' +
    
  2007.           '\n    in FancyTable (at **)',
    
  2008.       );
    
  2009. 
    
  2010.       class Link extends React.Component {
    
  2011.         render() {
    
  2012.           return <a>{this.props.children}</a>;
    
  2013.         }
    
  2014.       }
    
  2015. 
    
  2016.       expect(() => {
    
  2017.         ReactTestUtils.renderIntoDocument(
    
  2018.           <Link>
    
  2019.             <div>
    
  2020.               <Link />
    
  2021.             </div>
    
  2022.           </Link>,
    
  2023.         );
    
  2024.       }).toErrorDev(
    
  2025.         '\n    in a (at **)' +
    
  2026.           '\n    in Link (at **)' +
    
  2027.           '\n    in div (at **)' +
    
  2028.           '\n    in a (at **)' +
    
  2029.           '\n    in Link (at **)',
    
  2030.       );
    
  2031.     });
    
  2032. 
    
  2033.     it('should warn about incorrect casing on properties (ssr)', () => {
    
  2034.       expect(() => {
    
  2035.         ReactDOMServer.renderToString(
    
  2036.           React.createElement('input', {type: 'text', tabindex: '1'}),
    
  2037.         );
    
  2038.       }).toErrorDev('tabIndex');
    
  2039.     });
    
  2040. 
    
  2041.     it('should warn about incorrect casing on event handlers (ssr)', () => {
    
  2042.       expect(() => {
    
  2043.         ReactDOMServer.renderToString(
    
  2044.           React.createElement('input', {type: 'text', oninput: '1'}),
    
  2045.         );
    
  2046.       }).toErrorDev(
    
  2047.         'Invalid event handler property `oninput`. ' +
    
  2048.           'React events use the camelCase naming convention, ' +
    
  2049.           // Note: we don't know the right event name so we
    
  2050.           // use a generic one (onClick) as a suggestion.
    
  2051.           // This is because we don't bundle the event system
    
  2052.           // on the server.
    
  2053.           'for example `onClick`.',
    
  2054.       );
    
  2055.       ReactDOMServer.renderToString(
    
  2056.         React.createElement('input', {type: 'text', onKeydown: '1'}),
    
  2057.       );
    
  2058.       // We can't warn for `onKeydown` on the server because
    
  2059.       // there is no way tell if this is a valid event or not
    
  2060.       // without access to the event system (which we don't bundle).
    
  2061.     });
    
  2062. 
    
  2063.     it('should warn about incorrect casing on properties', () => {
    
  2064.       expect(() => {
    
  2065.         ReactTestUtils.renderIntoDocument(
    
  2066.           React.createElement('input', {type: 'text', tabindex: '1'}),
    
  2067.         );
    
  2068.       }).toErrorDev('tabIndex');
    
  2069.     });
    
  2070. 
    
  2071.     it('should warn about incorrect casing on event handlers', () => {
    
  2072.       expect(() => {
    
  2073.         ReactTestUtils.renderIntoDocument(
    
  2074.           React.createElement('input', {type: 'text', oninput: '1'}),
    
  2075.         );
    
  2076.       }).toErrorDev('onInput');
    
  2077.       expect(() => {
    
  2078.         ReactTestUtils.renderIntoDocument(
    
  2079.           React.createElement('input', {type: 'text', onKeydown: '1'}),
    
  2080.         );
    
  2081.       }).toErrorDev('onKeyDown');
    
  2082.     });
    
  2083. 
    
  2084.     it('should warn about class', () => {
    
  2085.       expect(() => {
    
  2086.         ReactTestUtils.renderIntoDocument(
    
  2087.           React.createElement('div', {class: 'muffins'}),
    
  2088.         );
    
  2089.       }).toErrorDev('className');
    
  2090.     });
    
  2091. 
    
  2092.     it('should warn about class (ssr)', () => {
    
  2093.       expect(() => {
    
  2094.         ReactDOMServer.renderToString(
    
  2095.           React.createElement('div', {class: 'muffins'}),
    
  2096.         );
    
  2097.       }).toErrorDev('className');
    
  2098.     });
    
  2099. 
    
  2100.     it('should warn about props that are no longer supported', () => {
    
  2101.       ReactTestUtils.renderIntoDocument(<div />);
    
  2102. 
    
  2103.       expect(() =>
    
  2104.         ReactTestUtils.renderIntoDocument(<div onFocusIn={() => {}} />),
    
  2105.       ).toErrorDev(
    
  2106.         'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
    
  2107.       );
    
  2108.       expect(() =>
    
  2109.         ReactTestUtils.renderIntoDocument(<div onFocusOut={() => {}} />),
    
  2110.       ).toErrorDev(
    
  2111.         'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
    
  2112.       );
    
  2113.     });
    
  2114. 
    
  2115.     it('should warn about props that are no longer supported without case sensitivity', () => {
    
  2116.       ReactTestUtils.renderIntoDocument(<div />);
    
  2117.       expect(() =>
    
  2118.         ReactTestUtils.renderIntoDocument(<div onfocusin={() => {}} />),
    
  2119.       ).toErrorDev(
    
  2120.         'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
    
  2121.       );
    
  2122.       expect(() =>
    
  2123.         ReactTestUtils.renderIntoDocument(<div onfocusout={() => {}} />),
    
  2124.       ).toErrorDev(
    
  2125.         'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
    
  2126.       );
    
  2127.     });
    
  2128. 
    
  2129.     it('should warn about props that are no longer supported (ssr)', () => {
    
  2130.       ReactDOMServer.renderToString(<div />);
    
  2131.       expect(() =>
    
  2132.         ReactDOMServer.renderToString(<div onFocusIn={() => {}} />),
    
  2133.       ).toErrorDev(
    
  2134.         'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
    
  2135.       );
    
  2136.       expect(() =>
    
  2137.         ReactDOMServer.renderToString(<div onFocusOut={() => {}} />),
    
  2138.       ).toErrorDev(
    
  2139.         'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
    
  2140.       );
    
  2141.     });
    
  2142. 
    
  2143.     it('should warn about props that are no longer supported without case sensitivity (ssr)', () => {
    
  2144.       ReactDOMServer.renderToString(<div />);
    
  2145.       expect(() =>
    
  2146.         ReactDOMServer.renderToString(<div onfocusin={() => {}} />),
    
  2147.       ).toErrorDev(
    
  2148.         'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
    
  2149.       );
    
  2150.       expect(() =>
    
  2151.         ReactDOMServer.renderToString(<div onfocusout={() => {}} />),
    
  2152.       ).toErrorDev(
    
  2153.         'React uses onFocus and onBlur instead of onFocusIn and onFocusOut.',
    
  2154.       );
    
  2155.     });
    
  2156. 
    
  2157.     it('gives source code refs for unknown prop warning', () => {
    
  2158.       expect(() =>
    
  2159.         ReactTestUtils.renderIntoDocument(<div class="paladin" />),
    
  2160.       ).toErrorDev(
    
  2161.         'Warning: Invalid DOM property `class`. Did you mean `className`?\n    in div (at **)',
    
  2162.       );
    
  2163.       expect(() =>
    
  2164.         ReactTestUtils.renderIntoDocument(<input type="text" onclick="1" />),
    
  2165.       ).toErrorDev(
    
  2166.         'Warning: Invalid event handler property `onclick`. Did you mean ' +
    
  2167.           '`onClick`?\n    in input (at **)',
    
  2168.       );
    
  2169.     });
    
  2170. 
    
  2171.     it('gives source code refs for unknown prop warning (ssr)', () => {
    
  2172.       expect(() =>
    
  2173.         ReactDOMServer.renderToString(<div class="paladin" />),
    
  2174.       ).toErrorDev(
    
  2175.         'Warning: Invalid DOM property `class`. Did you mean `className`?\n    in div (at **)',
    
  2176.       );
    
  2177.       expect(() =>
    
  2178.         ReactDOMServer.renderToString(<input type="text" oninput="1" />),
    
  2179.       ).toErrorDev(
    
  2180.         'Warning: Invalid event handler property `oninput`. ' +
    
  2181.           // Note: we don't know the right event name so we
    
  2182.           // use a generic one (onClick) as a suggestion.
    
  2183.           // This is because we don't bundle the event system
    
  2184.           // on the server.
    
  2185.           'React events use the camelCase naming convention, for example `onClick`.' +
    
  2186.           '\n    in input (at **)',
    
  2187.       );
    
  2188.     });
    
  2189. 
    
  2190.     it('gives source code refs for unknown prop warning for update render', () => {
    
  2191.       const container = document.createElement('div');
    
  2192. 
    
  2193.       ReactTestUtils.renderIntoDocument(<div className="paladin" />, container);
    
  2194.       expect(() =>
    
  2195.         ReactTestUtils.renderIntoDocument(<div class="paladin" />, container),
    
  2196.       ).toErrorDev(
    
  2197.         'Warning: Invalid DOM property `class`. Did you mean `className`?\n    in div (at **)',
    
  2198.       );
    
  2199.     });
    
  2200. 
    
  2201.     it('gives source code refs for unknown prop warning for exact elements', () => {
    
  2202.       expect(() =>
    
  2203.         ReactTestUtils.renderIntoDocument(
    
  2204.           <div className="foo1">
    
  2205.             <span class="foo2" />
    
  2206.             <div onClick={() => {}} />
    
  2207.             <strong onclick={() => {}} />
    
  2208.             <div className="foo5" />
    
  2209.             <div className="foo6" />
    
  2210.           </div>,
    
  2211.         ),
    
  2212.       ).toErrorDev([
    
  2213.         'Invalid DOM property `class`. Did you mean `className`?\n    in span (at **)',
    
  2214.         'Invalid event handler property `onclick`. Did you mean `onClick`?\n    in strong (at **)',
    
  2215.       ]);
    
  2216.     });
    
  2217. 
    
  2218.     it('gives source code refs for unknown prop warning for exact elements (ssr)', () => {
    
  2219.       expect(() =>
    
  2220.         ReactDOMServer.renderToString(
    
  2221.           <div className="foo1">
    
  2222.             <span class="foo2" />
    
  2223.             <div onClick="foo3" />
    
  2224.             <strong onclick="foo4" />
    
  2225.             <div className="foo5" />
    
  2226.             <div className="foo6" />
    
  2227.           </div>,
    
  2228.         ),
    
  2229.       ).toErrorDev([
    
  2230.         'Invalid DOM property `class`. Did you mean `className`?\n    in span (at **)',
    
  2231.         'Invalid event handler property `onclick`. ' +
    
  2232.           'React events use the camelCase naming convention, for example `onClick`.' +
    
  2233.           '\n    in strong (at **)',
    
  2234.       ]);
    
  2235.     });
    
  2236. 
    
  2237.     it('gives source code refs for unknown prop warning for exact elements in composition', () => {
    
  2238.       const container = document.createElement('div');
    
  2239. 
    
  2240.       class Parent extends React.Component {
    
  2241.         render() {
    
  2242.           return (
    
  2243.             <div>
    
  2244.               <Child1 />
    
  2245.               <Child2 />
    
  2246.               <Child3 />
    
  2247.               <Child4 />
    
  2248.             </div>
    
  2249.           );
    
  2250.         }
    
  2251.       }
    
  2252. 
    
  2253.       class Child1 extends React.Component {
    
  2254.         render() {
    
  2255.           return <span class="paladin">Child1</span>;
    
  2256.         }
    
  2257.       }
    
  2258. 
    
  2259.       class Child2 extends React.Component {
    
  2260.         render() {
    
  2261.           return <div>Child2</div>;
    
  2262.         }
    
  2263.       }
    
  2264. 
    
  2265.       class Child3 extends React.Component {
    
  2266.         render() {
    
  2267.           return <strong onclick="1">Child3</strong>;
    
  2268.         }
    
  2269.       }
    
  2270. 
    
  2271.       class Child4 extends React.Component {
    
  2272.         render() {
    
  2273.           return <div>Child4</div>;
    
  2274.         }
    
  2275.       }
    
  2276. 
    
  2277.       expect(() =>
    
  2278.         ReactTestUtils.renderIntoDocument(<Parent />, container),
    
  2279.       ).toErrorDev([
    
  2280.         'Invalid DOM property `class`. Did you mean `className`?\n    in span (at **)',
    
  2281.         'Invalid event handler property `onclick`. Did you mean `onClick`?\n    in strong (at **)',
    
  2282.       ]);
    
  2283.     });
    
  2284. 
    
  2285.     it('gives source code refs for unknown prop warning for exact elements in composition (ssr)', () => {
    
  2286.       const container = document.createElement('div');
    
  2287. 
    
  2288.       class Parent extends React.Component {
    
  2289.         render() {
    
  2290.           return (
    
  2291.             <div>
    
  2292.               <Child1 />
    
  2293.               <Child2 />
    
  2294.               <Child3 />
    
  2295.               <Child4 />
    
  2296.             </div>
    
  2297.           );
    
  2298.         }
    
  2299.       }
    
  2300. 
    
  2301.       class Child1 extends React.Component {
    
  2302.         render() {
    
  2303.           return <span class="paladin">Child1</span>;
    
  2304.         }
    
  2305.       }
    
  2306. 
    
  2307.       class Child2 extends React.Component {
    
  2308.         render() {
    
  2309.           return <div>Child2</div>;
    
  2310.         }
    
  2311.       }
    
  2312. 
    
  2313.       class Child3 extends React.Component {
    
  2314.         render() {
    
  2315.           return <strong onclick="1">Child3</strong>;
    
  2316.         }
    
  2317.       }
    
  2318. 
    
  2319.       class Child4 extends React.Component {
    
  2320.         render() {
    
  2321.           return <div>Child4</div>;
    
  2322.         }
    
  2323.       }
    
  2324. 
    
  2325.       expect(() =>
    
  2326.         ReactDOMServer.renderToString(<Parent />, container),
    
  2327.       ).toErrorDev([
    
  2328.         'Invalid DOM property `class`. Did you mean `className`?\n    in span (at **)',
    
  2329.         'Invalid event handler property `onclick`. ' +
    
  2330.           'React events use the camelCase naming convention, for example `onClick`.' +
    
  2331.           '\n    in strong (at **)',
    
  2332.       ]);
    
  2333.     });
    
  2334. 
    
  2335.     it('should suggest property name if available', () => {
    
  2336.       expect(() =>
    
  2337.         ReactTestUtils.renderIntoDocument(
    
  2338.           React.createElement('label', {for: 'test'}),
    
  2339.         ),
    
  2340.       ).toErrorDev(
    
  2341.         'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n    in label',
    
  2342.       );
    
  2343. 
    
  2344.       expect(() =>
    
  2345.         ReactTestUtils.renderIntoDocument(
    
  2346.           React.createElement('input', {type: 'text', autofocus: true}),
    
  2347.         ),
    
  2348.       ).toErrorDev(
    
  2349.         'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n    in input',
    
  2350.       );
    
  2351.     });
    
  2352. 
    
  2353.     it('should suggest property name if available (ssr)', () => {
    
  2354.       expect(() =>
    
  2355.         ReactDOMServer.renderToString(
    
  2356.           React.createElement('label', {for: 'test'}),
    
  2357.         ),
    
  2358.       ).toErrorDev(
    
  2359.         'Warning: Invalid DOM property `for`. Did you mean `htmlFor`?\n    in label',
    
  2360.       );
    
  2361.       expect(() =>
    
  2362.         ReactDOMServer.renderToString(
    
  2363.           React.createElement('input', {type: 'text', autofocus: true}),
    
  2364.         ),
    
  2365.       ).toErrorDev(
    
  2366.         'Warning: Invalid DOM property `autofocus`. Did you mean `autoFocus`?\n    in input',
    
  2367.       );
    
  2368.     });
    
  2369.   });
    
  2370. 
    
  2371.   describe('whitespace', () => {
    
  2372.     it('renders innerHTML and preserves whitespace', () => {
    
  2373.       const container = document.createElement('div');
    
  2374.       const html = '\n  \t  <span>  \n  testContent  \t  </span>  \n  \t';
    
  2375.       const elem = <div dangerouslySetInnerHTML={{__html: html}} />;
    
  2376. 
    
  2377.       ReactDOM.render(elem, container);
    
  2378.       expect(container.firstChild.innerHTML).toBe(html);
    
  2379.     });
    
  2380. 
    
  2381.     it('render and then updates innerHTML and preserves whitespace', () => {
    
  2382.       const container = document.createElement('div');
    
  2383.       const html = '\n  \t  <span>  \n  testContent1  \t  </span>  \n  \t';
    
  2384.       const elem = <div dangerouslySetInnerHTML={{__html: html}} />;
    
  2385.       ReactDOM.render(elem, container);
    
  2386. 
    
  2387.       const html2 = '\n  \t  <div>  \n  testContent2  \t  </div>  \n  \t';
    
  2388.       const elem2 = <div dangerouslySetInnerHTML={{__html: html2}} />;
    
  2389.       ReactDOM.render(elem2, container);
    
  2390. 
    
  2391.       expect(container.firstChild.innerHTML).toBe(html2);
    
  2392.     });
    
  2393.   });
    
  2394. 
    
  2395.   describe('Attributes with aliases', function () {
    
  2396.     it('sets aliased attributes on HTML attributes', function () {
    
  2397.       let el;
    
  2398.       expect(() => {
    
  2399.         el = ReactTestUtils.renderIntoDocument(<div class="test" />);
    
  2400.       }).toErrorDev(
    
  2401.         'Warning: Invalid DOM property `class`. Did you mean `className`?',
    
  2402.       );
    
  2403. 
    
  2404.       expect(el.className).toBe('test');
    
  2405.     });
    
  2406. 
    
  2407.     it('sets incorrectly cased aliased attributes on HTML attributes with a warning', function () {
    
  2408.       let el;
    
  2409.       expect(() => {
    
  2410.         el = ReactTestUtils.renderIntoDocument(<div cLASS="test" />);
    
  2411.       }).toErrorDev(
    
  2412.         'Warning: Invalid DOM property `cLASS`. Did you mean `className`?',
    
  2413.       );
    
  2414. 
    
  2415.       expect(el.className).toBe('test');
    
  2416.     });
    
  2417. 
    
  2418.     it('sets aliased attributes on SVG elements with a warning', function () {
    
  2419.       let el;
    
  2420.       expect(() => {
    
  2421.         el = ReactTestUtils.renderIntoDocument(
    
  2422.           <svg>
    
  2423.             <text arabic-form="initial" />
    
  2424.           </svg>,
    
  2425.         );
    
  2426.       }).toErrorDev(
    
  2427.         'Warning: Invalid DOM property `arabic-form`. Did you mean `arabicForm`?',
    
  2428.       );
    
  2429.       const text = el.querySelector('text');
    
  2430. 
    
  2431.       expect(text.hasAttribute('arabic-form')).toBe(true);
    
  2432.     });
    
  2433. 
    
  2434.     it('sets aliased attributes on custom elements', function () {
    
  2435.       const el = ReactTestUtils.renderIntoDocument(
    
  2436.         <div is="custom-element" class="test" />,
    
  2437.       );
    
  2438. 
    
  2439.       expect(el.getAttribute('class')).toBe('test');
    
  2440.     });
    
  2441. 
    
  2442.     it('aliased attributes on custom elements with bad casing', function () {
    
  2443.       const el = ReactTestUtils.renderIntoDocument(
    
  2444.         <div is="custom-element" claSS="test" />,
    
  2445.       );
    
  2446. 
    
  2447.       expect(el.getAttribute('class')).toBe('test');
    
  2448.     });
    
  2449. 
    
  2450.     it('updates aliased attributes on custom elements', function () {
    
  2451.       const container = document.createElement('div');
    
  2452.       ReactDOM.render(<div is="custom-element" class="foo" />, container);
    
  2453.       ReactDOM.render(<div is="custom-element" class="bar" />, container);
    
  2454. 
    
  2455.       expect(container.firstChild.getAttribute('class')).toBe('bar');
    
  2456.     });
    
  2457.   });
    
  2458. 
    
  2459.   describe('Custom attributes', function () {
    
  2460.     it('allows assignment of custom attributes with string values', function () {
    
  2461.       const el = ReactTestUtils.renderIntoDocument(<div whatever="30" />);
    
  2462. 
    
  2463.       expect(el.getAttribute('whatever')).toBe('30');
    
  2464.     });
    
  2465. 
    
  2466.     it('removes custom attributes', function () {
    
  2467.       const container = document.createElement('div');
    
  2468.       ReactDOM.render(<div whatever="30" />, container);
    
  2469. 
    
  2470.       expect(container.firstChild.getAttribute('whatever')).toBe('30');
    
  2471. 
    
  2472.       ReactDOM.render(<div whatever={null} />, container);
    
  2473. 
    
  2474.       expect(container.firstChild.hasAttribute('whatever')).toBe(false);
    
  2475.     });
    
  2476. 
    
  2477.     it('does not assign a boolean custom attributes as a string', function () {
    
  2478.       let el;
    
  2479.       expect(() => {
    
  2480.         el = ReactTestUtils.renderIntoDocument(<div whatever={true} />);
    
  2481.       }).toErrorDev(
    
  2482.         'Received `true` for a non-boolean attribute `whatever`.\n\n' +
    
  2483.           'If you want to write it to the DOM, pass a string instead: ' +
    
  2484.           'whatever="true" or whatever={value.toString()}.',
    
  2485.       );
    
  2486. 
    
  2487.       expect(el.hasAttribute('whatever')).toBe(false);
    
  2488.     });
    
  2489. 
    
  2490.     it('does not assign an implicit boolean custom attributes', function () {
    
  2491.       let el;
    
  2492.       expect(() => {
    
  2493.         // eslint-disable-next-line react/jsx-boolean-value
    
  2494.         el = ReactTestUtils.renderIntoDocument(<div whatever />);
    
  2495.       }).toErrorDev(
    
  2496.         'Received `true` for a non-boolean attribute `whatever`.\n\n' +
    
  2497.           'If you want to write it to the DOM, pass a string instead: ' +
    
  2498.           'whatever="true" or whatever={value.toString()}.',
    
  2499.       );
    
  2500. 
    
  2501.       expect(el.hasAttribute('whatever')).toBe(false);
    
  2502.     });
    
  2503. 
    
  2504.     it('assigns a numeric custom attributes as a string', function () {
    
  2505.       const el = ReactTestUtils.renderIntoDocument(<div whatever={3} />);
    
  2506. 
    
  2507.       expect(el.getAttribute('whatever')).toBe('3');
    
  2508.     });
    
  2509. 
    
  2510.     it('will not assign a function custom attributes', function () {
    
  2511.       let el;
    
  2512.       expect(() => {
    
  2513.         el = ReactTestUtils.renderIntoDocument(<div whatever={() => {}} />);
    
  2514.       }).toErrorDev('Warning: Invalid value for prop `whatever` on <div> tag');
    
  2515. 
    
  2516.       expect(el.hasAttribute('whatever')).toBe(false);
    
  2517.     });
    
  2518. 
    
  2519.     it('will assign an object custom attributes', function () {
    
  2520.       const el = ReactTestUtils.renderIntoDocument(<div whatever={{}} />);
    
  2521.       expect(el.getAttribute('whatever')).toBe('[object Object]');
    
  2522.     });
    
  2523. 
    
  2524.     it('allows Temporal-like objects as HTML (they are not coerced to strings first)', function () {
    
  2525.       class TemporalLike {
    
  2526.         valueOf() {
    
  2527.           // Throwing here is the behavior of ECMAScript "Temporal" date/time API.
    
  2528.           // See https://tc39.es/proposal-temporal/docs/plaindate.html#valueOf
    
  2529.           throw new TypeError('prod message');
    
  2530.         }
    
  2531.         toString() {
    
  2532.           return '2020-01-01';
    
  2533.         }
    
  2534.       }
    
  2535. 
    
  2536.       // `dangerouslySetInnerHTML` is never coerced to a string, so won't throw
    
  2537.       // even with a Temporal-like object.
    
  2538.       const container = document.createElement('div');
    
  2539.       ReactDOM.render(
    
  2540.         <div dangerouslySetInnerHTML={{__html: new TemporalLike()}} />,
    
  2541.         container,
    
  2542.       );
    
  2543.       expect(container.firstChild.innerHTML).toEqual('2020-01-01');
    
  2544.     });
    
  2545. 
    
  2546.     it('allows cased data attributes', function () {
    
  2547.       let el;
    
  2548.       expect(() => {
    
  2549.         el = ReactTestUtils.renderIntoDocument(<div data-fooBar="true" />);
    
  2550.       }).toErrorDev(
    
  2551.         'React does not recognize the `data-fooBar` prop on a DOM element. ' +
    
  2552.           'If you intentionally want it to appear in the DOM as a custom ' +
    
  2553.           'attribute, spell it as lowercase `data-foobar` instead. ' +
    
  2554.           'If you accidentally passed it from a parent component, remove ' +
    
  2555.           'it from the DOM element.\n' +
    
  2556.           '    in div (at **)',
    
  2557.       );
    
  2558.       expect(el.getAttribute('data-foobar')).toBe('true');
    
  2559.     });
    
  2560. 
    
  2561.     it('allows cased custom attributes', function () {
    
  2562.       let el;
    
  2563.       expect(() => {
    
  2564.         el = ReactTestUtils.renderIntoDocument(<div fooBar="true" />);
    
  2565.       }).toErrorDev(
    
  2566.         'React does not recognize the `fooBar` prop on a DOM element. ' +
    
  2567.           'If you intentionally want it to appear in the DOM as a custom ' +
    
  2568.           'attribute, spell it as lowercase `foobar` instead. ' +
    
  2569.           'If you accidentally passed it from a parent component, remove ' +
    
  2570.           'it from the DOM element.\n' +
    
  2571.           '    in div (at **)',
    
  2572.       );
    
  2573.       expect(el.getAttribute('foobar')).toBe('true');
    
  2574.     });
    
  2575. 
    
  2576.     it('warns on NaN attributes', function () {
    
  2577.       let el;
    
  2578.       expect(() => {
    
  2579.         el = ReactTestUtils.renderIntoDocument(<div whatever={NaN} />);
    
  2580.       }).toErrorDev(
    
  2581.         'Warning: Received NaN for the `whatever` attribute. If this is ' +
    
  2582.           'expected, cast the value to a string.\n    in div',
    
  2583.       );
    
  2584. 
    
  2585.       expect(el.getAttribute('whatever')).toBe('NaN');
    
  2586.     });
    
  2587. 
    
  2588.     it('removes a property when it becomes invalid', function () {
    
  2589.       const container = document.createElement('div');
    
  2590.       ReactDOM.render(<div whatever={0} />, container);
    
  2591.       expect(() =>
    
  2592.         ReactDOM.render(<div whatever={() => {}} />, container),
    
  2593.       ).toErrorDev('Warning: Invalid value for prop `whatever` on <div> tag.');
    
  2594.       const el = container.firstChild;
    
  2595.       expect(el.hasAttribute('whatever')).toBe(false);
    
  2596.     });
    
  2597. 
    
  2598.     it('warns on bad casing of known HTML attributes', function () {
    
  2599.       let el;
    
  2600.       expect(() => {
    
  2601.         el = ReactTestUtils.renderIntoDocument(<div SiZe="30" />);
    
  2602.       }).toErrorDev(
    
  2603.         'Warning: Invalid DOM property `SiZe`. Did you mean `size`?',
    
  2604.       );
    
  2605. 
    
  2606.       expect(el.getAttribute('size')).toBe('30');
    
  2607.     });
    
  2608.   });
    
  2609. 
    
  2610.   describe('Object stringification', function () {
    
  2611.     it('allows objects on known properties', function () {
    
  2612.       const el = ReactTestUtils.renderIntoDocument(<div acceptCharset={{}} />);
    
  2613.       expect(el.getAttribute('accept-charset')).toBe('[object Object]');
    
  2614.     });
    
  2615. 
    
  2616.     it('should pass objects as attributes if they define toString', () => {
    
  2617.       const obj = {
    
  2618.         toString() {
    
  2619.           return 'hello';
    
  2620.         },
    
  2621.       };
    
  2622.       const container = document.createElement('div');
    
  2623. 
    
  2624.       ReactDOM.render(<img src={obj} />, container);
    
  2625.       expect(container.firstChild.src).toBe('http://localhost/hello');
    
  2626. 
    
  2627.       ReactDOM.render(<svg arabicForm={obj} />, container);
    
  2628.       expect(container.firstChild.getAttribute('arabic-form')).toBe('hello');
    
  2629. 
    
  2630.       ReactDOM.render(<div unknown={obj} />, container);
    
  2631.       expect(container.firstChild.getAttribute('unknown')).toBe('hello');
    
  2632.     });
    
  2633. 
    
  2634.     it('passes objects on known SVG attributes if they do not define toString', () => {
    
  2635.       const obj = {};
    
  2636.       const container = document.createElement('div');
    
  2637. 
    
  2638.       ReactDOM.render(<svg arabicForm={obj} />, container);
    
  2639.       expect(container.firstChild.getAttribute('arabic-form')).toBe(
    
  2640.         '[object Object]',
    
  2641.       );
    
  2642.     });
    
  2643. 
    
  2644.     it('passes objects on custom attributes if they do not define toString', () => {
    
  2645.       const obj = {};
    
  2646.       const container = document.createElement('div');
    
  2647. 
    
  2648.       ReactDOM.render(<div unknown={obj} />, container);
    
  2649.       expect(container.firstChild.getAttribute('unknown')).toBe(
    
  2650.         '[object Object]',
    
  2651.       );
    
  2652.     });
    
  2653. 
    
  2654.     it('allows objects that inherit a custom toString method', function () {
    
  2655.       const parent = {toString: () => 'hello.jpg'};
    
  2656.       const child = Object.create(parent);
    
  2657.       const el = ReactTestUtils.renderIntoDocument(<img src={child} />);
    
  2658. 
    
  2659.       expect(el.src).toBe('http://localhost/hello.jpg');
    
  2660.     });
    
  2661. 
    
  2662.     it('assigns ajaxify (an important internal FB attribute)', function () {
    
  2663.       const options = {toString: () => 'ajaxy'};
    
  2664.       const el = ReactTestUtils.renderIntoDocument(<div ajaxify={options} />);
    
  2665. 
    
  2666.       expect(el.getAttribute('ajaxify')).toBe('ajaxy');
    
  2667.     });
    
  2668.   });
    
  2669. 
    
  2670.   describe('String boolean attributes', function () {
    
  2671.     it('does not assign string boolean attributes for custom attributes', function () {
    
  2672.       let el;
    
  2673.       expect(() => {
    
  2674.         el = ReactTestUtils.renderIntoDocument(<div whatever={true} />);
    
  2675.       }).toErrorDev(
    
  2676.         'Received `true` for a non-boolean attribute `whatever`.\n\n' +
    
  2677.           'If you want to write it to the DOM, pass a string instead: ' +
    
  2678.           'whatever="true" or whatever={value.toString()}.',
    
  2679.       );
    
  2680. 
    
  2681.       expect(el.hasAttribute('whatever')).toBe(false);
    
  2682.     });
    
  2683. 
    
  2684.     it('stringifies the boolean true for allowed attributes', function () {
    
  2685.       const el = ReactTestUtils.renderIntoDocument(<div spellCheck={true} />);
    
  2686. 
    
  2687.       expect(el.getAttribute('spellCheck')).toBe('true');
    
  2688.     });
    
  2689. 
    
  2690.     it('stringifies the boolean false for allowed attributes', function () {
    
  2691.       const el = ReactTestUtils.renderIntoDocument(<div spellCheck={false} />);
    
  2692. 
    
  2693.       expect(el.getAttribute('spellCheck')).toBe('false');
    
  2694.     });
    
  2695. 
    
  2696.     it('stringifies implicit booleans for allowed attributes', function () {
    
  2697.       // eslint-disable-next-line react/jsx-boolean-value
    
  2698.       const el = ReactTestUtils.renderIntoDocument(<div spellCheck />);
    
  2699. 
    
  2700.       expect(el.getAttribute('spellCheck')).toBe('true');
    
  2701.     });
    
  2702.   });
    
  2703. 
    
  2704.   describe('Boolean attributes', function () {
    
  2705.     it('warns on the ambiguous string value "false"', function () {
    
  2706.       let el;
    
  2707.       expect(() => {
    
  2708.         el = ReactTestUtils.renderIntoDocument(<div hidden="false" />);
    
  2709.       }).toErrorDev(
    
  2710.         'Received the string `false` for the boolean attribute `hidden`. ' +
    
  2711.           'The browser will interpret it as a truthy value. ' +
    
  2712.           'Did you mean hidden={false}?',
    
  2713.       );
    
  2714. 
    
  2715.       expect(el.getAttribute('hidden')).toBe('');
    
  2716.     });
    
  2717. 
    
  2718.     it('warns on the potentially-ambiguous string value "true"', function () {
    
  2719.       let el;
    
  2720.       expect(() => {
    
  2721.         el = ReactTestUtils.renderIntoDocument(<div hidden="true" />);
    
  2722.       }).toErrorDev(
    
  2723.         'Received the string `true` for the boolean attribute `hidden`. ' +
    
  2724.           'Although this works, it will not work as expected if you pass the string "false". ' +
    
  2725.           'Did you mean hidden={true}?',
    
  2726.       );
    
  2727. 
    
  2728.       expect(el.getAttribute('hidden')).toBe('');
    
  2729.     });
    
  2730.   });
    
  2731. 
    
  2732.   describe('Hyphenated SVG elements', function () {
    
  2733.     it('the font-face element is not a custom element', function () {
    
  2734.       let el;
    
  2735.       expect(() => {
    
  2736.         el = ReactTestUtils.renderIntoDocument(
    
  2737.           <svg>
    
  2738.             <font-face x-height={false} />
    
  2739.           </svg>,
    
  2740.         );
    
  2741.       }).toErrorDev(
    
  2742.         'Warning: Invalid DOM property `x-height`. Did you mean `xHeight`',
    
  2743.       );
    
  2744. 
    
  2745.       expect(el.querySelector('font-face').hasAttribute('x-height')).toBe(
    
  2746.         false,
    
  2747.       );
    
  2748.     });
    
  2749. 
    
  2750.     it('the font-face element does not allow unknown boolean values', function () {
    
  2751.       let el;
    
  2752.       expect(() => {
    
  2753.         el = ReactTestUtils.renderIntoDocument(
    
  2754.           <svg>
    
  2755.             <font-face whatever={false} />
    
  2756.           </svg>,
    
  2757.         );
    
  2758.       }).toErrorDev(
    
  2759.         'Received `false` for a non-boolean attribute `whatever`.\n\n' +
    
  2760.           'If you want to write it to the DOM, pass a string instead: ' +
    
  2761.           'whatever="false" or whatever={value.toString()}.\n\n' +
    
  2762.           'If you used to conditionally omit it with whatever={condition && value}, ' +
    
  2763.           'pass whatever={condition ? value : undefined} instead.',
    
  2764.       );
    
  2765. 
    
  2766.       expect(el.querySelector('font-face').hasAttribute('whatever')).toBe(
    
  2767.         false,
    
  2768.       );
    
  2769.     });
    
  2770.   });
    
  2771. 
    
  2772.   // These tests mostly verify the existing behavior.
    
  2773.   // It may not always makes sense but we can't change it in minors.
    
  2774.   describe('Custom elements', () => {
    
  2775.     it('does not strip unknown boolean attributes', () => {
    
  2776.       const container = document.createElement('div');
    
  2777.       ReactDOM.render(<some-custom-element foo={true} />, container);
    
  2778.       const node = container.firstChild;
    
  2779.       expect(node.getAttribute('foo')).toBe(
    
  2780.         ReactFeatureFlags.enableCustomElementPropertySupport ? '' : 'true',
    
  2781.       );
    
  2782.       ReactDOM.render(<some-custom-element foo={false} />, container);
    
  2783.       expect(node.getAttribute('foo')).toBe(
    
  2784.         ReactFeatureFlags.enableCustomElementPropertySupport ? null : 'false',
    
  2785.       );
    
  2786.       ReactDOM.render(<some-custom-element />, container);
    
  2787.       expect(node.hasAttribute('foo')).toBe(false);
    
  2788.       ReactDOM.render(<some-custom-element foo={true} />, container);
    
  2789.       expect(node.hasAttribute('foo')).toBe(true);
    
  2790.     });
    
  2791. 
    
  2792.     it('does not strip the on* attributes', () => {
    
  2793.       const container = document.createElement('div');
    
  2794.       ReactDOM.render(<some-custom-element onx="bar" />, container);
    
  2795.       const node = container.firstChild;
    
  2796.       expect(node.getAttribute('onx')).toBe('bar');
    
  2797.       ReactDOM.render(<some-custom-element onx="buzz" />, container);
    
  2798.       expect(node.getAttribute('onx')).toBe('buzz');
    
  2799.       ReactDOM.render(<some-custom-element />, container);
    
  2800.       expect(node.hasAttribute('onx')).toBe(false);
    
  2801.       ReactDOM.render(<some-custom-element onx="bar" />, container);
    
  2802.       expect(node.getAttribute('onx')).toBe('bar');
    
  2803.     });
    
  2804.   });
    
  2805. 
    
  2806.   it('receives events in specific order', () => {
    
  2807.     const eventOrder = [];
    
  2808.     const track = tag => () => eventOrder.push(tag);
    
  2809.     const outerRef = React.createRef();
    
  2810.     const innerRef = React.createRef();
    
  2811. 
    
  2812.     function OuterReactApp() {
    
  2813.       return (
    
  2814.         <div
    
  2815.           ref={outerRef}
    
  2816.           onClick={track('outer bubble')}
    
  2817.           onClickCapture={track('outer capture')}
    
  2818.         />
    
  2819.       );
    
  2820.     }
    
  2821. 
    
  2822.     function InnerReactApp() {
    
  2823.       return (
    
  2824.         <div
    
  2825.           ref={innerRef}
    
  2826.           onClick={track('inner bubble')}
    
  2827.           onClickCapture={track('inner capture')}
    
  2828.         />
    
  2829.       );
    
  2830.     }
    
  2831. 
    
  2832.     const container = document.createElement('div');
    
  2833.     document.body.appendChild(container);
    
  2834. 
    
  2835.     try {
    
  2836.       ReactDOM.render(<OuterReactApp />, container);
    
  2837.       ReactDOM.render(<InnerReactApp />, outerRef.current);
    
  2838. 
    
  2839.       document.addEventListener('click', track('document bubble'));
    
  2840.       document.addEventListener('click', track('document capture'), true);
    
  2841. 
    
  2842.       innerRef.current.click();
    
  2843. 
    
  2844.       if (ReactFeatureFlags.enableLegacyFBSupport) {
    
  2845.         // The order will change here, as the legacy FB support adds
    
  2846.         // the event listener onto the document after the one above has.
    
  2847.         expect(eventOrder).toEqual([
    
  2848.           'document capture',
    
  2849.           'outer capture',
    
  2850.           'inner capture',
    
  2851.           'document bubble',
    
  2852.           'inner bubble',
    
  2853.           'outer bubble',
    
  2854.         ]);
    
  2855.       } else {
    
  2856.         expect(eventOrder).toEqual([
    
  2857.           'document capture',
    
  2858.           'outer capture',
    
  2859.           'inner capture',
    
  2860.           'inner bubble',
    
  2861.           'outer bubble',
    
  2862.           'document bubble',
    
  2863.         ]);
    
  2864.       }
    
  2865.     } finally {
    
  2866.       document.body.removeChild(container);
    
  2867.     }
    
  2868.   });
    
  2869. 
    
  2870.   describe('iOS Tap Highlight', () => {
    
  2871.     it('adds onclick handler to elements with onClick prop', () => {
    
  2872.       const container = document.createElement('div');
    
  2873. 
    
  2874.       const elementRef = React.createRef();
    
  2875.       function Component() {
    
  2876.         return <div ref={elementRef} onClick={() => {}} />;
    
  2877.       }
    
  2878. 
    
  2879.       ReactDOM.render(<Component />, container);
    
  2880.       expect(typeof elementRef.current.onclick).toBe('function');
    
  2881.     });
    
  2882. 
    
  2883.     it('adds onclick handler to a portal root', () => {
    
  2884.       const container = document.createElement('div');
    
  2885.       const portalContainer = document.createElement('div');
    
  2886. 
    
  2887.       function Component() {
    
  2888.         return ReactDOM.createPortal(
    
  2889.           <div onClick={() => {}} />,
    
  2890.           portalContainer,
    
  2891.         );
    
  2892.       }
    
  2893. 
    
  2894.       ReactDOM.render(<Component />, container);
    
  2895.       expect(typeof portalContainer.onclick).toBe('function');
    
  2896.     });
    
  2897. 
    
  2898.     it('does not add onclick handler to the React root', () => {
    
  2899.       const container = document.createElement('div');
    
  2900. 
    
  2901.       function Component() {
    
  2902.         return <div onClick={() => {}} />;
    
  2903.       }
    
  2904. 
    
  2905.       ReactDOM.render(<Component />, container);
    
  2906.       expect(typeof container.onclick).not.toBe('function');
    
  2907.     });
    
  2908.   });
    
  2909. });