testing backbone.js view events with jasmine
Asked Answered
G

4

8

I'm trying to implement view tests for a Coffeescript implementation of the ubiquitous backbone.js 'todo' example (see github.com/rsim/backbone_coffeescript_demo.)

My jasmine tests of the above demo work pretty well, except for view events. I expect I am stuck on one or both of the following i) I do not understand the event binding in the view code, ii) I do not understand how to properly set up the Jasmine test of the view code events.

Here is an example of the 'edit' event...

class TodoApp.TodoView extends Backbone.View
  tagName: "li"
  template: TodoApp.template '#item-template'
  events:
    "dblclick div.todo-content" : "edit"
     ...

  initialize: ->
    _.bindAll this, 'render', 'close'
    @model.bind 'change', @render
    @model.bind 'destroy', => @remove()

  render: ->
    $(@el).html @template @model.toJSON()
    @setContent()
    this

  edit: ->
    $(@el).addClass "editing"
    @input.focus()
  ...

...now here's a test of whether focus was gained upon double clicking:

    describe "edit state", ->
      li = null

    beforeEach ->
       setFixtures('<ul id="todo-list"></ul>')
       model = new Backbone.Model id: 1, content: todoValue, done: false
       view = new TodoApp.TodoView model: model, template: readFixtures("_item_template.html")
       $("ul#todo-list").append(view.render().el)
           li = $('ul#todo-list li:first')
       target = li.find('div.todo-content')
       expect(target).toExist()
               target.trigger('dblclick') # here's the event!

    it "input takes focus", ->
       expect(li.find('.todo-input').is(':focus')).toBe(true)

The expectation on neither i) the spy nor ii) the focus is met.

Is there a peculiarity to testing backbone.js event code about which I should be aware in Jasmine?

Gianina answered 28/9, 2011 at 23:14 Comment(2)
I'm running into the same problem. Did you find a solution for this?Newmann
Also having a similar problem, still no ideas?Tao
T
2

you're spying on the view's edit method. this replaces the method with a spy object, which means the actual edit method won't get called. therefore, you're @input.focus will never fire.

since you want the test to actually call your edit method, i would remove the spy for it.

side note: don't call expect methods in your beforeEach. if you truly need to set an expectation on those, create an it block for them.

Tournai answered 29/9, 2011 at 1:35 Comment(2)
Thanks, I've removed any Jasmine spying and I still fail to get the expected focus.Gianina
I think your confusing spies and stubs. Spys wrap the method but allow the call through.Confocal
P
2

I'm not great with coffescript so I might be missing something but where are you setting up your spy?

In order to test event calling you may need to refresh the view's events once you've set up the spy.

spyOn(view, 'edit');
view.delegateEvents();
target.trigger('dblclick');

it("should call edit when target is double clicked", function() {
  expect(view.edit).toHaveBeenCalled()
});
Positive answered 21/9, 2012 at 12:3 Comment(0)
F
0

I did not write my test in coffeescript, but I did have the same problem, so I hope you will forgive me for answering in javadcript. I ended up breaking down your test into two different tests. First, I tested if calling the view's edit function set the focus on the input box. After that, I tested whether the edit was called when the label was double-clicked, and have not yet gotten that test to pass. But here's how I tested if the edit function worked.

 describe ("A todo item view", function() {
    var my_model;
    var todo_view;

    beforeEach(function() {
       my_model = new Todo({content:"todo value", done:false});
       todo_view = new TodoView({model:my_model});
    });


   it("should set the focus on the input box when the edit function is called", function(){ 
        $('body').append( todo_view.$el ); //append the view to Specrunner.html
        todo_view.edit(); //call the view's edit function
        var focus= document.activeElement;  //finds what element on the page has focus
        expect(focus).toBe('.todo-input');  //jasmine-jquery matcher checks if focused element has the .todo-input class
});

Something that might be causing problems is that your model And your view are declared inside beforeEach. Declaring them inside beforeEach means they only exist inside beforeEach's scope, and no longer exist when you run your it.

Also, does setFixtures do what you think it does? The focus cannot be set on an element that is not part of the DOM tree, so I appended the view's el to the body of the jasmine spec itself. (I'm using the HTML specrunner, not the command-line version) That makes it part of the dom tree and therefore allows it to have focus, and also makes whether it has focus testable.

Fritzsche answered 19/2, 2013 at 2:13 Comment(0)
Z
0

The issue with this is that Backbone.View events object is using event delegation. For the events to be able to be called work the element has to be part of DOM, you can do this by doing something like $('body').append(someView.el) in your beforeEach. Personally, I try not to test if Backbone is correctly setting the events and triggering clicks manually, is more practical for unit tests to call the callback handlers directly avoiding the DOM completely which can slow down your tests a lot.

For :focus is the same problem, there has to be an element in the DOM so that jQuery can tell if an element is focused. In this case it's better to set some state as part of your component and not checking for state via querying the DOM, e.g.: someView.hasFocus === true. Alternatively you can spy on the elements focus implementation and check if it was called.

Zippel answered 23/6, 2015 at 0:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.