19

I'm trying to simulate a keydown event in a unit test (angular2/TypeScript). I do not always have a DebugElement available, so I'm trying to emit the event on a native element. The problem I have is how to define the keyCode when creating the KeyboardEvent. The keyCode is not defined as part of KeyboardEventInit definition, and on the KeyboardEvent itself it is only exposed as a readonly property.

Simply just adding a keyCode property (and set the obj type as ) doesn't work either.

    let elm = <HTMLElement>content.nativeElement;
    let ev = new KeyboardEvent('keydown', {
        code: '123',
        //keyCode: 345,
        key: 'a',
    });
    elm.dispatchEvent(ev);

Any suggestions ?

Edit: According to the mdn link, keyCode is deprecated and should not be used, instead 'code' should be used. https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode

Jesper Kristiansen
  • 1,759
  • 3
  • 17
  • 29
  • Can you explain what exactly do you want to do ? – mrid Nov 10 '16 at 17:09
  • 1
    Seems to work in a [**fiddle**](https://jsfiddle.net/5p0abvzq/), with an actual element, are you sure `elm` contains an element with an event handler that catches the dispatched event ? – adeneo Nov 10 '16 at 17:11
  • yes, I have a component where I handle keyboard input. I'm listening for keyDown and based on keyCode does e.g. scrolling. The reason for not always use angular's DebugElement.triggerEventHandler is that once you modify the DOM programmatically it seems like DebugElement get out of sync with the nativeElements. For this reason I want to dispatch the event on the native element - in my unit test. – Jesper Kristiansen Nov 10 '16 at 17:12
  • https://jsfiddle.net/5p0abvzq/2/ yes it works for key, but not for keyCode which is what I'm trying to figure out – Jesper Kristiansen Nov 10 '16 at 17:15
  • ahh, someone pointer me to the mdn link https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode And there it clearly states that keyCode has been deprecated and should not be used. Instead, 'code' should be used. I will change my implementation to use code instead of keyCode – Jesper Kristiansen Nov 10 '16 at 18:45
  • Have not tried this. But could you wrap a KeyboardEvent in an EventEmitter? [This](https://stackoverflow.com/a/54839816/495157) may help for custom keys like escape and ALT etc. Specifically [this link](https://www.w3.org/TR/uievents-key/#named-key-attribute-values) – JGFMK Feb 23 '19 at 09:08

5 Answers5

4

I still think that this is a bug from the Typescript's side because of the initializer LanguageEvent has. In any case, a workaround would be to use Object.define to set the keyCode.

let arrowRight = new KeyboardEvent('keydown');
Object.defineProperty(arrowRight, 'keyCode', {
    get : () => 39
});
console.log(arrowRight.keyCode, arrowRight.key, arrowRight.code);

No matter the fact that the keyCode is deprecated, that doesn't mean that the initializer shouldn't support it. The most strong argument is the fact that Internet Explorer 9 and 11 depends on that and they don't support the code https://caniuse.com/#search=event.code or fully support the key https://caniuse.com/#search=event.key So I think that LanguageEvent should allow keyCode in its initializer.

let arrowRight = new KeyboardEvent('keydown', { keyCode: 39 });
Rambou
  • 968
  • 9
  • 22
1

keyCode is deprecated, Angular itself operates with key — I suggest you rewrite your logic to use it too. Keep in mind that it is browser dependent — in IE space bar is Space and in other browsers it is . Also Esc and Escape are different. Don't remember any other ones that differ.

waterplea
  • 3,462
  • 5
  • 31
  • 47
  • That still doesn't answer OP question. It's not a debate if angular supports this or not. I think it's a typescript issue. What about cases where we have IE 9 or IE 11. They only send keyCode not code. In those cases, there is a problem. You can see code and key BrowserSupport here https://caniuse.com/#search=Event.key in contrast to keyCode which is fully supported in IE 9 and 11. – Rambou May 18 '20 at 17:58
0

If you're using angular, you can set the keyboard event, and key in your HTML template and bind to your method.

See angular docs on this: https://angular.io/guide/user-input#key-event-filtering-with-keyenter

And since you don't need to test the angular API as it's not your system under test, you should simply write your unit tests by passing mocked data into the method you've bound the key event to.


All of this isn't particularly helpful though if you meant UI/Integration tests, as the above is really only valid for unit tests.

Garrett Manley
  • 337
  • 3
  • 11
0

or example, if you wanted to run a function when a user hit CTRL+ALT+D, your code would look something like this.

 window.addEventListener('keydown', (event: KeyboardEvent) => {
       // Fall back to event.which if event.keyCode is null
       const keycode = event.keyCode || event.which;
       if (keycode === 68 && event.ctrlKey && event.altKey) {
         // Do stuff here
       }
    });
Kavinda Senarathne
  • 1,813
  • 13
  • 15
0

Have a look :

<!DOCTYPE html>
<html>
<body>
  <script>
  function EventHandler(e) {
    e = e || window.event;
    var code = typeof e.which !== 'undefined' ? e.which : e.keyCode; 
    if(code == 112 || code == 113 
        || code == 114 || code == 115
        || code == 116 || code == 117
        || code == 118 || code == 119
        || code == 120 || code == 121
        || code == 122 || code == 123) {
      e.returnValue = false;
      var target2 = document.getElementById("iFrameId"); 
      var targetBody = target2.contentDocument.body; 
      var keyboardEvent = document.createEvent("KeyboardEvent");

      Object.defineProperty(keyboardEvent, 'keyCode', {
        get : function() {
          return this.keyCodeVal;
        }
      });

      Object.defineProperty(keyboardEvent, 'which', {
        get : function() {
          return this.keyCodeVal;
        }
      });

      var initMethod = typeof keyboardEvent.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent";

      keyboardEvent[initMethod](
         "keydown", // event type : keydown, keyup, keypress
         true, // bubbles
         true, // cancelable
         window, // viewArg: should be window
         false, // ctrlKeyArg
         false, // altKeyArg
         false, // shiftKeyArg
         false, // metaKeyArg
         code, // keyCodeArg : unsigned long the virtual key code, else 0
         0 // charCodeArgs : unsigned long the Unicode character associated with the depressed key, else 0
      );
      keyboardEvent.keyCodeVal = code;
      targetBody.dispatchEvent(keyboardEvent);        
    }
  }
  </script>

  <form>
    <input type="text" name="first" onkeydown="EventHandler()" > <br>
    <iframe id="iFrameId" src="frame.html" > </iframe>
  </form>
</body>
</html>
Kavinda Senarathne
  • 1,813
  • 13
  • 15