Send to Kindle

Thursday, April 17, 2014

Bare Minimum Polymer.dart Testing


I finally have my Polymer.dart tests passing again. But that is insufficient for Patterns in Polymer. I have to ensure that I am not programming by coincidence, which is a poor programming practice and disastrous book writing practice.

James Hurford was kind enough to give me a head start on this. He noted that he was able to get my Polymer.dart tests passing even without a few things that I had previously thought required—things like async() calls to flush the Polymer platform and onPolymer futures to ensure that platform was in place.

James hypothesized that the non-necessity of these actions might be due to the dev-channel Dart that he is running whereas I am still on stable 1.3. I have 1.4 at the ready, but begin with 1.3 to put that to the test.

I am explicitly invoking the Polymer.onReady future as part of my test, so I will just comment that out from my setUp() block:
@initMethod
main() {
  setUp((){
    // schedule(()=> Polymer.onReady);
    // More setup here...
  });
  // Tests here...
}
The schedule() here is not built into Dart's very excellent unittest library. Rather it comes from the also excellent scheduled_test package which extends unittest with some asynchronous goodness.

With that commented out, I also remove the async() calls. This was pulled into my Dart code because it was, at one point, necessary in both JavaScript and Dart versions of Polymer elements. I am not using this directly in the tests, but rather as a method on the Page Object that I am using to interact with the Polymer element:
class XPizzaComponent {
  String get currentPizzaStateDisplay => // ...
  Future addWholeTopping(String topping) {
    /* ... */
    return flush();
  }
  Future flush() {
    var _completer = new Completer();
    el.async((_)=> _completer.complete());
    return _completer.future;
  }
}
The async() method on Polymer elements is supremely handy when testing. It tells the Polymer element to immediately process all outstanding asynchronous operations like updating bound variables. It also tells the Polymer platform to “flush,” which updates all UI elements.

This came in extremely handy when the test interacted with the Polymer element (e.g. adding a whole topping to the <x-pizza> element) and needed to ensure that all updates had taken place before checking expectations. To put James' hypothesis to the test, I change the flush() method to return nothing instead of a Future:
class XPizzaComponent {
  String get currentPizzaStateDisplay => // ...
  Future addWholeTopping(String topping) {
    // ...
    return flush();
  }
   void flush() {}
}
And, just as James had found my tests all still pass:
➜  dart git:(master) ✗ ./test/run.sh
#READY
CONSOLE MESSAGE: unittest-suite-wait-for-done
CONSOLE MESSAGE: PASS: [defaults] it has no toppings
CONSOLE MESSAGE: PASS: [adding toppings] updates the pizza state accordingly
CONSOLE MESSAGE: 
CONSOLE MESSAGE: All 2 tests passed.
CONSOLE MESSAGE: unittest-suite-success
CONSOLE WARNING: line 213: PASS
So what gives? This works just fine in both Dart 1.3 and 1.4 without worrying about asynchronous operations. How did that happen?

Well, it turns out that there is a little nod to the asynchronous nature of Polymer that is still implicit. The scheduled test library is running each of those schedules in separate event loops. And, when each schedule finishes, it allows the main Dart event loop to continue processing whatever else it needs to—like Polymer bound variables and platform flushes. I can verify this by removing all schedules from my setUp():
  setUp((){
    // schedule(()=> Polymer.onReady);

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

      xPizza = new XPizzaComponent(_el);
      // return xPizza.flush();
    // });

    currentSchedule.onComplete.schedule(() => _el.remove());
  });
