Search code examples
javascriptunit-testingtestingjasminejasmine-jquery

Jasmine test event listener function for nodeList from given selector


I have following code which listens for keydown event in given array of nodeList.

var obj = {
 method: function(nodeSelector) {
    var nodeContainers = document.querySelectorAll(nodeSelector);
    var keyListenerFunc = this.keyListener.bind(this);

    this.bindListener(nodeContainers, keyListenerFunc);
  },
  isListNode: function (evt){
    return evt.target.nodeName.toLowerCase() === 'li';
  },
  isContainer: function(evt){
    return evt.target.parentNode.classList.contains(this.indicatorClass);
  },
  keyListener: function(evt) {
    if (evt.keyCode === 32 && (this.isContainer(evt) && this.isListNode(evt))) {
      evt.preventDefault();
      evt.target.click();
    }
  },
  bindListener: function(targets, callbackFunc) {
    [].forEach.call(targets, function(item) {
      item.addEventListener('keydown', callbackFunc);
    });
  },
  indicatorClass: 'indicator'
};

I'm using it like: obj.method('.someClassNames'); But now I want to test it completely including the triggering of keydown event. How can I attach event listener and then trigger keydown event on given dom nodes so that my Jasmine tests would work ? How can I create some dummy html code here and then trigger event on it ? I am expecting to write tests of this type =>

it('It should put event listeners on each carousel passed to the service', function(){});
it('It should call event.preventDefault', function(){});
it('It should call event.target.click', function(){});

My markup is follwing

var html = '<div class="someClassNames">'+
    '<div class="indicator">'+
      '<li>text</li>'+
    '</div>'
  '</div>';

I am assuming that I am going to need to trigger following keydown event but I am not sure as to how to trigger is on the given markup and check in the test description.

var e = new window.KeyboardEvent('keydown', {
      bubbles: true
});
Object.defineProperty(e, 'keyCode', {'value': 32}); 

I am very much new to testing with Jasmine and I couldn't find any examples that would help me test this scenario. I hope my example makes it clear.


Solution

  • few observations:

    • Note that the callbackFunc is actually assigned to the onkeydown
      attribute of the element. Hence you may want to spy on the
      element.onkeydown rather than obj.keyListener

    • Sometimes the render of the UI element may take place after spec has been run.

    • So to ensure that you have the element is present, I've used the setTimeout with a jasmine clock
    • If you really want to test your obk.keyListener, try using an anonymous function like here

    here is how I've it running. I've used mouseover as I'm lazy :)

    var obj = {
        testVar : "Object",
      method: function(nodeSelector) {
        var nodeContainers = document.querySelectorAll(nodeSelector);
        var keyListenerFunc = this.keyListener.bind(this);
        this.bindListener(nodeContainers, keyListenerFunc);
      },
      isListNode: function(evt) {
        return evt.target.nodeName.toLowerCase() === 'li';
      },
      isContainer: function(evt) {
        return evt.target.parentNode.classList.contains(this.indicatorClass);
      },
      keyListener: function(evt) {
        console.log('Yo! You hovered!');    
      },
      bindListener: function(targets, callbackFunc) {
        targets.forEach(function(item) {
          item.addEventListener('mouseover', callbackFunc, false);
        });
      },
      indicatorClass: 'indicator'
    };
    
    describe('Sample tests', function() {
    //this ensures you have the element set up 
      beforeEach(function() {
      jasmine.clock().install();
      jasmine.DEFAULT_TIMEOUT_INTERVAL = 200;
        setTimeout(function() {
          obj.method('div.indicator');
        }, 0);
      });
    
      it('It should put event listeners', function() {
        jasmine.clock().tick(10);
        var ele= document.getElementsByClassName("indicator")[0];
        spyOn(ele, 'onmouseover').and.callThrough();    
        $('.indicator').trigger('mouseover');    
        expect(ele.onmouseover).toHaveBeenCalled();
        expect(typeof ele.onmouseover).toBe('function'); 
      });
    });
    

    HTML CONTENT:

    <div class="someClassNames">
      <div class="indicator">
        <li>text</li>
        <br/> </div>
    </div>