Tuesday, September 30, 2014

Why Am I Getting a FOUC Warning in Polymer.dart?


The bad news is that I have a weird message popping up in some chapter projects from Patterns in Polymer. The good news is that I finally have the project code for every single chapter in under test (Dart and JavaScript). Some chapters have better test coverage than others, but coverage, once established, will only improve.

But what good is having tests if the underlying code is producing warnings?



This may be specific to Polymer.dart—I have not seen it in any of the JavaScript elements. But still, if I have a choice between no warning and some warnings, I will opt for the former.

This particular Polymer element is working as far as I can tell. But it still displays the following warning:
Custom element found in document body without an "unresolved" attribute on it or one of its parents. This means your app probably has a flash of unstyled content before it finishes loading. 
web/index.html:24:7
<hello-you>
Flash of unstyled content is a big deal in Polymer. Really, it is a big deal anywhere that styles might get applied after content has already rendered. Unstyled or incompletely styled content on a page looks unprofessional. So I understand why this warning exists.

I can even fix the problem easily—just add unresolved to my element in the containing page:
    <div class="container">
      <hello-you unresolved>
        <p>Introduce yourself for an amazing personalized experience!</p>
      </hello-you>
    </div>
But I do not know why this problem occurs in some of my Polymer elements, but not others. As an aside, I still dig seeing the warning directly in the browser (in development mode).

Eventually, I figure out that this occurs in custom Polymer elements that have distributed nodes. In the above, the <p> is distributed into the Polymer element's shadow DOM. Once I realize this, I see that it does, in fact, get FOUC'd:



When I first load the page, the distributed content is shown without any styles from the <hello-you> Polymer element and without being wrapped by the Polymer element. Once Polymer has initialized, this content shifts noticeably into its final location inside the Polymer element. FOUC!

Interestingly, I can resolve the warning by placing the unresolved attribute on the <hello-you> tag or the containing <div> tag:
    <div class="container" unresolved>
      <hello-you>
        <p>Introduce yourself for an amazing personalized experience!</p>
      </hello-you>
    </div>
But even though the warning goes away, the FOUC remains. The only way that I am able to eliminate the FOUC is to place the unresolved attribute on the page <body> tag:
  <body unresolved>
    <div class="container">
      <hello-you>
        <p>Introduce yourself for an amazing personalized experience!</p>
      </hello-you>
    </div>
  </body>
I am unsure if it is possible to combine the styles for distributed content and unresolved polyfills. That seems like grist for another day. For now, I am just happy to have a handle on this warning.




Day #199

Monday, September 29, 2014

Testing Angular and Polymer.dart


Regardless of the solution that I use to double bind variables between Angular.dart and Polymer.dart, I need to test it. Without tests, I will have no warning when new versions of the libraries break the last working version.

Before getting to the actual test, I need to make a change to the pubspec.yaml. Oddly, the latest versions of Angular.dart and Polymer.dart are incompatible—they have conflicting dependencies. Luckily, Dart Pub comes with a built-in solution for this, dependency_overrides. This setting lets application developers say, “I love you package X and I know you think that can only support version 0.10 of this other library, but let's give version 0.11 a try for Chris, OK?”

To get Angular.dart and Polymer.dart working, I had to convince Angular.dart that it might run with the following dependency_overrides:
name: angular_example
dependencies:
  angular: any
  polymer: any
  angular_node_bind: any
dependency_overrides:
  args: '>=0.10.0 <0.12.0'
  code_transformers: '>=0.1.4+2 <0.3.0'
  html5lib: '>=0.10.0 <0.13.0'
dev_dependencies:
  unittest: any
transformers:
- angular:
    html_files:
      - web/partials/custom.html
- polymer:
    entry_points:
    - web/index.html
    - test/index.html
The problem is that the next version of Polymer.dart might depend on args version 0.12, which is incompatible with Angular.dart and my overrides. So, for book testing purposes, I need to loosen those overrides even further:
name: angular_example
dependencies:
  angular: any
  polymer: any
  angular_node_bind: any
dependency_overrides:
  args: any
  code_transformers: any
  html5lib: any
# ...
A quick pub upgrade and... I am still using the same versions of these overridden dependencies:
Warning: You are using these overridden dependencies:
! args 0.12.0+2
! code_transformers 0.2.3+1
! html5lib 0.12.0
Nothing has changed, but I am ready for any future changes to Angular.dart or Polymer.dart that have proven troublesome in the recent pass.

With that, I am ready to write a double binding Angular.dart and Polymer.dart test. I already have testing page objects, so this is mostly just re-organizing to pull in the Angular code. The test becomes:
    test('angular sees the updated pizza state', (){
      schedule(()=> xPizza.addWholeTopping('green peppers'));
      schedule((){
        var angular_el = document.query('pre');
        expect(
          angular_el.text,
          contains('Whole: [green peppers]')
        );
      });
    });
I am using angular_node_bind to bind the current state of the pizza to an Angular scope variable inside a <pre> element. If I tell the page object to add green peppers to the whole pizza, the Polymer element should update itself with new values, which should trigger the Angular element to trigger itself to update.

There may be a simpler way to accomplish this, but the dumbest way to add an Angular application in test mode is to add it right to the test HTML:
<!DOCTYPE html>
<html ng-app lang="en">
<head>
  <!-- Load platforms polyfills -->
  <script src="packages/web_components/platform.js"></script>

  <!-- Load component(s) -->
  <link rel="import" href="packages/angular_example/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>
  <ng-view></ng-view>
</body>
</html>
Unfortunately, this means that I cannot pull the main.dart code that I have been using directly into my tests—both invoke initPolymer(). For now, I add a stripped-down version of the Angular app directly to my test's setup:
    setUp((){
      schedule(()=> Polymer.onReady);
      schedule((){
        var app = new PizzaStoreApp()
          ..bind(NodeBindDirective)
          ..bind(RouteInitializerFn, toValue: storeRouteInitializer);

        return applicationFactory()
          .addModule(app)
          .run();
      });
      schedule((){
        var _completer = new Completer();
        new Timer(new Duration(milliseconds: 50), ()=> _completer.complete());
        return _completer.future;
      });
      schedule((){
        _el = document.query('x-pizza') as PolymerElement;
        xPizza = new XPizzaComponent(_el);
        return xPizza.flush();
      });

      currentSchedule.onComplete.schedule(() => _el.remove());
    });
That is not too horrible, though it might be nice to move the Angular-specific code elsewhere—especially if it can be shared between the web application and test. The important line in there is the inclusion of NodeBindDirective (from angular_node_bind), which allows Angular to see updates to Polymer attributes. And indeed, if I remove that line, the test fails, but if I restore it, I find:
PASS: Angular sees the updated pizza state from the Polymer
I should note that I also need to include a 50 millisecond delay before my test will pass. The Angular application needs this time to load its custom partials and spin up. It would be good if I could eliminate that seemingly arbitrary delay.

But, for now, I am happy to have a useful test for Angular and Polymer playing nicely. This is a good stopping point.


Day #198

Sunday, September 28, 2014

Can't Bind Polymer.dart with Angular.dart's Built-In Syntax


I was able to get double binding working between Angular.dart and Polymer.dart last night, thanks mostly to the angular_node_bind directive. For demonstration, I bind the JSON from the <x-pizza> Polymer element into an Angular scope variable for display:



Jyrki Grohn was kind enough to remind me that there should be an easier way to accomplish this in Angular.dart version 0.14 (which I am using). Angular.dart now boasts bind-* syntax for double binding properties between Angular.dart and Polymer. So let's see how it works.

