Monday, March 23, 2015

New and Old Style Polymer.dart


Don't you hate it when code doesn't break?

Now, don't get me wrong, I like working code as much as the next programmer. Working code is smashing. But when you expect code to break and it doesn't, what's left? Well, a blog post I suppose…

I am not completely sold on the new code import alternative available in Polymer.dart 0.16. It certainly has its appeal. Instead of using old link-tag imports to pull in Polymer element definitions:
    <!-- Load component(s) -->
    <link rel="import"
          href="packages/page_objects/elements/x-pizza.html">

    <!-- Start Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
Now we can import the same element, <x-pizza> in this case, with normal Dart import statements:
    <!-- Load the component and start Polymer -->
    <script type="application/dart">
      import 'packages/page_objects/elements/x_pizza.dart';
      export 'package:polymer/init.dart';
    </script>
My only real concern about the new approach is that there is no JavaScript equivalent (perhaps a use for an ES6 module polyfill?). That aside, importing code with Dart's import seems preferable to a link-tag import, which takes some getting used to.

Of course, now that I am used to link-tag importing, the new code importing takes some getting used to. Last night, I was able to adapt existing code to the new style relatively easily. Polymer code, of course, has both template and backing code parts. With the link-tag approach, everything ran through the HTML template definition, which then had to <script> source the backing class definition. The new code import approach starts with the code, which then has to import the template definition.

That all worked fine last night (aside from a minor, unexplained glitch). Except nothing broke.

I suppose I should be grateful, but I expected my tests to break. And darn it all, they still work (both in the browser and headless with content_shell):
PASS
1       PASS
Expectation: [defaults] it has no toppings .

2       PASS
Expectation: [adding toppings] updates the pizza state accordingly .

All 2 tests passed
I did not necessarily expect either of the two tests to fail, but the 0.16 release notes mention that link-importing a Polymer element converted to the new style should generate a warning. I see no such warning even though my tests are using the link-import approach with my code-import Polymer element:
<!doctype html>
<html>
<head>
  <!-- Load component(s) -->
  <link rel="import" href="packages/page_objects/elements/x-pizza.html">

  <!-- The actual tests -->
  <script type="application/dart" src="test.dart"></script>
  <script src="packages/unittest/test_controller.js"></script>
</head>
<body></body>
So why am I not seeing an error?

The answer turns out to be simple enough—and it also explains my “glitch” from yesterday. As expected, it was PEBKAC. During my various refactorings and unfactorings (undoing the refactoring to reproduce the original state), I neglected to move the old-style link-tag imports from the <x-pizza> template definition:
<link rel="import" href="x-pizza-toppings.html">
<polymer-element name="x-pizza">
  <template>
    <h2>Build Your Pizza</h2>
    <x-pizza-toppings id="firstHalfToppings"
                      name="First Half Toppings"
                      ingredients="{{ingredients}}"></x-pizza-toppings>
    <!-- Second and whole toppings defined here... -->
  </template>
  <script type="application/dart" src="x_pizza.dart"></script>
</polymer-element>
If I remove the link-import of the <x-pizza-toppings> child element and the <script> sourcing of the x_pizza.dart backing class, then my tests fail. Yay!

The tests fail, but the sample page using the new code-import still works. I can get the test to pass by converting it to the code-import approach as well. The test container page becomes trivial, loading only the test file and the usual JavaScript test harness:
<!doctype html>
<html>
<head>
  <script type="application/dart" src="test.dart"></script>
  <script src="packages/unittest/test_controller.js"></script>
</head>
<body></body>
</html>
The test is then responsible for code-importing the element (and initializing Polymer in the usual test manner):
library x_pizza_test;

import 'package:scheduled_test/scheduled_test.dart';
import 'package:unittest/html_config.dart';
import 'package:polymer/polymer.dart';
import 'packages/page_objects/elements/x_pizza.dart';

main() {
  useHtmlConfiguration();
  initPolymer();
  // Tests here...
}
With that, my tests all pass and my sample page still works.

