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('SyntheticKeyboardEvent', () => {
    
  16.   let container;
    
  17. 
    
  18.   beforeEach(() => {
    
  19.     React = require('react');
    
  20.     ReactDOM = require('react-dom');
    
  21.     // The container has to be attached for events to fire.
    
  22.     container = document.createElement('div');
    
  23.     document.body.appendChild(container);
    
  24.   });
    
  25. 
    
  26.   afterEach(() => {
    
  27.     document.body.removeChild(container);
    
  28.     container = null;
    
  29.   });
    
  30. 
    
  31.   describe('KeyboardEvent interface', () => {
    
  32.     describe('charCode', () => {
    
  33.       describe('when event is `keypress`', () => {
    
  34.         describe('when charCode is present in nativeEvent', () => {
    
  35.           it('when charCode is 0 and keyCode is 13, returns 13', () => {
    
  36.             let charCode = null;
    
  37.             const node = ReactDOM.render(
    
  38.               <input
    
  39.                 onKeyPress={e => {
    
  40.                   charCode = e.charCode;
    
  41.                 }}
    
  42.               />,
    
  43.               container,
    
  44.             );
    
  45.             node.dispatchEvent(
    
  46.               new KeyboardEvent('keypress', {
    
  47.                 charCode: 0,
    
  48.                 keyCode: 13,
    
  49.                 bubbles: true,
    
  50.                 cancelable: true,
    
  51.               }),
    
  52.             );
    
  53.             expect(charCode).toBe(13);
    
  54.           });
    
  55. 
    
  56.           it('when charCode is 32 or bigger and keyCode is missing, returns charCode', () => {
    
  57.             let charCode = null;
    
  58.             const node = ReactDOM.render(
    
  59.               <input
    
  60.                 onKeyPress={e => {
    
  61.                   charCode = e.charCode;
    
  62.                 }}
    
  63.               />,
    
  64.               container,
    
  65.             );
    
  66.             node.dispatchEvent(
    
  67.               new KeyboardEvent('keypress', {
    
  68.                 charCode: 32,
    
  69.                 bubbles: true,
    
  70.                 cancelable: true,
    
  71.               }),
    
  72.             );
    
  73.             expect(charCode).toBe(32);
    
  74.           });
    
  75. 
    
  76.           it('when charCode is 13 and keyCode is missing, returns charCode', () => {
    
  77.             let charCode = null;
    
  78.             const node = ReactDOM.render(
    
  79.               <input
    
  80.                 onKeyPress={e => {
    
  81.                   charCode = e.charCode;
    
  82.                 }}
    
  83.               />,
    
  84.               container,
    
  85.             );
    
  86.             node.dispatchEvent(
    
  87.               new KeyboardEvent('keypress', {
    
  88.                 charCode: 13,
    
  89.                 bubbles: true,
    
  90.                 cancelable: true,
    
  91.               }),
    
  92.             );
    
  93.             expect(charCode).toBe(13);
    
  94.           });
    
  95. 
    
  96.           // Firefox creates a keypress event for function keys too. This removes
    
  97.           // the unwanted keypress events. Enter is however both printable and
    
  98.           // non-printable. One would expect Tab to be as well (but it isn't).
    
  99.           it('when charCode is smaller than 32 but is not 13, and keyCode is missing, ignores keypress', () => {
    
  100.             let called = false;
    
  101.             const node = ReactDOM.render(
    
  102.               <input
    
  103.                 onKeyPress={() => {
    
  104.                   called = true;
    
  105.                 }}
    
  106.               />,
    
  107.               container,
    
  108.             );
    
  109.             node.dispatchEvent(
    
  110.               new KeyboardEvent('keypress', {
    
  111.                 charCode: 31,
    
  112.                 bubbles: true,
    
  113.                 cancelable: true,
    
  114.               }),
    
  115.             );
    
  116.             expect(called).toBe(false);
    
  117.           });
    
  118. 
    
  119.           it('when charCode is 10, returns 13', () => {
    
  120.             let charCode = null;
    
  121.             const node = ReactDOM.render(
    
  122.               <input
    
  123.                 onKeyPress={e => {
    
  124.                   charCode = e.charCode;
    
  125.                 }}
    
  126.               />,
    
  127.               container,
    
  128.             );
    
  129.             node.dispatchEvent(
    
  130.               new KeyboardEvent('keypress', {
    
  131.                 charCode: 10,
    
  132.                 bubbles: true,
    
  133.                 cancelable: true,
    
  134.               }),
    
  135.             );
    
  136.             expect(charCode).toBe(13);
    
  137.           });
    
  138. 
    
  139.           it('when charCode is 10 and ctrl is pressed, returns 13', () => {
    
  140.             let charCode = null;
    
  141.             const node = ReactDOM.render(
    
  142.               <input
    
  143.                 onKeyPress={e => {
    
  144.                   charCode = e.charCode;
    
  145.                 }}
    
  146.               />,
    
  147.               container,
    
  148.             );
    
  149.             node.dispatchEvent(
    
  150.               new KeyboardEvent('keypress', {
    
  151.                 charCode: 10,
    
  152.                 ctrlKey: true,
    
  153.                 bubbles: true,
    
  154.                 cancelable: true,
    
  155.               }),
    
  156.             );
    
  157.             expect(charCode).toBe(13);
    
  158.           });
    
  159.         });
    
  160. 
    
  161.         // TODO: this seems IE8 specific.
    
  162.         // We can probably remove this normalization.
    
  163.         describe('when charCode is not present in nativeEvent', () => {
    
  164.           let charCodeDescriptor;
    
  165. 
    
  166.           beforeEach(() => {
    
  167.             charCodeDescriptor = Object.getOwnPropertyDescriptor(
    
  168.               KeyboardEvent.prototype,
    
  169.               'charCode',
    
  170.             );
    
  171.             delete KeyboardEvent.prototype.charCode;
    
  172.           });
    
  173. 
    
  174.           afterEach(() => {
    
  175.             // Don't forget to restore for other tests.
    
  176.             Object.defineProperty(
    
  177.               KeyboardEvent.prototype,
    
  178.               'charCode',
    
  179.               charCodeDescriptor,
    
  180.             );
    
  181.             charCodeDescriptor = null;
    
  182.           });
    
  183. 
    
  184.           it('when keyCode is 32 or bigger, returns keyCode', () => {
    
  185.             let charCode = null;
    
  186.             const node = ReactDOM.render(
    
  187.               <input
    
  188.                 onKeyPress={e => {
    
  189.                   charCode = e.charCode;
    
  190.                 }}
    
  191.               />,
    
  192.               container,
    
  193.             );
    
  194.             node.dispatchEvent(
    
  195.               new KeyboardEvent('keypress', {
    
  196.                 keyCode: 32,
    
  197.                 bubbles: true,
    
  198.                 cancelable: true,
    
  199.               }),
    
  200.             );
    
  201.             expect(charCode).toBe(32);
    
  202.           });
    
  203. 
    
  204.           it('when keyCode is 13, returns 13', () => {
    
  205.             let charCode = null;
    
  206.             const node = ReactDOM.render(
    
  207.               <input
    
  208.                 onKeyPress={e => {
    
  209.                   charCode = e.charCode;
    
  210.                 }}
    
  211.               />,
    
  212.               container,
    
  213.             );
    
  214.             node.dispatchEvent(
    
  215.               new KeyboardEvent('keypress', {
    
  216.                 keyCode: 13,
    
  217.                 bubbles: true,
    
  218.                 cancelable: true,
    
  219.               }),
    
  220.             );
    
  221.             expect(charCode).toBe(13);
    
  222.           });
    
  223. 
    
  224.           it('when keyCode is smaller than 32 and is not 13, ignores keypress', () => {
    
  225.             let called = false;
    
  226.             const node = ReactDOM.render(
    
  227.               <input
    
  228.                 onKeyPress={e => {
    
  229.                   called = true;
    
  230.                 }}
    
  231.               />,
    
  232.               container,
    
  233.             );
    
  234.             node.dispatchEvent(
    
  235.               new KeyboardEvent('keypress', {
    
  236.                 keyCode: 31,
    
  237.                 bubbles: true,
    
  238.                 cancelable: true,
    
  239.               }),
    
  240.             );
    
  241.             expect(called).toBe(false);
    
  242.           });
    
  243.         });
    
  244.       });
    
  245. 
    
  246.       describe('when event is not `keypress`', () => {
    
  247.         it('returns 0', () => {
    
  248.           let charCodeDown = null;
    
  249.           let charCodeUp = null;
    
  250.           const node = ReactDOM.render(
    
  251.             <input
    
  252.               onKeyDown={e => {
    
  253.                 charCodeDown = e.charCode;
    
  254.               }}
    
  255.               onKeyUp={e => {
    
  256.                 charCodeUp = e.charCode;
    
  257.               }}
    
  258.             />,
    
  259.             container,
    
  260.           );
    
  261.           node.dispatchEvent(
    
  262.             new KeyboardEvent('keydown', {
    
  263.               key: 'Del',
    
  264.               bubbles: true,
    
  265.               cancelable: true,
    
  266.             }),
    
  267.           );
    
  268.           node.dispatchEvent(
    
  269.             new KeyboardEvent('keyup', {
    
  270.               key: 'Del',
    
  271.               bubbles: true,
    
  272.               cancelable: true,
    
  273.             }),
    
  274.           );
    
  275.           expect(charCodeDown).toBe(0);
    
  276.           expect(charCodeUp).toBe(0);
    
  277.         });
    
  278.       });
    
  279. 
    
  280.       it('when charCode is smaller than 32 but is not 13, and keyCode is missing, charCode is 0', () => {
    
  281.         let charCode = null;
    
  282.         const node = ReactDOM.render(
    
  283.           <input
    
  284.             onKeyDown={e => {
    
  285.               charCode = e.charCode;
    
  286.             }}
    
  287.           />,
    
  288.           container,
    
  289.         );
    
  290.         node.dispatchEvent(
    
  291.           new KeyboardEvent('keydown', {
    
  292.             charCode: 31,
    
  293.             bubbles: true,
    
  294.             cancelable: true,
    
  295.           }),
    
  296.         );
    
  297.         expect(charCode).toBe(0);
    
  298.       });
    
  299.     });
    
  300. 
    
  301.     describe('keyCode', () => {
    
  302.       describe('when event is `keydown` or `keyup`', () => {
    
  303.         it('returns a passed keyCode', () => {
    
  304.           let keyCodeDown = null;
    
  305.           let keyCodeUp = null;
    
  306.           const node = ReactDOM.render(
    
  307.             <input
    
  308.               onKeyDown={e => {
    
  309.                 keyCodeDown = e.keyCode;
    
  310.               }}
    
  311.               onKeyUp={e => {
    
  312.                 keyCodeUp = e.keyCode;
    
  313.               }}
    
  314.             />,
    
  315.             container,
    
  316.           );
    
  317.           node.dispatchEvent(
    
  318.             new KeyboardEvent('keydown', {
    
  319.               keyCode: 40,
    
  320.               bubbles: true,
    
  321.               cancelable: true,
    
  322.             }),
    
  323.           );
    
  324.           node.dispatchEvent(
    
  325.             new KeyboardEvent('keyup', {
    
  326.               keyCode: 40,
    
  327.               bubbles: true,
    
  328.               cancelable: true,
    
  329.             }),
    
  330.           );
    
  331.           expect(keyCodeDown).toBe(40);
    
  332.           expect(keyCodeUp).toBe(40);
    
  333.         });
    
  334.       });
    
  335. 
    
  336.       describe('when event is `keypress`', () => {
    
  337.         it('returns 0', () => {
    
  338.           let keyCode = null;
    
  339.           const node = ReactDOM.render(
    
  340.             <input
    
  341.               onKeyPress={e => {
    
  342.                 keyCode = e.keyCode;
    
  343.               }}
    
  344.             />,
    
  345.             container,
    
  346.           );
    
  347.           node.dispatchEvent(
    
  348.             new KeyboardEvent('keypress', {
    
  349.               charCode: 65,
    
  350.               bubbles: true,
    
  351.               cancelable: true,
    
  352.             }),
    
  353.           );
    
  354.           expect(keyCode).toBe(0);
    
  355.         });
    
  356.       });
    
  357.     });
    
  358. 
    
  359.     describe('which', () => {
    
  360.       describe('when event is `keypress`', () => {
    
  361.         it('is consistent with `charCode`', () => {
    
  362.           let calls = 0;
    
  363.           const node = ReactDOM.render(
    
  364.             <input
    
  365.               onKeyPress={e => {
    
  366.                 expect(e.which).toBe(e.charCode);
    
  367.                 calls++;
    
  368.               }}
    
  369.             />,
    
  370.             container,
    
  371.           );
    
  372.           // Try different combinations from other tests.
    
  373.           node.dispatchEvent(
    
  374.             new KeyboardEvent('keypress', {
    
  375.               charCode: 0,
    
  376.               keyCode: 13,
    
  377.               bubbles: true,
    
  378.               cancelable: true,
    
  379.             }),
    
  380.           );
    
  381.           node.dispatchEvent(
    
  382.             new KeyboardEvent('keypress', {
    
  383.               charCode: 32,
    
  384.               bubbles: true,
    
  385.               cancelable: true,
    
  386.             }),
    
  387.           );
    
  388.           node.dispatchEvent(
    
  389.             new KeyboardEvent('keypress', {
    
  390.               charCode: 13,
    
  391.               bubbles: true,
    
  392.               cancelable: true,
    
  393.             }),
    
  394.           );
    
  395.           expect(calls).toBe(3);
    
  396.         });
    
  397.       });
    
  398. 
    
  399.       describe('when event is `keydown` or `keyup`', () => {
    
  400.         it('is consistent with `keyCode`', () => {
    
  401.           let calls = 0;
    
  402.           const node = ReactDOM.render(
    
  403.             <input
    
  404.               onKeyDown={e => {
    
  405.                 expect(e.which).toBe(e.keyCode);
    
  406.                 calls++;
    
  407.               }}
    
  408.               onKeyUp={e => {
    
  409.                 expect(e.which).toBe(e.keyCode);
    
  410.                 calls++;
    
  411.               }}
    
  412.             />,
    
  413.             container,
    
  414.           );
    
  415.           node.dispatchEvent(
    
  416.             new KeyboardEvent('keydown', {
    
  417.               key: 'Del',
    
  418.               bubbles: true,
    
  419.               cancelable: true,
    
  420.             }),
    
  421.           );
    
  422.           node.dispatchEvent(
    
  423.             new KeyboardEvent('keydown', {
    
  424.               charCode: 31,
    
  425.               bubbles: true,
    
  426.               cancelable: true,
    
  427.             }),
    
  428.           );
    
  429.           node.dispatchEvent(
    
  430.             new KeyboardEvent('keydown', {
    
  431.               keyCode: 40,
    
  432.               bubbles: true,
    
  433.               cancelable: true,
    
  434.             }),
    
  435.           );
    
  436.           node.dispatchEvent(
    
  437.             new KeyboardEvent('keyup', {
    
  438.               key: 'Del',
    
  439.               bubbles: true,
    
  440.               cancelable: true,
    
  441.             }),
    
  442.           );
    
  443.           node.dispatchEvent(
    
  444.             new KeyboardEvent('keyup', {
    
  445.               keyCode: 40,
    
  446.               bubbles: true,
    
  447.               cancelable: true,
    
  448.             }),
    
  449.           );
    
  450.           expect(calls).toBe(5);
    
  451.         });
    
  452.       });
    
  453.     });
    
  454. 
    
  455.     describe('code', () => {
    
  456.       it('returns code on `keydown`, `keyup` and `keypress`', () => {
    
  457.         let codeDown = null;
    
  458.         let codeUp = null;
    
  459.         let codePress = null;
    
  460.         const node = ReactDOM.render(
    
  461.           <input
    
  462.             onKeyDown={e => {
    
  463.               codeDown = e.code;
    
  464.             }}
    
  465.             onKeyUp={e => {
    
  466.               codeUp = e.code;
    
  467.             }}
    
  468.             onKeyPress={e => {
    
  469.               codePress = e.code;
    
  470.             }}
    
  471.           />,
    
  472.           container,
    
  473.         );
    
  474.         node.dispatchEvent(
    
  475.           new KeyboardEvent('keydown', {
    
  476.             code: 'KeyQ',
    
  477.             bubbles: true,
    
  478.             cancelable: true,
    
  479.           }),
    
  480.         );
    
  481.         node.dispatchEvent(
    
  482.           new KeyboardEvent('keyup', {
    
  483.             code: 'KeyQ',
    
  484.             bubbles: true,
    
  485.             cancelable: true,
    
  486.           }),
    
  487.         );
    
  488.         node.dispatchEvent(
    
  489.           new KeyboardEvent('keypress', {
    
  490.             code: 'KeyQ',
    
  491.             charCode: 113,
    
  492.             bubbles: true,
    
  493.             cancelable: true,
    
  494.           }),
    
  495.         );
    
  496.         expect(codeDown).toBe('KeyQ');
    
  497.         expect(codeUp).toBe('KeyQ');
    
  498.         expect(codePress).toBe('KeyQ');
    
  499.       });
    
  500.     });
    
  501.   });
    
  502. 
    
  503.   describe('EventInterface', () => {
    
  504.     it('is able to `preventDefault` and `stopPropagation`', () => {
    
  505.       let expectedCount = 0;
    
  506.       const eventHandler = event => {
    
  507.         expect(event.isDefaultPrevented()).toBe(false);
    
  508.         event.preventDefault();
    
  509.         expect(event.isDefaultPrevented()).toBe(true);
    
  510. 
    
  511.         expect(event.isPropagationStopped()).toBe(false);
    
  512.         event.stopPropagation();
    
  513.         expect(event.isPropagationStopped()).toBe(true);
    
  514.         expectedCount++;
    
  515.       };
    
  516.       const div = ReactDOM.render(
    
  517.         <div
    
  518.           onKeyDown={eventHandler}
    
  519.           onKeyUp={eventHandler}
    
  520.           onKeyPress={eventHandler}
    
  521.         />,
    
  522.         container,
    
  523.       );
    
  524. 
    
  525.       div.dispatchEvent(
    
  526.         new KeyboardEvent('keydown', {
    
  527.           keyCode: 40,
    
  528.           bubbles: true,
    
  529.           cancelable: true,
    
  530.         }),
    
  531.       );
    
  532.       div.dispatchEvent(
    
  533.         new KeyboardEvent('keyup', {
    
  534.           keyCode: 40,
    
  535.           bubbles: true,
    
  536.           cancelable: true,
    
  537.         }),
    
  538.       );
    
  539.       div.dispatchEvent(
    
  540.         new KeyboardEvent('keypress', {
    
  541.           charCode: 40,
    
  542.           keyCode: 40,
    
  543.           bubbles: true,
    
  544.           cancelable: true,
    
  545.         }),
    
  546.       );
    
  547.       expect(expectedCount).toBe(3);
    
  548.     });
    
  549.   });
    
  550. });