Unfortunately, it does not work for me. The angular_node_bind stuff worked by wrapping the Angular scope variable inside double square brackets:
<pre>{{ pizzaState }}</pre>
<p>
  <x-pizza value="[[ pizzaState ]]"></x-pizza>
</p>
In this case, the <x-pizza> element's value was reflected in Angular's pizzaState scope variable.

But if I try the bind-* syntax instead:
<pre>{{ pizzaState }}</pre>
<p>
  <x-pizza bind-value="pizzaState"></x-pizza>
</p>
Now the binding is not working:



And I cannot figure this one out. My default setting for value is to make it a reflectable published property:
@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // ...
  @PublishedProperty(reflect: true)
  String value;
  // ...
}
I also try vanilla @published and @observable—all for naught. No matter what I do this value is not reflected back to Angular.

I may dig into this more tomorrow—specifically examining the example code that illustrates working bind-* semantics. Then again, I have a working solution and it happens to mimic the JavaScript solution also presented in Patterns in Polymer, so I may just leave well enough alone.


Day #197

Saturday, September 27, 2014

Binding Variables Between Angular and Polymer (latest Dart packages)


Last night I was able to get Polymer.dart and Angular.dart working together again. But I don't have them working together yet.

I have my <x-pizza> Polymer element working within an Angular partial:



But I do not have the two communicating. When I originally wrote the Angular & Polymer chapter in Patterns in Polymer, I was able to reflect changes from the Polymer element back into the Angular application through the use of angular_node_bind. That was a long time ago, let's see if it still works.

Before diving into it, I need to get Angular setup properly. I had remove the variable binding element last night from my partial:
<h3>Oooh! Let's make beautiful pizza together</h3>
<pre>{{ pizzaState }}</pre>
<p>
  <x-pizza pizzastate="[[ pizzaState ]]"></x-pizza>
</p>
With that <pre> element bound to the pizzaState property, I get errors like:
No getter for 'pizzaState'.

STACKTRACE:
#0      StaticClosureMap.lookupGetter (package:angular/core/parser/static_closure_map.dart:15:25)
#1      DynamicParserBackend.newAccessScope (package:angular/core/parser/dynamic_parser.dart:112:38)
#2      DynamicParserImpl.parseAccessOrCallScope (package:angular/core/parser/dynamic_parser_impl.dart:257:67)
#3      DynamicParserImpl.parsePrimary (package:angular/core/parser/dynamic_parser_impl.dart:239:36)
#4      DynamicParserImpl.parseAccessOrCallMember (package:angular/core/parser/dynamic_parser_impl.dart:193:30)
...
This turns out to be caused by omitting the partial from the list of Angular files in my application's pubspec.yaml:
name: angular_example
dependencies:
  angular: any
  polymer: any
  angular_node_bind: any
dependency_overrides:
  args: '>=0.10.0 <0.12.0'
  code_transformers: '>=0.1.4+2 <0.3.0'
  html5lib: '>=0.10.0 <0.13.0'
dev_dependencies:
  unittest: any
transformers:
- angular:
    html_files:
      - web/partials/custom.html
- polymer:
    entry_points:
    - web/index.html
    - test/index.html
With this “custom” pizza partial listed, my error are fixed. But the bound attribute is still not reflected back into the Angular application.

To make that work, I need no changes to my <x-pizza> backing class definition:
@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // ...
  @published String pizzaState;
  // ...
}
I find that interesting because it does not appear to need the @PublishedProperty(reflect: true) annotation that I would have expected. The only change I that I need is using the angular_node_bind directive in my app:
// ...
import 'package:angular_node_bind/angular_node_bind.dart';
// ...
main() {
  initPolymer().run((){

    var app = new PizzaStoreApp()
      ..bind(NodeBindDirective)
      ..bind(NgRoutingUsePushState, toValue: new NgRoutingUsePushState.value(false))
      ..bind(RouteInitializerFn, toValue: storeRouteInitializer);

    applicationFactory()
      .addModule(app)
      .run();
  });
}
Happily, that directive still works just fine. I continue to bind the Angular pizzaState variable to the Polymer element's pizzastate attribute in the template:
<pre>{{ pizzaState }}</pre>
<p>
  <x-pizza pizzastate="[[ pizzaState ]]"></x-pizza>
</p>
With the double-square brackets serving as angular_node_bind's way of watching properties. And it works:



When the Polymer updates its pizzastate, Angular sees the change and updates its pizzaState variable, which is reflected in the <pre> element from the Angular template. Nice!

The WARNING on the page is Polymer's way of telling me in development mode that it was unable to interpret the <ng-view> element from Angular. I can certainly live with that since Polymer was not suppose to interpret it anyway. I think I am nearly satisfied with the solution to the point that I can again include it in the book.


Day #196

Friday, September 26, 2014

An Angular App Using a Polymer Element (Dart)


Thanks to a tip from James Hurford, I have a far better approach to working with the “wildly” incompatible Polymer.dart and Angular.dart libraries. Instead of hacking on a forked version of Angular.dart, as I did last night, I instead use Dart Pub's dependency_overrides option.

I am simply not going to start hacking on Angular.dart to get Polymer elements working inside an Angular.dart application—especially not as a solution for Patterns in Polymer. Overriding the minimal dependencies found from last night gives me the best chance of working with the most recent Polymer and Angular libraries to see if it is as all possible.

So I add those dependencies to my project's pubspec.yaml:
name: angular_example
dependencies:
  angular: any
  polymer: any
  angular_node_bind: any
dependency_overrides:
  args: '>=0.10.0 <0.12.0'
  code_transformers: '>=0.1.4+2 <0.3.0'
  html5lib: '>=0.10.0 <0.13.0'
dev_dependencies:
  unittest: any
transformers:
- polymer:
    entry_points:
    - web/index.html
    - test/index.html
I delete my pubspec.lock to be absolutely sure that I am getting a fresh run at this (a pub upgrade should do the same thing). Now when I run pub get, I find:
$ pub install
...
+ angular 0.14.0
...
+ polymer 0.15.0
...
Warning: You are using these overridden dependencies:
! args 0.11.0+1 (0.12.0+2 available)
! code_transformers 0.2.3+1
! html5lib 0.12.0
For reference, Angular.dart would otherwise restrict those three overridden dependencies as:
  args: '>=0.10.0 <0.11.0'
  code_transformers: '>=0.1.4+2 <0.2.0'
  html5lib: '>=0.10.0 <0.11.0'
OK, I have the Dart versions of Polymer and Angular installed in the same application. Now what?

Well, for tonight, I would be satisfied with a working version of the old Angular pizza building application that uses a custom Polymer element. This worked when it went into Patterns in Polymer. Hopefully it can live again.

I modify the Polymer setup as I know that version 0.15 likes it and retain the Angular tags and attributes:
<!doctype html>
<html ng-app lang="en">
  <head>
    <script src="packages/web_components/platform.js"></script>
    <link rel="import" href="packages/angular_example/elements/x-pizza.html">
    <script type="application/dart" src="main.dart"></script>
  </head>
  <body>
    <div class="container">
      <h1>Ye Olde Dart Pizza Shoppe</h1>
      <ng-view></ng-view>
    </div>
  </body>
</html>
Aside from the ng-app Angular attribute and the <ng-view> Angular tag, there is one other difference between this and a pure Polymer application: there is no export of package:polymer/init.dart to initialize Polymer. Instead that is done inside main.dart, which also initialized Angular.

At least it used to initialize Angular. Now it just generates errors in the Dartium console:
Exception: No top-level method 'ngBootstrap' declared.

