Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 349
emmabukacek Avatar asked Nov 17 '14 05:11

emmabukacek


2 Answers

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.

like image 91
Chris Avatar answered Nov 16 '22 21:11

Chris


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!

like image 3
emmabukacek Avatar answered Nov 16 '22 23:11

emmabukacek