Tuesday, March 31, 2015

Automated Polymer Testing with Grunt and web-component-tester


Today, I try my hand at combining Grunt and web-component-tester for local Polymer development.

Last night's experiment with Gulp was reasonable successful. Initial setup with Gulp and web-component-tester (WCT) was brilliantly easy. Even the first bits of customization was relatively simple. Quickly, however, I reached a point where I was hip-deep in node.js streams, error handling and the alphabet soup of NPM packages that Gulp uses to make sense of them.

Grunt will certainly require a little more initial effort, but I am all-but-certain that I can get my complete local setup in place. The best laid plans....

What I want is a means to automatically run my tests locally whenever I make a change to code or test files. I also a appreciate a little LiveReload support for my sample pages. I have done both with Grunt before, though not with web-component-tester.

I start by wiping my sample application's package.json file so that I can re-add Grunt, web-component-tester, and a couple of other dependencies that I know that I will want to use:
$ npm install grunt grunt-notify  grunt-contrib-watch web-component-tester --save-dev
This leaves me with a package.json that contains:
{
  "name": "wct_sample",
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-contrib-watch": "^0.6.1",
    "grunt-notify": "^0.4.1",
    "web-component-tester": "^2.2.6"
  }
}
I start my Gruntfile.js configuration file with the sample included in the WCT homepage:
module.exports = function(grunt) {
  pkg: grunt.file.readJSON('package.json'),
  grunt.initConfig({
    'wct-test': {
      local: {
        options: {remote: false}
      },
      remote: {
        options: {remote: true}
      },
      chrome: {
        options: {browsers: ['chrome']}
      }
    }
  });

  grunt.loadNpmTasks('web-component-tester');
};
That gives me wct-test:local, wct-test:remote, and wct-test:chrome tasks. The first is the one that will run my test suite (currently just a single test):
$ grunt wct-test:local:chrome
Running "wct-test:local:chrome" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 33822
Web server running on port 35707 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:35707/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:35707/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0)                    

Done, without errors.
Nice. That was just as easy to get running as the Gulp version (admittedly there is less configuration with Gulp for the very simple case).

Next, I would like to run my test suite whenever there is a change to any file in the tests or elements directories. This is why I included the grunt-contrib-watch development dependency at the outset. I update Gruntfile.js to include the watch tasks and definitions:
module.exports = function(grunt) {
  pkg: grunt.file.readJSON('package.json'),
  grunt.initConfig({
    'wct-test': {
      local: {
        options: {remote: false}
      },
      // ...
    },
    watch: {
      code: {
        files: ['index.html', 'elements/**'],
        options: {
          livereload: true
        }
      },
      tests: {
        files: ['elements/**', 'test/**'],
        tasks: ['wct-test:local'],
        options: {atBegin: true}
      }
    }
  });

  grunt.loadNpmTasks('web-component-tester');
  grunt.loadNpmTasks('grunt-contrib-watch');
};
The watch:code task will watch the specified files which impact the sample page. On update, they trigger the browser to LiveReload the page (if the extension is being used). The watch:tests task will watch code and test files for changes, running the WCT's wct-test:local task when it sees those changes. For good measure, I specify that the tests should be run at start as well.

And that all works nicely. When I invoke watch, my tests are run right away:
$ grunt watch
Running "watch" task
Waiting...
Running "wct-test:local" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 44706
Web server running on port 37026 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:37026/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:37026/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0)                    

Done, without errors.
Completed in 7.828s at Tue Mar 31 2015 23:37:56 GMT-0400 (EDT) - Waiting...
This task does not end, but polls for changes to the specified files. If I make a change to one of them, I find:
>> File "test/index.html" changed.
Running "wct-test:local" (wct-test) task
Starting Selenium server for local browsers
Selenium server running on port 49367
Web server running on port 41348 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:41348/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:41348/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0)                    

Done, without errors.
Completed in 7.491s at Tue Mar 31 2015 23:38:18 GMT-0400 (EDT) - Waiting...
To complete my desired setup, I would like to have desktop notifications fire at the end of each test run. This is where the grunt-notify package comes in handy. I want to use "notify hooks” in this case. Whenever a task completes successfully, these hooks will send a desktop notification with the result message.