NoSuchMethodError: method not found: 'ngBootstrap'
Receiver: top-level
Arguments: [...] 
No biggie. It seems that the Angular bootstrap process has changed. For now, I leave the initialization inside Polymer's run():
main() {
  initPolymer().run((){
    applicationFactory()
      .addModule(new PizzaStoreApp())
      .run();
  });
}
No doubt other stuff has changed, but I start with the old constructor for PizzaStoreApp and will work my way from there. What I find next is:
Exception: Module.DEFAULT_REFLECTOR not initialized for dependency injection.http://goo.gl/XFXx9G
This appears to be due to the lack of the Angular transformer in my pubspec.yaml. Despite what the documentation says, it would appear that this does more than aid building JavaScript. I add the Angular transformer:
name: angular_example
dependencies:
  angular: any
  polymer: any
  angular_node_bind: any
dependency_overrides:
  args: '>=0.10.0 <0.12.0'
  code_transformers: '>=0.1.4+2 <0.3.0'
  html5lib: '>=0.10.0 <0.13.0'
dev_dependencies:
  unittest: any
transformers:
- angular
- polymer:
    entry_points:
    - web/index.html
    - test/index.html
After a pub serve restart, there is much work to perform to update the Angular portion of my application to 0.14. But I eventually get it.

The application is intentionally “complex-ish” to ensure that I am not just getting by with a trivial example. Mostly this involves routing.

The main.dart file for my Angular & Polymer application starts with the usual imports for both libraries:
import 'package:polymer/polymer.dart';
import 'package:angular/angular.dart';
import 'package:angular/application_factory.dart';
import 'package:angular/routing/module.dart';
There used to be much initialization inside the main application module. Now it is just:
class PizzaStoreApp extends Module {
  PizzaStoreApp() {
    // Stuff used to happen here, now routing and binding happens later...
  }
}
I am unsure if I even need this anymore, but I will answer than another day.

The routing uses two partials, defined as:
void storeRouteInitializer(Router router, RouteViewFactory views) {
  views.configure({
    'start': ngRoute(
        defaultRoute: true,
        view: 'partials/home.html'
      ),
    'custom-pizza': ngRoute(
        path: '/pizza/custom',
        view: 'partials/custom.html'
      )
  });
}
That is relatively self-explanatory. I have two routes: the default route and the custom pizza route. Both use partial templates at the specified locations.

Finally, the Polymer and Angular initialization looks like:
main() {
  initPolymer().run((){
    var app = new PizzaStoreApp()
      ..bind(NgRoutingUsePushState, toValue: new NgRoutingUsePushState.value(false))
      ..bind(RouteInitializerFn, toValue: storeRouteInitializer);

    applicationFactory()
      .addModule(app)
      .run();
  });
}
It took a little time to figure out where the bind() calls go and what gets supplied to addModule(), but it mostly makes sense. What took me quite a while to figure out was how to disable push state. I really hate push state—especially for developing / reloading single page applications. But eventually I figured out how to bind the false value of NgRouteingUsePushState. There may be a cleaner way to accomplish this, but I am not going to worry about that now.

Because I have an Angular.dart 0.14 + Polymer.dart 0.15 application working:

Well, not quite working. I still need to double bind variables in Angular to Polymer attributes. But I am thrilled to have gotten this far tonight.


Day #195

Thursday, September 25, 2014

Polymer and Angular Dart are Wildly Incompatible


I am desperately trying to get all chapter projects under test in Patterns in Polymer. I have the JavaScript chapters done and am now working through the Dart versions of those same chapters. For most chapters, I already have tests, but sadly one chapter that lacks tests is the Angular.dart chapter. Hopefully I can mostly reuse the approach that I took in the AngularJS version of the chapter, which is admittedly less that great coverage, but infinitely better than what I have currently for the Dart code.

But first I need to get the darn thing to run.

The dependencies in my pubspec.yml are unconstrained:
name: angular_example
dependencies:
  angular: any
  polymer: any
  angular_node_bind: any
dev_dependencies:
  unittest: any
transformers:
- polymer:
    entry_points:
    - web/index.html
    - test/index.html
So the first think I do is a pub upgrade, which works just fine. I am unsure if angular_node_bind, which facilitates double binding of attributes between Polymer and Angular, is strictly necessary anymore, but I will find out.

Next, I update the setup of the web/index.html smoke test page that I have, fire up the server and find that the update needs an update:
[Warning from polymer (Linter) on angular_example|web/index.html]:
line 27, column 7 of web/index.html: definition for Polymer element with tag name "ng-view" not found.
<ng-view>
^^^^^^^^^
[Warning from polymer (Linter) on angular_example|web/index.html]:
line 24, column 3 of web/index.html: To run a polymer application you need to include the following HTML import: <link rel="import" href="packages/polymer/polymer.html">. This will include the common polymer logic needed to boostrap your application. The old style of initializing polymer with boot.js or initPolymer are now deprecated. 
<body>
^^^^^^
Build completed successfully
I will ignore the <ng-view> warning for now. I do wonder if there is a way to tell the linter to ignore certain tags that are under the control of another library—grist for another day (maybe). The other warning is worrisome. I had just moved all of my Polymer.dart elements over to sourcing the packages/web_components/platform.js script and now it seems that Polymer.dart has changed under my feet.

Except it hasn't.

I wonder if this same message is occurring for the chapter projects currently being tested every day (to accumulate new dependencies). This message is not present for any of these chapters, which leads me to check the pubspec.lock file revealing that Polymer.dart is still back at 0.10:
  # ...
  polymer:
    description: polymer
    source: hosted
    version: "0.10.0-pre.12"
It seems that angular_node_bind has not been updated for 4 months, leaving it constrained to an old Polymer version. So I drop it from pubspec.yml, run pub upgrade and find… that the really old version of Polymer is still being installed:
$ pub upgrade
  ....
  polymer 0.10.0-pre.12 (0.15.0 available)
  ...
Yeah, thanks Dart Pub, I know 0.15 is available, why the heck aren't you installing it?!

Hey, there is no need to get angry with pub, it can answer the question if I ask nicely—by upgrading just the problem dependency:
$ pub upgrade polymer
Resolving dependencies... (8.4s)
Incompatible version constraints on browser:
- angular 0.0.8 depends on version >=0.8.7 <0.9.0
- polymer 0.15.0 depends on version >=0.10.0 <0.11.0
Wait... that is a really old version of Angular.dart—it should be up to 0.14 nowadays. What gives pub?
$ pub upgrade angular polymer
Resolving dependencies... (7.9s)
Incompatible version constraints on args:
- angular 0.14.0 depends on version >=0.10.0 <0.11.0
- polymer 0.15.0 depends on version >=0.11.0 <0.13.0
Gosh. Darn. It.

This does not seem like a problem that I am going to solve in one night, but I would still like to have some idea of the extent of the problem. So I fork Angular.dart, clone it locally and hand-edit their pubspec.yaml to include a more recent of args:
name: angular
version: 0.14.0
# ...
dependencies:
  args: '>=0.10.0 <0.12.0'
  # ...
To use this forked Angular.dart repo, I switch the pubspec.yaml in my chapter project code to point to this local package:
name: angular_example
dependencies:
  angular:
    path: /home/chris/repos/angular.dart
  polymer: any
  # angular_node_bind: any
