Saturday, November 26, 2011

BDDing Backbone.js with Jasmine (Day 2)

‹prev | My Chain | next›

After my jasmine-fueled BDD session last night, it is time to poke my head up to see how it looks. The verdict is that I need to drive a few more things:


So, from top-to-bottom, I need to remove the sub-title date, need more appointments (those are only from November, it should include all appointments, the appointment titles should be defined, and the next / previous navigation elements should be gone.

Removing the subtitle and the navigation elements should be the responsibility of the Month View, not my new list view. So I add new specs to my Month view:

  describe("month view", function() {
    // ...
    describe("leaving", function() {
      it("removes its subtitle");
      it("removes navigation elements");
    });
  });
I already have spec setup code to create a Month view. For the setup in this spec, I need to also navigate to the list view, which should remove the subtitle and navigation elements:
    describe("leaving", function() {
      beforeEach(function() {
        window.calendar = new Cal($('#calendar'));
        Backbone.history.navigate('', true);
        Backbone.history.navigate('#list', true);
        // ....
      });
      // ...
    });
With the setup navigating to the list view, I can define my expectation:
      it("removes its subtitle", function() {
        expect($('h1')).not.toHaveText(new RegExp(year));
      });
Which fails:


To make that pass, I simply need to have a higher level view (the application view in this case), tell the Title View to remove itself when switching to the List View:
    var Application = Backbone.View.extend({
      // ...
      render: function() {
        if (this.view == 'list') {
          view = new CalendarList({collection: this.collection});
          this.title_view.remove();
        }
        else { /* ... */ }

        $(this.el).html(view.render().el);
      },
      // ...
    });
And now I have reached the green:


I write a similar spec for my navigation controls, except in this case I expect the controls to be hidden:
      it("removes navigation elements", function() {
        expect($('.previous')).not.toBeVisible();
      });
To make that pass, I again make the higher level view responsible for telling the lower order view to remove itself.

The last thing that I try to solve tonight is the mystery "unknown" titles. For that, I know that my fake (sinon.js) server response includes an appointment titled "Appt 001". So I create an expectation that the appointment list view should include that title:
  describe("list view", function() {
    var couch_doc1 = { "title": "Appt 001", /* ... */ }
    // ...
      , doc_list = { "rows": [ {"value": couch_doc2}, /* ... */ ] };

    // ...
    it("displays appointment titles", function() {
      expect($('#appointment-list')).toHaveText(/Appt 001/);
    });
  });
To make this pass, I simply have to recall that accessing Backbone model attributes is done via a get() method, not by accessing attributes directly on the model:
    var CalendarList = Backbone.View.extend({
      // ...
      render: function() {
        $(this.el).html(
          '<h2>Appointment List</h2>' +
          '<ol id="appointment-list">' +
          this.collection.reduce(function(memo, appointment) {
            return memo + '<li>' + appointment.title + '</li>';
          }, "") +
          '<ol>'

        );
        return this;
      }
    });
After changing that to appointment.get("title"), I have my green test. And quick sanity check reveals that I fixed most of my problems:

I call it a night here. Up tomorrow: I need to do some more experimenting with the router to support a recipe in Recipes with Backbone.


Day #217

No comments:

Post a Comment