With that, all tests fail:
➜  dart git:(master) ✗ ./test/run.sh
#EOF
#READY
CONSOLE MESSAGE: unittest-suite-wait-for-done
CONSOLE MESSAGE: FAIL: [defaults] it has no toppings
  Caught ScheduleError:
  | Expected: '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":[]}'
  |   Actual: ''
  |    Which: is different. Both strings start the same, but the given value is missing the following trailing characters: {"firstHal ...

CONSOLE MESSAGE: FAIL: [adding toppings] updates the pizza state accordingly
  Caught ScheduleError:
  | Expected: '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":["green peppers"]}'
  |   Actual: '{"firstHalfToppings":[],"secondHalfToppings":[],"wholeToppings":[""]}'
  |    Which: is different.
  | Expected: ... ppings":["green pepp ...
  |   Actual: ... ppings":[""]}
  |                         ^
  |  Differ at offset 66
CONSOLE MESSAGE:
CONSOLE MESSAGE: 0 PASSED, 2 FAILED, 0 ERRORS
CONSOLE MESSAGE: Uncaught Error: Exception: Some tests failed.
I can make that pass by adding either the original async()/flush() or the schedule for Polymer.onReady, but I do not need both. Even so, I think that I will likely wind up recommending both in Patterns in Polymer. It better mirrors the approach in the JavaScript Polymer. Also, I think they make conceptual sense.


Day #37

Wednesday, April 16, 2014

Never Forget the Polymer.dart Transformer (No Really, NEVER Forget It)


I thought to try to finish off the Strategy Pattern chapter in Patterns in Polymer tonight. Only when I loaded some “play” code in Dartium, I saw the following error in the console:
Exception: The "smoke" library has not been configured. Make sure you import and configure one of the implementations (package:smoke/mirrors.dart or package:smoke/static.dart).
#0      throwNotConfiguredError (package:smoke/src/implementation.dart:34:3)
#1      typeInspector (package:smoke/src/implementation.dart:26:5)
#2      query (package:smoke/smoke.dart:85:20)
#3      _getPublishedProperties (package:polymer/src/declaration.dart:409:31)
#4      PolymerDeclaration.publishAttributes (package:polymer/src/declaration.dart:175:39)
#5      PolymerDeclaration.buildType (package:polymer/src/declaration.dart:95:22)
#6      PolymerDeclaration.register (package:polymer/src/declaration.dart:72:14)
#7      _hookJsPolymer.registerDart.<anonymous closure> (package:polymer/src/loader.dart:100:75)
#8      _rootRun (dart:async/zone.dart:719)
#9      _ZoneDelegate.run (dart:async/zone.dart:453)
#10     _CustomizedZone.run (dart:async/zone.dart:663)
#11     _hookJsPolymer.registerDart (package:polymer/src/loader.dart:99:22)
Are. You. FREAKING. Kidding me?!

I specifically started tonight with writing so that I could get away from that stupid error. It is the same error that has plagued my testing efforts for the past two nights and now I am seeing it application code. Noooooo!

This was some relatively old Polymer.dart code that I am upgrading to the 0.10 version of the library. So I have already gone through it to tack on component=1 to the mime types so that the Dart VM will know to run all Polymer code in the same isolate:
<polymer-element name="store-changes-load">
  <template><!-- ... --></template>
  <script src="store_changes_load.dart"
          type="application/dart;component=1"></script>
</polymer-element>
I have already fixed the dependencies of my play application to use the appropriate bleeding-edge versions of the libraries in my pubspec.yaml file:
name: change_history
dependencies:
  polymer: ">0.10.0-pre"
  polymer_elements: ">0.2.0-pre"
dev_dependencies:
  unittest: any
So why, oh why am I still getting “smoke” package exceptions? Well, the title of this post pretty much gives it away: since this is old code, I have not specified a Polymer pub transformer in my Dart Pub configuration. If I add it to pubspec.yaml:
name: change_history
dependencies:
  polymer: ">0.10.0-pre"
  polymer_elements: ">0.2.0-pre"
dev_dependencies:
  unittest: any
transformers:
- polymer:
    entry_points: web/index.html
Then all of my Polymer code magically starts working.

And yup, if I go back to the pubspec.yaml from last night, add my test page to the list of files that are transformed by the Polymer transformer:
name: model_example
dependencies:
  polymer: ">=0.10.0-pre"
dev_dependencies:
  scheduled_test: any
transformers:
- polymer:
    entry_points:
    - web/index.html
    - test/index.html
Then my test magically passes.

Ah, life on the bleeding edge. Sometimes it does cut deep. But lesson learned: always include all pages using Polymer elements in the list of Polymer transformers—even test pages. I will never, NEVER forget that.


Day #36

Tuesday, April 15, 2014

Still Can't Test Polymer.dart


I cannot believe that I can no longer test Polymer.dart elements.

This was the very first chapter that I wrote in Patterns in Polymer and now it is completely broken. Not only that, I no longer have any way to automatically determine if the code for the other chapters in the book work. Not cool.

Fortunately, this is a pre-release version of Polymer.dart (0.10), so I can hope for one of two things: (1) the library authors address this to make it easier or (2) I can figure out how to make this work as-is. There is little that I can do for (1) aside from contact that authors, though I would prefer to understand things better first—I hate to bother folks if I can figure it out on my own with a little effort. I do try a pub update, but the most recent version of Polymer.dart (that matches ">=0.10.0-pre.7") is still the same 0.10.0-pre.9 that did not work for me last night.

So it looks like I need to investigate some more. Tonight, I follow the examples laid out in the Polymer.dart test directory. There are many tests in the package, though they include templates directly in the test page and the backing classes directly in the test code. This approach is of limited value when testing custom elements, but still it is a start.

So instead of importing the <x-pizza> element that I would like to test:
<head>
  <!-- Load Polymer -->
  <link rel="import" href="packages/polymer/polymer.html">

  <!-- Load component(s) -->
  <link rel="import" href="packages/model_example/elements/x-pizza.html">

  <script src="packages/unittest/test_controller.js"></script>
</head>
<body>
  <!-- The actual tests -->
  <script type="application/dart;component=1" src="test.dart"></script>
</body>
I instead pull the <polymer-element> from that source HTML directly into the <body> of my test page:
<head>
  <link rel="import" href="packages/polymer/polymer.html">
  <script src="packages/unittest/test_controller.js"></script>
</head>
<body>
  <polymer-element name="x-pizza">
    <template>
      <!-- Template code here... -->
    </template>
  </polymer-element>
  <polymer-element name="x-pizza-toppings">
    <template>
      <!-- Template code here... -->
    </template>
  </polymer-element>

  <!-- The actual tests -->
  <script type="application/dart;component=1" src="test.dart"></script>
</body>
There are actually two elements that comprise the element being tested, so I include both.

Then, in the test.dart file, I follow the Polymer.dart convention of defining the backing class directly in the test:
library x_pizza_test;

import 'dart:html';
import 'package:scheduled_test/scheduled_test.dart';
import 'package:polymer/polymer.dart';

@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // ...
}