To use hooks, I have to load the grunt-notify tasks as usual, then run the hooks task:
module.exports = function(grunt) {
  pkg: grunt.file.readJSON('package.json'),
  grunt.initConfig({ /* ... */ });

  grunt.loadNpmTasks('web-component-tester');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-notify');
  grunt.task.run('notify_hooks');
};
That is all that is needed, but I like to specify two configuration options: one to send notifications when the tests succeed (only failure messages are displayed by default) and one to display the message for 8 seconds (instead of the default 3):
module.exports = function(grunt) {
  pkg: grunt.file.readJSON('package.json'),
  grunt.initConfig({
    'wct-test': { /* ... */ },

    watch: { /* ... */ },

    notify_hooks: {
      options: {
        success: true,
        duration: 8
      }
    }
  });

  grunt.loadNpmTasks('web-component-tester');
  grunt.loadNpmTasks('grunt-contrib-watch');
  grunt.loadNpmTasks('grunt-notify');
  grunt.task.run('notify_hooks');
};
With that, I see a nice success message when I start Grunt:



And, if I break something, I see:



I might like to see the actual failure message in there, but that is a minor quibble (and a byproduct of WCT since I saw just the one line in Gulp as well). That aside, this is exactly what I want in my test setup and should serve nicely for actual development of Polymer code—something that I will put to the test over the next few days.


Day #15


Monday, March 30, 2015

Gulp and web-component-tester


I hope to build on last night's success with web-component-tester. With relatively little effort or confusion, I was able to write a simple-yet-legitimate test with this newish testing tool for Polymer. Tonight, I would like explore the automated test watching facilities.

First up, I will try Gulp for automated test watchers. I have never used Gulp before, mostly because the "Try Now" button on the homepage takes me to an install page instead of a live demo. I am easily confused by such things, but today I push through and follow the installation instructions so that I can then try.

Global and local install works fine:
$ npm install --global gulp
$ npm install --save-dev gulp
Next, I create a gulpfile.js as recommended on the web-component-tester page:
var gulp = require('gulp');
require('web-component-tester').gulp.init(gulp);
That fails because I do not have web-component-tester installed locally. The wct instructions only mention globally installing it, but it is clearly needed locally for Gulp. So I install it:
$ npm install --save-dev web-component-tester
With that, my package.json now contains:
{
  "name": "wct_sample",
  "devDependencies": {
    "gulp": "^3.8.11",
    "web-component-tester": "^2.2.6"
  }
}
More importantly, my gulplfile.js works and I can run last night's test:
gulp test:local
[23:52:52] Using gulpfile ~/repos/polymer-book/play/web-component-tester/js/gulpfile.js
[23:52:52] Starting 'wct:local'...
Starting Selenium server for local browsers
Selenium server running on port 45226
Web server running on port 33654 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:33654/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:33654/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0) 
That is of little use to me, however. The wct command-line from last night does the same exact thing without extra installation and configuration. What I want is a file watcher that will run the test:local task whenever my Polymer code or tests change.

For that, I create a default task in gulpfile.js that establishes a gulp.watch():
var gulp = require('gulp');
require('web-component-tester').gulp.init(gulp);

gulp.task('default', function() {
  gulp.watch(['elements/**', 'test/**'], ['test:local']);
});
Now I can start gulp with no command-line arguments and it will watch for changes:
$ gulp
[00:14:48] Using gulpfile ~/repos/polymer-book/play/web-component-tester/js/gulpfile.js
[00:14:48] Starting 'default'...
[00:14:48] Finished 'default' after 11 ms
...
If I make a change to an of the files being watched, my test runs:
$ gulp
[00:14:48] Using gulpfile ~/repos/polymer-book/play/web-component-tester/js/gulpfile.js
[00:14:48] Starting 'default'...
[00:14:48] Finished 'default' after 11 ms
[00:16:21] Starting 'wct:local'...
Starting Selenium server for local browsers
Selenium server running on port 54671
Web server running on port 42314 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:42314/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:42314/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0)                    
[00:16:28] Finished 'wct:local' after 6.29 s
[00:16:28] Starting 'test:local'...
[00:16:28] Finished 'test:local' after 16 μs
I prefer for my test suite to run when I first start a test watched task. To get that to work, I add test:local as a dependency for the default task:
var gulp = require('gulp');
require('web-component-tester').gulp.init(gulp);

gulp.task('default', ['test:local'], function() {
  gulp.watch(['elements/**', 'test/**'], ['test:local']);
});
Now my tests run as soon as I start the default watcher:
$ gulp
[00:24:52] Using gulpfile ~/repos/polymer-book/play/web-component-tester/js/gulpfile.js
[00:24:52] Starting 'wct:local'...
Starting Selenium server for local browsers
Selenium server running on port 47399
Web server running on port 60784 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:60784/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:60784/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0)                    
[00:24:58] Finished 'wct:local' after 6.1 s
[00:24:58] Starting 'test:local'...
[00:24:58] Finished 'test:local' after 18 μs
[00:24:58] Starting 'default'...
[00:24:58] Finished 'default' after 13 ms
The only thing left is popup notifications. If there is an easy way to get that working, it escapes me. Upon first pass at Gulp, the streams and events and all are a tad bewildering to me. So in the end, I fall back to manually installing node-notifier:
$ npm install --save-dev node-notifier
From there, I drop the web-component-tester Gulp tasks and directly invoke the testing tasks. If those fail, the I manually send notifications on error:
var gulp = require('gulp');
// require('web-component-tester').gulp.init(gulp);
var wct = require('web-component-tester').test;
var notifier = require('node-notifier');

