1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  *
    
  7.  * @emails react-core
    
  8.  */
    
  9. 
    
  10. 'use strict';
    
  11. 
    
  12. let React;
    
  13. let ReactDOM;
    
  14. 
    
  15. describe('BeforeInputEventPlugin', () => {
    
  16.   let container;
    
  17. 
    
  18.   function loadReactDOM(envSimulator) {
    
  19.     jest.resetModules();
    
  20.     if (envSimulator) {
    
  21.       envSimulator();
    
  22.     }
    
  23.     return require('react-dom');
    
  24.   }
    
  25. 
    
  26.   function simulateIE11() {
    
  27.     document.documentMode = 11;
    
  28.     window.CompositionEvent = {};
    
  29.   }
    
  30. 
    
  31.   function simulateWebkit() {
    
  32.     window.CompositionEvent = {};
    
  33.     window.TextEvent = {};
    
  34.   }
    
  35. 
    
  36.   function simulateComposition() {
    
  37.     window.CompositionEvent = {};
    
  38.   }
    
  39. 
    
  40.   function simulateNoComposition() {
    
  41.     // no composition event in Window - will use fallback
    
  42.   }
    
  43. 
    
  44.   function simulateEvent(elem, type, data) {
    
  45.     const event = new Event(type, {bubbles: true});
    
  46.     Object.assign(event, data);
    
  47.     elem.dispatchEvent(event);
    
  48.   }
    
  49. 
    
  50.   function simulateKeyboardEvent(elem, type, data) {
    
  51.     const {char, value, ...rest} = data;
    
  52.     const event = new KeyboardEvent(type, {
    
  53.       bubbles: true,
    
  54.       ...rest,
    
  55.     });
    
  56.     if (char) {
    
  57.       event.char = char;
    
  58.     }
    
  59.     if (value) {
    
  60.       elem.value = value;
    
  61.     }
    
  62.     elem.dispatchEvent(event);
    
  63.   }
    
  64. 
    
  65.   function simulatePaste(elem) {
    
  66.     const pasteEvent = new Event('paste', {
    
  67.       bubbles: true,
    
  68.     });
    
  69.     pasteEvent.clipboardData = {
    
  70.       dropEffect: null,
    
  71.       effectAllowed: null,
    
  72.       files: null,
    
  73.       items: null,
    
  74.       types: null,
    
  75.     };
    
  76.     elem.dispatchEvent(pasteEvent);
    
  77.   }
    
  78. 
    
  79.   beforeEach(() => {
    
  80.     React = require('react');
    
  81.     container = document.createElement('div');
    
  82.     document.body.appendChild(container);
    
  83.   });
    
  84. 
    
  85.   afterEach(() => {
    
  86.     delete document.documentMode;
    
  87.     delete window.CompositionEvent;
    
  88.     delete window.TextEvent;
    
  89.     delete window.opera;
    
  90.     document.body.removeChild(container);
    
  91.     container = null;
    
  92.   });
    
  93. 
    
  94.   function keyCode(char) {
    
  95.     return char.charCodeAt(0);
    
  96.   }
    
  97. 
    
  98.   const scenarios = [
    
  99.     {
    
  100.       eventSimulator: simulateEvent,
    
  101.       eventSimulatorArgs: [
    
  102.         'compositionstart',
    
  103.         {detail: {data: 'test'}, data: 'test'},
    
  104.       ],
    
  105.     },
    
  106.     {
    
  107.       eventSimulator: simulateEvent,
    
  108.       eventSimulatorArgs: [
    
  109.         'compositionupdate',
    
  110.         {detail: {data: 'test string'}, data: 'test string'},
    
  111.       ],
    
  112.     },
    
  113.     {
    
  114.       eventSimulator: simulateEvent,
    
  115.       eventSimulatorArgs: [
    
  116.         'compositionend',
    
  117.         {detail: {data: 'test string 3'}, data: 'test string 3'},
    
  118.       ],
    
  119.     },
    
  120.     {
    
  121.       eventSimulator: simulateEvent,
    
  122.       eventSimulatorArgs: ['textInput', {data: 'abcß'}],
    
  123.     },
    
  124.     {
    
  125.       eventSimulator: simulateKeyboardEvent,
    
  126.       eventSimulatorArgs: ['keypress', {which: keyCode('a')}],
    
  127.     },
    
  128.     {
    
  129.       eventSimulator: simulateKeyboardEvent,
    
  130.       eventSimulatorArgs: ['keypress', {which: keyCode(' ')}, ' '],
    
  131.     },
    
  132.     {
    
  133.       eventSimulator: simulateEvent,
    
  134.       eventSimulatorArgs: ['textInput', {data: ' '}],
    
  135.     },
    
  136.     {
    
  137.       eventSimulator: simulateKeyboardEvent,
    
  138.       eventSimulatorArgs: ['keypress', {which: keyCode('a'), ctrlKey: true}],
    
  139.     },
    
  140.     {
    
  141.       eventSimulator: simulateKeyboardEvent,
    
  142.       eventSimulatorArgs: ['keypress', {which: keyCode('b'), altKey: true}],
    
  143.     },
    
  144.     {
    
  145.       eventSimulator: simulateKeyboardEvent,
    
  146.       eventSimulatorArgs: [
    
  147.         'keypress',
    
  148.         {which: keyCode('c'), altKey: true, ctrlKey: true},
    
  149.       ],
    
  150.     },
    
  151.     {
    
  152.       eventSimulator: simulateKeyboardEvent,
    
  153.       eventSimulatorArgs: [
    
  154.         'keypress',
    
  155.         {which: keyCode('X'), char: '\uD83D\uDE0A'},
    
  156.       ],
    
  157.     },
    
  158.     {
    
  159.       eventSimulator: simulateEvent,
    
  160.       eventSimulatorArgs: ['textInput', {data: '\uD83D\uDE0A'}],
    
  161.     },
    
  162.     {
    
  163.       eventSimulator: simulateKeyboardEvent,
    
  164.       eventSimulatorArgs: ['keydown', {keyCode: 229, value: 'foo'}],
    
  165.     },
    
  166.     {
    
  167.       eventSimulator: simulateKeyboardEvent,
    
  168.       eventSimulatorArgs: ['keydown', {keyCode: 9, value: 'foobar'}],
    
  169.     },
    
  170.     {
    
  171.       eventSimulator: simulateKeyboardEvent,
    
  172.       eventSimulatorArgs: ['keydown', {keyCode: 229, value: 'foofoo'}],
    
  173.     },
    
  174.     {
    
  175.       eventSimulator: simulateKeyboardEvent,
    
  176.       eventSimulatorArgs: ['keyup', {keyCode: 9, value: 'fooBARfoo'}],
    
  177.     },
    
  178.     {
    
  179.       eventSimulator: simulateKeyboardEvent,
    
  180.       eventSimulatorArgs: ['keydown', {keyCode: 229, value: 'foofoo'}],
    
  181.     },
    
  182.     {
    
  183.       eventSimulator: simulateKeyboardEvent,
    
  184.       eventSimulatorArgs: ['keypress', {keyCode: 60, value: 'Barfoofoo'}],
    
  185.     },
    
  186.     {
    
  187.       eventSimulator: simulatePaste,
    
  188.       eventSimulatorArgs: [],
    
  189.     },
    
  190.   ];
    
  191. 
    
  192.   const environments = [
    
  193.     {
    
  194.       emulator: simulateWebkit,
    
  195.       assertions: [
    
  196.         {
    
  197.           run: ({
    
  198.             beforeInputEvent,
    
  199.             compositionStartEvent,
    
  200.             spyOnBeforeInput,
    
  201.             spyOnCompositionStart,
    
  202.           }) => {
    
  203.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  204.             expect(beforeInputEvent).toBeNull();
    
  205.             expect(spyOnCompositionStart).toHaveBeenCalledTimes(1);
    
  206.             expect(compositionStartEvent.type).toBe('compositionstart');
    
  207.             expect(compositionStartEvent.data).toBe('test');
    
  208.           },
    
  209.         },
    
  210.         {
    
  211.           run: ({
    
  212.             beforeInputEvent,
    
  213.             compositionUpdateEvent,
    
  214.             spyOnBeforeInput,
    
  215.             spyOnCompositionUpdate,
    
  216.           }) => {
    
  217.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  218.             expect(beforeInputEvent).toBeNull();
    
  219.             expect(spyOnCompositionUpdate).toHaveBeenCalledTimes(1);
    
  220.             expect(compositionUpdateEvent.type).toBe('compositionupdate');
    
  221.             expect(compositionUpdateEvent.data).toBe('test string');
    
  222.           },
    
  223.         },
    
  224.         {
    
  225.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  226.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  227.             expect(beforeInputEvent.nativeEvent.type).toBe('compositionend');
    
  228.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  229.             expect(beforeInputEvent.data).toBe('test string 3');
    
  230.           },
    
  231.         },
    
  232.         {
    
  233.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  234.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  235.             expect(beforeInputEvent.nativeEvent.type).toBe('textInput');
    
  236.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  237.             expect(beforeInputEvent.data).toBe('abcß');
    
  238.           },
    
  239.         },
    
  240.         {
    
  241.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  242.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  243.             expect(beforeInputEvent).toBeNull();
    
  244.           },
    
  245.         },
    
  246.         {
    
  247.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  248.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  249.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  250.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  251.             expect(beforeInputEvent.data).toBe(' ');
    
  252.           },
    
  253.         },
    
  254.         {
    
  255.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  256.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  257.             expect(beforeInputEvent).toBeNull();
    
  258.           },
    
  259.         },
    
  260.         {
    
  261.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  262.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  263.             expect(beforeInputEvent).toBeNull();
    
  264.           },
    
  265.         },
    
  266.         {
    
  267.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  268.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  269.             expect(beforeInputEvent).toBeNull();
    
  270.           },
    
  271.         },
    
  272.         {
    
  273.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  274.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  275.             expect(beforeInputEvent).toBeNull();
    
  276.           },
    
  277.         },
    
  278.         {
    
  279.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  280.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  281.             expect(beforeInputEvent).toBeNull();
    
  282.           },
    
  283.         },
    
  284.         {
    
  285.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  286.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  287.             expect(beforeInputEvent.nativeEvent.type).toBe('textInput');
    
  288.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  289.             expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
    
  290.           },
    
  291.         },
    
  292.         {
    
  293.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  294.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  295.             expect(beforeInputEvent).toBeNull();
    
  296.           },
    
  297.         },
    
  298.         {
    
  299.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  300.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  301.             expect(beforeInputEvent).toBeNull();
    
  302.           },
    
  303.         },
    
  304.         {
    
  305.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  306.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  307.             expect(beforeInputEvent).toBeNull();
    
  308.           },
    
  309.         },
    
  310.         {
    
  311.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  312.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  313.             expect(beforeInputEvent).toBeNull();
    
  314.           },
    
  315.         },
    
  316.         {
    
  317.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  318.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  319.             expect(beforeInputEvent).toBeNull();
    
  320.           },
    
  321.         },
    
  322.         {
    
  323.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  324.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  325.             expect(beforeInputEvent).toBeNull();
    
  326.           },
    
  327.         },
    
  328.         {
    
  329.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  330.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  331.             expect(beforeInputEvent).toBeNull();
    
  332.           },
    
  333.         },
    
  334.       ],
    
  335.     },
    
  336.     {
    
  337.       emulator: simulateIE11,
    
  338.       assertions: [
    
  339.         {
    
  340.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  341.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  342.             expect(beforeInputEvent).toBeNull();
    
  343.           },
    
  344.         },
    
  345.         {
    
  346.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  347.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  348.             expect(beforeInputEvent).toBeNull();
    
  349.           },
    
  350.         },
    
  351.         {
    
  352.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  353.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  354.             expect(beforeInputEvent).toBeNull();
    
  355.           },
    
  356.         },
    
  357.         {
    
  358.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  359.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  360.             expect(beforeInputEvent).toBeNull();
    
  361.           },
    
  362.         },
    
  363.         {
    
  364.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  365.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  366.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  367.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  368.             expect(beforeInputEvent.data).toBe('a');
    
  369.           },
    
  370.         },
    
  371.         {
    
  372.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  373.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  374.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  375.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  376.             expect(beforeInputEvent.data).toBe(' ');
    
  377.           },
    
  378.         },
    
  379.         {
    
  380.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  381.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  382.             expect(beforeInputEvent).toBeNull();
    
  383.           },
    
  384.         },
    
  385.         {
    
  386.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  387.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  388.             expect(beforeInputEvent).toBeNull();
    
  389.           },
    
  390.         },
    
  391.         {
    
  392.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  393.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  394.             expect(beforeInputEvent).toBeNull();
    
  395.           },
    
  396.         },
    
  397.         {
    
  398.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  399.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  400.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  401.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  402.             expect(beforeInputEvent.data).toBe('c');
    
  403.           },
    
  404.         },
    
  405.         {
    
  406.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  407.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  408.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  409.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  410.             expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
    
  411.           },
    
  412.         },
    
  413.         {
    
  414.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  415.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  416.             expect(beforeInputEvent).toBeNull();
    
  417.           },
    
  418.         },
    
  419.         {
    
  420.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  421.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  422.             expect(beforeInputEvent).toBeNull();
    
  423.           },
    
  424.         },
    
  425.         {
    
  426.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  427.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  428.             expect(beforeInputEvent).toBeNull();
    
  429.           },
    
  430.         },
    
  431.         {
    
  432.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  433.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  434.             expect(beforeInputEvent).toBeNull();
    
  435.           },
    
  436.         },
    
  437.         {
    
  438.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  439.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  440.             expect(beforeInputEvent).toBeNull();
    
  441.           },
    
  442.         },
    
  443.         {
    
  444.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  445.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  446.             expect(beforeInputEvent).toBeNull();
    
  447.           },
    
  448.         },
    
  449.         {
    
  450.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  451.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  452.             expect(beforeInputEvent).toBeNull();
    
  453.           },
    
  454.         },
    
  455.         {
    
  456.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  457.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  458.             expect(beforeInputEvent).toBeNull();
    
  459.           },
    
  460.         },
    
  461.       ],
    
  462.     },
    
  463.     {
    
  464.       emulator: simulateNoComposition,
    
  465.       assertions: [
    
  466.         {
    
  467.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  468.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  469.             expect(beforeInputEvent).toBeNull();
    
  470.           },
    
  471.         },
    
  472.         {
    
  473.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  474.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  475.             expect(beforeInputEvent).toBeNull();
    
  476.           },
    
  477.         },
    
  478.         {
    
  479.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  480.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  481.             expect(beforeInputEvent).toBeNull();
    
  482.           },
    
  483.         },
    
  484.         {
    
  485.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  486.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  487.             expect(beforeInputEvent).toBeNull();
    
  488.           },
    
  489.         },
    
  490.         {
    
  491.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  492.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  493.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  494.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  495.             expect(beforeInputEvent.data).toBe('a');
    
  496.           },
    
  497.         },
    
  498.         {
    
  499.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  500.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  501.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  502.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  503.             expect(beforeInputEvent.data).toBe(' ');
    
  504.           },
    
  505.         },
    
  506.         {
    
  507.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  508.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  509.             expect(beforeInputEvent).toBeNull();
    
  510.           },
    
  511.         },
    
  512.         {
    
  513.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  514.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  515.             expect(beforeInputEvent).toBeNull();
    
  516.           },
    
  517.         },
    
  518.         {
    
  519.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  520.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  521.             expect(beforeInputEvent).toBeNull();
    
  522.           },
    
  523.         },
    
  524.         {
    
  525.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  526.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  527.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  528.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  529.             expect(beforeInputEvent.data).toBe('c');
    
  530.           },
    
  531.         },
    
  532.         {
    
  533.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  534.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  535.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  536.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  537.             expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
    
  538.           },
    
  539.         },
    
  540.         {
    
  541.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  542.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  543.             expect(beforeInputEvent).toBeNull();
    
  544.           },
    
  545.         },
    
  546.         {
    
  547.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  548.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  549.             expect(beforeInputEvent).toBeNull();
    
  550.           },
    
  551.         },
    
  552.         {
    
  553.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  554.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  555.             expect(beforeInputEvent.nativeEvent.type).toBe('keydown');
    
  556.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  557.             expect(beforeInputEvent.data).toBe('bar');
    
  558.           },
    
  559.         },
    
  560.         {
    
  561.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  562.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  563.             expect(beforeInputEvent).toBeNull();
    
  564.           },
    
  565.         },
    
  566.         {
    
  567.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  568.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  569.             expect(beforeInputEvent.nativeEvent.type).toBe('keyup');
    
  570.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  571.             expect(beforeInputEvent.data).toBe('BAR');
    
  572.           },
    
  573.         },
    
  574.         {
    
  575.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  576.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  577.             expect(beforeInputEvent).toBeNull();
    
  578.           },
    
  579.         },
    
  580.         {
    
  581.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  582.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  583.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  584.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  585.             expect(beforeInputEvent.data).toBe('Bar');
    
  586.           },
    
  587.         },
    
  588.         {
    
  589.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  590.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  591.             expect(beforeInputEvent).toBeNull();
    
  592.           },
    
  593.         },
    
  594.       ],
    
  595.     },
    
  596.     {
    
  597.       emulator: simulateComposition,
    
  598.       assertions: [
    
  599.         {
    
  600.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  601.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  602.             expect(beforeInputEvent).toBeNull();
    
  603.           },
    
  604.         },
    
  605.         {
    
  606.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  607.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  608.             expect(beforeInputEvent).toBeNull();
    
  609.           },
    
  610.         },
    
  611.         {
    
  612.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  613.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  614.             expect(beforeInputEvent.nativeEvent.type).toBe('compositionend');
    
  615.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  616.             expect(beforeInputEvent.data).toBe('test string 3');
    
  617.           },
    
  618.         },
    
  619.         {
    
  620.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  621.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  622.             expect(beforeInputEvent).toBeNull();
    
  623.           },
    
  624.         },
    
  625.         {
    
  626.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  627.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  628.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  629.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  630.             expect(beforeInputEvent.data).toBe('a');
    
  631.           },
    
  632.         },
    
  633.         {
    
  634.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  635.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  636.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  637.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  638.             expect(beforeInputEvent.data).toBe(' ');
    
  639.           },
    
  640.         },
    
  641.         {
    
  642.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  643.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  644.             expect(beforeInputEvent).toBeNull();
    
  645.           },
    
  646.         },
    
  647.         {
    
  648.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  649.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  650.             expect(beforeInputEvent).toBeNull();
    
  651.           },
    
  652.         },
    
  653.         {
    
  654.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  655.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  656.             expect(beforeInputEvent).toBeNull();
    
  657.           },
    
  658.         },
    
  659.         {
    
  660.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  661.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  662.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  663.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  664.             expect(beforeInputEvent.data).toBe('c');
    
  665.           },
    
  666.         },
    
  667.         {
    
  668.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  669.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(1);
    
  670.             expect(beforeInputEvent.nativeEvent.type).toBe('keypress');
    
  671.             expect(beforeInputEvent.type).toBe('beforeinput');
    
  672.             expect(beforeInputEvent.data).toBe('\uD83D\uDE0A');
    
  673.           },
    
  674.         },
    
  675.         {
    
  676.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  677.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  678.             expect(beforeInputEvent).toBeNull();
    
  679.           },
    
  680.         },
    
  681.         {
    
  682.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  683.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  684.             expect(beforeInputEvent).toBeNull();
    
  685.           },
    
  686.         },
    
  687.         {
    
  688.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  689.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  690.             expect(beforeInputEvent).toBeNull();
    
  691.           },
    
  692.         },
    
  693.         {
    
  694.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  695.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  696.             expect(beforeInputEvent).toBeNull();
    
  697.           },
    
  698.         },
    
  699.         {
    
  700.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  701.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  702.             expect(beforeInputEvent).toBeNull();
    
  703.           },
    
  704.         },
    
  705.         {
    
  706.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  707.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  708.             expect(beforeInputEvent).toBeNull();
    
  709.           },
    
  710.         },
    
  711.         {
    
  712.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  713.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  714.             expect(beforeInputEvent).toBeNull();
    
  715.           },
    
  716.         },
    
  717.         {
    
  718.           run: ({beforeInputEvent, spyOnBeforeInput}) => {
    
  719.             expect(spyOnBeforeInput).toHaveBeenCalledTimes(0);
    
  720.             expect(beforeInputEvent).toBeNull();
    
  721.           },
    
  722.         },
    
  723.       ],
    
  724.     },
    
  725.   ];
    
  726. 
    
  727.   const testInputComponent = (env, scenes) => {
    
  728.     let beforeInputEvent;
    
  729.     let compositionStartEvent;
    
  730.     let compositionUpdateEvent;
    
  731.     let spyOnBeforeInput;
    
  732.     let spyOnCompositionStart;
    
  733.     let spyOnCompositionUpdate;
    
  734.     ReactDOM = loadReactDOM(env.emulator);
    
  735.     const node = ReactDOM.render(
    
  736.       <input
    
  737.         type="text"
    
  738.         onBeforeInput={e => {
    
  739.           spyOnBeforeInput();
    
  740.           beforeInputEvent = e;
    
  741.         }}
    
  742.         onCompositionStart={e => {
    
  743.           spyOnCompositionStart();
    
  744.           compositionStartEvent = e;
    
  745.         }}
    
  746.         onCompositionUpdate={e => {
    
  747.           spyOnCompositionUpdate();
    
  748.           compositionUpdateEvent = e;
    
  749.         }}
    
  750.       />,
    
  751.       container,
    
  752.     );
    
  753. 
    
  754.     scenes.forEach((s, id) => {
    
  755.       beforeInputEvent = null;
    
  756.       compositionStartEvent = null;
    
  757.       compositionUpdateEvent = null;
    
  758.       spyOnBeforeInput = jest.fn();
    
  759.       spyOnCompositionStart = jest.fn();
    
  760.       spyOnCompositionUpdate = jest.fn();
    
  761.       s.eventSimulator.apply(null, [node, ...s.eventSimulatorArgs]);
    
  762.       env.assertions[id].run({
    
  763.         beforeInputEvent,
    
  764.         compositionStartEvent,
    
  765.         compositionUpdateEvent,
    
  766.         spyOnBeforeInput,
    
  767.         spyOnCompositionStart,
    
  768.         spyOnCompositionUpdate,
    
  769.       });
    
  770.     });
    
  771.   };
    
  772. 
    
  773.   const testContentEditableComponent = (env, scenes) => {
    
  774.     let beforeInputEvent;
    
  775.     let compositionStartEvent;
    
  776.     let compositionUpdateEvent;
    
  777.     let spyOnBeforeInput;
    
  778.     let spyOnCompositionStart;
    
  779.     let spyOnCompositionUpdate;
    
  780.     ReactDOM = loadReactDOM(env.emulator);
    
  781.     const node = ReactDOM.render(
    
  782.       <div
    
  783.         contentEditable={true}
    
  784.         onBeforeInput={e => {
    
  785.           spyOnBeforeInput();
    
  786.           beforeInputEvent = e;
    
  787.         }}
    
  788.         onCompositionStart={e => {
    
  789.           spyOnCompositionStart();
    
  790.           compositionStartEvent = e;
    
  791.         }}
    
  792.         onCompositionUpdate={e => {
    
  793.           spyOnCompositionUpdate();
    
  794.           compositionUpdateEvent = e;
    
  795.         }}
    
  796.       />,
    
  797.       container,
    
  798.     );
    
  799. 
    
  800.     scenes.forEach((s, id) => {
    
  801.       beforeInputEvent = null;
    
  802.       compositionStartEvent = null;
    
  803.       compositionUpdateEvent = null;
    
  804.       spyOnBeforeInput = jest.fn();
    
  805.       spyOnCompositionStart = jest.fn();
    
  806.       spyOnCompositionUpdate = jest.fn();
    
  807.       s.eventSimulator.apply(null, [node, ...s.eventSimulatorArgs]);
    
  808.       env.assertions[id].run({
    
  809.         beforeInputEvent,
    
  810.         compositionStartEvent,
    
  811.         compositionUpdateEvent,
    
  812.         spyOnBeforeInput,
    
  813.         spyOnCompositionStart,
    
  814.         spyOnCompositionUpdate,
    
  815.       });
    
  816.     });
    
  817.   };
    
  818. 
    
  819.   it('should extract onBeforeInput when simulating in Webkit on input[type=text]', () => {
    
  820.     testInputComponent(environments[0], scenarios);
    
  821.   });
    
  822.   it('should extract onBeforeInput when simulating in Webkit on contenteditable', () => {
    
  823.     testContentEditableComponent(environments[0], scenarios);
    
  824.   });
    
  825. 
    
  826.   it('should extract onBeforeInput when simulating in IE11 on input[type=text]', () => {
    
  827.     testInputComponent(environments[1], scenarios);
    
  828.   });
    
  829.   it('should extract onBeforeInput when simulating in IE11 on contenteditable', () => {
    
  830.     testContentEditableComponent(environments[1], scenarios);
    
  831.   });
    
  832. 
    
  833.   it('should extract onBeforeInput when simulating in env with no CompositionEvent on input[type=text]', () => {
    
  834.     testInputComponent(environments[2], scenarios);
    
  835.   });
    
  836. 
    
  837.   // in an environment using composition fallback onBeforeInput will not work
    
  838.   // as expected on a contenteditable as keydown and keyup events are translated
    
  839.   // to keypress events
    
  840. 
    
  841.   it('should extract onBeforeInput when simulating in env with only CompositionEvent on input[type=text]', () => {
    
  842.     testInputComponent(environments[3], scenarios);
    
  843.   });
    
  844. 
    
  845.   it('should extract onBeforeInput when simulating in env with only CompositionEvent on contenteditable', () => {
    
  846.     testContentEditableComponent(environments[3], scenarios);
    
  847.   });
    
  848. });