1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @emails react-core
    
  8.  */
    
  9. 
    
  10. 'use strict';
    
  11. 
    
  12. const React = require('react');
    
  13. const ReactDOM = require('react-dom');
    
  14. 
    
  15. function expectWarnings(tags, warnings = [], withoutStack = 0) {
    
  16.   tags = [...tags];
    
  17.   warnings = [...warnings];
    
  18. 
    
  19.   let element = null;
    
  20.   const containerTag = tags.shift();
    
  21.   const container =
    
  22.     containerTag === 'svg'
    
  23.       ? document.createElementNS('http://www.w3.org/2000/svg', containerTag)
    
  24.       : document.createElement(containerTag);
    
  25. 
    
  26.   while (tags.length) {
    
  27.     const Tag = tags.pop();
    
  28.     element = <Tag>{element}</Tag>;
    
  29.   }
    
  30. 
    
  31.   if (warnings.length) {
    
  32.     expect(() => ReactDOM.render(element, container)).toErrorDev(warnings, {
    
  33.       withoutStack,
    
  34.     });
    
  35.   }
    
  36. }
    
  37. 
    
  38. describe('validateDOMNesting', () => {
    
  39.   it('allows valid nestings', () => {
    
  40.     expectWarnings(['table', 'tbody', 'tr', 'td', 'b']);
    
  41.     expectWarnings(
    
  42.       ['body', 'datalist', 'option'],
    
  43.       [
    
  44.         gate(flags => !flags.enableHostSingletons)
    
  45.           ? 'render(): Rendering components directly into document.body is discouraged'
    
  46.           : null,
    
  47.       ].filter(Boolean),
    
  48.       1,
    
  49.     );
    
  50.     expectWarnings(['div', 'a', 'object', 'a']);
    
  51.     expectWarnings(['div', 'p', 'button', 'p']);
    
  52.     expectWarnings(['p', 'svg', 'foreignObject', 'p']);
    
  53.     expectWarnings(['html', 'body', 'div']);
    
  54. 
    
  55.     // Invalid, but not changed by browser parsing so we allow them
    
  56.     expectWarnings(['div', 'ul', 'ul', 'li']);
    
  57.     expectWarnings(['div', 'label', 'div']);
    
  58.     expectWarnings(['div', 'ul', 'li', 'section', 'li']);
    
  59.     expectWarnings(['div', 'ul', 'li', 'dd', 'li']);
    
  60.   });
    
  61. 
    
  62.   it('prevents problematic nestings', () => {
    
  63.     expectWarnings(
    
  64.       ['a', 'a'],
    
  65.       [
    
  66.         'validateDOMNesting(...): <a> cannot appear as a descendant of <a>.\n' +
    
  67.           '    in a (at **)',
    
  68.       ],
    
  69.     );
    
  70.     expectWarnings(
    
  71.       ['form', 'form'],
    
  72.       [
    
  73.         'validateDOMNesting(...): <form> cannot appear as a descendant of <form>.\n' +
    
  74.           '    in form (at **)',
    
  75.       ],
    
  76.     );
    
  77.     expectWarnings(
    
  78.       ['p', 'p'],
    
  79.       [
    
  80.         'validateDOMNesting(...): <p> cannot appear as a descendant of <p>.\n' +
    
  81.           '    in p (at **)',
    
  82.       ],
    
  83.     );
    
  84.     expectWarnings(
    
  85.       ['table', 'tr'],
    
  86.       [
    
  87.         'validateDOMNesting(...): <tr> cannot appear as a child of <table>. ' +
    
  88.           'Add a <tbody>, <thead> or <tfoot> to your code to match the DOM tree generated by the browser.\n' +
    
  89.           '    in tr (at **)',
    
  90.       ],
    
  91.     );
    
  92.     expectWarnings(
    
  93.       ['div', 'ul', 'li', 'div', 'li'],
    
  94.       [
    
  95.         'validateDOMNesting(...): <li> cannot appear as a descendant of <li>.\n' +
    
  96.           '    in li (at **)\n' +
    
  97.           '    in div (at **)\n' +
    
  98.           '    in li (at **)\n' +
    
  99.           '    in ul (at **)',
    
  100.       ],
    
  101.     );
    
  102.     expectWarnings(
    
  103.       ['div', 'html'],
    
  104.       [
    
  105.         'validateDOMNesting(...): <html> cannot appear as a child of <div>.\n' +
    
  106.           '    in html (at **)',
    
  107.       ],
    
  108.     );
    
  109.     if (gate(flags => flags.enableHostSingletons)) {
    
  110.       expectWarnings(
    
  111.         ['body', 'body'],
    
  112.         [
    
  113.           'validateDOMNesting(...): <body> cannot appear as a child of <body>.\n' +
    
  114.             '    in body (at **)',
    
  115.         ],
    
  116.       );
    
  117.     } else {
    
  118.       expectWarnings(
    
  119.         ['body', 'body'],
    
  120.         [
    
  121.           'render(): Rendering components directly into document.body is discouraged',
    
  122.           'validateDOMNesting(...): <body> cannot appear as a child of <body>.\n' +
    
  123.             '    in body (at **)',
    
  124.         ],
    
  125.         1,
    
  126.       );
    
  127.     }
    
  128.     if (gate(flags => flags.enableHostSingletons)) {
    
  129.       expectWarnings(
    
  130.         ['svg', 'foreignObject', 'body', 'p'],
    
  131.         [
    
  132.           'validateDOMNesting(...): <body> cannot appear as a child of <foreignObject>.\n' +
    
  133.             '    in body (at **)\n' +
    
  134.             '    in foreignObject (at **)',
    
  135.           'Warning: You are mounting a new body component when a previous one has not first unmounted. It is an error to render more than one body component at a time and attributes and children of these components will likely fail in unpredictable ways. Please only render a single instance of <body> and if you need to mount a new one, ensure any previous ones have unmounted first.\n' +
    
  136.             '    in body (at **)',
    
  137.         ],
    
  138.       );
    
  139.     } else {
    
  140.       expectWarnings(
    
  141.         ['svg', 'foreignObject', 'body', 'p'],
    
  142.         [
    
  143.           'validateDOMNesting(...): <body> cannot appear as a child of <foreignObject>.\n' +
    
  144.             '    in body (at **)\n' +
    
  145.             '    in foreignObject (at **)',
    
  146.         ],
    
  147.       );
    
  148.     }
    
  149.   });
    
  150. });