gulp.task('default', function() {
  gulp.watch('{elements,test}/**', function(done) {
    wct({plugins: {local: {}, sauce: false}}, function(error){
      if (error) {
        notifier.notify({
          'title': 'Build failed',
          'message': '' + error
        });
      }
      else {
        notifier.notify({'title': 'Build passed', 'message': 'Yay!'});
      }
    });
  });
});
That is pretty ugly. It works, but here has to be a simpler way to accomplish. For me, the answer is likely going to be Grunt, which I already use elsewhere in Patterns in Polymer. Unless someone else has an easy way to get this working cleanly in Gulp?


Day #14

Sunday, March 29, 2015

My First web-component-tester Test


Next up in my research-while-awaiting-copy-edits checklist is the web-component-tester. This is probably the most requested topic about which folks have inquired as they read through this blog and the resulting Patterns in Polymer, so this is a great time to investigate.

For better or worse, I use Karma and Jasmine in the book. When I first got started with testing Polymer elements, there were no testing solutions (well Polymer.dart had some, but nothing in JavaScript land). Over time, the solutions that popped up or were suggested did not sit right with me for one one reason or another. Hopefully web-component-tester (wct) will break that trend and, based on the project README, I have every expectation that it will.

I have Java available in my command line $PATH:
$ java -version
java version "1.7.0_75"
OpenJDK Runtime Environment (IcedTea 2.5.4) (7u75-2.5.4-1~trusty1)
OpenJDK 64-Bit Server VM (build 24.75-b04, mixed mode)
So I should be able to install the tool with the usual global node.js NPM install:
npm install -g web-component-tester

> ws@0.5.0 install
...
> sauce-connect-launcher@0.9.3 postinstall
...
> wct-sauce@1.2.0 postinstall
...
> wct-local@1.3.1 postinstall
...
-----
selenium-standalone installation finished
-----
...
/home/chris/local/node-v0.10.20/bin/wct -> /home/chris/local/node-v0.10.20/lib/node_modules/web-component-tester/bin/wct
/home/chris/local/node-v0.10.20/bin/wct-st -> /home/chris/local/node-v0.10.20/lib/node_modules/web-component-tester/bin/wct-st
web-component-tester@2.2.6 /home/chris/local/node-v0.10.20/lib/node_modules/web-component-tester
...
I am using Bower to install Polymer—my bower.json for this sample project is:
{
  "name": "wct_example",
  "dependencies": {
    "polymer": "Polymer/polymer"
  }
}
Since there is no configuration (e.g. via .bowerrc), Polymer and the associated webcomponentjs package gets installed in the bower_components directory. I am testing a favorite from Patterns in Polymer, <x-pizza>, whose definition resides in the elements subdirectory. I adapt the setup in the sample wct page accordingly:
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <!-- <script src="../../webcomponentsjs/webcomponents.min.js"></script> -->
  <script src="../bower_components/webcomponentsjs/webcomponents.min.js"></script>
  <script src="../../web-component-tester/browser.js"></script>
  <link rel="import" href="../elements/x-pizza.html">
</head>
<body>
  <x-pizza id="fixture"></x-pizza>
  <script>
    // Tests will go here...
  </script>
</body>
</html>
I am unsure why the sample given on the project page uses the "../../webcomponentsjs/webcomponents.min.js" location for the webcomponents library. In my sample, that resides outside of the project directory entirely. Regardless, if this file is stored in test/index.html, then the "../bower_components/webcomponentsjs/webcomponents.min.js" location works for me. Additionally, I <link> import the <x-pizza> element definition and establish it as a fixture for my tests.

Next up, I write a simple test of <x-pizza>. To start, I would simply like to assert that it starts with no toppings:
    suite('<x-pizza>', function() {
      test('starts with no toppings', function() {
        assert.equal(
          document.getElementById('fixture').pizzaState,
          '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":[]}'
        );
      });
    });
Amazingly, that works:
$ wct test/index.html
Starting Selenium server for local browsers
Selenium server running on port 35270
Web server running on port 33741 and serving from /home/chris/repos/polymer-book/play/web-component-tester
chrome 41                Beginning tests via http://localhost:33741/js/test/index.html?cli_browser_id=0
chrome 41                Tests passed
firefox 36               Beginning tests via http://localhost:33741/js/test/index.html?cli_browser_id=1
firefox 36               Tests passed
Test run ended with great success