With that, I get:
$ pub upgrade
Resolving dependencies... (21.8s)
Incompatible version constraints on analyzer:
- angular 0.14.0 depends on version >=0.15.0 <0.19.0
- polymer 0.10.0-pre.0 depends on version >=0.12.2 <0.13.0
Bleh. Now I am back to trying to install an old version of Polymer. I explicitly stick with the most recent by pub upgrading the polymer dependency:
$ pub upgrade polymer
Resolving dependencies... (1.6s)
Incompatible version constraints on code_transformers:
- angular 0.14.0 depends on version >=0.1.4+2 <0.2.0
- polymer 0.15.0 depends on version >=0.2.3 <0.3.0
So I need to again hand-edit Angular.dart's pubspec.yaml, after which I find:
$ pub upgrade polymer
Resolving dependencies... (1.7s)
Incompatible version constraints on html5lib:
- angular 0.14.0 depends on version >=0.10.0 <0.11.0
After another hand edit, I am finally able to pub upgrade:
$ pub upgrade polymer
...
* angular 0.14.0 from path /home/chris/repos/angular.dart (was 0.12.0)
...
> polymer 0.15.0 (was 0.10.0-pre.12)
...
In the end I had to change three different dependencies to get the latest Angular.dart and Polymer.dart installed side-by-side:
name: angular
# ... 
dependencies:
  args: '>=0.10.0 <0.12.0'
  # ...
  code_transformers: '>=0.1.4+2 <0.3.0'
  # ...
  html5lib: '>=0.10.0 <0.13.0'
  # ...
That is very much a bummer. It is especially troublesome for the book. Do I pursue this path and attempt to figure out if Angular.dart works with these new dependencies? Or do I stick with the old version of the packages? Neither is particularly appealing, but I will likely start with a limited attempt at working with a dependency augmented version of Angular.dart. I would hope that these two packages will be eventually consistent and dependency augmentation would position me best for that eventual future.

Day #194

Wednesday, September 24, 2014

Still, Still, Still Can't Test Keyboard Events in Dart (Still)


I have spent a lot of time trying to test keyboard events in Dart. A lot of time. And all with no success. Until last night.

As a side note, there is a school of thought that says that you cannot really know a language until you know its weaknesses. I generally have a tough time thinking of these on the spot so I keep a few in my proverbial back pocket. For the longest time, my greatest Dart annoyance has been the inability to create (and thus test) custom keyboard events. If this ever gets 100% fixed, I am going to have to think long and hard for other Dart foibles.

Assuming that my woes really are fixed.

Sadly, last night's solution is likely not an indicator that all is well. I had previously been unsuccessful in generating TextInput events in Polymer.dart elements, so when it worked last night, I though this astounding. It turns out that I have been testing with TextInput events for a while. A bunch of tests in the ICE Code Editor still rely on these to generate useful results.

But I might as well try the keyboard events again. It has been a while. In the ICE Code Editor, we have acceptance tests that read like:
      test("down arrow key moves forward in list", (){
        helpers.typeCtrl('O');
        helpers.typeIn('project 1');

        // project 11
        // project 10 *
        // project 1

        helpers.arrowDown(2);

        expect(
          document.activeElement.text,
          equals('Project 10')
        );
      });
If I hate Dart's keyboard event support, I love its tests. This test says that I:
  1. type Ctrl+O to open the Open Project dialog
  2. type in “project 1” to filter all projects that contain that substring (the three that we have are included in comments)
  3. Hit the down arrow key twice
After that, the expectation is that the active menu item will be “project 10.”

Ideally, a test like that would use keyboard events for just about all of that. Is the letter “o” pressed with the Control key modifier? On keyup in the project filter input, change the list of projects. On keyup when the key is an arrow key, move the active element up or down. But none of those tests actually use keyboard events. Some get away with TextInput events. Some, like the arrowDown() helper function, use absolutely awful hacks:
arrowDown([times=1]) {
  // var e = new KeyEvent('keydown', keyCode: KeyCode.DOWN).wrapped;
  var fake_button = document.query('#fake_down_key');
  if (fake_button == null) return;

  new Iterable.generate(times, (i) {
    // document.activeElement.dispatchEvent(e);
    fake_button.click();
  }).toList();
}
Instead of using keyboard events, I use a fake input field. Awful.

The reason the keyboard events do not work? Because Dart does not populate the keyCode property of the event being dispatched. This tends to make dispatching such events useless, hence the awful hack.

To see if thing have changed, I remove the hacks and reinstate the keyboard event code:
arrowDown([times=1]) {
  var e = new KeyEvent('keydown', keyCode: KeyCode.DOWN).wrapped;
  new Iterable.generate(times, (i) {
    document.activeElement.dispatchEvent(e);
  }).toList();
}
The KeyEvent class is the only way to add character data to keyboard events in Dart. Its wrapped property gives me the usual keyboard event for dispatch. The documentation for KeyEvent looks suspiciously unchanged and indeed, running this test still fails:
CONSOLE MESSAGE: FAIL: Keyboard Shortcuts Open Projects Dialog down arrow key moves forward in list
  Expected: 'Project 10'
    Actual: ''
     Which: is different. Both strings start the same, but the given value is missing the following trailing characters: Project 10 ...
Dang.

The suggested use of KeyEvents in the documentation is of no use to me in tests. I would have to create a Stream wrapper around this particular input field inside the application code, but somehow expose it outside the application so my test could add events to that stream. That might be acceptable if I had a single stream that I needed to expose for testing, but I have dozens.

So it seems the hack must remain. On the bright side, I still have a ready answer for something about Dart that I dislike. Actually, that is not much of a bright side.


Day #193

Tuesday, September 23, 2014

Verifying a Polymer.dart Fix with a Text Input Event Test?!


I thought I had the internationalization project code for Patterns in Polymer 100% solid. It turns out that I missed one thing: the bound variable in a pluralization is not updating properly.

After last night, I have <hello-you> element localization into English, Spanish, and French working just fine. I can start the element localized or change it on the fly. All except the number of red balloons the user currently happens to own:



As I found last night, this is not completely broken. Only the bound variable in the pluralization fails to update. If I switch the locale to Spanish, the message then displays correctly:



So what have I done?

The answer turns out to be “not much.” In fact, the answer is that I did nothing—the underlying Polymer library (and by extension the Polymer.dart library) changed when I was not looking. And, since I lack a test for this particular bit of functionality, it went completely unnoticed.

The actual problem is simple enough. The <x-translate> element is not listening for changes to the labels (including the count):
@CustomTag('x-translate')
class XTranslate extends PolymerElement {
  XTranslate.created(): super.created();
  // ...
  enteredView() {
    super.enteredView();
    labels.changes.listen((_)=> update());
  }
  // ...
}
It looks very much like it should be listening, but this listener is never established because enteredView() is never called. The enteredView() method used to be a callback method, but has since been replaced with attached(), making the fix trivial:
@CustomTag('x-translate')
class XTranslate extends PolymerElement {
  XTranslate.created(): super.created();
  // ...
  attached() {
    super.attached();
    labels.changes.listen((_)=> update());
  }
  // ...
}
With that, I have everything working again:



It works now, but I am only setting myself up for future failure. If the library changed once, it is likely to change again (the library is still in alpha release, after all). The only way to prevent this from breaking again without my knowledge is to write a test. But how?

Ideally I would want a test like the following (scheduled_test schedules are there to ensure serial execution):
    test('localizes the number of balloons', (){
      schedule((){
        _el.$['count'].value = '99';
      });
      schedule((){
        expect(_el.shadowRoot.text, contains("J'ai 99 ballons rouges."));
      });
    });
But that does work. Polymer does recognize the updated value as a reason to change its attributes. And I know that sending input events does not work.

Bleh. I may as well try some of those. Who knows? Maybe the Dart / Polymer folks fixed this....

Holy Cow! They fixed it!

If I send a Text Input event to the element:
    test('localizes the number of balloons', (){
      schedule((){
        var evt = new TextEvent('textInput', data: '99');
        _el.$['count'].
          dispatchEvent(evt);
      });
      schedule((){
        expect(_el.shadowRoot.text, contains("J'ai 99 ballons rouges."));
      });
    });