@CustomTag('x-pizza-toppings')
class XPizzaToppings extends PolymerElement {
  // ...
}

@initMethod
main() {
  setUp((){
    schedule(()=> Polymer.onReady);
    // Other setup...
  });
  // Tests here...
}
With that, it still does not work:
Exception: Unhandled exception:
Exception: The "smoke" library has not been configured. Make sure you import and configure one of the implementations (package:smoke/mirrors.dart or package:smoke/static.dart).
#0      throwNotConfiguredError (package:smoke/src/implementation.dart:34:3)
#1      typeInspector (package:smoke/src/implementation.dart:26:5)
#2      query (package:smoke/smoke.dart:85:20)
#3      _getPublishedProperties (package:polymer/src/declaration.dart:409:31)
#4      PolymerDeclaration.publishAttributes (package:polymer/src/declaration.dart:175:39)
#5      PolymerDeclaration.buildType (package:polymer/src/declaration.dart:95:22)
#6      PolymerDeclaration.register (package:polymer/src/declaration.dart:72:14)
#7      _hookJsPolymer.registerDart.<anonymous closure> (package:polymer/src/loader.dart:100:75)
#8      _rootRun (dart:async/zone.dart:719)
#9      _ZoneDelegate.run (dart:async/zone.dart:453)
#10     _CustomizedZone.run (dart:async/zone.dart:663)
#11     _hookJsPolymer.registerDart (package:polymer/src/loader.dart:99:22)
Bother.

I need to keep working on the chapters in Patterns in Polymer that do work, so I call it a night here. Tomorrow, I will try working with simpler Polymer elements—like those in the Polymer.dart test suite (I may even try those tests directly). If that still does not work, the Polymer.dart tests mention a test_suite.dart file that I have not yet found. Perhaps the answers I seek can be found in that file.


Day #35

Monday, April 14, 2014

Can No Longer Test Polymer.dart Code


My tests are broken. Again. Time to fix my tests.

