Sunday, September 22, 2013

Full-Stack Backbone.js Testing with Dart


Over the last few days, I have more or less gotten Dart's awesome unit testing working with a Backbone.js application. Thanks to the magic of Dart's js-interop, I have a real test running against the Funky Calendar from Recipes with Backbone. And after last night, I have setup and teardown working. For my latest feat of insanity (and I mean that, there's no real reason to do this) I will attempt testing the creation of appointments in the Backbone.js calendar.

If there is a deficiency to testing Dart applications, it is the inability to stub HTTP requests. Although this makes unit testing tougher, it has the advantage of forcing developers into a more full-stack frame of mind. Towards that end, I have a “real fake” test server (plummbur-kruk) backing my REST-like Backbone application in test. In the test setup, I am already creating a calendar appointment for the current month:
    setUp((){
      document.head.append(new BaseElement()..href = Kruk.SERVER_ROOT);;

      el = document.body.append(new Element.html('<div id=calendar>'));

       var doc = '''
         {
           "title": "Get Funky",
           "description": "asdf",
           "startDate": "${fifteenth}"
         }''';

      return Future.wait([
        Kruk.create(doc),
        Kruk.alias('/widgets', as: '/appointments')
      ]);
    });
With that setup in place, can start my Backbone application and eventually check my expectation that the appointment on the 15th of the month is included in the app:
expect(cell.text, matches("Get Funky"));
Tonight, I would like add a new test that clicks on particular date, enters appointment information, clicks the save button, and finally verifies that the appointment is now included on the calendar.

I start with the usual Backbone test thing (though with a Dart js-interop twist) by creating an instance of the Backbone application and ensuring that Backbone history is running properly:
    test("can create new appointments", (){
      new js.Proxy(js.context.Cal, query('#calendar'));
      js.context.Backbone.history.loadUrl();
      // ...
    });
Next, I find the table cell that contains the 14th of the month and click on it:
    test("can create new appointments", (){
      // ...
      var cell = queryAll('td').
        where((el)=> el.id == fourteenth).
        first
          ..click();
      // ...
    });
This should result in a popup dialog with a few appointment fields. I fill in those appointment values next:
    test("can create new appointments", (){
      // ...
      query('#calendar-add-appointment')
        ..query('input.title').
          value = 'Test Appointment'
        ..query('input.description').
          value = 'asdf';
      // ...
    });
Finally, I click the OK button in the popup:
    test("can create new appointments", (){
      // ...
      query('.ui-dialog-buttonset').
        query('button').
        click();
      // ...
    });
With that, I should be able to check my expectation that the fourteenth of the month now contains an appointment created in my test (after a brief wait for all of the Backbone events to propagate):
    test("can create new appointments", (){
      // ...
      new Timer(
        new Duration(milliseconds: 10),
        expectAsync0((){
          expect(cell.text, matches("Test Appointment"));
        })
      );
    });
But, for some reason, that fails:
FAIL: the initial view can create new appointments
  Expected: match 'Test Appointment'
    Actual: '14'
All that is in the table cell for the 14th is the day of the month. If I remove the teardown step to see the status of the (un-styled) popup dialog, I find that everything seems to have worked except for the clicking of the button:



And oddly enough, if I use jQuery (which is available to me via js-interop), I am able to click the button:
      js.context.jQuery('.ui-dialog-buttonset button').click();
Which results in a successful test:
PASS: the initial view can create new appointments
I am at a loss to explain why the click event in jQuery works, but it does not work from Dart. Perhaps there is an additional event property that is being added by jQuery? I have a working test, but would very much like to have the test in pure Dart (or at least have a idea why that won't work). So I will likely use that as an excuse to continue investigation tomorrow…


Day #882


No comments:

Post a Comment