Then my test now passes:
PASS: <hello-you/> (i18n) defaults to English
PASS: <hello-you/> (i18n) can localize to French
PASS: <hello-you/> (i18n) can localize to Spanish
PASS: <hello-you locale="fr"/> (French) starts in French
PASS: <hello-you locale="fr"/> (French) localizes the number of balloons
PASS: <hello-you locale="es"/> (Spanish) starts in Spanish
I am so darn excited about this. I wonder if this means that all text input event generation works now? That would be amazing (it has long been my go-to answer to name something I don't like about Dart). But, for now, I am thrilled to have this particular element well tested for the future.


Day #192

Monday, September 22, 2014

TDD a Fix for Broken i18n in Polymer.dart


I think my Polymer.dart element is broken. Somewhere in the various upgrades to Polymer.dart, the internationalization project code for Patterns in Polymer stopped working. It sure would have been nice had my continuous integration server been working when that happened, but I can only change the future.

The problem is not that localization does not work at all. When I specify the locale with an attribute (<hello-you locale="fr"><hello-you>), it localizes just fine:



The problem occurs when changing locales with the drop-down menu. Specifically, the labels for the element remain in the original locale when the drop-down changes:



Since I can only change the future, I refuse to allow this to happen to me again. I will have tests that catch this problem. The needs for such tests is that much more important by virtue of the code already failing once.

Before jumping into writing the test case for my failure, I start with a simpler case. I know that specifying the locale via published Polymer attribute works, so I start with a test for that:
    test('starts in French', (){
      schedule((){
        expect(_el.shadowRoot.text, contains('Bonjour'));
      });
    });
The schedule() comes from the scheduled_test, which is a testing package for readable asynchronous tests built on top of unittest. This schedule is guaranteed to come after any schedules in the test setup, which can be quite helpful.

Helpful perhaps, but it does not solve all problems. This test fails:
FAIL: <hello-you locale="fr"/> (French) starts in French
  Caught ScheduleError:
  | Expected: contains 'Bonjour'
  |   Actual: '\n'
  |   '    \n'
  |   '      Hello \n'
  |   '    \n'
The test itself is sound. The problem winds up being in the setup of my test:
    setUp((){
      schedule(()=> Polymer.onReady);

      schedule((){
        var _completer = new Completer();
        _el = createElement('<hello-you locale="fr"></hello-you>');
        document.body.append(_el);

        _el.async((_){ _completer.complete(); });

        return _completer.future;
      });
    });
    // ...
  });
There are two schedules in my setup. One waits until the Polymer library itself is ready. The second one waits for this particular element is ready. A Polymer element is really ready when it updates its bound variables and renders. Polymer normally does this when appropriate, but it is possible to encourage this to happen immediately with async(). So the second schedule tells my Polymer element to update its bound variables, draw itself, then invoke the supplied callback which completes the schedule's completer.

Normally, this is enough when testing Polymer elements. But in this case, completing the completer when the element is ready is not sufficient. I also have to wait a few milliseconds for the JSON localization files to load and apply to the Polymer element:
      schedule((){
        var _completer = new Completer();
        _el = createElement('');
        document.body.append(_el);

        _el.async((_){
          // wait for l10n to be loaded
          new Timer(
            new Duration(milliseconds: 50),
            ()=> _completer.complete()
          );
        });

        return _completer.future;
      });
In all likelihood this a Polymer code smell. The complexity of this test setup is probably trying to tell me that my custom element should itself expose a future that completes when all localization data is loaded. I table that for the moment as I would like to focus on the task at hand.

With the additional wait time, my element now renders both French and Spanish versions:
CONSOLE MESSAGE: PASS: <hello-you locale="fr"/> (French) starts in French
CONSOLE MESSAGE: PASS: <hello-you locale="es"/> (Spanish) starts in Spanish
Perhaps this is all I need for my switching code?

Well, no. Even with the delay, a test for changing the locale still fails:
    test('defaults to English', (){
      schedule((){
        expect(_el.shadowRoot.text, contains('Hello'));
      });
    });

    test('can localize to French', (){
      schedule(()=> _el.locale = 'fr');
      schedule((){
        expect(_el.shadowRoot.text, contains('Bonjour'));
      });
    });
The text inside the Polymer element remains English, which is not too surprising. I have only written new test code so far, not made code changes.

So what is the actual problem? Eventually, I realize that the <hello-you> element and its translation strategy element, <x-translate> do not seem to be communicating via the bound variables that they share:
<polymer-element name="hello-you">
  <template>
    <!-- ... -->
    <x-translate id="l10n"
                 locale={{locale}}
                 labels={{labels}}></x-translate>
  </template>
  <script type="application/dart" src="hello_you.dart"></script>
</polymer-element>
I know that the properties are set correctly—otherwise my working tests would not be working—it is possible set the locale once via attribute, just not update them. Wait…

I know this. In fact I just ran into this in the JavaScript version of Polymer. The solution there was to mark a Polymer element's property as published and reflectable. I know how to do that in JavaScript, but what about Dart?

The answer is @PublishedProperty. Instead of declaring locale as just published:
@CustomTag('x-translate')
class XTranslate extends PolymerElement {
  @published String locale = 'en';
  // ...
}
I need to instead declare it as a published property whose value is reflected in the attribute whenever a change is made:
@CustomTag('x-translate')
class XTranslate extends PolymerElement {
  @PublishedProperty(reflect: true)
  String locale = 'en';
  // ...
}
With that, I have my element changing locales in response to drop-down menu changes:



More importantly, I now have 5 passing tests to ensure that this never again breaks without my knowledge:
CONSOLE MESSAGE: PASS: <hello-you/> (i18n) defaults to English
CONSOLE MESSAGE: PASS: <hello-you/> (i18n) can localize to French
CONSOLE MESSAGE: PASS: <hello-you/> (i18n) can localize to Spanish
CONSOLE MESSAGE: PASS: <hello-you locale="fr"/> (French) starts in French
CONSOLE MESSAGE: PASS: <hello-you locale="es"/> (Spanish) starts in Spanish
CONSOLE MESSAGE: 
CONSOLE MESSAGE: All 5 tests passed.
This “reflectable” property stuff is starting to get on my nerves. This is at least the third time that its behavior has surprised me. Maybe someday I will expect the actual behavior. Until then, I have tests verifying that I have correctly written my code for my desired behavior.


Day #191

Sunday, September 21, 2014

Pub as a Simple Test Server


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

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

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

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

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

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

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

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

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

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

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

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

Day #190

Saturday, September 20, 2014

Indirection Clean-up of Polymer Jasmine Tests


While testing my JavaScript Polymer elements, I do this a lot:
      beforeEach(function(done) {
        el.locale = 'fr';
        setTimeout(done, 0); // One event loop for element to update
      });
No, really, a lot:
  beforeEach(function(done){
    container = document.createElement("div");
    container.innerHTML = '';
    document.body.appendChild(container);
    el = document.querySelector('hello-you');

    setTimeout(done, 0); // One event loop for elements to register in Polymer
  });
In fact, I do it so often that I hard-coded it in the generator in eee-polymer-tests:
    beforeEach(function(done){
      // Maybe do some prep work here...
      setTimeout(done, 0); // One event loop for Polymer to process
    });
The problem that I am dealing with is, as the comments indicate, that Polymer waits until the next event loop before doing its thing. This promotes Polymer code and elements that are nice and responsive, but makes for noisy tests. I wonder if I might clean things up some.

