emmabukacek
emmabukacek

Reputation: 193

Simulate fake keypress in jasmine

I'm trying to simulate a keypress in Jasmine (the test browser is PhantomJS) so I can unit test some of my functions that use key presses. Unfortunately, I can't test it properly with Jasmine because I'm getting errors.

Here's the code I'm trying to test:

function Controls() {
  'use strict';
  this.codes = {
    37: 'left',
    39: 'right',
    38: 'forward',
    40: 'backward'
  };
  this.states = {
    'left': false,
    'right': false,
    'forward': false,
    'backward': false
  };
  document.addEventListener('keydown', function() { this.onKey.bind(this, true); }, false);
  document.addEventListener('keyup', function() { this.onKey.bind(this, false); }, false);
}

Controls.prototype.onKey = function(val, e) {
  var state = this.codes[e.keyCode];

  if (typeof state === 'undefined') return false;

  this.states[state] = val;

  // Stop the key press from repeating
  e.preventDefault();
  e.stopPropagation();

  return true;
};

controls = new Controls();

(NOTE: The above code wraps the functions in the addEventListener with anon functions so that Jasmine will actually attempt to run my tests. Without the anonymous wrap, this results in another error: TypeError: 'undefined' is not a function (evaulating 'this.onKey.bind(this, true)' Solving this problem would be great too. )

And this is my attempt at testing:

function keyPress(key) {
  var pressEvent = document.createEvent('KeyboardEvent');
  pressEvent.initKeyEvent('keydown', true, true, window,
                            false, false, false, false,
                            0, key);
  document.dispatchEvent(pressEvent);
}

describe("Controls", function() {
  var controls;

  it("should initialize properly", function() {
    controls = new Controls();
    expect(controls).not.toBe(null);
  });

  it("should reflect changes in state when arrow keys are used", function() {
    keyPress(37);
    expect(controls.states[state]).toBe(37);
  });
});

This results in an error from Jasmine:

TypeError: 'undefined' is not a function (evaluating 'pressEvent.initKeyEvent('keydown', true, true, window, false, false, false, false, 0, key)')

I'm still very new to Jasmine (and for that matter, Javascript), so any help in general would be appreciated.

Upvotes: 12

Views: 22740

Answers (3)

Victor Zakharov
Victor Zakharov

Reputation: 26434

The modern way:

function keyPress(key: string, el: HTMLElement) {
  const event = new KeyboardEvent('keydown', { key });
  el.dispatchEvent(event);
}

Call it like this:

keyPress('space', el as HTMLElement);

It worked with the latest version of Devextreme components (v22.1.3).

Upvotes: 2

Chris
Chris

Reputation: 2806

It looks like your problem is with how you're creating your event.

The example code below shows how an event is created, triggered, and intercepted.

var keyPressed = null;

function keyPress(key) {
  var event = document.createEvent('Event');
  event.keyCode = key; // Deprecated, prefer .key instead.
  event.key = key;
  event.initEvent('keydown');
  document.dispatchEvent(event);
}

document.addEventListener('keydown', function(e){
   keyPressed = e.key;
});

keyPress(37)
alert(keyPressed);

Here's a plunker too: http://plnkr.co/edit/TxGXcT0DnPa44C0PkHkN?p=preview

As commented, see here for information regarding .keyCode deprecation.

Upvotes: 18

emmabukacek
emmabukacek

Reputation: 193

EDIT: This issue should be resolved as of PhantomJS 2. More information in the issue below.

So the reason

this.onKey.bind(this, true)

was returning an error is because as of the date of this post, PhantomJS doesn't support binds yet. You can find more information on that here:

https://github.com/ariya/phantomjs/issues/10522

and a longer explanation from ariya here:

https://groups.google.com/forum/#!msg/phantomjs/r0hPOmnCUpc/uxusqsl2LNoJ

A way around this issue is to create a polyfill as suggested by creationix:

https://github.com/c9/smith/blob/master/tests/public/test.js#L2-L7

Given all of this information, it should make testing a little more manageable knowing that I can circumvent the prototype issue. Thanks for all of the help!

Upvotes: 3

Related Questions