chrome 41 (1/0/0)                       firefox 36 (1/0/0) 
I realize that was a very simple test, but I am extremely happy with how easy it was to get wct setup and to run my first test. I do not have a strong opinion on Jasmine vs. Mocha/Chai—they are fairly interchangeable in my mind. So all in all, this seems a nice win for testing—especially since wct hides any Polymer/ready waiting. The proof will come with more complicated tests, on which I start tomorrow.


Day #13

Saturday, March 28, 2015

Debouncing with scheduleJob in Polymer.dart


I make these little notes for myself and then I have no idea what they mean.

While awaiting the copy edits for the latest Patterns in Polymer, I have been reviewing research tasks that I have assigned myself. I have a pretty good handle on the challenges of the <google-map> API (especially in Dart), so I have checked that off. Next on the list is “native Polymer debounce.” Since Polymer tends to have as many polyfills as it has native components, I doubt the debounce is native, but let's see...

Mercifully, Michael Bleigh was king enough to use the word “debounce” on his post about Polymer Features That You May Have Missed. And indeed, I missed that one, which is called job().

At first blush, job() seems fairly straight-forward in the JavaScript sample from the article, so I'll have a go at it in Polymer.dart. Oddly enough, I have a debounce example in Patterns in Polymer, but it is only in the Dart version of the book. Using 3rd party libraries is so easy in Dart that I wrote that chapter about using 3rd party JavaScript libraries in Polymer.dart. The example in the book is using the Underscore debounce() method:
class HelloYou extends PolymerElement {
  // ...
  HelloYou.created(): super.created();

  var _debouncedLucky;
  feelingLucky() {
    if (_debouncedLucky == null) {
      _debouncedLucky = context['_'].
        callMethod('debounce', [_feelingLucky, 750, true]);
    }
    _debouncedLucky.apply([]);
  }

  _feelingLucky() { /* ... */ }
}
The feelingLucky() method is bound to a button in the template. The private _feelingLucky() Dart method is debounced by Underscore thanks to js-interop.

If I am correct, I believe that I can replace all of that with scheduleJob():
@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  HelloYou.created(): super.created();
  // ...
  PolymerJob _debouncedLucky;
  feelingLucky() {
    _debouncedLucky = scheduleJob(_debouncedLucky, _feelingLucky, 750);
  }

  _feelingLucky() { /* ... */ }
}
Of course, I am not correct. The debounce duration needs to be supplied as a Duraction, not an integer number of seconds. Dart complains as much:
Exception: Uncaught Error: Class 'int' has no instance getter 'inMilliseconds'.
To supply the required Duration, I declare a delay duration of 750 milliseconds and supply that to scheduleJob():
final delay = const Duration(milliseconds: 750);