Before starting down this road, I should say that I probably will not change anything. The first thing that I will try is a helper function. I hate helper functions. I hate indirection in tests. If I have to debug a test or trace through a test, I guarantee that I will delete that test (and maybe replace it). Still, I seem to do this setTimeout() / done() dance a lot, so...

I start by borrowing some of the terminology from the very excellent sheduled_test package in Dart. I try to schedule a Polymer operation in my tests with:
function schedulePolymer(fn){
  return function(done) {
    fn();
    setTimeout(done, 0); // One event loop for elements to register in Polymer
  };
}
By returning a function, I can supply this directly to Jasmine's (or Mocha's) beforeEach() function:
  beforeEach(
    schedulePolymer(function(){
      container = document.createElement("div");
      container.innerHTML = '<hello-you></hello-you>';
      document.body.appendChild(container);
      el = document.querySelector('hello-you');
    })
  );
That works. Well, my tests all still pass. But I am not really sure that clears up intent of my tests sufficiently to justify the existence of this helper function. And although the word schedule is nice in that implies a synchronous operation, I do not feel like it works well in this case. Perhaps if there was an entire library dedicated to scheduling JavaScript tests serially, this would make more sense.

Maybe if I write a Polymer-specific beforeEach():
function beforeEachPolymer(fn){
  return beforeEach(function(done) {
    fn();
    setTimeout(done, 0); // One event loop for elements to register in Polymer
  });
}
This lets me rewrite my beforeEach() as:
    beforeEachPolymer(function(){
      container = document.createElement("div");
      container.innerHTML = '';
      document.body.appendChild(container);
      el = document.querySelector('hello-you');
    });
This has the added benefit of not seeming out of place with afterEach() calls that I typically place immediately after beforeEach() calls:
  afterEach(function(){
    document.body.removeChild(container);
  });
This beforeEachPolymer() is not a huge win, but it does help when performing minor changes before checking expectations. Last night's i18n tests included a series of beforeEach() calls that needed to wait after changing the locale:
    describe('it can localize to French', function(){
      beforeEach(function(done) {
        el.locale = 'fr';
        setTimeout(done, 0); // One event loop for element to update
      });

      it('(fr)', function(){
        expect(el.shadowRoot.textContent).toContain('Bonjour');
      });
    });

    describe('it can localize to Spanish', function(){
      beforeEach(function(done) {
        el.locale = 'es';
        setTimeout(done, 0); // One event loop for element to update
      });

      it('(es)', function(){
        expect(el.shadowRoot.textContent).toContain('Hola');
      });
    });
That is rather noisy — especially since the setTimeout() / done() lines are so much wider than the code that actually tests my Polymer element. With beforeEachPolymer(), I now have:
    describe('it can localize to French', function(){
      beforeEachPolymer(function() {
        el.locale = 'fr';
      });

      it('(fr)', function(){
        expect(el.shadowRoot.textContent).toContain('Bonjour');
      });
    });

    describe('it can localize to Spanish', function(){
      beforeEachPolymer(function() {
        el.locale = 'es';
      });

      it('(es)', function(){
        expect(el.shadowRoot.textContent).toContain('Hola');
      });
    });
The intent of which is much clearer. But is it clear enough to justify test indirection? I just do not know.

I do not think that switching to promises would really offer much help—especially since Polymer is not much of a promise-driven library at present. I loathe to pull in yet another testing library—I already need a runner, a library, and an assertion library. Do I really need to pull in yet another library that people may or may not look upon as a standard? I have to admit, the old Jasmine 1 waits() and runs() stuff is looking pretty nice at this point. I do not think that is a viable solution to offer in Patterns in Polymer, but I may start using that in my own Polymer projects.


Day #189

Friday, September 19, 2014

Testing Core-Ajax Polymer Elements with Karma


One of the benefits of a test runner like Karma is that it, y'know, runs tests. More accurately, it runs tests on a test web server, which is a detail that I would like to explore testing the internationalization solution in Patterns in Polymer.

As best I can tell, there is not a single solution for i18n with Polymer. Way back when, I played with using the i18next JavaScript package in Polymer and liked it quite a bit. I just do not know if I like it as the solution. Especially since the book is simultaneously written in JavaScript and Dart, I settled on a strategy that allowed swapping implementations.

Tonight, I hope to test the default JavaScript strategy from the book, which is a simple, hand-rolled solution that loads localization files via <core-ajax> tags. Normally if I am testing Ajax in JavaScript, I would want to pull in the Sinon.js testing library to stub out HTTP requests like those that load the l10n files. But, since Karma is running its own web server for testing, I ought to be able to add the l10n files to the list of files being served and actually load the l10n files in my tests.

In fact, when I run the solitary test that currently tests my i18n chapter, I already see 404s when my Polymer element attempts to load the l10n files:
$  karma start --single-run
INFO [karma]: Karma v0.12.23 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 37.0.2062 (Linux)]: Connected on socket DjAiWyWkVjQjEfu7_8nN with id 73763037
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0 secs / 0.078 secs)
WARN [web-server]: 404: /locales/en/translation.json
WARN [web-server]: 404: /locales/fr/translation.json
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.096 secs / 0.078 secs)
First, I need to include the l10n files in the list of files that Karma serves:
    files: [
      'bower_components/platform/platform.js',
      'test/PolymerSetup.js',
      {pattern: 'elements/**', included: false, served: true},
      {pattern: 'locales/**', included: false, served: true},
      {pattern: 'bower_components/**', included: false, served: true},
      'test/**/*Spec.js'
    ],
These are not code files—just JSON—so I do not want them automatically included and evaluated on the test page. I just want them served on demand. Hence included: false (the contents are not added directly to the test page) and served: true (the web server will serve them when requested).

That is just part of the puzzle. When I fire up the tests, I still get 404s for the l10n files:
WARN [web-server]: 404: /locales/en/translation.json
WARN [web-server]: 404: /locales/fr/translation.json
This is due to Karma serving everything from the /base URL path. If index.html is at the top-level of my project, then I would need to access it as /base/index.html.

Thankfully, Karma has a somewhat obscure proxies setting which allows resources to be reverse proxied:
proxies:  {
      '/locales/': '/base/locales/'
    }
With that, and a log-level of debug, I see that I have my l10n files being served and proxied:
...
DEBUG [proxy]: proxying request - /locales/en/translation.json to localhost:9876
DEBUG [proxy]: proxying request - /locales/fr/translation.json to localhost:9876
With that, I can write my tests that expect different localizatons:
    it('defaults to English', function(){
      expect(el.shadowRoot.textContent).toContain('Hello');
    });
Updating the locale is slightly tricky, but only because I have to wait for Polymer to update the element display. It only takes a single event loop, but the only way to accomplish this in Jasmine is with a setTimeout call to the asynchronous done():
    describe('it can localize to French', function(){
      beforeEach(function(done) {
        el.locale = 'fr';
        setTimeout(done, 0); // One event loop for element to update
      });

      it('(fr)', function(){
        expect(el.shadowRoot.textContent).toContain('Bonjour');
      });
    });
But, with that, and a similar test for Spanish:
    describe('it can localize to Spanish', function(){
      beforeEach(function(done) {
        el.locale = 'es';
        setTimeout(done, 0); // One event loop for element to update
      });

      it('(es)', function(){
        expect(el.shadowRoot.textContent).toContain('Hola');
      });
    });
Now I have much more meaningful tests for the book's i18n solution. This will be especially useful as Polymer evolves. Should my approach suddenly not localize with a new version of the Polymer library, I will know immediately.