Actually, I have tests that have not passed for months at this point. Tonight, I am just going to fix the test that broke in the last 4 days (since I upgraded to Polymer.dart). Hmmm... this might not bode well for a solid test suite for Patterns in Polymer. I'll save that for another day. For now, I just want to get one Polymer element under test...

The first thing that I need to change is the no-longer-used initPolymer() from my test setup:
library x_pizza_test;

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


main() {
  initPolymer();

  // DOM element to hold test element
  PolymerElement _el;
  // Page object for better testing
  XPizzaComponent xPizza;

  // Tests here...
}
After deleting that line, I need another way to start the Polymer platform in my tests. In application code, this is now done with a <link> import, so I add that to the test page that holds my browser tests:
<head>
  <!-- Load Polymer -->
  <link rel="import" href="packages/polymer/polymer.html">

  <!-- Load component(s) -->
  <link rel="import" href="packages/model_example/elements/x-pizza.html">
  <script type="application/dart" src="test.dart"></script>
  <script src="packages/unittest/test_controller.js"></script>
</head>
With that, I get a nice failing test:
FAIL: [defaults] it has no toppings
  Caught ScheduleError:
  | Class 'HtmlElement' has no instance method 'async'.
  | 
  | NoSuchMethodError: method not found: 'async'
  | Receiver: Instance of 'HtmlElement'
  | Arguments: [Closure: (dynamic) => dynamic]
I think this is an indication that Polymer has not finished initializing when the test runs. The async() method on Polymer elements is something of a cure-all when it comes to dealing with the asynchronous nature of Polymer elements. It updates all observed variables in the associated Polymer and tells the polyfilled platform to flush—to process all queued UI updates. Since this is failing—and failing on an HtmlElement element instead of a PolymerElement—it seems likely that I need to wait for Polymer… to be ready.

I have to confess that I am unsure how to check for Polymer to be ready in Dart. In JavaScript, it is done by listening for a polymer-ready event, so I create a scheduled_test (unittest suped up for async) schedule to listen for polymer-ready:
main() {
  PolymerElement _el;
  XPizzaComponent xPizza;

  setUp((){
    schedule((){
      var _completer = new Completer();
      document.body.on['polymer-ready'].listen((_){
        _completer.complete();
      });
      return _completer.future;
    });
    // More setup...
  });
  // Tests here...
}
That seems to work. At least it changes the error message, which I count for progress.

The error message is now the Dart VM complaining about two main() entry points:
Dartium currently only allows a single Dart script tag per application, and in the future it will run them in separtate isolates.  To prepare for this all the following script tags need to be updated to use the mime-type "application/dart;component=1" instead of "application/dart":
​
Error: 

http://localhost:8081/:-1:
Only one Dart script tag allowed per document
Taking the advice the error message, I make my test part of the web component isolate with the mime type of application/dart;component=1:
<head>
  <!-- Load Polymer -->
  <link rel="import" href="packages/polymer/polymer.html">

  <!-- Load component(s) -->
  <link rel="import" href="packages/model_example/elements/x-pizza.html">
  <script src="packages/unittest/test_controller.js"></script>
</head>
<body>
  <!-- The actual tests -->
  <script type="application/dart;component=1" src="test.dart"></script>
</body>
That still does not work. The error message is now a baffling:
Exception: Unhandled exception:
Exception: The "smoke" library has not been configured. Make sure you import and configure one of the implementations (package:smoke/mirrors.dart or package:smoke/static.dart).
#0      throwNotConfiguredError (package:smoke/src/implementation.dart:34:3)
#1      typeInspector (package:smoke/src/implementation.dart:26:5)
#2      query (package:smoke/smoke.dart:85:20)
#3      _getPublishedProperties (package:polymer/src/declaration.dart:409:31)
#4      PolymerDeclaration.publishAttributes (package:polymer/src/declaration.dart:175:39)
#5      PolymerDeclaration.buildType (package:polymer/src/declaration.dart:95:22)
#6      PolymerDeclaration.register (package:polymer/src/declaration.dart:72:14)
#7      _hookJsPolymer.registerDart. (package:polymer/src/loader.dart:100:75)
#8      _rootRun (dart:async/zone.dart:719)
#9      _ZoneDelegate.run (dart:async/zone.dart:453)
#10     _CustomizedZone.run (dart:async/zone.dart:663)
#11     _hookJsPolymer.registerDart (package:polymer/src/loader.dart:99:22)
Ya know, if every time the library changes it takes me a hour of fiddling to get a single test passing, then I really have no hope of testing the Dart version of the book. But test I must, so...