@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  HelloYou.created(): super.created();
  // ...
  PolymerJob _debouncedLucky;
  feelingLucky() {
    _debouncedLucky = scheduleJob(_debouncedLucky, _feelingLucky, delay);
  }

  _feelingLucky() { /* ... * }
}
With that, I can click the feeling lucky button as many times as I like in the UI, but, unless I wait 750 milliseconds between clicks, nothing happens. Once I wait, my simple <hello-you> element tries to guess my lucky color while saying howdy:



That is a handy little feature that I had overlooked. Well, not overlooked so much as deferred, but thankfully I can mention this by name in the book now.


Day #12

Friday, March 27, 2015

Adopting <google-map-marker> Children in Polymer.dart


Up tonight, I would like to muck around with custom, Google Map-like, Polymer.dart elements. Thanks to the custom_element_apigen, I can pull the JavaScript <google-map> element into Dart code. It also pulls in <google-map-marker> and other, related elements. I can even individually use them in custom-built Polymer elements. But what about together?

Specifically, what if I wanted to mark up my <x-map> Polymer.dart element (powered by <google-map>) with <x-meetup> marker elements (powered by <google-map-marker>)? That is, I would the following to result in a map with the venerable B'more on Rails meetup displayed:
      <x-map latitude="39.283" longitude="-76.616">
        <x-meetup
           name="B'more on Rails"
           url="http://bmoreonrails.org"
           latitude="39.2810662"
           longitude="-76.5808788"></x-meetup>
      </x-map>
I think this is going to be tricky for two reasons. First, the <google-map> element is rendering in the shadow DOM of <x-map>:



Similarly, the <google-map-marker> element in <x-meetup> would reside in the shadow DOM of <x-meetup>. How on earth do I get the <google-map> and <google-map-marker> on the same document fragment, let alone working together?

The second thing that makes this tricky is that <x-meetup> resides in the light DOM of the document and is projected into <x-map>. The problem there is that, to the best of my knowledge, there is no easy way to get distributed nodes in Polymer.dart.

It turns out that I do not have to worry about distributed nodes in this case. Instead, once the container <x-map> element is attached to the document, I can iterate over the children:
@HtmlImport('x-map.html')
library x_map;

import 'package:polymer/polymer.dart';
import '../google_map.dart';
import 'x_meetup.dart';

@CustomTag('x-map')
class XMap extends PolymerElement {
  // ...
  XMap.created(): super.created();

  attached(){
    children.forEach((el){
       // Add markers from each child the map...
    });
  }
}
I am unsure how best to grab the <google-map-marker> from the children. It seems wrong to root through a child's shadow DOM. So instead, I assume the element has a googleMapMarker property that exposes this element (likely as part of a mixin):
@CustomTag('x-map')
class XMap extends PolymerElement {
  // ...
  XMap.created(): super.created();

  Element get map => $['google-map'];

  attached(){
    children.forEach((el){
      var marker = el.googleMapMarker.clone(true);
      map.append(marker);
    });
  }
}
Once I have the marker, I clone it so that it can be added to the <x-map> shadow root.

The map getter finds the <google-map> via dollar sign lookup of the google-map ID:
<polymer-element name="x-map">
  <template>
    <h1>x map</h1>
    <google-map
       id="google-map"
       latitude="{{latitude}}"
       longitude="{{longitude}}"></google-map>
  </template>
</polymer-element>
As for the <x-meetup> element that has a <google-map-marker> element, I define the attributes and the googleMapMarker getter in x_meetup.dart as:
@HtmlImport('x-meetup.html')
library x_map;

import 'package:polymer/polymer.dart';
import '../google_map.dart';

@CustomTag('x-meetup')
class XMeetup extends PolymerElement {
  @published String name;
  @published String url;
  @published float latitude = 0.0;
  @published float longitude = 0.0;
  @published Element map;

  XMeetup.created(): super.created();

  Element get googleMapMarker => $['marker'];
}
Finally the x-meetup.html template builds the <google-map-marker> element as:
<polymer-element name="x-meetup">
  <template>
    <google-map-marker
       id="marker"
       map="{{map}}"
       latitude="{{latitude}}"
       longitude="{{longitude}}"
       title="{{name}}"
       labelContent="{{name}}"
       labelClass="labels">
      <p>This is... <a href="{{url}}">{{name}}</a></p>
    </google-map-marker>
  </template>
</polymer-element>
The class looks this element up via the marker ID, returns it back to the <x-map> class, which clones it and inserts it in the <x-map> element's <google-map>. The end result is a marker showing where B'more on Rails meets:



This might be somewhat contrived, but it does have the advantage of taking what could be consistent data for a bunch of meetups:
        <x-meetup
           name="B'more on Rails"
           url="http://bmoreonrails.org"
           latitude="39.2810662"
           longitude="-76.5808788"></x-meetup>
And presenting it in a consistent way on the map. For instance, thanks to the <p> tag in the <x-meetup> template:
<polymer-element name="x-meetup">
  <template>
    <google-map-marker
       id="marker"
       map="{{map}}"
       latitude="{{latitude}}"
       longitude="{{longitude}}"
       title="{{name}}"
       labelContent="{{name}}"
       labelClass="labels">
      <p>This is... <a href="{{url}}">{{name}}</a></p>
    </google-map-marker>
  </template>
</polymer-element>
These meetup markers get a nifty little info window:



There may be better ways to go about accomplishing this, but in the end, a fairly minimal amount of code produced some nice markers on a custom map. That is a nice win.

Day #11

Thursday, March 26, 2015

Using the google-map Polymer in Polymer.dart Elements


Thanks to the magic of custom_element_apigen, I was able to use the JavaScript <google-map> Polymer element in a Dart application last night. In the end it was, dare I say it, easy.

Tonight, I would like to dynamically use <google-map> from a custom built Polymer.dart element, let's call it <x-map>. I start by altering my sample page to import this definition instead of directly pulling in the custom_element_apigen version of <google-map>:
<html lang="en">
  <head>
    <script type="application/dart">
      import 'package:my_maps/elements/x_map.dart';
      export 'package:polymer/init.dart';
    </script>
  </head>
  <body unresolved>
    <div class="container">
      <h1>A Simple Map...</h1>
      <x-map latitude="39.283" longitude="-76.616"></x-map>
    </div>
  </body>
</html>
Instead of the usual, boring map of San Francisco, I start with coordinates of a city with some character. In the backing class defined in x_map.dart, I need to support the latitude and longitude attributes. I also need to import the <google-map> element:
@HtmlImport('x-map.html')
library x_map;

import 'package:polymer/polymer.dart';
import '../google_map.dart';

@CustomTag('x-map')
class XMap extends PolymerElement {
  @published float latitude = 0.0;
  @published float longitude = 0.0;

  XMap.created(): super.created();
}
As a side-note, I confess that I am alarmed at how quickly I am getting used to this code-import of Polymer definitions. I have used the <link> imports throughout Patterns in Polymer. It would be hard to switch at this point—especially since the JavaScript version of the book would still have to use <link> imports—but darn these are nice.

At any rate, I import google_map.dart, which is the Dart wrapper generated last night. I also annotate my desired, published attributes. So next up is the HTML template definition.

My first pass at <x-map> is a trivial Polymer template. In it, I bind my latitude and longitude attributes in the <google-map> attributes of the same name:
<polymer-element name="x-map">
  <template>
    <h1>x map</h1>
    <google-map
       latitude="{{latitude}}"
       longitude="{{longitude}}"></google-map>
    <style>
      @import '/packages/my_maps/assets/bootstrap.min.css';
      google-map {
        height: 600px;
      }
    </style>
  </template>
</polymer-element>
I need to style <google-map> lest it display with zero height. With that, I have a working custom Polymer element that itself uses a Google map:



Next up, I would like to verify that I can easily update attributes of <google-map> from my custom element. I could add text fields, but that's dull. Let's try a paper-slider instead.

I update the Dart Pub packaging configuration to include paper elements:
name: my_maps
dependencies:
  polymer: any
  paper_elements: any
dev_dependencies:
  custom_element_apigen: any
transformers:
- polymer:
    entry_points:
    - web/index.html
After a quick pub install I am ready to add a slider to my element. I import it into the backing class:
@HtmlImport('x-map.html')
library x_map;

import 'package:polymer/polymer.dart';
import 'package:paper_elements/paper_slider.dart';
import '../google_map.dart';

@CustomTag('x-map')
class XMap extends PolymerElement {
  // ...
}
Then add it to the HTML template definition:
<polymer-element name="x-map">
  <template>
    <h1>x map</h1>
    <google-map
       latitude="{{latitude}}"
       longitude="{{longitude}}"></google-map>

    <p>
      <paper-slider 
         min="{{longitude - 2}}"
         max="{{longitude + 2}}" 
         value="{{longitude}}"></paper-slider>
    </p>
    <>-- ... -->
  </template>
</polymer-element>
That works brilliantly:



The slider is bound to the longitude of the map, so updating it (as I did above by clicking to the right) updates my element's longitude, which is bound to the <google-map> longitude. The end result is that updating the slider updates the map.

So far, working with <google-map> from Polymer.dart seems quite nice. Up tomorrow, I think that I would like to add custom children to my custom <x-map> such that they act as markers on the map. I have the feeling that the shadow DOM is going to make that a little tricky.


Day #10

Wednesday, March 25, 2015

Custom Element API Generation for Polymer.dart


You could say that I've forgotten more about Polymer than most programmers will ever know. I would probably agree, but that's just because I have a crap memory.

I was able to get the <google-map> JavaScript Polymer element to work in a Polymer.dart context yesterday, but only by hand-editing the JavaScript source files that I had installed via Bower—not at all cool. Happily, James Hurford reminded me that I should be using custom_element_apigen to wrap JavaScript Polymer elements in Dart.

I have looked at it in the past, but in my defense, it caused me grief. So maybe it was repression instead of a failing memory. Let's stick with that story, shall we?

I give custom_element_apigen another try tonight by first adding it to the list of development dependencies in my sample application:
name: my_maps
dependencies:
  polymer: any
dev_dependencies:
  custom_element_apigen: any
transformers:
- polymer:
    entry_points:
    - web/index.html
I have to update the location in which Bower installs its packages so that custom_element_apigen can find the elements that I want to use in my Dart application. So I modify the .bowerrc configuration file to install everything in lib/src:
{
  "directory": "lib/src"
}
Installing now places these dependencies in lib/src as desired:
$ bower install
...
polymer#0.5.1 lib/src/polymer
├── core-component-page#0.5.5
└── webcomponentsjs#0.5.1

google-map#0.4.1 lib/src/google-map
└── google-apis#0.4.4  

webcomponentsjs#0.5.1 lib/src/webcomponentsjs

core-component-page#0.5.5 lib/src/core-component-page
├── polymer#0.5.1
└── webcomponentsjs#0.5.1

google-apis#0.4.4 lib/src/google-apis
└── core-shared-lib#0.5.5

core-shared-lib#0.5.5 lib/src/core-shared-lib
└── polymer#0.5.1
With that, I am ready to create a configuration file for custom_element_apigen. The project is still woefully (and self-admittedly) under-documented. Thankfully, I still have my old configuration file from the last time, so I adapt it for <google-map>. I save the following in apigen.yaml:
files_to_generate:
  - google-map/google-map.html
I then run the generate command. In addition to lack of documentation, it also lacks output. Even so, it does not take too much work to find the generated files:
$ pub run custom_element_apigen:update apigen.yaml 
Done                                      

$ ls -ltr lib
total 32
drwxr-xr-x 2 chris chris 4096 Mar 24 23:57 assets
drwxr-xr-x 2 chris chris 4096 Mar 25 23:04 elements
drwxr-xr-x 8 chris chris 4096 Mar 25 23:04 src
-rw-r--r-- 1 chris chris  114 Mar 25 23:07 google_map_nodart.html
-rw-r--r-- 1 chris chris  164 Mar 25 23:07 google_map.html
-rw-r--r-- 1 chris chris 9065 Mar 25 23:07 google_map.dart
Based on the nodart file, it seems that custom_element_apigen supports both the new and old style importing approaches. In my sample application page, I opt for the new, code-import style:
<!doctype html>
<html lang="en">
  <head>
    <script type="application/dart">
      import 'package:my_maps/google_map.dart';
      export 'package:polymer/init.dart';
    </script>
  </head>
  <body unresolved>
    <google-map
       latitude="37.779"
       longitude="-122.3892"
       minZoom="9"
       maxZoom="11"
       fit></google-map>
  </body>
</html>
When I try to run my simple application, pub serve reports:
[Error from polymer (PolymerBootstrapTransformer) on my_maps|web/index.html]:
Could not load asset my_maps|lib/google_maps_api.dart
[Warning from polymer (PolymerBootstrapTransformer) on my_maps|web/index.html]:
line 2, column 1 of lib/google_map_nodart.html: Failed to inline HTML import: Could not find asset my_maps|lib/google_maps_api_nodart.html.
null. See http://goo.gl/5HPeuP#web_components_4 for details.
<link rel="import" href="google_maps_api_nodart.html">
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
So I update my apigen.yaml file to include this element from the Bower-installed JavaScript dependency:
files_to_generate:
  - google-map/google-map.html
  - google-apis/google-maps-api.html
I re-run pub run custom_element_apigen:update apigen.yaml, restart pub serve, and it fails again. This time with a different element. I repeat this process until my agigen.yaml file contains:
files_to_generate:
  - google-map/google-map.html
  - google-apis/google-maps-api.html
  - core-shared-lib/core-shared-lib.html
I again re-run pub run custom_element_apigen:update apigen.yaml, again restart pub serve, and finally, I have a working <google-map> in a Polymer.dart application:



Nice.

I had used explicit Bower dependencies last night in a attempt to eliminate webcomponentjs.js incompatibilities between my Polymer.dart code and my Polymer.js elements:
{
  "name": "my-maps",
  "dependencies": {
    "google-map": "GoogleWebComponents/google-map#0.4.1",
    "polymer": "Polymer/polymer#0.5.1",
    "webcomponentsjs": "Polymer/webcomponentsjs#0.5.1"
  }
}
I relax those dependencies now:
{
  "name": "my-maps",
  "dependencies": {
    "google-map": "GoogleWebComponents/google-map#~0.4.2",
    "polymer": "Polymer/polymer#~0.5.5",
    "webcomponentsjs": "Polymer/webcomponentsjs#~0.5.5"
  }
}
After a re-install of the Bower dependencies, I have:
$ bower install
...
webcomponentsjs#0.5.5 lib/src/webcomponentsjs

google-map#0.4.2 lib/src/google-map

polymer#0.5.5 lib/src/polymer
...
Polymer.dart bundles version 0.5.1 of webcomponentsjs so this could cause problems. Without re-running the apigen command, I try to load my application. Pub generates it OK, but it does not work in the browser and I see similar error messages in the console as I saw last night:
Uncaught HierarchyRequestError: Failed to execute 'appendChild' on 'Node': Nodes of type 'HTML' may not be inserted inside nodes of type '#document'.
Hopefully custom_element_apigen is smart enough to resolve this. I re-run the generate command:
$ pub run custom_element_apigen:update apigen.yaml
Done
After restarting pub serve I find that the application is working again. Yay!

Forcing webcomponentsjs dependencies would have been a significant pain had that not worked—especially if it prevented the use of newer JavaScript custom elements in Polymer.dart. Thankfully the custom_element_apigen package has me covered.

Happily, this worked out much better for me than my last attempt at working with custom_element_apigen. I still need to experiment with using the generated elements in other elements (material for tomorrow). But this is promising. It would be nice if the generator were a little more helpful about the files that need to be generated, but this was not too bad.

There is an outside chance that I may even remember custom_element_apigen in 6 months!


Day #9

Tuesday, March 24, 2015

Ugly Google Maps API from Polymer.dart


Up tonight, I would like to mess about with the Google Maps API a little.

While I await the final copy edits on Patterns in Polymer, I need to keep my mind engaged with Polymer. I will have to run through changes at least one more time and would like this stuff as fresh as possible. In other words, this seems an opportune time to work through topics about which folks inquired but did not seem an obvious fit in the book. I will code in the language in which the question was originally asked.

So, to explore the Google Maps API, I will be coding in Dart. The question is a little old -- I think I got it late last year. Today, I believe the answer for how to approach this begins and ends with the google-map web component. The question then becomes, how easy is it to install with Polymer.dart?

It has at least been done once by the esteemed James Hurford. James' approach hardly seems easy and it has been a while, so it may no longer work. There is one way to find out...

I start not with Dart, but with a Bower install to obtain the <google-map> Polymer element. First, I specify that Bower should install in my application's lib/elements/bower_components directory so that my Dart code can access it. I do this by creating a .bowerrc with:
{
  "directory": "lib/elements/bower_components"
}
Now I can bower install:
$ bower install GoogleWebComponents/google-map --save
...
google-map#0.4.2 lib/elements/bower_components/google-map
└── google-apis#0.4.4

google-apis#0.4.4 lib/elements/bower_components/google-apis
└── core-shared-lib#0.5.5

core-shared-lib#0.5.5 lib/elements/bower_components/core-shared-lib
└── polymer#0.5.5

polymer#0.5.5 lib/elements/bower_components/polymer
├── core-component-page#0.5.5
└── webcomponentsjs#0.5.5

core-component-page#0.5.5 lib/elements/bower_components/core-component-page
├── polymer#0.5.5
└── webcomponentsjs#0.5.5

webcomponentsjs#0.5.5 lib/elements/bower_components/webcomponentsjs
Before creating an element that use <google-map>, let's see if I can simply use it from an application page:
<!doctype html>
<html lang="en">
  <head>
    <title>My Maps</title>
    <link
       rel="import"
       href="packages/my_maps/elements/bower_components/google-map/google-map.html">
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
  </head>
  <body unresolved>
    <google-map
       latitude="37.779"
       longitude="-122.3892"
       minZoom="9"
       maxZoom="11"
       fit></google-map>
  </body>
</html>
After a pub install to grab Polymer.dart, I find that this most obvious approach will not work. I get errors in the console along the lines of:
Uncaught HierarchyRequestError: Failed to execute 'appendChild' on 'Node': Nodes of type 'HTML' may not be inserted inside nodes of type '#document'.

Uncaught TypeError: Cannot read property 'resolveDom' of undefined

Exception: Uncaught Error: NoSuchMethodError: method not found: 'whenPolymerReady'
Receiver: Instance of 'JsObject'
Arguments: [Closure: () => dynamic]
Stack Trace:
#0      JsObject.callMethod (dart:js:230)
#1      _hookJsPolymer (package:polymer/src/loader.dart:82:23)
#2      startPolymer.<anonymous closure> (package:polymer/src/loader.dart:44:19)
...
These errors would seem to be coming from duplicate polymer.js definitions—one from the Polymer.dart application and one that is pulled in from the <google-map> dependencies.

As James found, If I work through each of the following:
  • core-shared-lib/core-shared-lib.html
  • google-apis/google-maps-api.html
  • google-map/google-map-directions.html
  • google-map/google-map-search.html
  • google-map/google-map.html
And I hand-edit the <link> import of Polymer, changing from the JavaScript version to the one supplied by Dart:
<link rel="import" href="../../../../../packages/polymer/polymer.html">
Then I get my map:



That is pretty ugly. There is no way that anyone should be hand-editing web component definitions like that in order to get them to work. I need to find another approach if I am going to recommend using this element.

I do try to lock the JavaScript versions to to the same version used by Polymer.dart via my bower.json file:
{
  "name": "my-maps",
  "dependencies": {
    "google-map": "GoogleWebComponents/google-map#0.4.1",
    "polymer": "Polymer/polymer#0.5.1",
    "webcomponentsjs": "Polymer/webcomponentsjs#0.5.1"
  }
}
But that is of no avail. I still have to hand-edit the files in the list to prevent the error from the duplicate Polymer definitions. Looks like I need to try a different approach tomorrow.


Day #8

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