I am marginally dissatisfied with the overhead of beforeEach() just to setTimeout() / done(). I do not normally care too much about the readability of tests, but this seems to unnecessarily obscure the intent of the test. I have larger concerns (like producing screencasts for the Extras readers), so I may just live with this. Still, it bothers me so I may have a look at this in the upcoming days.


Day #188

Thursday, September 18, 2014

Upgrading Old Polymer Elements to Core Elements


I made it through every single chapter in Patterns in Polymer last night. Except one.

I finally have a super test build for the book project. Since Patterns in Polymer is written simultaneously in JavaScript and Dart, my super test build needs to run tests for each chapter in both Dart and JavaScript. After last night, the super test build runs JavaScripts tests for every chapter save for the internationalization chapter.

There are three reasons that I could not get the i18n chapter tested last night. First, I had written no tests when I originally wrote the chapter (bad developer). The eee-polymer-tests generator / dependency manager is capable of generating a simple test, but... The second reason that I could not get the chapter under test is that I am relying on <core-ajax> elements to load JSON localization files. Last, but not least, I am actually relying on <polymer-ajax> elements (the predecessor to <core-ajax>) to load the 110n files.

In other words, I have my work cut out for me to get this tested.

The approach I take is to get the Polymer library updated while still using the old <polymer-ajax>, then try eee-polymer-tests' default test, then upgrade to the new <core-ajax>. Finally, if all goes well, I will try to add some legitimate tests.

My bower.json does not restrict Polymer versions (to enable easy updates to the latest Polymer for testing):
{
  "name": "i18n",
  // ...
  "dependencies": {
    "polymer": "Polymer/polymer",
    "bootstrap": "~3.1.0",
    "i18next": "~1.7.2",
    "polymer-elements": "~0.2.0"
  }
}
Running bower update does not go smoothly, mainly because polymer-elements is so darn old. But, whenever I run across an incompatibility, I opt for the newer instance:
Unable to find a suitable version for platform, please choose one:
    1) platform#0.2.4 which resolved to 0.2.4 and is required by core-component-page#0.2.4, polymer#0.2.4
    2) platform#^0.4.0 which resolved to 0.4.1 and is required by core-component-page#0.4.1, polymer#0.4.1
Eventually, I get everything installed and up to date. Amazingly, when I fire the internationalized <hello-you> element up in the browser, it still works perfectly:



OK, so step #1 is done. Next, I try eee-polymer-tests' simple test. I add eee-polymer-tests to the NPM package.json dependencies:
{
  "name": "i18n",
  "devDependencies": {
    "grunt": "~0.4.1",
    "grunt-contrib-watch": "~0.5.3",
    "eee-polymer-tests": "eee-c/eee-polymer-tests"
  }
}
Then I run npm install and instruct the generator to create a test for <hello-you>. That too works:
karma start                                                       
INFO [karma]: Karma v0.12.23 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 37.0.2062 (Linux)]: Connected on socket ebljUvRurILtOe32-8Gq with id 2242723
Chrome 37.0.2062 (Linux): Executed 1 of 1 SUCCESS (0.093 secs / 0.085 secs)
WARN [web-server]: 404: /locales/en/translation.json
WARN [web-server]: 404: /locales/fr/translation.json
WARN [web-server]: 404: /locales/es/translation.json
The test simply checks that a shadow DOM is present if the <hello-you> element is inserted. It is not much of a test, but it does tell me that Polymer is loading properly for this element, which is especially good to know since the element was written so long ago.

Before writing a real test, this is a good time to upgrade from <polymer-ajax> to <core-ajax>. After removing polymer-ajax from the dependencies in bower.json, I tell bower to replace it with core-ajax:
$ bower install Polymer/core-ajax -S
bower core-ajax#*               cached git://github.com/Polymer/core-ajax.git#0.4.1
bower core-ajax#*             validate 0.4.1 against git://github.com/Polymer/core-ajax.git#*
The -S option saves the dependency in bower.json:
{
  "name": "i18n",
  // ...
  "dependencies": {
    "polymer": "Polymer/polymer",
    "bootstrap": "~3.1.0",
    "i18next": "~1.7.2",
    "core-ajax": "Polymer/core-ajax#~0.4.1"
  }
}
I delete bower_component and re-install to ensure that I am rid of any leftover cruft from the previously very old dependency. But after doing so, the element still works:



And my simple test still passes.

That will suffice as a stopping point for tonight. Some of the book text will need to be updated after these changes, so will get to that now. More importantly, I can add the last chapter to my super build—even if it is only covered by a very simple test. Up tomorrow, I will do something about that very simple test—I think that I can write some useful tests for this chapter.


Day #187


Wednesday, September 17, 2014

Auto-Choosing Xvfb Display Ports


My current plan is to work through every chapter in Patterns in Polymer, getting the code and tests up to date with the latest Polymer and Karma + Jasmine. Once I have a regular build in place, I will revisit each chapter's content to ensure that it too is up-to-snuff, then perhaps add a chapter or two.

As an aside, I have encountered my share of annoyances with minor Polymer breakages but I can accept those—this is, after all an alpha release of the library. More frustrating is the syntax in Jasmine changing with every patch release (e.g. andReturn() becomes and.returnValue() is my latest favorite). Between this and major syntax changes in other testing libraries for no apparent reason, it almost enough to put me off testing.

Despite the various annoyances, things are moving steadily. I have an outside chance of getting all of the JavaScript code and tests fixed tonight. Except the Dart build randomly breaks for no apparent reason.

The book is written in both Dart and JavaScript so I need to run tests for both sample codebases. Unlike JavaScript testing libraries and JavaScript expectation libraries and JavaScript test runners, Dart's testing has been quite stable. So what gives?

Well, Dart tests, like JavaScript tests, require a windowing environment in which to execute browser tests. On Linux, this is normally done with Xvfb and on Ubuntu/Debian I have been using the nifty little xvfb-run run script to execute tests:
    # Run the Karma tests
    xvfb-run karma start --single-run --log-level warn
Unfortunately this turns out to be the cause of my Dart failure:
...
xvfb-run: error: Xvfb failed to start
Build step 'Execute shell' marked build as failure
It turns out that the Dart build, which I delay for unrelated reasons, is conflicting with the windowing display port being used by my JavaScript build.

Ugh. I start dreaming up how I might retry the build with a different port or additional delay or maybe even run only one build at a time. Mercifully, before I get around to attempting to implement any of that, I read the xvfb-run man page (to be honest, I was reading it to figure out how to implement some of those crazy ideas). In addition to making it easy to wrap processes inside a windowing environment, the xvfb-run script also has a flag to do just what I need:
-a, --auto-servernum
Try to get a free server number, starting at 99, or the argument to --server-num.

So in the end, all I need to do is add the -a switch to my tests:
    # Run the Karma tests
    xvfb-run -a karma start --single-run --log-level warn
I can even verify this by running my Dart tests several times during the JavaScript build (did I mention that Dart tests tend to be markedly faster than their JavaScript counterparts?). During most builds, there is a note about RANDR missing from the Xvfb windowing environment (which is perfectly OK):
Xlib:  extension "RANDR" missing on display ":99".
During most of the test runs, I see the message on port :99, but on one I see:
Xlib:  extension "RANDR" missing on display ":100".
In other words, the -a flag detected in this case that the JavaScript tests were currently being run on display port :99 and automatically ran on :100 instead. Cool!

With that, I can get back to fixing tests and code without the worry of seemingly random failures.


Day #186

Tuesday, September 16, 2014

Updating Published Polymer Attributes


If a codebase's tests don't run regularly, the codebase is not tested. A team can spend hundreds of person-hours writing hundreds of tests. But if those tests are not executed at least once a day, the team might just as well delete them. They occupy space with no purpose.