As for the warning message indicated in the release notes, I am unable to reproduce it. The code-import Polymer element still works from the same page when link-tag imported:
    <link rel="import"
          href="packages/page_objects/elements/x-pizza.html">
    <script type="application/dart">
      import 'package:polymer/polymer.dart';
      import 'packages/page_objects/elements/x_pizza.dart';

      main() {
        initPolymer();
      }
    </script>
Of course, that is silly—it link-tag imports the template and code imports the backing class. If I want both the code-import and link-tag import to work with my Polymer element, I have to use the “nodart” intermediate template approach linked in the release notes.

That is, the template-only HTML file (i.e. without link-imports and <script> sourcing) moves to x-pizza-nodart.html:
<polymer-element name="x-pizza">
  <template>
    <-- Template HTML here... -->
  </template>
</polymer-element>
The Dart class that now does the code import needs to refer to this “nodart” template:
@HtmlImport('x-pizza-nodart.html')
library x_pizza;

@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // Code definition here...
}
Lastly, a link-tag importable x-pizza.html definition link-imports the template and <script> sources the backing class:
<link rel="import" href="x-pizza-nodart.html">
<script type="application/dart" src="x_pizza.dart"></script>
With that, I can go back to the main page to link-import the element:
    <link rel="import"
          href="packages/page_objects/elements/x-pizza.html">
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
Or, without making any changes to the Polymer code or template HTML, I can code-import:
    <script type="application/dart">
      import 'packages/page_objects/elements/x_pizza.dart';
      export 'package:polymer/init.dart';
    </script>
In the first case, the x-pizza.html template finds both the backing class and the template. In the second case, the x_pizza.dart code imports the template. In both cases, there is a minimum of fuss and no errors. I do wonder why I am unable to generate the warning described in the release notes, but I doubt I will lose too much sleep over it.


Day #7

Sunday, March 22, 2015

Importing Polymer.dart Elements without <link> Tags


Doing away with <link> imports in Polymer.dart 0.16 is pretty darn cool. It could also present me with a challenge in Patterns in Polymer. Hopefully I can make it work without too much effort, especially with the 1.2 edition right around the corner. There is only one way to find out!

My main concern is that, wherever possible in the book, I use the same text for both the JavaScript and Dart versions. This cuts down on copy edits and keeps me focused on concepts rather than nuances between the two implementations. This change would seem to necessitate more nuances—even if mostly confined to the setup sections of each chapter.

Much of the initialization in the Dart version of the book pulls in the <x-pizza> definition and initializes Polymer like this:
    <!-- Load component(s) -->
    <link rel="import"
          href="packages/page_objects/elements/x-pizza.html">

    <!-- Start Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
The new alternative available in 0.16 is to start Polymer and load the definition at the same time:
    <!-- Load the component and start Polymer -->
    <script type="application/dart">
      import 'packages/page_objects/elements/x_pizza.dart';
      export 'package:polymer/init.dart';
    </script>
I can't just make that change. In the previous version of Polymer.dart, the link-tag-imported HTML template definition pulled in the code definition. This new approach is importing the code definition, so now it needs a way to pull in the template definition.

Without updating my element definition, I get a warning that <x-pizza> is no longer defined:
[Warning from polymer (Linter) on page_objects|web/index.html]:
line 14, column 7 of web/index.html: custom element with name "x-pizza" not found. See http://goo.gl/5HPeuP#polymer_11 for details.
<x-pizza>
^^^^^^^^^
Build completed successfully
Why that counts as a successful build, I could not say, but it definitely needs a little something extra. My x_pizza.dart backing code had been:
import 'package:polymer/polymer.dart';
import 'dart:convert';
import 'dart:async';

@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // Super cool pizza stuff here...
}
To make this work with no-link-tag importing, I have to annotate the code as a library using @HtmlImport—the @HtmlImport annotation effectively taking the place of the old link-tag importing:
@HtmlImport('x-pizza.html')
library x_pizza;