I will have to pick back up with this tomorrow.



Day #34


Sunday, April 13, 2014

No Debugging Polymer


It pains me to admit this, but most of my debugging in Polymer is done in the venerable print STDERR style. If something is not quite working, I add print / console.log statement at the last known point in the code that I know is behaving and work from there.

I have gotten a lot of mileage from this style of debugging over the years, but recently have grown fond of the Chrome (and Dart) debugger. That said, there is much event debugging in Polymer that would be simplified if I could just watch all events play out as they happen.

Which is exactly what the logging flags in Polymer would seem to do. There are also debug flags, but I am not clear what they do. I'll check that out after the logging flags.

First up, I replace the regular Polymer libraries with the development versions. In my application's bower.json, I normally depend on the regular Polymer library:
{
  "name": "mdv_example",
  // ...
  "dependencies": {
    "polymer": "Polymer/polymer"
  }
}
So instead I manually install the development versions of Polymer and its associated platform with Bower:
$ bower install polymer=Polymer/polymer-dev platform=Polymer/platform-dev
bower polymer#*                 cached git://github.com/Polymer/polymer-dev.git#0.2.2
bower polymer#*               validate 0.2.2 against git://github.com/Polymer/polymer-dev.git#*
bower platform#*                cached git://github.com/Polymer/platform-dev.git#0.2.2
bower platform#*              validate 0.2.2 against git://github.com/Polymer/platform-dev.git#*
Then I fire up my application with some logging flags. It seems like this can be done within the application, in the URL, or on the script tag that loads the platform. I opt for the latter:
<script src="bower_components/platform/platform.js" debug log="watch,bind,ready"></script>
And, when I load my page…

I see nothing. There is no change in behavior or in the console log.

Looking at the installed bower_components I find that the original Polymer and platform packages remain unchanged. The -dev versions are installed alongside, but they have not replaced the original packages as I had expected. So I force the issue by removing the original and replacing them with the -dev versions:
$ cd bower_components
$ rm -rf platform
$ mv platform-dev platform
$ rm -rf polymer
$ mv polymer-dev polymer
And, with that, it still does not work. I see no debugging at all:



And I cannot figure this out. Am I missing something obvious or did this break? Hopefully some intrepid soul can point me in the right direction. Otherwise, print STDERR may continue to be my go-to solution.


Day #33

Saturday, April 12, 2014

Binding JSON to Polymer Attributes


So how do you pass list data into a Polymer element?

I was pretty darn certain that starting with a smaller <x-pizza-toppings> element in the model driven view chapter of Patterns in Polymer would be brilliant. And then I tried using it.

Adding it the page works just fine:



But this was originally meant to be a composable, internal-only element for use in a whole pizza builder element, <x-pizza>. To ensure that all topping elements (first half, second half, whole pizza) used the same list of ingredients, I created the list in <x-pizza>:
@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  final List ingredients = [
    'pepperoni',
    'sausage',
    'green peppers'
  ];
  // ...
}
And then bound that variable to all instances of <x-pizza-toppings> in the corresponding template:
<link rel="import" href="x-pizza-toppings.html">
<polymer-element name="x-pizza">
  <template>
    <!-- ... -->
    <x-pizza-toppings id="firstHalfToppings"
                      name="First Half Toppings"
                      ingredients="{{ingredients}}"></x-pizza-toppings>
    <!-- ... -->
  </template>
  <script type="application/dart;component=1" src="x_pizza.dart"></script>
</polymer-element>
But how do you do that from the outside when you don't have a List, Array or Object? How can I bind a list of strings (the ingredients) to the ingredients attribute of <x-pizza-toppings>?

I may have found the answer by accident three nights ago. While fiddling with Polymer.dart JavaScript interoperablity, I found that the only way to make certain things work was to bind attributes as JSON instead of native programming types. Perhaps that will work here?
      <x-pizza-toppings
         ingredients="['pepperoni', 'sausage', 'green peppers']">
      </x-pizza-toppings>
