Sunday, September 21, 2014

Pub as a Simple Test Server


I am fairly excited to have a continuous integration solution for Patterns in Polymer. This is probably something I should have done a long time back, but I now run tests whenever Dart is updated and at least daily to check for newer versions of Polymer. This should go a long way toward helping (forcing) me to keep the book up to date. I am not quite done, however.

I have the each chapter's project code under test for the JavaScript edition of the book. But the Dart edition is not quite so complete. Hopefully this will not be too hard to rectify. Many chapters share similar code so the tests can be similar (or identical), which is what I found with the JavaScript tests. But I am not going to start there. Instead, I am curious if my struggles with testing the internationalization solution in JavaScript will continue into Dart.

I do not have any “real” trouble testing Polymer elements in JavaScript, just expressing the tests succinctly. The need to wait an event loop for Polymer to render changes leads to awkward beforeEach() blocks like:
    describe('it can localize to French', function(){
      beforeEach(function(done) {
        el.locale = 'fr';
        setTimeout(done, 0); // One event loop for element to update
      });

      it('(fr)', function(){
        expect(el.shadowRoot.textContent).toContain('Bonjour');
      });
    });
Hopefully Dart's async support will make this a little cleaner.

The pubspec.yaml configuration file depends on unconstrained versions of Polymer and scheduled_test:
name: i18n
dependencies:
  polymer: any
dev_dependencies:
  scheduled_test: any
transformers:
- polymer:
    entry_points: web/index.html
The scheduled_test library is a slight variation on unittest (both are from the Dart core team), but with better asynchronous support.

A test of the element is fairly straigh-forward. With scheduled_tests, I need to wrap everything that would otherwise happen asynchronously in a schedule(). In this case, I schedule Polymer being ready, the <hello-you> element being ready, and then the test itself:
main() {
  initPolymer();

  PolymerElement _el;
  setUp((){
    schedule(()=> Polymer.onReady);

    schedule((){
      _el = createElement('<hello-you></hello-you>');
      document.body.append(_el);

      var _completer = new Completer();
      _el.async((_)=> _completer.complete());
      return _completer.future;
    });

    currentSchedule.onComplete.schedule(() => _el.remove());
  });

  group("<hello-you/> (i18n)", (){
    test('defaults to English', (){
      schedule((){
        expect(_el.shadowRoot.text, contains('Hello'));
      });
    });
  });
  // ...
}
That passes, and has the definite advantage of being cleaner than the JavaScript equivalent (which required those unsightly beforeEach() blocks). But, this only passes when I run this in the browser from pub serve, which starts a test build on http://localhost:8081.

The problem is that my i18n solution loads its localizations from separate JSON files via Ajax (actually core-ajax Polymer elememts). Loading files via Ajax will not work with a normal content_shell command-line run:
$ content_shell --dump-render-tree --allow-file-access-from-files test/index.html
#READY
CONSOLE MESSAGE: unittest-suite-wait-for-done
CONSOLE MESSAGE: FAIL: <hello-you/> (i18n) defaults to English
  Caught The schedule had 3 errors:
  ScheduleError: "Instance of '_XMLHttpRequestProgressEvent'"
  Stack chain:
  | dart:html  _handleMutation
...
It looks like I am going to have to start pub server, then run content_shell tests against it instead of local files:
$ content_shell --dump-render-tree http://localhost:8081/
#READY
CONSOLE MESSAGE: unittest-suite-wait-for-done
CONSOLE MESSAGE: PASS: <hello-you/> (i18n) defaults to English
CONSOLE MESSAGE: 
CONSOLE MESSAGE: All 1 tests passed.
CONSOLE MESSAGE: unittest-suite-success
CONSOLE WARNING: line 213: PASS
Which is not ideal, but it will work. Dart Pub, which is the package manager for Dart, includes a web server that is primarily useful for smoke testing applications and elements. It normally spins up a web server on localhost:8080. But, if pub notices a test subdirectory, it will spin up a secondary server on localhost:8081:
$ pub serve
...
Loading polymer transformers... (1.1s)
Serving i18n web  on http://localhost:8080
Serving i18n test on http://localhost:8081
Since Dart pub is already installed as part of Dart, I do not need to worry about extra dependencies on my test server. I just spin up pub serve and run my tests as usual. In fact, this is a nice answer to the one of the benefits of a test runner in JavaScript-land.

Day #190

No comments:

Post a Comment