import 'package:polymer/polymer.dart';
import 'dart:convert';
import 'dart:async';

@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // ...
}
The old link-tag importing still works if one prefers that, but this definitely cuts down on the number of lines required in the containing web page.

For this particular code, I am not quite done. Everything compiles and runs without error, but the <x-pizza> element no longer displays any UI. There are no warnings in the browser console, but I do see this message:
No elements registered in a while, but still waiting on 1 elements to be registered. Check that you have a class with an @CustomTag annotation for each of the following tags: 'x-pizza-toppings'
The problem here is that my <x-pizza> uses the <x-pizza-toppings> Polymer element for adding toppings (first half, second half, and whole) to the pizza. This error is telling me that I must also adapt it to the new approach.

I find this a strange error because I am still link-tag importing this child element from the <x-pizza> template:
<link rel="import" href="x-pizza-toppings.html">
<polymer-element name="x-pizza">
  <!-- Pizza html template stuff here.. -->
</polymer-element>
This template definition for <x-pizza-toppings> pulls in the backing class via the usual script tag:
<link rel="import" href="../../../packages/polymer/polymer.html">
<polymer-element name="x-pizza-toppings">
  <!-- Pizza topping html template stuff here.. -->
  <script type="application/dart" src="x_pizza_toppings.dart"></script>
</polymer-element>
And in that backing class is the @CustomTag that Polymer is complaining about:
import 'package:polymer/polymer.dart';

@CustomTag('x-pizza-toppings')
class XPizzaToppings extends PolymerElement {
  // Pizza topping code...
}
I am able to get this working, but then things get really strange...

To get it working, I convert <x-pizza-toppings> to the new no-link-tag style class definition:
@HtmlImport('x-pizza-toppings.html')
library x_pizza_toppings;

import 'package:polymer/polymer.dart';

@CustomTag('x-pizza-toppings')
  // Pizza topping code here...
}
In the code for the XPizza backing class, I now have to import (the Dart kind, not the link-tag kind) the x_pizza_toppings.dart code:
@HtmlImport('x-pizza.html')
library x_pizza;

import 'package:polymer/polymer.dart';
import 'dart:convert';
import 'dart:async';
import 'x_pizza_toppings.dart';

@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  //  Pizza stuff here...
}
I also have to work back through the HTML template definitions in both x-pizza.html and x-pizza-toppings.html to remove the old link imports and the <script> tags that reference the backing class. Removing the <script> tags makes sense—the backing class is now responsible for pulling the template, not the other way around. So that is not too strange.

What is strange here is that I can no longer reproduce the original “waiting on 1 elements to be registered” error condition. If it revert my changes completely and retrace my steps, things either work or fail in different (more easily understood) ways. It feels like some bad code got cached somewhere (in the .pub directory?) during my initial pass at this, but can no longer be produced. That or I did something crazy the first pass through.

I am uncomfortable with this—it feels like I need to be quicker to manually delete .pub when I start doing this kind of thing. At the same time, I shouldn't have to do anything of the sort. For now, I chalk it up to pre-release glitches and (more likely) a dumb mistake that I can no longer remember.

In the end, I like the new approach. The waiting-for-registration problem leaves me a little unsettled, so I am not quite ready to adopt it as the approach in Patterns in Polymer. But it is probably worth a mention somewhere.


Day #6

Saturday, March 21, 2015

Scheduled Polymer and Angular (Dart) Tests


I continue to evaluate Patterns in Polymer against the latest pre-release version of Polymer.dart (0.16). Last night I verified that Angular.dart solution from the book still works. Previously, I verified that simple testing still works. Tonight, I hope to verify that testing with Angular.dart works—thus verifying that more complex testing is still working with Polymer.dart 0.16.

I'm fond of scheduled_test for testing asynchronous Dart code. It sits right on top of the standard unittest, so most of the usual methods and matchers work. What it adds to unittest is nicer support for scheduling otherwise asynchronous actions in order, making tests for this type of code deterministic.