This seems utterly obvious, yet I see it time and time again. And as much as I detest this poor excuse for programming, I am guilty of no less with the Patterns in Polymer project code. As I work through each chapter adding at least rudimentary Karma / Jasmine tests to the custom Polymer elements therein, I am surprised… at how many tests I actually did write.

And so far, the tests have been easy to fix once I updated the element definitions to the latest Polymer library. Until tonight. Tonight, I find that some of the console logging that I expect from a Polymer observer is not being called:
$ karma start
INFO [karma]: Karma v0.12.23 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 37.0.2062 (Linux)]: Connected on socket wjIrCJXi9b1e1VLgybGG with id 7435382
Chrome 37.0.2062 (Linux)  sees changes to your_name FAILED
        Expected spy Console Log to have been called with [ 'your_name is now: Bob' ] but it was never called.
            at Object.<anonymous> (/home/chris/repos/polymer-book/book/code-js/changes_from_outside/test/HelloYouSpec.js:106:9)
Chrome 37.0.2062 (Linux): Executed 1 of 5 (1 FAILED) ERROR (0.065 secs / 0.061 secs)
At first I assumed than an API had changed since I originally wrote this code and test (it was all the way back in March, after all). Upon closer examination, something else seems to be going wrong. I am hesitant to say that Polymer is broken, but my understanding of it is.

Regardless, it sure would have been nice to have a regular build running so that I knew exactly when this did break.

First, the problem. I am spying on console.log() which is called by a MutationObserver:
function watchPolymer(el) {
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      console.log(
        mutation.attributeName +
        ' is now: ' +
        mutation.target[mutation.attributeName]
      );
    });
  });
  observer.observe(el, {attributes: true});
}
By virtue of the attributes: true on the observe() method, this MutationObserver should fire whenever an attribute changes. And it does, if I find the <hello-you> Polymer element and update one of its attributes, the MutationObserver fires:
> el = document.querySelector('hello-you')
    <hello-you color=​"greenish">​…​</hello-you>​
> el.attributes.getNamedItem('color').value = 'orange'
    color is now: orange watcher.js:14
    "orange"
But if the value of the color property changes in the element, say when the feelingLucky() button is pressed, nothing happens:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="hello-you" attributes="your_name color" color>
  <template><!-- ... --></template>
  <script>
Polymer('hello-you', {
  your_name: '',
  color: '',

  feelingLucky: function() {
    console.log(this.color);

    var colors = ['red', 'blue', 'green'];
    var num = Math.floor(colors.length*Math.random());
    this.color = colors[num];
    this.$.hello.style.color = this.color;
  }
});
</script>
</polymer-element>
Dang it. I am publishing the color attribute by including it in the space separated list of published attributes in the aptly named attributes attribute of the definition. But no matter what I do, changing the property is not reflected in the element's attribute.

What is even stranger is that, the console.log() on the feelingLucky() does see the initial value of the attribute. If I use this custom <hello-you> element with a “greenish” color:
<hello-you color="greenish"></hello-you>
Then, when I first press the feelingLucky() button, I see greenish logged in the JavaScript console:
color is now: greenish     watcher.js:14
greenish                   hello-you.html:27
It is almost like data binding is one way. That, or Polymer has some kind of internal setting that prevents published attributes from getting updated.

Wait....

I have seen this before. I know I have. Despite numerous searches and banging my head against this code for quite some time, I had no luck identifying this problem. But I know that I either getAttribute() or setAttribute()'d before to work this exact problem. And indeed I did.

I have now learned through bitter experience not once, but twice that there is special undocumented syntax that needs to be applied if one wants attributes of a Polymer element to be updated when the underlying property value is updated. I cannot set the published attributes via the attribute attribute of polymer-element anymore. Instead I have to use the published property and set reflect to true:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="hello-you">
  <template><!-- ... --></template>
  <script>
Polymer('hello-you', {
  publish: {
    your_name: {value: '', reflect: true},
    color: {value: '', reflect: true},
  },
  // ...
});
</script>
</polymer-element>
I was nearing my wits' end with this problem. That I have now been forced to solve this twice is a source of much annoyance. Hopefully the title of this post will ensure that I will have better search luck the next time that I have to do battle with this feature.



Day #185

Monday, September 15, 2014

Why Must I Be A Programmer in Love (with Inheritance)?


Why do I hate code inheritance so much? Possibly because no matter how much I pretend to hate it, it still seems to be my first instinct for extending class behavior. Which only makes me hate it more. Or at least pretend to.

Tonight's case in point comes from an unlikely source: the Karma configuration for one of the chapter projects in Patterns in Polymer. I am (slowly) working through each chapter standardizing on the dependencies and generated structure provided by eee-polmer-tests. One of the peer dependencies of eee-polymer-tests is Karma and eee-polymer-tests is kind enough to include a file with common configuration options.

I use those common configuration options in each Polymer project with a karma.conf.js that looks like:
module.exports = function(config) {
  var common = require('./node_modules/eee-polymer-tests/karma-common.conf.js');

  config.set(common.mixin_common_opts(config, {
    // Override common settings here...
  }));
};
OK, this is not classical class inheritance. This is typical JavaScript prototypical inheritance. My problem remains the same. I see a problem and I want to solve it by putting junk in the object doing the extending. Worse, once I start down that road, I begin inventing ways to make the inheritance smart.

While converting some old tests over to this new approach, I realized that I was missing two things: the html2js Karma preprocessor and the inclusion of HTML fixtures in the configuration. The fix is simple enough:
module.exports = function(config) {
  var common = require('./node_modules/eee-polymer-tests/karma-common.conf.js');

  config.set(common.mixin_common_opts(config, {
    // Override common settings here...
    preprocessors: {
      'test/*.html': 'html2js'
    },

    files: [
      'test/PolymerSetup.js',
      {pattern: 'elements/**', included: false, served: true},
      {pattern: 'bower_components/**', included: false, served: true},
      'test/**/*Spec.js',
      'test/*.html'
    ]

  }));
};
I have added the html2js preprocessor (I am also required to npm install karma-html2js-preprocessor --save-dev). I have copied the list of files from eee-polymer-test's common Karma configuration and added the test/*.html fixtures to the list.

And this is enough to trigger my Pavlovian inheritance response: “I should add preprocessors by default, I should change the mixin_common_opts() so that I can add the one entry to the list rather than copying and pasting, I should support special attribute that are added to existing values instead of overwriting them.”

Maybe this is why inheritance is so insidious. Because each of those things is small enough that they make sense. I would not be violating YAGNI—I would use them immediately. Each would legitimately solve a problem for me. So why not just give in? Why not just implement the configuration like this and be done with it?

There are at least two reasons. The first is a nagging sense that I would be in violation of POLS—that I (or someone else using this code) would find the inheritance confusing or non-obvious. The second reason is that I am jumping to implementation without really considering the problem.

And here's the problem. Here's my dirty little secret. I don't use fixtures in my Polymer testing. In my very first Polymer tests, I used them mostly as I was trying to understand how best to approach testing. The Polymer fixtures worked, but since then I have mostly dynamically created the elements right in my tests. So maybe this is a case of YAGNI after all. But it's not that I don't need the code—this is a situation where I don't need the solution.

As a nod to my struggles, I will likely add the list of files from the common configuration into the generated karma.conf.js as a comment. That way, if I do run into this again, I will have all of the information that I need right in the configuration file.

In the end, I nearly gave into my seemingly natural reflex to add some form of inheritance to everything. Thankfully, I was able to listen to my better nature. Maybe someday that will be my first instinct.


Day #184