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. 
    
  8. 'use strict';
    
  9. 
    
  10. describe('ReactDOMEventListener', () => {
    
  11.   let React;
    
  12.   let OuterReactDOM;
    
  13.   let InnerReactDOM;
    
  14.   let container;
    
  15. 
    
  16.   beforeEach(() => {
    
  17.     window.TextEvent = function () {};
    
  18.     jest.resetModules();
    
  19.     jest.isolateModules(() => {
    
  20.       React = require('react');
    
  21.       OuterReactDOM = require('react-dom');
    
  22.     });
    
  23.     jest.isolateModules(() => {
    
  24.       InnerReactDOM = require('react-dom');
    
  25.     });
    
  26.     expect(OuterReactDOM).not.toBe(InnerReactDOM);
    
  27.   });
    
  28. 
    
  29.   afterEach(() => {
    
  30.     cleanup();
    
  31.   });
    
  32. 
    
  33.   function cleanup() {
    
  34.     if (container) {
    
  35.       OuterReactDOM.unmountComponentAtNode(container);
    
  36.       document.body.removeChild(container);
    
  37.       container = null;
    
  38.     }
    
  39.   }
    
  40. 
    
  41.   function render(tree) {
    
  42.     cleanup();
    
  43.     container = document.createElement('div');
    
  44.     document.body.appendChild(container);
    
  45.     OuterReactDOM.render(tree, container);
    
  46.   }
    
  47. 
    
  48.   describe('bubbling events', () => {
    
  49.     it('onAnimationEnd', () => {
    
  50.       testNativeBubblingEvent({
    
  51.         type: 'div',
    
  52.         reactEvent: 'onAnimationEnd',
    
  53.         reactEventType: 'animationend',
    
  54.         nativeEvent: 'animationend',
    
  55.         dispatch(node) {
    
  56.           node.dispatchEvent(
    
  57.             new Event('animationend', {
    
  58.               bubbles: true,
    
  59.               cancelable: true,
    
  60.             }),
    
  61.           );
    
  62.         },
    
  63.       });
    
  64.     });
    
  65. 
    
  66.     it('onAnimationIteration', () => {
    
  67.       testNativeBubblingEvent({
    
  68.         type: 'div',
    
  69.         reactEvent: 'onAnimationIteration',
    
  70.         reactEventType: 'animationiteration',
    
  71.         nativeEvent: 'animationiteration',
    
  72.         dispatch(node) {
    
  73.           node.dispatchEvent(
    
  74.             new Event('animationiteration', {
    
  75.               bubbles: true,
    
  76.               cancelable: true,
    
  77.             }),
    
  78.           );
    
  79.         },
    
  80.       });
    
  81.     });
    
  82. 
    
  83.     it('onAnimationStart', () => {
    
  84.       testNativeBubblingEvent({
    
  85.         type: 'div',
    
  86.         reactEvent: 'onAnimationStart',
    
  87.         reactEventType: 'animationstart',
    
  88.         nativeEvent: 'animationstart',
    
  89.         dispatch(node) {
    
  90.           node.dispatchEvent(
    
  91.             new Event('animationstart', {
    
  92.               bubbles: true,
    
  93.               cancelable: true,
    
  94.             }),
    
  95.           );
    
  96.         },
    
  97.       });
    
  98.     });
    
  99. 
    
  100.     it('onAuxClick', () => {
    
  101.       testNativeBubblingEvent({
    
  102.         type: 'div',
    
  103.         reactEvent: 'onAuxClick',
    
  104.         reactEventType: 'auxclick',
    
  105.         nativeEvent: 'auxclick',
    
  106.         dispatch(node) {
    
  107.           node.dispatchEvent(
    
  108.             new KeyboardEvent('auxclick', {
    
  109.               bubbles: true,
    
  110.               cancelable: true,
    
  111.             }),
    
  112.           );
    
  113.         },
    
  114.       });
    
  115.     });
    
  116. 
    
  117.     it('onBlur', () => {
    
  118.       testNativeBubblingEvent({
    
  119.         type: 'input',
    
  120.         reactEvent: 'onBlur',
    
  121.         reactEventType: 'blur',
    
  122.         nativeEvent: 'focusout',
    
  123.         dispatch(node) {
    
  124.           const e = new Event('focusout', {
    
  125.             bubbles: true,
    
  126.             cancelable: true,
    
  127.           });
    
  128.           node.dispatchEvent(e);
    
  129.         },
    
  130.       });
    
  131.     });
    
  132. 
    
  133.     // This test will fail in legacy mode (only used in WWW)
    
  134.     // because we emulate the React 16 behavior where
    
  135.     // the click handler is attached to the document.
    
  136.     // @gate !enableLegacyFBSupport
    
  137.     it('onClick', () => {
    
  138.       testNativeBubblingEvent({
    
  139.         type: 'div',
    
  140.         reactEvent: 'onClick',
    
  141.         reactEventType: 'click',
    
  142.         nativeEvent: 'click',
    
  143.         dispatch(node) {
    
  144.           node.click();
    
  145.         },
    
  146.       });
    
  147.     });
    
  148. 
    
  149.     it('onContextMenu', () => {
    
  150.       testNativeBubblingEvent({
    
  151.         type: 'div',
    
  152.         reactEvent: 'onContextMenu',
    
  153.         reactEventType: 'contextmenu',
    
  154.         nativeEvent: 'contextmenu',
    
  155.         dispatch(node) {
    
  156.           node.dispatchEvent(
    
  157.             new MouseEvent('contextmenu', {
    
  158.               bubbles: true,
    
  159.               cancelable: true,
    
  160.             }),
    
  161.           );
    
  162.         },
    
  163.       });
    
  164.     });
    
  165. 
    
  166.     it('onCopy', () => {
    
  167.       testNativeBubblingEvent({
    
  168.         type: 'div',
    
  169.         reactEvent: 'onCopy',
    
  170.         reactEventType: 'copy',
    
  171.         nativeEvent: 'copy',
    
  172.         dispatch(node) {
    
  173.           node.dispatchEvent(
    
  174.             new Event('copy', {
    
  175.               bubbles: true,
    
  176.               cancelable: true,
    
  177.             }),
    
  178.           );
    
  179.         },
    
  180.       });
    
  181.     });
    
  182. 
    
  183.     it('onCut', () => {
    
  184.       testNativeBubblingEvent({
    
  185.         type: 'div',
    
  186.         reactEvent: 'onCut',
    
  187.         reactEventType: 'cut',
    
  188.         nativeEvent: 'cut',
    
  189.         dispatch(node) {
    
  190.           node.dispatchEvent(
    
  191.             new Event('cut', {
    
  192.               bubbles: true,
    
  193.               cancelable: true,
    
  194.             }),
    
  195.           );
    
  196.         },
    
  197.       });
    
  198.     });
    
  199. 
    
  200.     it('onDoubleClick', () => {
    
  201.       testNativeBubblingEvent({
    
  202.         type: 'div',
    
  203.         reactEvent: 'onDoubleClick',
    
  204.         reactEventType: 'dblclick',
    
  205.         nativeEvent: 'dblclick',
    
  206.         dispatch(node) {
    
  207.           node.dispatchEvent(
    
  208.             new KeyboardEvent('dblclick', {
    
  209.               bubbles: true,
    
  210.               cancelable: true,
    
  211.             }),
    
  212.           );
    
  213.         },
    
  214.       });
    
  215.     });
    
  216. 
    
  217.     it('onDrag', () => {
    
  218.       testNativeBubblingEvent({
    
  219.         type: 'div',
    
  220.         reactEvent: 'onDrag',
    
  221.         reactEventType: 'drag',
    
  222.         nativeEvent: 'drag',
    
  223.         dispatch(node) {
    
  224.           node.dispatchEvent(
    
  225.             new MouseEvent('drag', {
    
  226.               bubbles: true,
    
  227.               cancelable: true,
    
  228.             }),
    
  229.           );
    
  230.         },
    
  231.       });
    
  232.     });
    
  233. 
    
  234.     it('onDragEnd', () => {
    
  235.       testNativeBubblingEvent({
    
  236.         type: 'div',
    
  237.         reactEvent: 'onDragEnd',
    
  238.         reactEventType: 'dragend',
    
  239.         nativeEvent: 'dragend',
    
  240.         dispatch(node) {
    
  241.           node.dispatchEvent(
    
  242.             new MouseEvent('dragend', {
    
  243.               bubbles: true,
    
  244.               cancelable: true,
    
  245.             }),
    
  246.           );
    
  247.         },
    
  248.       });
    
  249.     });
    
  250. 
    
  251.     it('onDragEnter', () => {
    
  252.       testNativeBubblingEvent({
    
  253.         type: 'div',
    
  254.         reactEvent: 'onDragEnter',
    
  255.         reactEventType: 'dragenter',
    
  256.         nativeEvent: 'dragenter',
    
  257.         dispatch(node) {
    
  258.           node.dispatchEvent(
    
  259.             new MouseEvent('dragenter', {
    
  260.               bubbles: true,
    
  261.               cancelable: true,
    
  262.             }),
    
  263.           );
    
  264.         },
    
  265.       });
    
  266.     });
    
  267. 
    
  268.     it('onDragExit', () => {
    
  269.       testNativeBubblingEvent({
    
  270.         type: 'div',
    
  271.         reactEvent: 'onDragExit',
    
  272.         reactEventType: 'dragexit',
    
  273.         nativeEvent: 'dragexit',
    
  274.         dispatch(node) {
    
  275.           node.dispatchEvent(
    
  276.             new MouseEvent('dragexit', {
    
  277.               bubbles: true,
    
  278.               cancelable: true,
    
  279.             }),
    
  280.           );
    
  281.         },
    
  282.       });
    
  283.     });
    
  284. 
    
  285.     it('onDragLeave', () => {
    
  286.       testNativeBubblingEvent({
    
  287.         type: 'div',
    
  288.         reactEvent: 'onDragLeave',
    
  289.         reactEventType: 'dragleave',
    
  290.         nativeEvent: 'dragleave',
    
  291.         dispatch(node) {
    
  292.           node.dispatchEvent(
    
  293.             new MouseEvent('dragleave', {
    
  294.               bubbles: true,
    
  295.               cancelable: true,
    
  296.             }),
    
  297.           );
    
  298.         },
    
  299.       });
    
  300.     });
    
  301. 
    
  302.     it('onDragOver', () => {
    
  303.       testNativeBubblingEvent({
    
  304.         type: 'div',
    
  305.         reactEvent: 'onDragOver',
    
  306.         reactEventType: 'dragover',
    
  307.         nativeEvent: 'dragover',
    
  308.         dispatch(node) {
    
  309.           node.dispatchEvent(
    
  310.             new MouseEvent('dragover', {
    
  311.               bubbles: true,
    
  312.               cancelable: true,
    
  313.             }),
    
  314.           );
    
  315.         },
    
  316.       });
    
  317.     });
    
  318. 
    
  319.     it('onDragStart', () => {
    
  320.       testNativeBubblingEvent({
    
  321.         type: 'div',
    
  322.         reactEvent: 'onDragStart',
    
  323.         reactEventType: 'dragstart',
    
  324.         nativeEvent: 'dragstart',
    
  325.         dispatch(node) {
    
  326.           node.dispatchEvent(
    
  327.             new MouseEvent('dragstart', {
    
  328.               bubbles: true,
    
  329.               cancelable: true,
    
  330.             }),
    
  331.           );
    
  332.         },
    
  333.       });
    
  334.     });
    
  335. 
    
  336.     it('onDrop', () => {
    
  337.       testNativeBubblingEvent({
    
  338.         type: 'div',
    
  339.         reactEvent: 'onDrop',
    
  340.         reactEventType: 'drop',
    
  341.         nativeEvent: 'drop',
    
  342.         dispatch(node) {
    
  343.           node.dispatchEvent(
    
  344.             new MouseEvent('drop', {
    
  345.               bubbles: true,
    
  346.               cancelable: true,
    
  347.             }),
    
  348.           );
    
  349.         },
    
  350.       });
    
  351.     });
    
  352. 
    
  353.     it('onFocus', () => {
    
  354.       testNativeBubblingEvent({
    
  355.         type: 'input',
    
  356.         reactEvent: 'onFocus',
    
  357.         reactEventType: 'focus',
    
  358.         nativeEvent: 'focusin',
    
  359.         dispatch(node) {
    
  360.           const e = new Event('focusin', {
    
  361.             bubbles: true,
    
  362.             cancelable: true,
    
  363.           });
    
  364.           node.dispatchEvent(e);
    
  365.         },
    
  366.       });
    
  367.     });
    
  368. 
    
  369.     it('onGotPointerCapture', () => {
    
  370.       testNativeBubblingEvent({
    
  371.         type: 'div',
    
  372.         reactEvent: 'onGotPointerCapture',
    
  373.         reactEventType: 'gotpointercapture',
    
  374.         nativeEvent: 'gotpointercapture',
    
  375.         dispatch(node) {
    
  376.           node.dispatchEvent(
    
  377.             new Event('gotpointercapture', {
    
  378.               bubbles: true,
    
  379.               cancelable: true,
    
  380.             }),
    
  381.           );
    
  382.         },
    
  383.       });
    
  384.     });
    
  385. 
    
  386.     it('onKeyDown', () => {
    
  387.       testNativeBubblingEvent({
    
  388.         type: 'input',
    
  389.         reactEvent: 'onKeyDown',
    
  390.         reactEventType: 'keydown',
    
  391.         nativeEvent: 'keydown',
    
  392.         dispatch(node) {
    
  393.           node.dispatchEvent(
    
  394.             new KeyboardEvent('keydown', {
    
  395.               bubbles: true,
    
  396.               cancelable: true,
    
  397.             }),
    
  398.           );
    
  399.         },
    
  400.       });
    
  401.     });
    
  402. 
    
  403.     it('onKeyPress', () => {
    
  404.       testNativeBubblingEvent({
    
  405.         type: 'input',
    
  406.         reactEvent: 'onKeyPress',
    
  407.         reactEventType: 'keypress',
    
  408.         nativeEvent: 'keypress',
    
  409.         dispatch(node) {
    
  410.           node.dispatchEvent(
    
  411.             new KeyboardEvent('keypress', {
    
  412.               keyCode: 13,
    
  413.               bubbles: true,
    
  414.               cancelable: true,
    
  415.             }),
    
  416.           );
    
  417.         },
    
  418.       });
    
  419.     });
    
  420. 
    
  421.     it('onKeyUp', () => {
    
  422.       testNativeBubblingEvent({
    
  423.         type: 'input',
    
  424.         reactEvent: 'onKeyUp',
    
  425.         reactEventType: 'keyup',
    
  426.         nativeEvent: 'keyup',
    
  427.         dispatch(node) {
    
  428.           node.dispatchEvent(
    
  429.             new KeyboardEvent('keyup', {
    
  430.               bubbles: true,
    
  431.               cancelable: true,
    
  432.             }),
    
  433.           );
    
  434.         },
    
  435.       });
    
  436.     });
    
  437. 
    
  438.     it('onLostPointerCapture', () => {
    
  439.       testNativeBubblingEvent({
    
  440.         type: 'div',
    
  441.         reactEvent: 'onLostPointerCapture',
    
  442.         reactEventType: 'lostpointercapture',
    
  443.         nativeEvent: 'lostpointercapture',
    
  444.         dispatch(node) {
    
  445.           node.dispatchEvent(
    
  446.             new Event('lostpointercapture', {
    
  447.               bubbles: true,
    
  448.               cancelable: true,
    
  449.             }),
    
  450.           );
    
  451.         },
    
  452.       });
    
  453.     });
    
  454. 
    
  455.     it('onMouseDown', () => {
    
  456.       testNativeBubblingEvent({
    
  457.         type: 'div',
    
  458.         reactEvent: 'onMouseDown',
    
  459.         reactEventType: 'mousedown',
    
  460.         nativeEvent: 'mousedown',
    
  461.         dispatch(node) {
    
  462.           node.dispatchEvent(
    
  463.             new MouseEvent('mousedown', {
    
  464.               bubbles: true,
    
  465.               cancelable: true,
    
  466.             }),
    
  467.           );
    
  468.         },
    
  469.       });
    
  470.     });
    
  471. 
    
  472.     it('onMouseOut', () => {
    
  473.       testNativeBubblingEvent({
    
  474.         type: 'div',
    
  475.         reactEvent: 'onMouseOut',
    
  476.         reactEventType: 'mouseout',
    
  477.         nativeEvent: 'mouseout',
    
  478.         dispatch(node) {
    
  479.           node.dispatchEvent(
    
  480.             new MouseEvent('mouseout', {
    
  481.               bubbles: true,
    
  482.               cancelable: true,
    
  483.             }),
    
  484.           );
    
  485.         },
    
  486.       });
    
  487.     });
    
  488. 
    
  489.     it('onMouseOver', () => {
    
  490.       testNativeBubblingEvent({
    
  491.         type: 'div',
    
  492.         reactEvent: 'onMouseOver',
    
  493.         reactEventType: 'mouseover',
    
  494.         nativeEvent: 'mouseover',
    
  495.         dispatch(node) {
    
  496.           node.dispatchEvent(
    
  497.             new MouseEvent('mouseover', {
    
  498.               bubbles: true,
    
  499.               cancelable: true,
    
  500.             }),
    
  501.           );
    
  502.         },
    
  503.       });
    
  504.     });
    
  505. 
    
  506.     it('onMouseUp', () => {
    
  507.       testNativeBubblingEvent({
    
  508.         type: 'div',
    
  509.         reactEvent: 'onMouseUp',
    
  510.         reactEventType: 'mouseup',
    
  511.         nativeEvent: 'mouseup',
    
  512.         dispatch(node) {
    
  513.           node.dispatchEvent(
    
  514.             new MouseEvent('mouseup', {
    
  515.               bubbles: true,
    
  516.               cancelable: true,
    
  517.             }),
    
  518.           );
    
  519.         },
    
  520.       });
    
  521.     });
    
  522. 
    
  523.     it('onPaste', () => {
    
  524.       testNativeBubblingEvent({
    
  525.         type: 'div',
    
  526.         reactEvent: 'onPaste',
    
  527.         reactEventType: 'paste',
    
  528.         nativeEvent: 'paste',
    
  529.         dispatch(node) {
    
  530.           node.dispatchEvent(
    
  531.             new Event('paste', {
    
  532.               bubbles: true,
    
  533.               cancelable: true,
    
  534.             }),
    
  535.           );
    
  536.         },
    
  537.       });
    
  538.     });
    
  539. 
    
  540.     it('onPointerCancel', () => {
    
  541.       testNativeBubblingEvent({
    
  542.         type: 'div',
    
  543.         reactEvent: 'onPointerCancel',
    
  544.         reactEventType: 'pointercancel',
    
  545.         nativeEvent: 'pointercancel',
    
  546.         dispatch(node) {
    
  547.           node.dispatchEvent(
    
  548.             new Event('pointercancel', {
    
  549.               bubbles: true,
    
  550.               cancelable: true,
    
  551.             }),
    
  552.           );
    
  553.         },
    
  554.       });
    
  555.     });
    
  556. 
    
  557.     it('onPointerDown', () => {
    
  558.       testNativeBubblingEvent({
    
  559.         type: 'div',
    
  560.         reactEvent: 'onPointerDown',
    
  561.         reactEventType: 'pointerdown',
    
  562.         nativeEvent: 'pointerdown',
    
  563.         dispatch(node) {
    
  564.           node.dispatchEvent(
    
  565.             new Event('pointerdown', {
    
  566.               bubbles: true,
    
  567.               cancelable: true,
    
  568.             }),
    
  569.           );
    
  570.         },
    
  571.       });
    
  572.     });
    
  573. 
    
  574.     it('onPointerMove', () => {
    
  575.       testNativeBubblingEvent({
    
  576.         type: 'div',
    
  577.         reactEvent: 'onPointerMove',
    
  578.         reactEventType: 'pointermove',
    
  579.         nativeEvent: 'pointermove',
    
  580.         dispatch(node) {
    
  581.           node.dispatchEvent(
    
  582.             new Event('pointermove', {
    
  583.               bubbles: true,
    
  584.               cancelable: true,
    
  585.             }),
    
  586.           );
    
  587.         },
    
  588.       });
    
  589.     });
    
  590. 
    
  591.     it('onPointerOut', () => {
    
  592.       testNativeBubblingEvent({
    
  593.         type: 'div',
    
  594.         reactEvent: 'onPointerOut',
    
  595.         reactEventType: 'pointerout',
    
  596.         nativeEvent: 'pointerout',
    
  597.         dispatch(node) {
    
  598.           node.dispatchEvent(
    
  599.             new Event('pointerout', {
    
  600.               bubbles: true,
    
  601.               cancelable: true,
    
  602.             }),
    
  603.           );
    
  604.         },
    
  605.       });
    
  606.     });
    
  607. 
    
  608.     it('onPointerOver', () => {
    
  609.       testNativeBubblingEvent({
    
  610.         type: 'div',
    
  611.         reactEvent: 'onPointerOver',
    
  612.         reactEventType: 'pointerover',
    
  613.         nativeEvent: 'pointerover',
    
  614.         dispatch(node) {
    
  615.           node.dispatchEvent(
    
  616.             new Event('pointerover', {
    
  617.               bubbles: true,
    
  618.               cancelable: true,
    
  619.             }),
    
  620.           );
    
  621.         },
    
  622.       });
    
  623.     });
    
  624. 
    
  625.     it('onPointerUp', () => {
    
  626.       testNativeBubblingEvent({
    
  627.         type: 'div',
    
  628.         reactEvent: 'onPointerUp',
    
  629.         reactEventType: 'pointerup',
    
  630.         nativeEvent: 'pointerup',
    
  631.         dispatch(node) {
    
  632.           node.dispatchEvent(
    
  633.             new Event('pointerup', {
    
  634.               bubbles: true,
    
  635.               cancelable: true,
    
  636.             }),
    
  637.           );
    
  638.         },
    
  639.       });
    
  640.     });
    
  641. 
    
  642.     it('onReset', () => {
    
  643.       testNativeBubblingEvent({
    
  644.         type: 'form',
    
  645.         reactEvent: 'onReset',
    
  646.         reactEventType: 'reset',
    
  647.         nativeEvent: 'reset',
    
  648.         dispatch(node) {
    
  649.           const e = new Event('reset', {
    
  650.             bubbles: true,
    
  651.             cancelable: true,
    
  652.           });
    
  653.           node.dispatchEvent(e);
    
  654.         },
    
  655.       });
    
  656.     });
    
  657. 
    
  658.     it('onSubmit', () => {
    
  659.       testNativeBubblingEvent({
    
  660.         type: 'form',
    
  661.         reactEvent: 'onSubmit',
    
  662.         reactEventType: 'submit',
    
  663.         nativeEvent: 'submit',
    
  664.         dispatch(node) {
    
  665.           const e = new Event('submit', {
    
  666.             bubbles: true,
    
  667.             cancelable: true,
    
  668.           });
    
  669.           node.dispatchEvent(e);
    
  670.         },
    
  671.       });
    
  672.     });
    
  673. 
    
  674.     it('onTouchCancel', () => {
    
  675.       testNativeBubblingEvent({
    
  676.         type: 'div',
    
  677.         reactEvent: 'onTouchCancel',
    
  678.         reactEventType: 'touchcancel',
    
  679.         nativeEvent: 'touchcancel',
    
  680.         dispatch(node) {
    
  681.           node.dispatchEvent(
    
  682.             new Event('touchcancel', {
    
  683.               bubbles: true,
    
  684.               cancelable: true,
    
  685.             }),
    
  686.           );
    
  687.         },
    
  688.       });
    
  689.     });
    
  690. 
    
  691.     it('onTouchEnd', () => {
    
  692.       testNativeBubblingEvent({
    
  693.         type: 'div',
    
  694.         reactEvent: 'onTouchEnd',
    
  695.         reactEventType: 'touchend',
    
  696.         nativeEvent: 'touchend',
    
  697.         dispatch(node) {
    
  698.           node.dispatchEvent(
    
  699.             new Event('touchend', {
    
  700.               bubbles: true,
    
  701.               cancelable: true,
    
  702.             }),
    
  703.           );
    
  704.         },
    
  705.       });
    
  706.     });
    
  707. 
    
  708.     it('onTouchMove', () => {
    
  709.       testNativeBubblingEvent({
    
  710.         type: 'div',
    
  711.         reactEvent: 'onTouchMove',
    
  712.         reactEventType: 'touchmove',
    
  713.         nativeEvent: 'touchmove',
    
  714.         dispatch(node) {
    
  715.           node.dispatchEvent(
    
  716.             new Event('touchmove', {
    
  717.               bubbles: true,
    
  718.               cancelable: true,
    
  719.             }),
    
  720.           );
    
  721.         },
    
  722.       });
    
  723.     });
    
  724. 
    
  725.     it('onTouchStart', () => {
    
  726.       testNativeBubblingEvent({
    
  727.         type: 'div',
    
  728.         reactEvent: 'onTouchStart',
    
  729.         reactEventType: 'touchstart',
    
  730.         nativeEvent: 'touchstart',
    
  731.         dispatch(node) {
    
  732.           node.dispatchEvent(
    
  733.             new Event('touchstart', {
    
  734.               bubbles: true,
    
  735.               cancelable: true,
    
  736.             }),
    
  737.           );
    
  738.         },
    
  739.       });
    
  740.     });
    
  741. 
    
  742.     it('onTransitionEnd', () => {
    
  743.       testNativeBubblingEvent({
    
  744.         type: 'div',
    
  745.         reactEvent: 'onTransitionEnd',
    
  746.         reactEventType: 'transitionend',
    
  747.         nativeEvent: 'transitionend',
    
  748.         dispatch(node) {
    
  749.           node.dispatchEvent(
    
  750.             new Event('transitionend', {
    
  751.               bubbles: true,
    
  752.               cancelable: true,
    
  753.             }),
    
  754.           );
    
  755.         },
    
  756.       });
    
  757.     });
    
  758. 
    
  759.     it('onWheel', () => {
    
  760.       testNativeBubblingEvent({
    
  761.         type: 'div',
    
  762.         reactEvent: 'onWheel',
    
  763.         reactEventType: 'wheel',
    
  764.         nativeEvent: 'wheel',
    
  765.         dispatch(node) {
    
  766.           node.dispatchEvent(
    
  767.             new Event('wheel', {
    
  768.               bubbles: true,
    
  769.               cancelable: true,
    
  770.             }),
    
  771.           );
    
  772.         },
    
  773.       });
    
  774.     });
    
  775.   });
    
  776. 
    
  777.   describe('non-bubbling events that bubble in React', () => {
    
  778.     it('onAbort', () => {
    
  779.       testEmulatedBubblingEvent({
    
  780.         type: 'video',
    
  781.         reactEvent: 'onAbort',
    
  782.         reactEventType: 'abort',
    
  783.         nativeEvent: 'abort',
    
  784.         dispatch(node) {
    
  785.           const e = new Event('abort', {
    
  786.             bubbles: false,
    
  787.             cancelable: true,
    
  788.           });
    
  789.           node.dispatchEvent(e);
    
  790.         },
    
  791.       });
    
  792.     });
    
  793. 
    
  794.     it('onCancel', () => {
    
  795.       testEmulatedBubblingEvent({
    
  796.         type: 'dialog',
    
  797.         reactEvent: 'onCancel',
    
  798.         reactEventType: 'cancel',
    
  799.         nativeEvent: 'cancel',
    
  800.         dispatch(node) {
    
  801.           const e = new Event('cancel', {
    
  802.             bubbles: false,
    
  803.             cancelable: true,
    
  804.           });
    
  805.           node.dispatchEvent(e);
    
  806.         },
    
  807.       });
    
  808.     });
    
  809. 
    
  810.     it('onCanPlay', () => {
    
  811.       testEmulatedBubblingEvent({
    
  812.         type: 'video',
    
  813.         reactEvent: 'onCanPlay',
    
  814.         reactEventType: 'canplay',
    
  815.         nativeEvent: 'canplay',
    
  816.         dispatch(node) {
    
  817.           const e = new Event('canplay', {
    
  818.             bubbles: false,
    
  819.             cancelable: true,
    
  820.           });
    
  821.           node.dispatchEvent(e);
    
  822.         },
    
  823.       });
    
  824.     });
    
  825. 
    
  826.     it('onCanPlayThrough', () => {
    
  827.       testEmulatedBubblingEvent({
    
  828.         type: 'video',
    
  829.         reactEvent: 'onCanPlayThrough',
    
  830.         reactEventType: 'canplaythrough',
    
  831.         nativeEvent: 'canplaythrough',
    
  832.         dispatch(node) {
    
  833.           const e = new Event('canplaythrough', {
    
  834.             bubbles: false,
    
  835.             cancelable: true,
    
  836.           });
    
  837.           node.dispatchEvent(e);
    
  838.         },
    
  839.       });
    
  840.     });
    
  841. 
    
  842.     it('onClose', () => {
    
  843.       testEmulatedBubblingEvent({
    
  844.         type: 'dialog',
    
  845.         reactEvent: 'onClose',
    
  846.         reactEventType: 'close',
    
  847.         nativeEvent: 'close',
    
  848.         dispatch(node) {
    
  849.           const e = new Event('close', {
    
  850.             bubbles: false,
    
  851.             cancelable: true,
    
  852.           });
    
  853.           node.dispatchEvent(e);
    
  854.         },
    
  855.       });
    
  856.     });
    
  857. 
    
  858.     it('onDurationChange', () => {
    
  859.       testEmulatedBubblingEvent({
    
  860.         type: 'video',
    
  861.         reactEvent: 'onDurationChange',
    
  862.         reactEventType: 'durationchange',
    
  863.         nativeEvent: 'durationchange',
    
  864.         dispatch(node) {
    
  865.           const e = new Event('durationchange', {
    
  866.             bubbles: false,
    
  867.             cancelable: true,
    
  868.           });
    
  869.           node.dispatchEvent(e);
    
  870.         },
    
  871.       });
    
  872.     });
    
  873. 
    
  874.     it('onEmptied', () => {
    
  875.       testEmulatedBubblingEvent({
    
  876.         type: 'video',
    
  877.         reactEvent: 'onEmptied',
    
  878.         reactEventType: 'emptied',
    
  879.         nativeEvent: 'emptied',
    
  880.         dispatch(node) {
    
  881.           const e = new Event('emptied', {
    
  882.             bubbles: false,
    
  883.             cancelable: true,
    
  884.           });
    
  885.           node.dispatchEvent(e);
    
  886.         },
    
  887.       });
    
  888.     });
    
  889. 
    
  890.     it('onEncrypted', () => {
    
  891.       testEmulatedBubblingEvent({
    
  892.         type: 'video',
    
  893.         reactEvent: 'onEncrypted',
    
  894.         reactEventType: 'encrypted',
    
  895.         nativeEvent: 'encrypted',
    
  896.         dispatch(node) {
    
  897.           const e = new Event('encrypted', {
    
  898.             bubbles: false,
    
  899.             cancelable: true,
    
  900.           });
    
  901.           node.dispatchEvent(e);
    
  902.         },
    
  903.       });
    
  904.     });
    
  905. 
    
  906.     it('onEnded', () => {
    
  907.       testEmulatedBubblingEvent({
    
  908.         type: 'video',
    
  909.         reactEvent: 'onEnded',
    
  910.         reactEventType: 'ended',
    
  911.         nativeEvent: 'ended',
    
  912.         dispatch(node) {
    
  913.           const e = new Event('ended', {
    
  914.             bubbles: false,
    
  915.             cancelable: true,
    
  916.           });
    
  917.           node.dispatchEvent(e);
    
  918.         },
    
  919.       });
    
  920.     });
    
  921. 
    
  922.     it('onError', () => {
    
  923.       testEmulatedBubblingEvent({
    
  924.         type: 'img',
    
  925.         reactEvent: 'onError',
    
  926.         reactEventType: 'error',
    
  927.         nativeEvent: 'error',
    
  928.         dispatch(node) {
    
  929.           const e = new Event('error', {
    
  930.             bubbles: false,
    
  931.             cancelable: true,
    
  932.           });
    
  933.           node.dispatchEvent(e);
    
  934.         },
    
  935.       });
    
  936.     });
    
  937. 
    
  938.     it('onInvalid', () => {
    
  939.       testEmulatedBubblingEvent({
    
  940.         type: 'input',
    
  941.         reactEvent: 'onInvalid',
    
  942.         reactEventType: 'invalid',
    
  943.         nativeEvent: 'invalid',
    
  944.         dispatch(node) {
    
  945.           const e = new Event('invalid', {
    
  946.             bubbles: false,
    
  947.             cancelable: true,
    
  948.           });
    
  949.           node.dispatchEvent(e);
    
  950.         },
    
  951.       });
    
  952.     });
    
  953. 
    
  954.     it('onLoad', () => {
    
  955.       testEmulatedBubblingEvent({
    
  956.         type: 'img',
    
  957.         reactEvent: 'onLoad',
    
  958.         reactEventType: 'load',
    
  959.         nativeEvent: 'load',
    
  960.         dispatch(node) {
    
  961.           const e = new Event('load', {
    
  962.             bubbles: false,
    
  963.             cancelable: true,
    
  964.           });
    
  965.           node.dispatchEvent(e);
    
  966.         },
    
  967.       });
    
  968.     });
    
  969. 
    
  970.     it('onLoadedData', () => {
    
  971.       testEmulatedBubblingEvent({
    
  972.         type: 'video',
    
  973.         reactEvent: 'onLoadedData',
    
  974.         reactEventType: 'loadeddata',
    
  975.         nativeEvent: 'loadeddata',
    
  976.         dispatch(node) {
    
  977.           const e = new Event('loadeddata', {
    
  978.             bubbles: false,
    
  979.             cancelable: true,
    
  980.           });
    
  981.           node.dispatchEvent(e);
    
  982.         },
    
  983.       });
    
  984.     });
    
  985. 
    
  986.     it('onLoadedMetadata', () => {
    
  987.       testEmulatedBubblingEvent({
    
  988.         type: 'video',
    
  989.         reactEvent: 'onLoadedMetadata',
    
  990.         reactEventType: 'loadedmetadata',
    
  991.         nativeEvent: 'loadedmetadata',
    
  992.         dispatch(node) {
    
  993.           const e = new Event('loadedmetadata', {
    
  994.             bubbles: false,
    
  995.             cancelable: true,
    
  996.           });
    
  997.           node.dispatchEvent(e);
    
  998.         },
    
  999.       });
    
  1000.     });
    
  1001. 
    
  1002.     it('onLoadStart', () => {
    
  1003.       testEmulatedBubblingEvent({
    
  1004.         type: 'video',
    
  1005.         reactEvent: 'onLoadStart',
    
  1006.         reactEventType: 'loadstart',
    
  1007.         nativeEvent: 'loadstart',
    
  1008.         dispatch(node) {
    
  1009.           const e = new Event('loadstart', {
    
  1010.             bubbles: false,
    
  1011.             cancelable: true,
    
  1012.           });
    
  1013.           node.dispatchEvent(e);
    
  1014.         },
    
  1015.       });
    
  1016.     });
    
  1017. 
    
  1018.     it('onPause', () => {
    
  1019.       testEmulatedBubblingEvent({
    
  1020.         type: 'video',
    
  1021.         reactEvent: 'onPause',
    
  1022.         reactEventType: 'pause',
    
  1023.         nativeEvent: 'pause',
    
  1024.         dispatch(node) {
    
  1025.           const e = new Event('pause', {
    
  1026.             bubbles: false,
    
  1027.             cancelable: true,
    
  1028.           });
    
  1029.           node.dispatchEvent(e);
    
  1030.         },
    
  1031.       });
    
  1032.     });
    
  1033. 
    
  1034.     it('onPlay', () => {
    
  1035.       testEmulatedBubblingEvent({
    
  1036.         type: 'video',
    
  1037.         reactEvent: 'onPlay',
    
  1038.         reactEventType: 'play',
    
  1039.         nativeEvent: 'play',
    
  1040.         dispatch(node) {
    
  1041.           const e = new Event('play', {
    
  1042.             bubbles: false,
    
  1043.             cancelable: true,
    
  1044.           });
    
  1045.           node.dispatchEvent(e);
    
  1046.         },
    
  1047.       });
    
  1048.     });
    
  1049. 
    
  1050.     it('onPlaying', () => {
    
  1051.       testEmulatedBubblingEvent({
    
  1052.         type: 'video',
    
  1053.         reactEvent: 'onPlaying',
    
  1054.         reactEventType: 'playing',
    
  1055.         nativeEvent: 'playing',
    
  1056.         dispatch(node) {
    
  1057.           const e = new Event('playing', {
    
  1058.             bubbles: false,
    
  1059.             cancelable: true,
    
  1060.           });
    
  1061.           node.dispatchEvent(e);
    
  1062.         },
    
  1063.       });
    
  1064.     });
    
  1065. 
    
  1066.     it('onProgress', () => {
    
  1067.       testEmulatedBubblingEvent({
    
  1068.         type: 'video',
    
  1069.         reactEvent: 'onProgress',
    
  1070.         reactEventType: 'progress',
    
  1071.         nativeEvent: 'progress',
    
  1072.         dispatch(node) {
    
  1073.           const e = new Event('progress', {
    
  1074.             bubbles: false,
    
  1075.             cancelable: true,
    
  1076.           });
    
  1077.           node.dispatchEvent(e);
    
  1078.         },
    
  1079.       });
    
  1080.     });
    
  1081. 
    
  1082.     it('onRateChange', () => {
    
  1083.       testEmulatedBubblingEvent({
    
  1084.         type: 'video',
    
  1085.         reactEvent: 'onRateChange',
    
  1086.         reactEventType: 'ratechange',
    
  1087.         nativeEvent: 'ratechange',
    
  1088.         dispatch(node) {
    
  1089.           const e = new Event('ratechange', {
    
  1090.             bubbles: false,
    
  1091.             cancelable: true,
    
  1092.           });
    
  1093.           node.dispatchEvent(e);
    
  1094.         },
    
  1095.       });
    
  1096.     });
    
  1097. 
    
  1098.     it('onResize', () => {
    
  1099.       testEmulatedBubblingEvent({
    
  1100.         type: 'video',
    
  1101.         reactEvent: 'onResize',
    
  1102.         reactEventType: 'resize',
    
  1103.         nativeEvent: 'resize',
    
  1104.         dispatch(node) {
    
  1105.           const e = new Event('resize', {
    
  1106.             bubbles: false,
    
  1107.             cancelable: true,
    
  1108.           });
    
  1109.           node.dispatchEvent(e);
    
  1110.         },
    
  1111.       });
    
  1112.     });
    
  1113. 
    
  1114.     it('onSeeked', () => {
    
  1115.       testEmulatedBubblingEvent({
    
  1116.         type: 'video',
    
  1117.         reactEvent: 'onSeeked',
    
  1118.         reactEventType: 'seeked',
    
  1119.         nativeEvent: 'seeked',
    
  1120.         dispatch(node) {
    
  1121.           const e = new Event('seeked', {
    
  1122.             bubbles: false,
    
  1123.             cancelable: true,
    
  1124.           });
    
  1125.           node.dispatchEvent(e);
    
  1126.         },
    
  1127.       });
    
  1128.     });
    
  1129. 
    
  1130.     it('onSeeking', () => {
    
  1131.       testEmulatedBubblingEvent({
    
  1132.         type: 'video',
    
  1133.         reactEvent: 'onSeeking',
    
  1134.         reactEventType: 'seeking',
    
  1135.         nativeEvent: 'seeking',
    
  1136.         dispatch(node) {
    
  1137.           const e = new Event('seeking', {
    
  1138.             bubbles: false,
    
  1139.             cancelable: true,
    
  1140.           });
    
  1141.           node.dispatchEvent(e);
    
  1142.         },
    
  1143.       });
    
  1144.     });
    
  1145. 
    
  1146.     it('onStalled', () => {
    
  1147.       testEmulatedBubblingEvent({
    
  1148.         type: 'video',
    
  1149.         reactEvent: 'onStalled',
    
  1150.         reactEventType: 'stalled',
    
  1151.         nativeEvent: 'stalled',
    
  1152.         dispatch(node) {
    
  1153.           const e = new Event('stalled', {
    
  1154.             bubbles: false,
    
  1155.             cancelable: true,
    
  1156.           });
    
  1157.           node.dispatchEvent(e);
    
  1158.         },
    
  1159.       });
    
  1160.     });
    
  1161. 
    
  1162.     it('onSuspend', () => {
    
  1163.       testEmulatedBubblingEvent({
    
  1164.         type: 'video',
    
  1165.         reactEvent: 'onSuspend',
    
  1166.         reactEventType: 'suspend',
    
  1167.         nativeEvent: 'suspend',
    
  1168.         dispatch(node) {
    
  1169.           const e = new Event('suspend', {
    
  1170.             bubbles: false,
    
  1171.             cancelable: true,
    
  1172.           });
    
  1173.           node.dispatchEvent(e);
    
  1174.         },
    
  1175.       });
    
  1176.     });
    
  1177. 
    
  1178.     it('onTimeUpdate', () => {
    
  1179.       testEmulatedBubblingEvent({
    
  1180.         type: 'video',
    
  1181.         reactEvent: 'onTimeUpdate',
    
  1182.         reactEventType: 'timeupdate',
    
  1183.         nativeEvent: 'timeupdate',
    
  1184.         dispatch(node) {
    
  1185.           const e = new Event('timeupdate', {
    
  1186.             bubbles: false,
    
  1187.             cancelable: true,
    
  1188.           });
    
  1189.           node.dispatchEvent(e);
    
  1190.         },
    
  1191.       });
    
  1192.     });
    
  1193. 
    
  1194.     it('onToggle', () => {
    
  1195.       testEmulatedBubblingEvent({
    
  1196.         type: 'details',
    
  1197.         reactEvent: 'onToggle',
    
  1198.         reactEventType: 'toggle',
    
  1199.         nativeEvent: 'toggle',
    
  1200.         dispatch(node) {
    
  1201.           const e = new Event('toggle', {
    
  1202.             bubbles: false,
    
  1203.             cancelable: true,
    
  1204.           });
    
  1205.           node.dispatchEvent(e);
    
  1206.         },
    
  1207.       });
    
  1208.     });
    
  1209. 
    
  1210.     it('onVolumeChange', () => {
    
  1211.       testEmulatedBubblingEvent({
    
  1212.         type: 'video',
    
  1213.         reactEvent: 'onVolumeChange',
    
  1214.         reactEventType: 'volumechange',
    
  1215.         nativeEvent: 'volumechange',
    
  1216.         dispatch(node) {
    
  1217.           const e = new Event('volumechange', {
    
  1218.             bubbles: false,
    
  1219.             cancelable: true,
    
  1220.           });
    
  1221.           node.dispatchEvent(e);
    
  1222.         },
    
  1223.       });
    
  1224.     });
    
  1225. 
    
  1226.     it('onWaiting', () => {
    
  1227.       testEmulatedBubblingEvent({
    
  1228.         type: 'video',
    
  1229.         reactEvent: 'onWaiting',
    
  1230.         reactEventType: 'waiting',
    
  1231.         nativeEvent: 'waiting',
    
  1232.         dispatch(node) {
    
  1233.           const e = new Event('waiting', {
    
  1234.             bubbles: false,
    
  1235.             cancelable: true,
    
  1236.           });
    
  1237.           node.dispatchEvent(e);
    
  1238.         },
    
  1239.       });
    
  1240.     });
    
  1241.   });
    
  1242. 
    
  1243.   describe('non-bubbling events that do not bubble in React', () => {
    
  1244.     it('onScroll', () => {
    
  1245.       testNonBubblingEvent({
    
  1246.         type: 'div',
    
  1247.         reactEvent: 'onScroll',
    
  1248.         reactEventType: 'scroll',
    
  1249.         nativeEvent: 'scroll',
    
  1250.         dispatch(node) {
    
  1251.           const e = new Event('scroll', {
    
  1252.             bubbles: false,
    
  1253.             cancelable: true,
    
  1254.           });
    
  1255.           node.dispatchEvent(e);
    
  1256.         },
    
  1257.       });
    
  1258.     });
    
  1259. 
    
  1260.     it('onScrollEnd', () => {
    
  1261.       testNonBubblingEvent({
    
  1262.         type: 'div',
    
  1263.         reactEvent: 'onScrollEnd',
    
  1264.         reactEventType: 'scrollend',
    
  1265.         nativeEvent: 'scrollend',
    
  1266.         dispatch(node) {
    
  1267.           const e = new Event('scrollend', {
    
  1268.             bubbles: false,
    
  1269.             cancelable: true,
    
  1270.           });
    
  1271.           node.dispatchEvent(e);
    
  1272.         },
    
  1273.       });
    
  1274.     });
    
  1275.   });
    
  1276. 
    
  1277.   // The tests for these events are currently very limited
    
  1278.   // because they are fully synthetic, and so they don't
    
  1279.   // work very well across different roots. For now, we'll
    
  1280.   // just document the current state in these tests.
    
  1281.   describe('enter/leave events', () => {
    
  1282.     it('onMouseEnter and onMouseLeave', () => {
    
  1283.       const log = [];
    
  1284.       const targetRef = React.createRef();
    
  1285.       render(
    
  1286.         <Fixture
    
  1287.           type="div"
    
  1288.           targetRef={targetRef}
    
  1289.           targetProps={{
    
  1290.             onMouseEnter: e => {
    
  1291.               log.push('---- inner enter');
    
  1292.             },
    
  1293.             onMouseLeave: e => {
    
  1294.               log.push('---- inner leave');
    
  1295.             },
    
  1296.           }}
    
  1297.           parentProps={{
    
  1298.             onMouseEnter: e => {
    
  1299.               log.push('--- inner parent enter');
    
  1300.             },
    
  1301.             onMouseLeave: e => {
    
  1302.               log.push('--- inner parent leave');
    
  1303.             },
    
  1304.           }}
    
  1305.           outerProps={{
    
  1306.             onMouseEnter: e => {
    
  1307.               log.push('-- outer enter');
    
  1308.             },
    
  1309.             onMouseLeave: e => {
    
  1310.               log.push('-- outer leave');
    
  1311.             },
    
  1312.           }}
    
  1313.           outerParentProps={{
    
  1314.             onMouseEnter: e => {
    
  1315.               log.push('- outer parent enter');
    
  1316.             },
    
  1317.             onMouseLeave: e => {
    
  1318.               log.push('- outer parent leave');
    
  1319.             },
    
  1320.           }}
    
  1321.         />,
    
  1322.       );
    
  1323.       expect(log.length).toBe(0);
    
  1324.       targetRef.current.dispatchEvent(
    
  1325.         new MouseEvent('mouseover', {
    
  1326.           bubbles: true,
    
  1327.           cancelable: true,
    
  1328.           relatedTarget: null,
    
  1329.         }),
    
  1330.       );
    
  1331.       // This order isn't ideal because each root
    
  1332.       // has a separate traversal.
    
  1333.       expect(log).toEqual(unindent`
    
  1334.         --- inner parent enter
    
  1335.         ---- inner enter
    
  1336.         - outer parent enter
    
  1337.         -- outer enter
    
  1338.       `);
    
  1339.       log.length = 0;
    
  1340.       targetRef.current.dispatchEvent(
    
  1341.         new MouseEvent('mouseout', {
    
  1342.           bubbles: true,
    
  1343.           cancelable: true,
    
  1344.           relatedTarget: document.body,
    
  1345.         }),
    
  1346.       );
    
  1347.       expect(log).toEqual(unindent`
    
  1348.         ---- inner leave
    
  1349.         --- inner parent leave
    
  1350.         -- outer leave
    
  1351.         - outer parent leave
    
  1352.       `);
    
  1353.     });
    
  1354. 
    
  1355.     it('onPointerEnter and onPointerLeave', () => {
    
  1356.       const log = [];
    
  1357.       const targetRef = React.createRef();
    
  1358.       render(
    
  1359.         <Fixture
    
  1360.           type="div"
    
  1361.           targetRef={targetRef}
    
  1362.           targetProps={{
    
  1363.             onPointerEnter: e => {
    
  1364.               log.push('---- inner enter');
    
  1365.             },
    
  1366.             onPointerLeave: e => {
    
  1367.               log.push('---- inner leave');
    
  1368.             },
    
  1369.           }}
    
  1370.           parentProps={{
    
  1371.             onPointerEnter: e => {
    
  1372.               log.push('--- inner parent enter');
    
  1373.             },
    
  1374.             onPointerLeave: e => {
    
  1375.               log.push('--- inner parent leave');
    
  1376.             },
    
  1377.           }}
    
  1378.           outerProps={{
    
  1379.             onPointerEnter: e => {
    
  1380.               log.push('-- outer enter');
    
  1381.             },
    
  1382.             onPointerLeave: e => {
    
  1383.               log.push('-- outer leave');
    
  1384.             },
    
  1385.           }}
    
  1386.           outerParentProps={{
    
  1387.             onPointerEnter: e => {
    
  1388.               log.push('- outer parent enter');
    
  1389.             },
    
  1390.             onPointerLeave: e => {
    
  1391.               log.push('- outer parent leave');
    
  1392.             },
    
  1393.           }}
    
  1394.         />,
    
  1395.       );
    
  1396.       expect(log.length).toBe(0);
    
  1397.       targetRef.current.dispatchEvent(
    
  1398.         new Event('pointerover', {
    
  1399.           bubbles: true,
    
  1400.           cancelable: true,
    
  1401.           relatedTarget: null,
    
  1402.         }),
    
  1403.       );
    
  1404.       // This order isn't ideal because each root
    
  1405.       // has a separate traversal.
    
  1406.       expect(log).toEqual(unindent`
    
  1407.         --- inner parent enter
    
  1408.         ---- inner enter
    
  1409.         - outer parent enter
    
  1410.         -- outer enter
    
  1411.       `);
    
  1412.       log.length = 0;
    
  1413.       targetRef.current.dispatchEvent(
    
  1414.         new Event('pointerout', {
    
  1415.           bubbles: true,
    
  1416.           cancelable: true,
    
  1417.           relatedTarget: document.body,
    
  1418.         }),
    
  1419.       );
    
  1420.       expect(log).toEqual(unindent`
    
  1421.         ---- inner leave
    
  1422.         --- inner parent leave
    
  1423.         -- outer leave
    
  1424.         - outer parent leave
    
  1425.       `);
    
  1426.     });
    
  1427.   });
    
  1428. 
    
  1429.   const setUntrackedValue = Object.getOwnPropertyDescriptor(
    
  1430.     HTMLInputElement.prototype,
    
  1431.     'value',
    
  1432.   ).set;
    
  1433. 
    
  1434.   // The tests for these events are currently very limited
    
  1435.   // because they are fully synthetic, and so they don't
    
  1436.   // work very well across different roots. For now, we'll
    
  1437.   // just document the current state in these tests.
    
  1438.   describe('polyfilled events', () => {
    
  1439.     it('onBeforeInput', () => {
    
  1440.       const log = [];
    
  1441.       const targetRef = React.createRef();
    
  1442.       render(
    
  1443.         <Fixture
    
  1444.           type="input"
    
  1445.           targetRef={targetRef}
    
  1446.           targetProps={{
    
  1447.             onBeforeInput: e => {
    
  1448.               log.push('---- inner');
    
  1449.             },
    
  1450.             onBeforeInputCapture: e => {
    
  1451.               log.push('---- inner capture');
    
  1452.             },
    
  1453.           }}
    
  1454.           parentProps={{
    
  1455.             onBeforeInput: e => {
    
  1456.               log.push('--- inner parent');
    
  1457.             },
    
  1458.             onBeforeInputCapture: e => {
    
  1459.               log.push('--- inner parent capture');
    
  1460.             },
    
  1461.           }}
    
  1462.           outerProps={{
    
  1463.             onBeforeInput: e => {
    
  1464.               log.push('-- outer');
    
  1465.             },
    
  1466.             onBeforeInputCapture: e => {
    
  1467.               log.push('-- outer capture');
    
  1468.             },
    
  1469.           }}
    
  1470.           outerParentProps={{
    
  1471.             onBeforeInput: e => {
    
  1472.               log.push('- outer parent');
    
  1473.             },
    
  1474.             onBeforeInputCapture: e => {
    
  1475.               expect(e.type).toBe('beforeinput');
    
  1476.               log.push('- outer parent capture');
    
  1477.             },
    
  1478.           }}
    
  1479.         />,
    
  1480.       );
    
  1481.       expect(log.length).toBe(0);
    
  1482.       const e = new Event('textInput', {
    
  1483.         bubbles: true,
    
  1484.       });
    
  1485.       e.data = 'abcd';
    
  1486.       targetRef.current.dispatchEvent(e);
    
  1487.       // Since this is a polyfilled event,
    
  1488.       // the capture and bubble phases are
    
  1489.       // emulated, and don't align between roots.
    
  1490.       expect(log).toEqual(unindent`
    
  1491.         --- inner parent capture
    
  1492.         ---- inner capture
    
  1493.         ---- inner
    
  1494.         --- inner parent
    
  1495.         - outer parent capture
    
  1496.         -- outer capture
    
  1497.         -- outer
    
  1498.         - outer parent
    
  1499.       `);
    
  1500.     });
    
  1501. 
    
  1502.     it('onChange', () => {
    
  1503.       const log = [];
    
  1504.       const targetRef = React.createRef();
    
  1505.       render(
    
  1506.         <Fixture
    
  1507.           type="input"
    
  1508.           targetRef={targetRef}
    
  1509.           targetProps={{
    
  1510.             onChange: e => {
    
  1511.               log.push('---- inner');
    
  1512.             },
    
  1513.             onChangeCapture: e => {
    
  1514.               log.push('---- inner capture');
    
  1515.             },
    
  1516.           }}
    
  1517.           parentProps={{
    
  1518.             onChange: e => {
    
  1519.               log.push('--- inner parent');
    
  1520.             },
    
  1521.             onChangeCapture: e => {
    
  1522.               log.push('--- inner parent capture');
    
  1523.             },
    
  1524.           }}
    
  1525.           outerProps={{
    
  1526.             onChange: e => {
    
  1527.               log.push('-- outer');
    
  1528.             },
    
  1529.             onChangeCapture: e => {
    
  1530.               log.push('-- outer capture');
    
  1531.             },
    
  1532.           }}
    
  1533.           outerParentProps={{
    
  1534.             onChange: e => {
    
  1535.               log.push('- outer parent');
    
  1536.             },
    
  1537.             onChangeCapture: e => {
    
  1538.               expect(e.type).toBe('change');
    
  1539.               log.push('- outer parent capture');
    
  1540.             },
    
  1541.           }}
    
  1542.         />,
    
  1543.       );
    
  1544.       expect(log.length).toBe(0);
    
  1545.       setUntrackedValue.call(targetRef.current, 'hello');
    
  1546.       targetRef.current.dispatchEvent(
    
  1547.         new Event('input', {
    
  1548.           bubbles: true,
    
  1549.         }),
    
  1550.       );
    
  1551.       // The outer React doesn't receive the event at all
    
  1552.       // because it is not responsible for this input.
    
  1553.       expect(log).toEqual(unindent`
    
  1554.         --- inner parent capture
    
  1555.         ---- inner capture
    
  1556.         ---- inner
    
  1557.         --- inner parent
    
  1558.       `);
    
  1559.     });
    
  1560. 
    
  1561.     it('onCompositionStart', () => {
    
  1562.       const log = [];
    
  1563.       const targetRef = React.createRef();
    
  1564.       render(
    
  1565.         <Fixture
    
  1566.           type="input"
    
  1567.           targetRef={targetRef}
    
  1568.           targetProps={{
    
  1569.             onCompositionStart: e => {
    
  1570.               log.push('---- inner');
    
  1571.             },
    
  1572.             onCompositionStartCapture: e => {
    
  1573.               log.push('---- inner capture');
    
  1574.             },
    
  1575.           }}
    
  1576.           parentProps={{
    
  1577.             onCompositionStart: e => {
    
  1578.               log.push('--- inner parent');
    
  1579.             },
    
  1580.             onCompositionStartCapture: e => {
    
  1581.               log.push('--- inner parent capture');
    
  1582.             },
    
  1583.           }}
    
  1584.           outerProps={{
    
  1585.             onCompositionStart: e => {
    
  1586.               log.push('-- outer');
    
  1587.             },
    
  1588.             onCompositionStartCapture: e => {
    
  1589.               log.push('-- outer capture');
    
  1590.             },
    
  1591.           }}
    
  1592.           outerParentProps={{
    
  1593.             onCompositionStart: e => {
    
  1594.               log.push('- outer parent');
    
  1595.             },
    
  1596.             onCompositionStartCapture: e => {
    
  1597.               expect(e.type).toBe('compositionstart');
    
  1598.               log.push('- outer parent capture');
    
  1599.             },
    
  1600.           }}
    
  1601.         />,
    
  1602.       );
    
  1603.       expect(log.length).toBe(0);
    
  1604.       const e = new Event('compositionstart', {
    
  1605.         bubbles: true,
    
  1606.       });
    
  1607.       targetRef.current.dispatchEvent(e);
    
  1608.       // Since this is a polyfilled event,
    
  1609.       // the capture and bubble phases are
    
  1610.       // emulated, and don't align between roots.
    
  1611.       expect(log).toEqual(unindent`
    
  1612.         --- inner parent capture
    
  1613.         ---- inner capture
    
  1614.         ---- inner
    
  1615.         --- inner parent
    
  1616.         - outer parent capture
    
  1617.         -- outer capture
    
  1618.         -- outer
    
  1619.         - outer parent
    
  1620.       `);
    
  1621.     });
    
  1622. 
    
  1623.     it('onCompositionEnd', () => {
    
  1624.       const log = [];
    
  1625.       const targetRef = React.createRef();
    
  1626.       render(
    
  1627.         <Fixture
    
  1628.           type="input"
    
  1629.           targetRef={targetRef}
    
  1630.           targetProps={{
    
  1631.             onCompositionEnd: e => {
    
  1632.               log.push('---- inner');
    
  1633.             },
    
  1634.             onCompositionEndCapture: e => {
    
  1635.               log.push('---- inner capture');
    
  1636.             },
    
  1637.           }}
    
  1638.           parentProps={{
    
  1639.             onCompositionEnd: e => {
    
  1640.               log.push('--- inner parent');
    
  1641.             },
    
  1642.             onCompositionEndCapture: e => {
    
  1643.               log.push('--- inner parent capture');
    
  1644.             },
    
  1645.           }}
    
  1646.           outerProps={{
    
  1647.             onCompositionEnd: e => {
    
  1648.               log.push('-- outer');
    
  1649.             },
    
  1650.             onCompositionEndCapture: e => {
    
  1651.               log.push('-- outer capture');
    
  1652.             },
    
  1653.           }}
    
  1654.           outerParentProps={{
    
  1655.             onCompositionEnd: e => {
    
  1656.               log.push('- outer parent');
    
  1657.             },
    
  1658.             onCompositionEndCapture: e => {
    
  1659.               expect(e.type).toBe('compositionend');
    
  1660.               log.push('- outer parent capture');
    
  1661.             },
    
  1662.           }}
    
  1663.         />,
    
  1664.       );
    
  1665.       expect(log.length).toBe(0);
    
  1666.       const e = new Event('compositionend', {
    
  1667.         bubbles: true,
    
  1668.       });
    
  1669.       targetRef.current.dispatchEvent(e);
    
  1670.       // Since this is a polyfilled event,
    
  1671.       // the capture and bubble phases are
    
  1672.       // emulated, and don't align between roots.
    
  1673.       expect(log).toEqual(unindent`
    
  1674.         --- inner parent capture
    
  1675.         ---- inner capture
    
  1676.         ---- inner
    
  1677.         --- inner parent
    
  1678.         - outer parent capture
    
  1679.         -- outer capture
    
  1680.         -- outer
    
  1681.         - outer parent
    
  1682.       `);
    
  1683.     });
    
  1684. 
    
  1685.     it('onCompositionUpdate', () => {
    
  1686.       const log = [];
    
  1687.       const targetRef = React.createRef();
    
  1688.       render(
    
  1689.         <Fixture
    
  1690.           type="input"
    
  1691.           targetRef={targetRef}
    
  1692.           targetProps={{
    
  1693.             onCompositionUpdate: e => {
    
  1694.               log.push('---- inner');
    
  1695.             },
    
  1696.             onCompositionUpdateCapture: e => {
    
  1697.               log.push('---- inner capture');
    
  1698.             },
    
  1699.           }}
    
  1700.           parentProps={{
    
  1701.             onCompositionUpdate: e => {
    
  1702.               log.push('--- inner parent');
    
  1703.             },
    
  1704.             onCompositionUpdateCapture: e => {
    
  1705.               log.push('--- inner parent capture');
    
  1706.             },
    
  1707.           }}
    
  1708.           outerProps={{
    
  1709.             onCompositionUpdate: e => {
    
  1710.               log.push('-- outer');
    
  1711.             },
    
  1712.             onCompositionUpdateCapture: e => {
    
  1713.               log.push('-- outer capture');
    
  1714.             },
    
  1715.           }}
    
  1716.           outerParentProps={{
    
  1717.             onCompositionUpdate: e => {
    
  1718.               log.push('- outer parent');
    
  1719.             },
    
  1720.             onCompositionUpdateCapture: e => {
    
  1721.               expect(e.type).toBe('compositionupdate');
    
  1722.               log.push('- outer parent capture');
    
  1723.             },
    
  1724.           }}
    
  1725.         />,
    
  1726.       );
    
  1727.       expect(log.length).toBe(0);
    
  1728.       const e = new Event('compositionupdate', {
    
  1729.         bubbles: true,
    
  1730.       });
    
  1731.       targetRef.current.dispatchEvent(e);
    
  1732.       // Since this is a polyfilled event,
    
  1733.       // the capture and bubble phases are
    
  1734.       // emulated, and don't align between roots.
    
  1735.       expect(log).toEqual(unindent`
    
  1736.         --- inner parent capture
    
  1737.         ---- inner capture
    
  1738.         ---- inner
    
  1739.         --- inner parent
    
  1740.         - outer parent capture
    
  1741.         -- outer capture
    
  1742.         -- outer
    
  1743.         - outer parent
    
  1744.       `);
    
  1745.     });
    
  1746. 
    
  1747.     it('onSelect', () => {
    
  1748.       const log = [];
    
  1749.       const targetRef = React.createRef();
    
  1750.       render(
    
  1751.         <Fixture
    
  1752.           type="input"
    
  1753.           targetRef={targetRef}
    
  1754.           targetProps={{
    
  1755.             onSelect: e => {
    
  1756.               log.push('---- inner');
    
  1757.             },
    
  1758.             onSelectCapture: e => {
    
  1759.               log.push('---- inner capture');
    
  1760.             },
    
  1761.           }}
    
  1762.           parentProps={{
    
  1763.             onSelect: e => {
    
  1764.               log.push('--- inner parent');
    
  1765.             },
    
  1766.             onSelectCapture: e => {
    
  1767.               log.push('--- inner parent capture');
    
  1768.             },
    
  1769.           }}
    
  1770.           outerProps={{
    
  1771.             onSelect: e => {
    
  1772.               log.push('-- outer');
    
  1773.             },
    
  1774.             onSelectCapture: e => {
    
  1775.               log.push('-- outer capture');
    
  1776.             },
    
  1777.           }}
    
  1778.           outerParentProps={{
    
  1779.             onSelect: e => {
    
  1780.               log.push('- outer parent');
    
  1781.             },
    
  1782.             onSelectCapture: e => {
    
  1783.               expect(e.type).toBe('select');
    
  1784.               log.push('- outer parent capture');
    
  1785.             },
    
  1786.           }}
    
  1787.         />,
    
  1788.       );
    
  1789.       expect(log.length).toBe(0);
    
  1790.       targetRef.current.focus();
    
  1791.       targetRef.current.dispatchEvent(
    
  1792.         new Event('keydown', {
    
  1793.           bubbles: true,
    
  1794.         }),
    
  1795.       );
    
  1796.       // The outer React doesn't receive the event at all
    
  1797.       // because it is not responsible for this input.
    
  1798.       expect(log).toEqual(unindent`
    
  1799.         --- inner parent capture
    
  1800.         ---- inner capture
    
  1801.         ---- inner
    
  1802.         --- inner parent
    
  1803.       `);
    
  1804.     });
    
  1805.   });
    
  1806. 
    
  1807.   // Events that bubble in React and in the browser.
    
  1808.   // React delegates them to the root.
    
  1809.   function testNativeBubblingEvent(config) {
    
  1810.     testNativeBubblingEventWithTargetListener(config);
    
  1811.     testNativeBubblingEventWithoutTargetListener(config);
    
  1812.     testReactStopPropagationInOuterCapturePhase(config);
    
  1813.     testReactStopPropagationInInnerCapturePhase(config);
    
  1814.     testReactStopPropagationInInnerBubblePhase(config);
    
  1815.     testReactStopPropagationInOuterBubblePhase(config);
    
  1816.     testNativeStopPropagationInOuterCapturePhase(config);
    
  1817.     testNativeStopPropagationInInnerCapturePhase(config);
    
  1818.     testNativeStopPropagationInInnerBubblePhase(config);
    
  1819.     testNativeStopPropagationInOuterBubblePhase(config);
    
  1820.   }
    
  1821. 
    
  1822.   // Events that bubble in React but not in the browser.
    
  1823.   // React attaches them to the elements.
    
  1824.   function testEmulatedBubblingEvent(config) {
    
  1825.     testEmulatedBubblingEventWithTargetListener(config);
    
  1826.     testEmulatedBubblingEventWithoutTargetListener(config);
    
  1827.     testReactStopPropagationInOuterCapturePhase(config);
    
  1828.     testReactStopPropagationInInnerCapturePhase(config);
    
  1829.     testReactStopPropagationInInnerBubblePhase(config);
    
  1830.     testNativeStopPropagationInOuterCapturePhase(config);
    
  1831.     testNativeStopPropagationInInnerCapturePhase(config);
    
  1832.     testNativeStopPropagationInInnerEmulatedBubblePhase(config);
    
  1833.   }
    
  1834. 
    
  1835.   // Events that don't bubble either in React or in the browser.
    
  1836.   function testNonBubblingEvent(config) {
    
  1837.     testNonBubblingEventWithTargetListener(config);
    
  1838.     testNonBubblingEventWithoutTargetListener(config);
    
  1839.     testReactStopPropagationInOuterCapturePhase(config);
    
  1840.     testReactStopPropagationInInnerCapturePhase(config);
    
  1841.     testReactStopPropagationInInnerBubblePhase(config);
    
  1842.     testNativeStopPropagationInOuterCapturePhase(config);
    
  1843.     testNativeStopPropagationInInnerCapturePhase(config);
    
  1844.   }
    
  1845. 
    
  1846.   function testNativeBubblingEventWithTargetListener(eventConfig) {
    
  1847.     const log = [];
    
  1848.     const targetRef = React.createRef();
    
  1849.     render(
    
  1850.       <Fixture
    
  1851.         type={eventConfig.type}
    
  1852.         targetRef={targetRef}
    
  1853.         targetProps={{
    
  1854.           [eventConfig.reactEvent]: e => {
    
  1855.             log.push('---- inner');
    
  1856.           },
    
  1857.           [eventConfig.reactEvent + 'Capture']: e => {
    
  1858.             log.push('---- inner capture');
    
  1859.           },
    
  1860.         }}
    
  1861.         parentProps={{
    
  1862.           [eventConfig.reactEvent]: e => {
    
  1863.             log.push('--- inner parent');
    
  1864.           },
    
  1865.           [eventConfig.reactEvent + 'Capture']: e => {
    
  1866.             log.push('--- inner parent capture');
    
  1867.           },
    
  1868.         }}
    
  1869.         outerProps={{
    
  1870.           [eventConfig.reactEvent]: e => {
    
  1871.             log.push('-- outer');
    
  1872.           },
    
  1873.           [eventConfig.reactEvent + 'Capture']: e => {
    
  1874.             log.push('-- outer capture');
    
  1875.           },
    
  1876.         }}
    
  1877.         outerParentProps={{
    
  1878.           [eventConfig.reactEvent]: e => {
    
  1879.             log.push('- outer parent');
    
  1880.           },
    
  1881.           [eventConfig.reactEvent + 'Capture']: e => {
    
  1882.             expect(e.type).toBe(eventConfig.reactEventType);
    
  1883.             log.push('- outer parent capture');
    
  1884.           },
    
  1885.         }}
    
  1886.       />,
    
  1887.     );
    
  1888.     expect(log.length).toBe(0);
    
  1889.     eventConfig.dispatch(targetRef.current);
    
  1890.     // Should print all listeners.
    
  1891.     expect(log).toEqual(unindent`
    
  1892.       - outer parent capture
    
  1893.       -- outer capture
    
  1894.       --- inner parent capture
    
  1895.       ---- inner capture
    
  1896.       ---- inner
    
  1897.       --- inner parent
    
  1898.       -- outer
    
  1899.       - outer parent
    
  1900.     `);
    
  1901.   }
    
  1902. 
    
  1903.   function testEmulatedBubblingEventWithTargetListener(eventConfig) {
    
  1904.     const log = [];
    
  1905.     const targetRef = React.createRef();
    
  1906.     render(
    
  1907.       <Fixture
    
  1908.         type={eventConfig.type}
    
  1909.         targetRef={targetRef}
    
  1910.         targetProps={{
    
  1911.           [eventConfig.reactEvent]: e => {
    
  1912.             log.push('---- inner');
    
  1913.           },
    
  1914.           [eventConfig.reactEvent + 'Capture']: e => {
    
  1915.             log.push('---- inner capture');
    
  1916.           },
    
  1917.         }}
    
  1918.         parentProps={{
    
  1919.           [eventConfig.reactEvent]: e => {
    
  1920.             log.push('--- inner parent');
    
  1921.           },
    
  1922.           [eventConfig.reactEvent + 'Capture']: e => {
    
  1923.             log.push('--- inner parent capture');
    
  1924.           },
    
  1925.         }}
    
  1926.         outerProps={{
    
  1927.           [eventConfig.reactEvent]: e => {
    
  1928.             log.push('-- outer');
    
  1929.           },
    
  1930.           [eventConfig.reactEvent + 'Capture']: e => {
    
  1931.             log.push('-- outer capture');
    
  1932.           },
    
  1933.         }}
    
  1934.         outerParentProps={{
    
  1935.           [eventConfig.reactEvent]: e => {
    
  1936.             log.push('- outer parent');
    
  1937.           },
    
  1938.           [eventConfig.reactEvent + 'Capture']: e => {
    
  1939.             expect(e.type).toBe(eventConfig.reactEventType);
    
  1940.             log.push('- outer parent capture');
    
  1941.           },
    
  1942.         }}
    
  1943.       />,
    
  1944.     );
    
  1945.     expect(log.length).toBe(0);
    
  1946.     eventConfig.dispatch(targetRef.current);
    
  1947.     // This event doesn't bubble natively, but React emulates it.
    
  1948.     // Since the element is created by the inner React, the bubbling
    
  1949.     // stops at the inner parent and never reaches the outer React.
    
  1950.     // In the future, we might consider not bubbling these events
    
  1951.     // at all, in which case inner parent also wouldn't be logged.
    
  1952.     expect(log).toEqual(unindent`
    
  1953.       - outer parent capture
    
  1954.       -- outer capture
    
  1955.       --- inner parent capture
    
  1956.       ---- inner capture
    
  1957.       ---- inner
    
  1958.       --- inner parent
    
  1959.     `);
    
  1960.   }
    
  1961. 
    
  1962.   function testNonBubblingEventWithTargetListener(eventConfig) {
    
  1963.     const log = [];
    
  1964.     const targetRef = React.createRef();
    
  1965.     render(
    
  1966.       <Fixture
    
  1967.         type={eventConfig.type}
    
  1968.         targetRef={targetRef}
    
  1969.         targetProps={{
    
  1970.           [eventConfig.reactEvent]: e => {
    
  1971.             log.push('---- inner');
    
  1972.           },
    
  1973.           [eventConfig.reactEvent + 'Capture']: e => {
    
  1974.             log.push('---- inner capture');
    
  1975.           },
    
  1976.         }}
    
  1977.         parentProps={{
    
  1978.           [eventConfig.reactEvent]: e => {
    
  1979.             log.push('--- inner parent');
    
  1980.           },
    
  1981.           [eventConfig.reactEvent + 'Capture']: e => {
    
  1982.             log.push('--- inner parent capture');
    
  1983.           },
    
  1984.         }}
    
  1985.         outerProps={{
    
  1986.           [eventConfig.reactEvent]: e => {
    
  1987.             log.push('-- outer');
    
  1988.           },
    
  1989.           [eventConfig.reactEvent + 'Capture']: e => {
    
  1990.             log.push('-- outer capture');
    
  1991.           },
    
  1992.         }}
    
  1993.         outerParentProps={{
    
  1994.           [eventConfig.reactEvent]: e => {
    
  1995.             log.push('- outer parent');
    
  1996.           },
    
  1997.           [eventConfig.reactEvent + 'Capture']: e => {
    
  1998.             expect(e.type).toBe(eventConfig.reactEventType);
    
  1999.             log.push('- outer parent capture');
    
  2000.           },
    
  2001.         }}
    
  2002.       />,
    
  2003.     );
    
  2004.     expect(log.length).toBe(0);
    
  2005.     eventConfig.dispatch(targetRef.current);
    
  2006.     // This event doesn't bubble natively, and React is
    
  2007.     // not emulating it either. So it only reaches the
    
  2008.     // target and stops there.
    
  2009.     expect(log).toEqual(unindent`
    
  2010.       - outer parent capture
    
  2011.       -- outer capture
    
  2012.       --- inner parent capture
    
  2013.       ---- inner capture
    
  2014.       ---- inner
    
  2015.     `);
    
  2016.   }
    
  2017. 
    
  2018.   function testNativeBubblingEventWithoutTargetListener(eventConfig) {
    
  2019.     const log = [];
    
  2020.     const targetRef = React.createRef();
    
  2021.     render(
    
  2022.       <Fixture
    
  2023.         type={eventConfig.type}
    
  2024.         targetRef={targetRef}
    
  2025.         targetProps={
    
  2026.           {
    
  2027.             // No listener on the target itself.
    
  2028.           }
    
  2029.         }
    
  2030.         parentProps={{
    
  2031.           [eventConfig.reactEvent]: e => {
    
  2032.             log.push('--- inner parent');
    
  2033.           },
    
  2034.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2035.             log.push('--- inner parent capture');
    
  2036.           },
    
  2037.         }}
    
  2038.         outerProps={{
    
  2039.           [eventConfig.reactEvent]: e => {
    
  2040.             log.push('-- outer');
    
  2041.           },
    
  2042.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2043.             log.push('-- outer capture');
    
  2044.           },
    
  2045.         }}
    
  2046.         outerParentProps={{
    
  2047.           [eventConfig.reactEvent]: e => {
    
  2048.             log.push('- outer parent');
    
  2049.           },
    
  2050.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2051.             expect(e.type).toBe(eventConfig.reactEventType);
    
  2052.             log.push('- outer parent capture');
    
  2053.           },
    
  2054.         }}
    
  2055.       />,
    
  2056.     );
    
  2057.     expect(log.length).toBe(0);
    
  2058.     eventConfig.dispatch(targetRef.current);
    
  2059.     // Should print all listeners except the innermost one.
    
  2060.     expect(log).toEqual(unindent`
    
  2061.       - outer parent capture
    
  2062.       -- outer capture
    
  2063.       --- inner parent capture
    
  2064.       --- inner parent
    
  2065.       -- outer
    
  2066.       - outer parent
    
  2067.     `);
    
  2068.   }
    
  2069. 
    
  2070.   function testEmulatedBubblingEventWithoutTargetListener(eventConfig) {
    
  2071.     const log = [];
    
  2072.     const targetRef = React.createRef();
    
  2073.     render(
    
  2074.       <Fixture
    
  2075.         type={eventConfig.type}
    
  2076.         targetRef={targetRef}
    
  2077.         targetProps={
    
  2078.           {
    
  2079.             // No listener on the target itself.
    
  2080.           }
    
  2081.         }
    
  2082.         parentProps={{
    
  2083.           [eventConfig.reactEvent]: e => {
    
  2084.             log.push('--- inner parent');
    
  2085.           },
    
  2086.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2087.             log.push('--- inner parent capture');
    
  2088.           },
    
  2089.         }}
    
  2090.         outerProps={{
    
  2091.           [eventConfig.reactEvent]: e => {
    
  2092.             log.push('-- outer');
    
  2093.           },
    
  2094.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2095.             log.push('-- outer capture');
    
  2096.           },
    
  2097.         }}
    
  2098.         outerParentProps={{
    
  2099.           [eventConfig.reactEvent]: e => {
    
  2100.             log.push('- outer parent');
    
  2101.           },
    
  2102.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2103.             expect(e.type).toBe(eventConfig.reactEventType);
    
  2104.             log.push('- outer parent capture');
    
  2105.           },
    
  2106.         }}
    
  2107.       />,
    
  2108.     );
    
  2109.     expect(log.length).toBe(0);
    
  2110.     eventConfig.dispatch(targetRef.current);
    
  2111.     // This event doesn't bubble natively, but React emulates it.
    
  2112.     // Since the element is created by the inner React, the bubbling
    
  2113.     // stops at the inner parent and never reaches the outer React.
    
  2114.     // In the future, we might consider not bubbling these events
    
  2115.     // at all, in which case inner parent also wouldn't be logged.
    
  2116.     expect(log).toEqual(unindent`
    
  2117.       - outer parent capture
    
  2118.       -- outer capture
    
  2119.       --- inner parent capture
    
  2120.       --- inner parent
    
  2121.     `);
    
  2122.   }
    
  2123. 
    
  2124.   function testNonBubblingEventWithoutTargetListener(eventConfig) {
    
  2125.     const log = [];
    
  2126.     const targetRef = React.createRef();
    
  2127.     render(
    
  2128.       <Fixture
    
  2129.         type={eventConfig.type}
    
  2130.         targetRef={targetRef}
    
  2131.         targetProps={
    
  2132.           {
    
  2133.             // No listener on the target itself.
    
  2134.           }
    
  2135.         }
    
  2136.         parentProps={{
    
  2137.           [eventConfig.reactEvent]: e => {
    
  2138.             log.push('--- inner parent');
    
  2139.           },
    
  2140.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2141.             log.push('--- inner parent capture');
    
  2142.           },
    
  2143.         }}
    
  2144.         outerProps={{
    
  2145.           [eventConfig.reactEvent]: e => {
    
  2146.             log.push('-- outer');
    
  2147.           },
    
  2148.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2149.             log.push('-- outer capture');
    
  2150.           },
    
  2151.         }}
    
  2152.         outerParentProps={{
    
  2153.           [eventConfig.reactEvent]: e => {
    
  2154.             log.push('- outer parent');
    
  2155.           },
    
  2156.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2157.             expect(e.type).toBe(eventConfig.reactEventType);
    
  2158.             log.push('- outer parent capture');
    
  2159.           },
    
  2160.         }}
    
  2161.       />,
    
  2162.     );
    
  2163.     expect(log.length).toBe(0);
    
  2164.     eventConfig.dispatch(targetRef.current);
    
  2165.     // This event doesn't bubble native, and React doesn't
    
  2166.     // emulate bubbling either. Since we don't have a target
    
  2167.     // listener, only capture phase listeners fire.
    
  2168.     expect(log).toEqual(unindent`
    
  2169.       - outer parent capture
    
  2170.       -- outer capture
    
  2171.       --- inner parent capture
    
  2172.     `);
    
  2173.   }
    
  2174. 
    
  2175.   function testReactStopPropagationInOuterCapturePhase(eventConfig) {
    
  2176.     const log = [];
    
  2177.     const targetRef = React.createRef();
    
  2178.     render(
    
  2179.       <Fixture
    
  2180.         type={eventConfig.type}
    
  2181.         targetRef={node => {
    
  2182.           targetRef.current = node;
    
  2183.           if (node) {
    
  2184.             // No cleanup, assume we render once.
    
  2185.             node.addEventListener(eventConfig.nativeEvent, e => {
    
  2186.               // We *don't* expect this to appear in the log
    
  2187.               // at all because the event is stopped earlier.
    
  2188.               log.push('---- inner (native)');
    
  2189.             });
    
  2190.           }
    
  2191.         }}
    
  2192.         targetProps={{
    
  2193.           [eventConfig.reactEvent]: e => {
    
  2194.             log.push('---- inner');
    
  2195.           },
    
  2196.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2197.             log.push('---- inner capture');
    
  2198.           },
    
  2199.         }}
    
  2200.         parentProps={{
    
  2201.           [eventConfig.reactEvent]: e => {
    
  2202.             log.push('--- inner parent');
    
  2203.           },
    
  2204.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2205.             log.push('--- inner parent capture');
    
  2206.           },
    
  2207.         }}
    
  2208.         outerProps={{
    
  2209.           [eventConfig.reactEvent]: e => {
    
  2210.             log.push('-- outer');
    
  2211.           },
    
  2212.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2213.             e.stopPropagation(); // <---------
    
  2214.             log.push('-- outer capture');
    
  2215.           },
    
  2216.         }}
    
  2217.         outerParentProps={{
    
  2218.           [eventConfig.reactEvent]: e => {
    
  2219.             log.push('- outer parent');
    
  2220.           },
    
  2221.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2222.             expect(e.type).toBe(eventConfig.reactEventType);
    
  2223.             log.push('- outer parent capture');
    
  2224.           },
    
  2225.         }}
    
  2226.       />,
    
  2227.     );
    
  2228.     expect(log.length).toBe(0);
    
  2229.     eventConfig.dispatch(targetRef.current);
    
  2230.     // Should stop at the outer capture.
    
  2231.     // We don't get to the inner root at all.
    
  2232.     expect(log).toEqual(unindent`
    
  2233.       - outer parent capture
    
  2234.       -- outer capture
    
  2235.     `);
    
  2236.   }
    
  2237. 
    
  2238.   function testReactStopPropagationInInnerCapturePhase(eventConfig) {
    
  2239.     const log = [];
    
  2240.     const targetRef = React.createRef();
    
  2241.     render(
    
  2242.       <Fixture
    
  2243.         type={eventConfig.type}
    
  2244.         targetRef={node => {
    
  2245.           targetRef.current = node;
    
  2246.           if (node) {
    
  2247.             // No cleanup, assume we render once.
    
  2248.             node.addEventListener(eventConfig.nativeEvent, e => {
    
  2249.               // We *don't* expect this to appear in the log
    
  2250.               // at all because the event is stopped earlier.
    
  2251.               log.push('---- inner (native)');
    
  2252.             });
    
  2253.           }
    
  2254.         }}
    
  2255.         targetProps={{
    
  2256.           [eventConfig.reactEvent]: e => {
    
  2257.             log.push('---- inner');
    
  2258.           },
    
  2259.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2260.             log.push('---- inner capture');
    
  2261.           },
    
  2262.         }}
    
  2263.         parentProps={{
    
  2264.           [eventConfig.reactEvent]: e => {
    
  2265.             log.push('--- inner parent');
    
  2266.           },
    
  2267.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2268.             e.stopPropagation(); // <---------
    
  2269.             log.push('--- inner parent capture');
    
  2270.           },
    
  2271.         }}
    
  2272.         outerProps={{
    
  2273.           [eventConfig.reactEvent]: e => {
    
  2274.             log.push('-- outer');
    
  2275.           },
    
  2276.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2277.             log.push('-- outer capture');
    
  2278.           },
    
  2279.         }}
    
  2280.         outerParentProps={{
    
  2281.           [eventConfig.reactEvent]: e => {
    
  2282.             log.push('- outer parent');
    
  2283.           },
    
  2284.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2285.             expect(e.type).toBe(eventConfig.reactEventType);
    
  2286.             log.push('- outer parent capture');
    
  2287.           },
    
  2288.         }}
    
  2289.       />,
    
  2290.     );
    
  2291.     expect(log.length).toBe(0);
    
  2292.     eventConfig.dispatch(targetRef.current);
    
  2293.     // We get to the inner root, but we don't
    
  2294.     // get to the target and we don't bubble.
    
  2295.     expect(log).toEqual(unindent`
    
  2296.       - outer parent capture
    
  2297.       -- outer capture
    
  2298.       --- inner parent capture
    
  2299.     `);
    
  2300.   }
    
  2301. 
    
  2302.   function testReactStopPropagationInInnerBubblePhase(eventConfig) {
    
  2303.     const log = [];
    
  2304.     const targetRef = React.createRef();
    
  2305.     render(
    
  2306.       <Fixture
    
  2307.         type={eventConfig.type}
    
  2308.         targetRef={targetRef}
    
  2309.         targetProps={{
    
  2310.           [eventConfig.reactEvent]: e => {
    
  2311.             e.stopPropagation(); // <---------
    
  2312.             log.push('---- inner');
    
  2313.           },
    
  2314.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2315.             log.push('---- inner capture');
    
  2316.           },
    
  2317.         }}
    
  2318.         parentProps={{
    
  2319.           [eventConfig.reactEvent]: e => {
    
  2320.             log.push('--- inner parent');
    
  2321.           },
    
  2322.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2323.             log.push('--- inner parent capture');
    
  2324.           },
    
  2325.         }}
    
  2326.         outerRef={node => {
    
  2327.           if (node) {
    
  2328.             // No cleanup, assume we render once.
    
  2329.             node.addEventListener(eventConfig.nativeEvent, e => {
    
  2330.               // We *don't* expect this to appear in the log
    
  2331.               // at all because the event is stopped earlier.
    
  2332.               log.push('-- outer (native)');
    
  2333.             });
    
  2334.           }
    
  2335.         }}
    
  2336.         outerProps={{
    
  2337.           [eventConfig.reactEvent]: e => {
    
  2338.             log.push('-- outer');
    
  2339.           },
    
  2340.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2341.             log.push('-- outer capture');
    
  2342.           },
    
  2343.         }}
    
  2344.         outerParentProps={{
    
  2345.           [eventConfig.reactEvent]: e => {
    
  2346.             log.push('- outer parent');
    
  2347.           },
    
  2348.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2349.             expect(e.type).toBe(eventConfig.reactEventType);
    
  2350.             log.push('- outer parent capture');
    
  2351.           },
    
  2352.         }}
    
  2353.       />,
    
  2354.     );
    
  2355.     expect(log.length).toBe(0);
    
  2356.     eventConfig.dispatch(targetRef.current);
    
  2357.     // Should stop at the target and not go further.
    
  2358.     expect(log).toEqual(unindent`
    
  2359.       - outer parent capture
    
  2360.       -- outer capture
    
  2361.       --- inner parent capture
    
  2362.       ---- inner capture
    
  2363.       ---- inner
    
  2364.     `);
    
  2365.   }
    
  2366. 
    
  2367.   function testReactStopPropagationInOuterBubblePhase(eventConfig) {
    
  2368.     const log = [];
    
  2369.     const targetRef = React.createRef();
    
  2370.     render(
    
  2371.       <Fixture
    
  2372.         type={eventConfig.type}
    
  2373.         targetRef={targetRef}
    
  2374.         targetProps={{
    
  2375.           [eventConfig.reactEvent]: e => {
    
  2376.             log.push('---- inner');
    
  2377.           },
    
  2378.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2379.             log.push('---- inner capture');
    
  2380.           },
    
  2381.         }}
    
  2382.         parentProps={{
    
  2383.           [eventConfig.reactEvent]: e => {
    
  2384.             log.push('--- inner parent');
    
  2385.           },
    
  2386.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2387.             log.push('--- inner parent capture');
    
  2388.           },
    
  2389.         }}
    
  2390.         outerProps={{
    
  2391.           [eventConfig.reactEvent]: e => {
    
  2392.             e.stopPropagation(); // <---------
    
  2393.             log.push('-- outer');
    
  2394.           },
    
  2395.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2396.             log.push('-- outer capture');
    
  2397.           },
    
  2398.         }}
    
  2399.         outerParentProps={{
    
  2400.           [eventConfig.reactEvent]: e => {
    
  2401.             log.push('- outer parent');
    
  2402.           },
    
  2403.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2404.             expect(e.type).toBe(eventConfig.reactEventType);
    
  2405.             log.push('- outer parent capture');
    
  2406.           },
    
  2407.         }}
    
  2408.       />,
    
  2409.     );
    
  2410.     expect(log.length).toBe(0);
    
  2411.     eventConfig.dispatch(targetRef.current);
    
  2412.     // Should not reach the parent outer bubble handler.
    
  2413.     expect(log).toEqual(unindent`
    
  2414.       - outer parent capture
    
  2415.       -- outer capture
    
  2416.       --- inner parent capture
    
  2417.       ---- inner capture
    
  2418.       ---- inner
    
  2419.       --- inner parent
    
  2420.       -- outer
    
  2421.     `);
    
  2422.   }
    
  2423. 
    
  2424.   function testNativeStopPropagationInOuterCapturePhase(eventConfig) {
    
  2425.     const log = [];
    
  2426.     const targetRef = React.createRef();
    
  2427.     render(
    
  2428.       <Fixture
    
  2429.         type={eventConfig.type}
    
  2430.         targetRef={targetRef}
    
  2431.         targetProps={{
    
  2432.           [eventConfig.reactEvent]: e => {
    
  2433.             log.push('---- inner');
    
  2434.           },
    
  2435.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2436.             log.push('---- inner capture');
    
  2437.           },
    
  2438.         }}
    
  2439.         parentProps={{
    
  2440.           [eventConfig.reactEvent]: e => {
    
  2441.             log.push('--- inner parent');
    
  2442.           },
    
  2443.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2444.             log.push('--- inner parent capture');
    
  2445.           },
    
  2446.         }}
    
  2447.         outerProps={{
    
  2448.           [eventConfig.reactEvent]: e => {
    
  2449.             log.push('-- outer');
    
  2450.           },
    
  2451.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2452.             log.push('-- outer capture');
    
  2453.           },
    
  2454.         }}
    
  2455.         outerParentRef={node => {
    
  2456.           if (node) {
    
  2457.             // No cleanup, assume we render once.
    
  2458.             node.addEventListener(
    
  2459.               eventConfig.nativeEvent,
    
  2460.               e => {
    
  2461.                 log.push('- outer parent capture (native)');
    
  2462.                 e.stopPropagation(); // <---------
    
  2463.               },
    
  2464.               {capture: true},
    
  2465.             );
    
  2466.           }
    
  2467.         }}
    
  2468.         outerParentProps={{
    
  2469.           [eventConfig.reactEvent]: e => {
    
  2470.             log.push('- outer parent');
    
  2471.           },
    
  2472.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2473.             expect(e.type).toBe(eventConfig.reactEventType);
    
  2474.             log.push('- outer parent capture');
    
  2475.           },
    
  2476.         }}
    
  2477.       />,
    
  2478.     );
    
  2479.     expect(log.length).toBe(0);
    
  2480.     eventConfig.dispatch(targetRef.current);
    
  2481.     // The outer root has already received the event,
    
  2482.     // so the capture phrase runs for it. But the inner
    
  2483.     // root is prevented from receiving it by the native
    
  2484.     // handler in the outer native capture phase.
    
  2485.     expect(log).toEqual(unindent`
    
  2486.       - outer parent capture
    
  2487.       -- outer capture
    
  2488.       - outer parent capture (native)
    
  2489.     `);
    
  2490.   }
    
  2491. 
    
  2492.   function testNativeStopPropagationInInnerCapturePhase(eventConfig) {
    
  2493.     const log = [];
    
  2494.     const targetRef = React.createRef();
    
  2495.     render(
    
  2496.       <Fixture
    
  2497.         type={eventConfig.type}
    
  2498.         targetRef={targetRef}
    
  2499.         targetProps={{
    
  2500.           [eventConfig.reactEvent]: e => {
    
  2501.             log.push('---- inner');
    
  2502.           },
    
  2503.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2504.             log.push('---- inner capture');
    
  2505.           },
    
  2506.         }}
    
  2507.         parentRef={node => {
    
  2508.           if (node) {
    
  2509.             // No cleanup, assume we render once.
    
  2510.             node.addEventListener(
    
  2511.               eventConfig.nativeEvent,
    
  2512.               e => {
    
  2513.                 log.push('--- inner parent capture (native)');
    
  2514.                 e.stopPropagation(); // <---------
    
  2515.               },
    
  2516.               {capture: true},
    
  2517.             );
    
  2518.           }
    
  2519.         }}
    
  2520.         parentProps={{
    
  2521.           [eventConfig.reactEvent]: e => {
    
  2522.             log.push('--- inner parent');
    
  2523.           },
    
  2524.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2525.             log.push('--- inner parent capture');
    
  2526.           },
    
  2527.         }}
    
  2528.         outerProps={{
    
  2529.           [eventConfig.reactEvent]: e => {
    
  2530.             log.push('-- outer');
    
  2531.           },
    
  2532.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2533.             log.push('-- outer capture');
    
  2534.           },
    
  2535.         }}
    
  2536.         outerParentProps={{
    
  2537.           [eventConfig.reactEvent]: e => {
    
  2538.             log.push('- outer parent');
    
  2539.           },
    
  2540.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2541.             expect(e.type).toBe(eventConfig.reactEventType);
    
  2542.             log.push('- outer parent capture');
    
  2543.           },
    
  2544.         }}
    
  2545.       />,
    
  2546.     );
    
  2547.     expect(log.length).toBe(0);
    
  2548.     eventConfig.dispatch(targetRef.current);
    
  2549.     // The inner root has already received the event, so
    
  2550.     // all React capture phase listeners should run.
    
  2551.     // But then the native handler stops propagation
    
  2552.     // so none of the bubbling React handlers would run.
    
  2553.     expect(log).toEqual(unindent`
    
  2554.       - outer parent capture
    
  2555.       -- outer capture
    
  2556.       --- inner parent capture
    
  2557.       ---- inner capture
    
  2558.       --- inner parent capture (native)
    
  2559.     `);
    
  2560.   }
    
  2561. 
    
  2562.   function testNativeStopPropagationInInnerBubblePhase(eventConfig) {
    
  2563.     const log = [];
    
  2564.     const targetRef = React.createRef();
    
  2565.     render(
    
  2566.       <Fixture
    
  2567.         type={eventConfig.type}
    
  2568.         targetRef={node => {
    
  2569.           targetRef.current = node;
    
  2570.           if (node) {
    
  2571.             // No cleanup, assume we render once.
    
  2572.             node.addEventListener(eventConfig.nativeEvent, e => {
    
  2573.               log.push('---- inner (native)');
    
  2574.               e.stopPropagation(); // <---------
    
  2575.             });
    
  2576.           }
    
  2577.         }}
    
  2578.         targetProps={{
    
  2579.           [eventConfig.reactEvent]: e => {
    
  2580.             log.push('---- inner');
    
  2581.           },
    
  2582.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2583.             log.push('---- inner capture');
    
  2584.           },
    
  2585.         }}
    
  2586.         parentProps={{
    
  2587.           [eventConfig.reactEvent]: e => {
    
  2588.             log.push('--- inner parent');
    
  2589.           },
    
  2590.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2591.             log.push('--- inner parent capture');
    
  2592.           },
    
  2593.         }}
    
  2594.         outerProps={{
    
  2595.           [eventConfig.reactEvent]: e => {
    
  2596.             log.push('-- outer');
    
  2597.           },
    
  2598.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2599.             log.push('-- outer capture');
    
  2600.           },
    
  2601.         }}
    
  2602.         outerParentProps={{
    
  2603.           [eventConfig.reactEvent]: e => {
    
  2604.             log.push('- outer parent');
    
  2605.           },
    
  2606.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2607.             expect(e.type).toBe(eventConfig.reactEventType);
    
  2608.             log.push('- outer parent capture');
    
  2609.           },
    
  2610.         }}
    
  2611.       />,
    
  2612.     );
    
  2613.     expect(log.length).toBe(0);
    
  2614.     eventConfig.dispatch(targetRef.current);
    
  2615.     // The capture phase is entirely unaffected.
    
  2616.     // Then, we get into the bubble phase.
    
  2617.     // We start with the native innermost handler.
    
  2618.     // It stops propagation, so nothing else happens.
    
  2619.     expect(log).toEqual(unindent`
    
  2620.       - outer parent capture
    
  2621.       -- outer capture
    
  2622.       --- inner parent capture
    
  2623.       ---- inner capture
    
  2624.       ---- inner (native)
    
  2625.     `);
    
  2626.   }
    
  2627. 
    
  2628.   function testNativeStopPropagationInInnerEmulatedBubblePhase(eventConfig) {
    
  2629.     const log = [];
    
  2630.     const targetRef = React.createRef();
    
  2631.     render(
    
  2632.       <Fixture
    
  2633.         type={eventConfig.type}
    
  2634.         targetRef={node => {
    
  2635.           targetRef.current = node;
    
  2636.           if (node) {
    
  2637.             // No cleanup, assume we render once.
    
  2638.             node.addEventListener(eventConfig.nativeEvent, e => {
    
  2639.               log.push('---- inner (native)');
    
  2640.               e.stopPropagation(); // <---------
    
  2641.             });
    
  2642.           }
    
  2643.         }}
    
  2644.         targetProps={{
    
  2645.           [eventConfig.reactEvent]: e => {
    
  2646.             log.push('---- inner');
    
  2647.           },
    
  2648.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2649.             log.push('---- inner capture');
    
  2650.           },
    
  2651.         }}
    
  2652.         parentProps={{
    
  2653.           [eventConfig.reactEvent]: e => {
    
  2654.             log.push('--- inner parent');
    
  2655.           },
    
  2656.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2657.             log.push('--- inner parent capture');
    
  2658.           },
    
  2659.         }}
    
  2660.         outerProps={{
    
  2661.           [eventConfig.reactEvent]: e => {
    
  2662.             log.push('-- outer');
    
  2663.           },
    
  2664.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2665.             log.push('-- outer capture');
    
  2666.           },
    
  2667.         }}
    
  2668.         outerParentProps={{
    
  2669.           [eventConfig.reactEvent]: e => {
    
  2670.             log.push('- outer parent');
    
  2671.           },
    
  2672.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2673.             expect(e.type).toBe(eventConfig.reactEventType);
    
  2674.             log.push('- outer parent capture');
    
  2675.           },
    
  2676.         }}
    
  2677.       />,
    
  2678.     );
    
  2679.     expect(log.length).toBe(0);
    
  2680.     eventConfig.dispatch(targetRef.current);
    
  2681.     // This event does not natively bubble, so React
    
  2682.     // attaches the listener directly to the element.
    
  2683.     // As a result, by the time our custom native listener
    
  2684.     // fires, it is too late to do anything -- the React
    
  2685.     // emulated bubbilng has already happened.
    
  2686.     expect(log).toEqual(unindent`
    
  2687.       - outer parent capture
    
  2688.       -- outer capture
    
  2689.       --- inner parent capture
    
  2690.       ---- inner capture
    
  2691.       ---- inner
    
  2692.       --- inner parent
    
  2693.       ---- inner (native)
    
  2694.     `);
    
  2695.   }
    
  2696. 
    
  2697.   function testNativeStopPropagationInOuterBubblePhase(eventConfig) {
    
  2698.     const log = [];
    
  2699.     const targetRef = React.createRef();
    
  2700.     render(
    
  2701.       <Fixture
    
  2702.         type={eventConfig.type}
    
  2703.         targetRef={targetRef}
    
  2704.         targetProps={{
    
  2705.           [eventConfig.reactEvent]: e => {
    
  2706.             log.push('---- inner');
    
  2707.           },
    
  2708.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2709.             log.push('---- inner capture');
    
  2710.           },
    
  2711.         }}
    
  2712.         parentProps={{
    
  2713.           [eventConfig.reactEvent]: e => {
    
  2714.             log.push('--- inner parent');
    
  2715.           },
    
  2716.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2717.             log.push('--- inner parent capture');
    
  2718.           },
    
  2719.         }}
    
  2720.         outerRef={node => {
    
  2721.           if (node) {
    
  2722.             // No cleanup, assume we render once.
    
  2723.             node.addEventListener(eventConfig.nativeEvent, e => {
    
  2724.               log.push('-- outer (native)');
    
  2725.               e.stopPropagation(); // <---------
    
  2726.             });
    
  2727.           }
    
  2728.         }}
    
  2729.         outerProps={{
    
  2730.           [eventConfig.reactEvent]: e => {
    
  2731.             log.push('-- outer');
    
  2732.           },
    
  2733.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2734.             log.push('-- outer capture');
    
  2735.           },
    
  2736.         }}
    
  2737.         outerParentProps={{
    
  2738.           [eventConfig.reactEvent]: e => {
    
  2739.             log.push('- outer parent');
    
  2740.           },
    
  2741.           [eventConfig.reactEvent + 'Capture']: e => {
    
  2742.             expect(e.type).toBe(eventConfig.reactEventType);
    
  2743.             log.push('- outer parent capture');
    
  2744.           },
    
  2745.         }}
    
  2746.       />,
    
  2747.     );
    
  2748.     expect(log.length).toBe(0);
    
  2749.     eventConfig.dispatch(targetRef.current);
    
  2750.     // The event bubbles upwards through the inner tree.
    
  2751.     // Then it reaches the native handler which stops propagation.
    
  2752.     // As a result, it never reaches the outer React root,
    
  2753.     // and thus the outer React event handlers don't fire.
    
  2754.     expect(log).toEqual(unindent`
    
  2755.       - outer parent capture
    
  2756.       -- outer capture
    
  2757.       --- inner parent capture
    
  2758.       ---- inner capture
    
  2759.       ---- inner
    
  2760.       --- inner parent
    
  2761.       -- outer (native)
    
  2762.     `);
    
  2763.   }
    
  2764. 
    
  2765.   function Fixture({
    
  2766.     type,
    
  2767.     targetRef,
    
  2768.     targetProps,
    
  2769.     parentRef,
    
  2770.     parentProps,
    
  2771.     outerRef,
    
  2772.     outerProps,
    
  2773.     outerParentRef,
    
  2774.     outerParentProps,
    
  2775.   }) {
    
  2776.     const inner = React.useMemo(
    
  2777.       () => (
    
  2778.         <Inner
    
  2779.           type={type}
    
  2780.           targetRef={targetRef}
    
  2781.           targetProps={targetProps}
    
  2782.           parentRef={parentRef}
    
  2783.           parentProps={parentProps}
    
  2784.         />
    
  2785.       ),
    
  2786.       [type, targetRef, targetProps, parentProps],
    
  2787.     );
    
  2788.     return (
    
  2789.       <Outer
    
  2790.         outerRef={outerRef}
    
  2791.         outerProps={outerProps}
    
  2792.         outerParentRef={outerParentRef}
    
  2793.         outerParentProps={outerParentProps}>
    
  2794.         <NestedReact>{inner}</NestedReact>
    
  2795.       </Outer>
    
  2796.     );
    
  2797.   }
    
  2798. 
    
  2799.   function NestedReact({children}) {
    
  2800.     const ref = React.useRef();
    
  2801.     React.useLayoutEffect(() => {
    
  2802.       const parent = ref.current;
    
  2803.       const innerContainer = document.createElement('div');
    
  2804.       parent.appendChild(innerContainer);
    
  2805.       InnerReactDOM.render(children, innerContainer);
    
  2806.       return () => {
    
  2807.         InnerReactDOM.unmountComponentAtNode(innerContainer);
    
  2808.         parent.removeChild(innerContainer);
    
  2809.       };
    
  2810.     }, [children, ref]);
    
  2811.     return <div ref={ref} />;
    
  2812.   }
    
  2813. 
    
  2814.   function Inner({type, targetRef, targetProps, parentRef, parentProps}) {
    
  2815.     const T = type;
    
  2816.     return (
    
  2817.       <div {...parentProps} ref={parentRef}>
    
  2818.         <T {...targetProps} ref={targetRef} />
    
  2819.       </div>
    
  2820.     );
    
  2821.   }
    
  2822. 
    
  2823.   function Outer({
    
  2824.     outerRef,
    
  2825.     outerProps,
    
  2826.     outerParentProps,
    
  2827.     outerParentRef,
    
  2828.     children,
    
  2829.   }) {
    
  2830.     return (
    
  2831.       <div {...outerParentProps} ref={outerParentRef}>
    
  2832.         <div {...outerProps} ref={outerRef}>
    
  2833.           {children}
    
  2834.         </div>
    
  2835.       </div>
    
  2836.     );
    
  2837.   }
    
  2838. 
    
  2839.   function unindent(str) {
    
  2840.     return str[0]
    
  2841.       .split('\n')
    
  2842.       .map(s => s.trim())
    
  2843.       .filter(s => s !== '');
    
  2844.   }
    
  2845. });