The scheduled_test version of the simple, Polymer 0.16 test from the other day is:
library angular_test;

import 'package:scheduled_test/scheduled_test.dart';
import 'package:unittest/html_config.dart';
import 'package:polymer/polymer.dart';

main() {
  useHtmlConfiguration();
  initPolymer();

  var _el;
  group("<x-pizza>", (){
    setUp((){
      schedule(()=> Polymer.onReady);

      schedule((){
        _el = createElement('<x-pizza></x-pizza>');
        document.body.append(_el);
      });

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

    test('has a shadow root', (){
      schedule(() =>
        expect(query('x-pizza').shadowRoot, isNotNull)
      );
    });
  });
}
I import the testing, test output formatter, and Polymer packages. In the main() entry point, I use the test formatter and initialize Polymer, then I start my test. In setUp(), I have two schedules. The first returns the Polymer.onReady future. The scheduled_test library will wait until this future completes before running any other schedules. So, in setUp(), the custom <x-pizza> Polymer element will not be added to the test page until Polymer is ready to process it—perfect! The last bit of code in setUp() is the scheduled_test version of a tear-down block—it removes the custom element after the test is executed.

As for the test itself, it schedules the expectation that the shadow DOM of the custom element is present—a very basic sanity check that Polymer is working. It is important that this test wrap a schedule. Without it, this test will run before the setUp() schedules, which are placed into a queue. In other words, omitting the test schedule would evaluate expectation before Polymer was ready and before the element had been added to the page.

That test passed, but what about a Polymer test on an Angular page? The Angular.dart package contains a lot of testing infrastructure, but I find it hard to use for application testing. It seems of more benefit for testing Angular itself, which is its purpose after all.

So I create an entire application in my test:
    group("angular", (){
      XPizzaComponent xPizza;

      setUp((){
        schedule((){
          var app = new PizzaStoreApp()
            ..bind(NodeBindDirective)
            ..bind(RouteInitializerFn, toValue: storeRouteInitializer);

          applicationFactory()
            .addModule(app)
            .rootContextType(Pizza)
            .run();
        });
        // More schedules here...
      });
      // Tests here...
    });
The PizzaStoreApp() and router are dumbed-down versions of the ones used in the actual application. It is probably a mistake on my part, but my router actually points to a separate page in my test directory:
void storeRouteInitializer(Router router, RouteViewFactory views) {
  views.configure({
    'custom-pizza': ngRoute(
        defaultRoute: true,
        view: 'custom.html'
      )
  });
}
I am purposely trying to test that my Polymer element works when used in an Angular route, so I need something like this, but I am unsure if this is the best way to go about doing so. If nothing else, this requires me to wait for the custom.html partial to be loaded and inserted into the view. And I have no idea how to do this in Angular.dart, so I cheat:
    group("angular", (){
      setUp((){
        schedule((){ /* Schedule Angular app setup */ });o
        schedule((){
          var _completer = new Completer();
          new Timer(new Duration(milliseconds: 50), ()=> _completer.complete());
          return _completer.future;
        });
        // ...
      });
    });
If this were AngularJS, I could wait for a $viewContentLoaded event before completing this schedule. Unfortunately, there is no Anguar.dart equivalent for this event. So I am forced to wait some arbitrary amount of time. Ew.

Ugliness aside, my tests still work. I can verify that an Angular value bound to my Polymer element is updated in response to a change in the Polymer element:
      test('can double-bind to Polymer properties', (){
        schedule(()=> xPizza.addWholeTopping('green peppers'));
        schedule((){
          var angular_el = document.query('pre');
          expect(
            angular_el.text,
            contains('"whole":["green peppers"]')
          );
        });
      });
The xPizza object is a Page Object that makes testing interaction a little clearer in my tests. In the end, these (and other) tests pass with the latest Polymer.dart:
PASS
1       PASS
Expectation: <x-pizza> has a shadow root .

2       PASS
Expectation: <x-pizza> defaults to a blank pizza .

3       PASS
Expectation: <x-pizza> adding toppings updates the pizza state with new toppings .

4       PASS
Expectation: <x-pizza> angular can double-bind to Polymer properties .

All 4 tests passed
I really ought to get a better handle on testing Angular applications, but the hack that I have been using all along in Patterns in Polymer continues to work.



Day #5

Friday, March 20, 2015

Bleeding Edge Polymer.dart and Angular.dart


When last I checked, the state of Polymer.dart and Angular.dart was pretty rough. I had to override multiple dependencies just to get the two to install side-by-side. At the time, that seemed to work, but hardly seemed a long-term solution. Let's see if the situation has improved.

I continue to work with the latest development release version of Dart (the newest Polymer.dart requires it):
$ dart --version
Dart VM version: 1.9.0-dev.10.12 (Fri Mar 20 06:26:15 2015) on "linux_x64"
In the angular example code from Patterns in Polymer, I start with optimism in my heart. I comment out the old dependency overrides in my pubspec.yaml package file:
name: angular_example
dependencies:
  angular: any
  polymer: any
  angular_node_bind:
    git: https://github.com/eee-c/angular_node_bind.git
# dependency_overrides:
#   args: any
#   code_transformers: any
#   html5lib: any
#   observe: any
dev_dependencies:
  scheduled_test: any
transformers:
- angular:
    html_files:
      - web/partials/custom.html
- polymer:
    entry_points:
      - web/index.html
      - test/index.html
And then I attempt to upgrade to the latest, compatible versions:
$ pub upgrade   
Resolving dependencies... (38.5s) 
And it just stops there. Well, it does not stop—the CPU is pegged and the fan on my laptop bolts into high gear.

Some time later, pub gives up the ghost:
Connection closed before full header was received
So it seems that I must give Dart Pub a gentle nudge at minimum. I start by explicitly requiring the latest Polymer:
name: angular_example
dependencies:
  angular: any
  polymer: ">=0.16.0"
  angular_node_bind:
    git: https://github.com/eee-c/angular_node_bind.git
# ...
Since this is in support of a Polymer book, it seems more appropriate to get the latest Polymer library working with a possibly older version of Angular. But even that fails. I still timeout when trying to pub upgrade. It would appear that a gentle nudge is insufficient. Time for a good firm shove...

I check the generated pubspec.lock of a Polymer only project and an Angular-old project. Comparing the two, I find that Polymer is using a newer version of the observe package and a new version of the collection package.

In the pubspec.lock for the Polymer-only project, I have:
# ...
  collection:
    description: collection
    source: hosted
    version: "1.1.0"
# ...
  observe:
    description: observe
    source: hosted
    version: "0.13.0+1"
# ...
In the pubspec.lock for the Angular-only project, I have:
# ...
  collection:
    description: collection
    source: hosted
    version: "0.9.4"
# ...
  observe:
    description: observe
    source: hosted
    version: "0.12.2+1"
# ....
In both cases, I opt for Polymer to win (again because I am more interested in evaluating Polymer). So I update my Polymer-Angular example application to include some dependency overrides:
name: angular_example
dependencies:
  angular: any
  polymer: ">=0.16.0"
  angular_node_bind:
    git: https://github.com/eee-c/angular_node_bind.git
dependency_overrides:
  collection: '>=0.9.1 <1.2.0'
  observe: '>=0.12.0 <0.14.0'
# ...
That is less than ideal, but I console myself with two rationalizations. First, this halves the number of dependency overrides that I previously had to use. Second, I am using a pre-release version of Polymer. So I do not look too closely and claim progress.

Especially since a pub upgrade now completes successfully:
$ pub upgrade
Resolving dependencies... (11.7s)
  ...
  angular 1.1.0
  angular_node_bind 0.2.0 from git https://github.com/eee-c/angular_node_bind.git at d7b694
  ...
! collection 1.1.0 (overridden)
  ...
> di 3.3.4 (was 3.3.3)
...
! observe 0.13.0+1 (overridden)
  ...
> polymer 0.16.0+6 (was 0.15.5+4)
  ...
Warning: You are using these overridden dependencies:
! collection 1.1.0
! observe 0.13.0+1
Changed 14 dependencies! 
...
Best of all, the Angular and Polymer code still double binds the shared attributes. Angular binds the <x-pizza> element's value attribute to a local variable of its own, which it also binds in a <textarea>:



When I update the Polymer element, adding sausage to the second half of my <x-pizza> Polymer element, Angular sees the attribute change and binds the new value into the <textarea>:



Similarly, when I manually edit the JSON in the <textarea>, the <x-pizza> Polymer element is updated:



Yay!

I close by noting that a recent commit to Angular.dart relaxed the observe package sufficiently so that overrides will no longer be needed. At that point, only an override of the collection dependency will stand between Angular.dart and Polymer.dart in the same project. Exciting!

Day #4

Thursday, March 19, 2015

Simple Solutions to Asynchronous Polymer.dart Testing


In the development release of Dart and Polymer.dart, the simplest that I can write is still complex:
  group("<hello-you>", (){
    setUp((){ /* Add element here... */ });

    test("has a shadowRoot", (){
      Polymer.onReady.then(expectAsync((_) {
        expect(
          query('hello-you').shadowRoot,
          isNotNull
        );
      }));
    });
  });
I add my custom element to the page and expect that, by virtue of Polymer goodness, it has a Shadow DOM. This is not a completely useless test—verifying that Polymer is decorating an otherwise unknown HTML element tells me a lot. Still, it would be nice if there was not so much… asynchronous goop obfuscating an otherwise simple expectation.

At its heart, this test is just:
        expect(
          query('hello-you').shadowRoot,
          isNotNull
        );
But it is hard to see with everything else that needs to take place to ensure that the test runs and that it runs in content_shell, the dirt-simple headless testing solution for Dart.

My last thought yesterday remains my first thought tonight. Why not simplify this with a Polymer-specific test matcher? This turns out to be the wrong idea, but bear with me...

I start with a function that tries to wrap the expected asynchronous call like so:
polymerExpect(actual, matcher) {
  Polymer.onReady.then(expectAsync((_) {
    expect(actual, matcher);
  }));
}
The main reason that this is wrong is that the actual value would have already been determined before this helper method is invoked. That is, if I try to rewrite my test with this helper, it would look like:
    test("has a shadowRoot", (){
      polymerExpect(
        query('hello-you').shadowRoot,
        isNotNull
      );
    });
Since there is no wait-for-Polymer or any other delay in here, this code is evaluated immediately. I query for the <hello-you> element in the test page and send its shadowRoot into polymerExpect() without waiting for Polymer to be ready. In other words, I send null as the "actual" value—not what I want.

While contemplating how to rewrite this so that evaluation of the shadowRoot can be deferred, I finally realize how I should have done this from the start—and it does not even require using the scheduled_test asynchronous wrapper around unittest.

If the setup() block in unittest returns a Future, the test runner will block until that Future completes. So all I have to do is return Polymer.onReady from setup():
  group("<hello-you>", (){
    setUp((){
      // Add the element here...
      return Polymer.onReady;
    });

    test("has a shadowRoot", (){
      expect(
        query('hello-you').shadowRoot,
        isNotNull
      );
    });
  });
With that, my test is as simple (and readable) as possible and it runs in Dartium and content_shell alike.

I feel pretty dumb for not recognizing that before. As penance for my stupidity, I write two new tests for this element. It is from an example very early in Patterns in Polymer, so it does not have ID attributes to make it easier to find elements within the shadow DOM. That aside, the tests are fairly straight forward:
    test("name updates the template", (){
      _el.your_name = "Bob";
      _el.async(expectAsync((_){
        var h2 = _el.shadowRoot.querySelector('h2');
        expect(h2.text, 'Hello Bob');
      }));
    });

    test("color changes", (){
      var h2 = _el.shadowRoot.querySelector('h2');
      expect(h2.style.cssText, '');

      _el.shadowRoot.querySelector('input[type=submit]').click();
      _el.async(expectAsync((_){
        expect(h2.style.cssText, startsWith('color'));
      }));
    });
The <hello-you> element has the your_name property bound to the <h2> of its template. The first test verifies that the <h2> contains “Bob” if the your_name property is set to "Bob". The second verifies that the <h2> starts life without a color style, but is randomly assigned a color when the button in <hello-you> is clicked.

Both tests rely on my old friend async(), which mostly ensures that the Polymer UI and computed properties have been updated. With those tests added, I have three tests passing:
PASS
1       PASS
Expectation: <hello-you> has a shadowRoot .

2       PASS
Expectation: <hello-you> name updates the template .

3       PASS
Expectation: <hello-you> color changes .

All 3 tests passed
The added tests are nice (and verify that multiple tests work), but the real win is removing the unnecessary asynchronous code from my tests. Many Polymer tests involve some form of asynchronous code. Best not to start with things cluttered.


Day #3

Wednesday, March 18, 2015

Proper (Async) Testing of Polymer.dart


I have this test. It's not even really a test. It's not making assertions about my very simple <hello-you> element. It just verifies that Polymer.dart is working correctly. It even works when run in Dartium:



Buy my very simple test does not work in headless testing. Stranger still is that it used to work. But in the latest development version of Dart (1.9.0-dev.10.7) and the latest Polymer.dart (0.16.0+3), my simple test will not work headless.

Well, I think it is not working. When I run the test in content_shell, which is nomrally the easiest way to headless test in Dart, I get nothing:
$ content_shell --dump-render-tree ./test/index.html 
...

#EOF
#EOF
#EOF
So what is going wrong here? Is the development version of Dart buggy? Is the newly released Polymer.dart to blame? Is content_shell not longer a good solution for headless testing? The answer is almost certainly that I am doing something wrong, but what?

The actual test that I got working in Dartium last night seems harmless enough:
import 'package:unittest/unittest.dart';
import 'package:unittest/html_config.dart';
import 'package:polymer/polymer.dart';

main() {
  useHtmlConfiguration();

  initPolymer();
  Polymer.onReady.then((_) {
    // Setup to add the element to the page here...

    group("<hello-you>", (){
      test("has a shadowRoot", (){
        expect(
          query('hello-you').shadowRoot,
          isNotNull
        );
      });
    });
  });
}
I import unittest, a unittest HTML formatter, and Polymer. I use the HTML formatter, initialize Polymer, and, as I found last night, I have to wait for Polymer to be ready before making my assertions. In the browser, this works. My <hello-you> element has a Shadow DOM like all web components should. But for whatever reason, there is not even any output from content_shell.

It does little good to whine about the problem. So I start breaking it down. Do simple tests work from content_shell? Let's try testing my sanity:
main() {
  useHtmlConfiguration();

  test("sanity", (){
    expect(1 + 1, 2);
  });
  // Polymer tests below...
}
Surprisingly, that works:
content_shell --dump-render-tree ./test/index.html
...
PASS
1       PASS
Expectation: sanity .

All 1 tests passed
#EOF
#EOF
#EOF
It works, but… why does this new test count as “All 1 tests”? I did not delete the Polymer test. It is not even seeing it. Wait…

Ah, the joys of asynchronous testing. It is super nice that Dart has facilities built-in for elegant asynchronous testing, but it sure would be nice if it would tell you when you're doing something wrong. The problem in this case is nothing in my test is blocking the browser from exiting. I have a Future waiting around to execute if Polymer.onReady ever completes. But for all content_shell knows, this will never complete.

I have to instruct my tests that this is an asynchronous operation. More to the point, I have to find a way to instruct the test runner to block until this asynchronous function is called when Polymer.onReady completes. This is the purview of expectAsync().

The expectAsync() method is a simple wrapper around a function that does just what I need—it blocks the test runner until the expected asynchronous function is called. The more immediate help for me is that content_shell now knows to wait for this asynchronous test to be invoked.

Since expectAsync() is a test matcher, it needs to reside inside a test(). So I have to rearrange my test such that the expectAsync() for Polymer.onReady() is inside the test():
  group("<hello-you>", (){
    var _el;
    setUp((){
      _el = createElement('<hello-you></hello-you>');
      document.body.append(_el);
    });

    tearDown((){
      _el.remove();
    });

    test("has a shadowRoot", (){
      Polymer.onReady.then(expectAsync((_) {
        expect(
          query('hello-you').shadowRoot,
          isNotNull
        );
      }));
    });
  });
That seems like the kind of thing that might make for a nice Polymer.dart test matcher. I may root around to see if one is already defined in Polymer itself. It would be really nice if something in unittest were able to identify this kind of problem. I have definitely seen this before—and probably should have recognized the symptoms sooner. But it would be nice not to have to worry about missing this in the future. Perhaps a better solution is to switch to the better async-supporting scheduled_test library. One way or another, this will impact Patterns in Polymer—even if only the testing chapters. A topic for tomorrow.

Day #2


Tuesday, March 17, 2015

Testing Polymer.dart 0.16


The latest edition of Patterns in Polymer is done save for copy edits. So of course, there is a major new release of Polymer.dart.

While I await the remaining edits from my copy editor, I am poking around the edges of the book's source code. Kicking the tires on Polymer.dart 0.16 seems a good place to start. Polymer.dart 0.16 is so new that it only works with Dart 1.9, which is only in development release. So I start by downloading it and content_shell for testing (using the chromium/download_contentshell.sh script that comes with the Dart SDK).

In the book, I have been using unconstrained dependencies so that I can always test with the latest versions:
name: polymer_intro
dependencies:
  polymer: any
dev_dependencies:
  unittest: any
transformers:
- polymer:
    entry_points:
      - web/index.html
      - test/index.html
Now that I have Dart 1.9 installed, I can pub upgrade to get the Polymer 0.16 that requires it:
➜  polymer git:(master) ✗ pub upgrade
Resolving dependencies... (4.2s)
...
> polymer 0.16.0+3 (was 0.15.5+3)
  polymer_expressions 0.13.1
...
> web_components 0.10.5+3 (was 0.10.1)
...
Changed 11 dependencies! 
A quick pub serve to fire up the web server built into the Dart Pub package manager and I find...

My Polymer elements still work just fine:



Well, at least this very simple one from the first chapter of the book still works. No doubt there will be some adventure, but this is encouraging.

All is not well, however, as my tests are failing. Adding insult to injury is that I am not really testing this element. I have a very basic test that asserts nothing more than the element has a shadow root:
  group("<hello-you>", (){
    test('has a shadowRoot', (){
      expect(
        query('hello-you').shadowRoot,
        isNotNull
      );
    });
  });
Unfortunately, I no longer see that shadow root:
CONSOLE MESSAGE: FAIL: <hello-you> has a shadowRoot
  Expected: not null
    Actual: <null>
Happily, the fix is relatively easy. I find that I can get the tests to pass if I wrap them in a Polymer.onReady() block:
library hello_you_test;

import 'package:unittest/unittest.dart';
import 'package:unittest/html_config.dart';
import 'dart:html';
import 'package:polymer/polymer.dart';

main() {
  useHtmlConfiguration();

  initPolymer();
  Polymer.onReady.then((_) {
    var _el;
    setUp((){
      _el = createElement('<hello-you></hello-you>');
      document.body.append(_el);
    });
    group("<hello-you>", (){ /* ... */ });
  });
}
With that, I have my test passing in Polymer.dart 0.16:



It seems that this test is not passing in content_shell, but I will worry about that tomorrow. For now, the upgrade to 0.16 seems to be relatively smooth.

Day #1