And indeed that does work:



Cool! But does it work in the JavaScript version of Polymer as well? Yup:



It makes sense that Polymer would behave this way, but it is good to have confirmation. I cannot find any documentation for this behavior, but binding JSON seems a reasonable thing to do. And, since it behaves like this in both Dart and JavaScript, it seems safe to use this feature.

So, phew! I can finish off the MDV chapter rewrite without any further worries. Well, until I try the next easy thing...


Day #32

Friday, April 11, 2014

Not Just Composable, but Smaller, Composable Parts


Today, in my efforts to finish off Patterns in Polymer, I am (yet again) revisiting the approach that I took to model driven views (MDV).

Separate feedback reports have identified confusion and offered suggestions for that chapter. I have struggled with exactly how to go about addressing the issue, but I think that I have finally got it. Part, if not all, of the problem was that the element that I am currently using to drive the narrative in the chapter is too big. The element is the <x-pizza> Polymer element that I go on to use in an SVG chapter and the Strategy Pattern chapter on which I am currently working.

In the MDV chapter, the template alone is nearly 50 lines of HTML templating that starts with:
<polymer-element name="x-pizza">
  <template>
    <h2>Build Your Pizza</h2>
    <pre>{{pizzaState}}</pre>
    <p>
      <select class="form-control" value="{{currentFirstHalf}}">
        <option>Choose an ingredient...</option>
        <option value="{{ingredient}}" template repeat="{{ingredient in ingredients}}">
          {{ingredient}}
        </option>
      </select>
      <button on-click="{{addFirstHalf}}" type="button" class="btn btn-default">
        Add First Half Topping
      </button>
    </p>
    <!-- ... -->
  </template>
  <script type="application/dart" src="x_pizza.dart"></script>
</polymer-element>
(the JavaScript template is identical aside from the <script> tag being used.

At least one reader suggested breaking the thing up into a main <x-pizza> element and smaller <x-pizza-topping> elements, that latter holding the actual model describing the currently selected pizza toppings. As the reviewer correctly put it, this “would be more inline with the polymer ideal of small, composable elements.” Recently, I have done just that, which led to the Strategy Pattern.

But the question still remains, how do I introduce MDV without getting into a discussion of composition in the Polymer work in addition to MDV? The answer is so simple that I don't know why it didn't occur to me before: I will introduce only the <x-pizza-toppings> element in the MDV chapter. I can then compose it into the <x-pizza> element in the Strategy chapter.

The only other outstanding item on that chapter has to do with making classes and properties observable. A reader found that this did not work. I suspect that this was due to not importing the Polymer package (which I neglected to mention in the book). Given the re-organization that I have already done, I will almost certainly not wind up doing this, but it seems a good thing to know. In the Dart version of this element, I had been keeping the state in an actual object with a class and everything:
import 'package:polymer/polymer.dart';

@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // Polymer stuff that uses a Pizza model
}

@observable
class Pizza {
  List<String> firstHalfToppings = toObservable([]);
  List<String> secondHalfToppings = toObservable([]);
  List<String> wholeToppings = toObservable([]);
}
Before ripping this out and starting from scratch, I give it a try with the most recent version of Polymer.dart. I still works, though I get a warning:
[Warning from Observable on model_example|lib/elements/x_pizza.dart]:
package:model_example/elements/x_pizza.dart:61:1: @observable on a class no longer has any effect. It should be placed on individual fields.
And, indeed that works fine. The individual toppings properties are already observable by virtue of the toObservable() function wrapper. I remove the @observable annotation from the Pizza class:
class Pizza {
  List<String> firstHalfToppings = toObservable([]);
  List<String> secondHalfToppings = toObservable([]);
  List<String> wholeToppings = toObservable([]);
}
With that, I have no warnings and the example still works:



As I said, that is good to know, but I will now remove that class entirely, opting instead to use a simple list as my “model”:
@CustomTag('x-pizza-toppings')
class XPizzaToppings extends PolymerElement {
  // ...
  List<String> model = [];
  // ...
}
That is much easier to introduce, but still should serve to illustrate MDV nicely.




Day #31