Wednesday, April 30, 2014

Polymer Elements as Text Fixtures


Up today, I test angular-bind-polymer.

As much fun as it has been mucking around with dynamically building Polymer definitions, I think my timebox around the exploration has expired. Off the top of my head, I cannot even say why I decided a test was needed in the first place (who needs a memory when you blog every day — it was because I think I can significantly simplify the code).

The angular-bind-polymer library enables double data binding between AngularJS attributes and Polymer attributes. Pushing changes down from AngularJS into a Polymer works out of the box. Listening for changes from Polymer so that bound variables in the current Angular scope does not work (at least it did not a while back). Hence the need the angular-bind-polymer directive.

To test my directive, I need a generic custom element, which is what started me on my little adventure to generate dynamic Polymer definitions. I had great fun along the way, but it is time to get the test written as best I can so that I can move on. Which, of course, begs the question of what is best practice when you need to work with a generic Polymer in tests?

I think I probably came close late last week when I wrote a simple definition in a test fixture file and loaded it as part of the Karma / Jasmine setup:
var script = document.createElement("script");
script.src = "/base/bower_components/platform/platform.js";
document.getElementsByTagName("head")[0].appendChild(script);

var link = document.createElement("link");
link.rel = "import";
link.href = "/base/test/x-double.html";
document.getElementsByTagName("head")[0].appendChild(link);
// ...
With the definition loaded in the Karma setup, I can then test pure custom elements by building them in test setup (aka beforeEach):
  var container, customElement;
  beforeEach(function(){
    container = document.createElement('div');
    document.body.appendChild(container);

    container.innerHTML = '';
    customElement = container.children[0];

    var done = false;
    setTimeout(function(){ done = true; }, 0);
    waitsFor(function(){ return done; });
  });
Creating the container <div> and the <x-double> custom element are simple DOM manipulations—as is adding them to the document body. I declare the container and customElement outside the beforeEach() function scope so that I can remove them after each test and so that I can set actual test expectations on the customElement. The only real trick in there is the waitsFor() which I figured out last week. It ensures that the Polymer library has had a chance to decorate <x-double> with all of the methods that are described in the Polymer definition.

Setting expectations in tests is then simple:
  it('works', function(){
    expect(customElement.getAttribute('out')).toEqual('84');
  });
Now to get the Angular directive test setup properly. Tomorrow.



Day #50

Tuesday, April 29, 2014

Modifying Dynamically Defined Polymer Elements (I'm stopping here)


The question before me today is, is it possible to dynamically define (or at least update) a Polymer definitions inside beforeEach() blocks of Karma / Jasmine tests?

And I think the answer is going to be... ugh.

Digging through the custom element registration process, with an assist from Addy Osmani, I eventually discover CustomElements, which is responsible for decorating custom elements with custom element code definitions like those in Polymer. Most of that currently defers to native code in Chrome, making it a little tough to trace through. But there is a flag for CustomElements that tells it to use the polyfilled definition:
<script>
window.CustomElements = {flags: {register: true}};
</script>
With that, I can trace through the custom element registration and assignment process, but to little avail. My dynamically created Polymer still works:
var double_el = document.createElement('polymer-element');
double_el.setAttribute('name', 'x-double');
double_el.setAttribute('attributes', 'in out');

var script = document.createElement('script');
script.innerHTML = 'console.log("yo");\n'
                  + '    Polymer("x-double", {\n'
                  + '      publish: {\n'
                  + '        out: {value: 0, reflect: true}\n'
                  + '      },\n'
                  + '      ready: function(){\n'
                  + '        console.log("yo!!!!");\n'
                  + '        this.inChanged();\n'
                  + '      },\n'
                  + '      inChanged: function(){\n'
                  + '        this.out = parseInt(this.in) * 2;\n'
                  + '      }\n'
                  + '    });';
double_el.appendChild(script);

document.getElementsByTagName("head")[0].appendChild(double_el);
And I can see it on the page, in the Polymer Platform, and in the registered CustomElements list.
But I if there is a way to assign that definition to elements on the page, I cannot find it. This works just fine when Polymer elements are defined normally, but my elements in the page remain HTMLUknownElements. The upgrade methods in CustomElements seem the most promising, but do not actually apply the Polymer methods to my custom elements:
> el = document.querySelector('x-double')
  => <x-double in=​"6"><​
> x = Polymer.getRegisteredPrototype('x-double')
  => Object {publish: Object, ready: function, inChanged: function}
> CustomElements.upgrade(el)
  => undefined
> el.$
  => undefined
> el.__upgraded__
  => undefined
Much of the CustomElements code is hidden underneath a closure, so for all I know, the method that I need to decorate my dynamically defined custom elements is buried under there. So I take a different tack.

I already know how to dynamically define Polymer custom elements before all tests—I just add the definition as soon as the Polymer platform has loaded. So if I can do that, then modify the prototype inside my beforeEach() blocks, I may have a “good enough” solution.

And that turns out to be fairly easy to implement. I wind up grabbing the Polymer element's prototype from the CustomElements registry:
beforeEach(function(){
    var x = CustomElements.registry['x-double'];
    x.inChanged = function() {
      this.out = parseInt(this.in) * 2;      
    };
});
With that, I get my element's desired behavior.

It would have been nice to figure out how to apply new Polymer behavior to elements, but I have to admit that I probably will not need to do that in practice—updating the prototype ought to be sufficient. At the risk of going full-on “sour grapes,” this also has the benefit of being easier to redefine the Polymer behavior across different tests. Re-registering Polymer definitions is generally an error, so this should help.

I am done with this avenue of exploration. I am calling a halt to it. I 100% will not try again.

Unless someone has a suggestion?


Day #49

Monday, April 28, 2014

Dynamic Polymer Definition BeforeEach


I swore I wouldn't do this again…

I am now able to dynamically define a Polymer custom element (the actual definition, not just using one) on a regular page and in the setup for my Karma / Jasmine test suite. But… can I dynamically define an element before each test, not just before the entire test suite runs?

After last night, I have this working before all tests thanks to a cleverly placed onload:
var script = document.createElement("script");
script.src = "/base/bower_components/platform/platform.js";
script.onload = defineTestPolymerElements;
document.getElementsByTagName("head")[0].appendChild(script);

var link = document.createElement("link");
link.rel = 'import';
link.href = "/base/bower_components/polymer/polymer.html";
document.getElementsByTagName("head")[0].appendChild(link);
The defineTestPolymerElements() function assembles the Polymer definition from a judicious combination of document.createElement() and innerHTML. What that does is, as soon as the Polymer's polyfill platform loads, adds the dynamically generated <polymer-element> definition to the page. That would occur before the Polymer library itself is loaded because that will not happen until the platform library loads, extending the normal <link> tag to also import custom HTML.

But what happens if I remove the onload and add the definition to the page after the Polymer library loads? To find out, I comment out the script.onload assignment from the “before all” setup. Then, inside the describe() block of my test, I add the beforeEach() to do the same dynamic Polymer element definition:
describe('Double binding', function(){
  // Other setup...
  beforeEach(defineTestPolymerElements);
  // Tests go here...
});
When I run that, my test fails:
Expected null to equal '84'.
Error: Expected null to equal '84'.
    at new jasmine.ExpectationResult 
The Polymer definition is created in the DOM as desired:



Console log statements added inside the definition's <script> tag are even evaluated. But the <x-double> element that ought to be defined is still seen as an HTMLUndefinedElement.

After digging through the Polymer source and the associated platform code, I have to confess that I am stumped. The registration process of Polymer elements seems to work through a queue, but if I check that queue under debug, it seems as though the element is no longer in the queue. I would think that meant that the element was defined, but, obviously I am not seeing that.

I am curious enough about this that I will likely give it one more try tomorrow, but after that, I need to cut my losses and move on.


Day #48

Sunday, April 27, 2014

Dynamically Generating Polymer Definitions in Karma (Jasmine)


I now know the trick for dynamically generating Polymer definitions. I just don't know how to get that trick to work in Karma / Jasmine tests.

As I found last night, the trick to dynamically generate Polymer elements in regular web pages is to build the <script> portions of the definition via document.createElement() rather than building the <script> tag via innerHTML assignments. Easy enough, I can live with that.

But when I tried to do the same think in my Jasmine tests, I kept getting an error about Polymer not being defined:
Uncaught ReferenceError: Polymer is not defined
Bleary eyed, I was unable to figure out why this would be the case. With fresh eyes today, I realize that sample page and my tests are not quite the same. In my sample page, I have hard-coded the <script> tag for the Polymer platform:
<!doctype html>
<html>
  <head>
    <script src="bower_components/platform/platform.js"></script>
    <link rel="import" href="../bower_components/polymer/polymer.html">

    <script>
      var double_el = document.createElement('polymer-element');
      double_el.setAttribute('name', 'x-double');
      double_el.setAttribute('attributes', 'in out');
      
      var script = document.createElement('script');
      script.innerHTML = '    Polymer("x-double", { /* ... */ });';
      double_el.appendChild(script);
      document.getElementsByTagName("head")[0].appendChild(double_el);
    </script>
  </head>
  <body>
     <!-- ... --> 
     <x-double in="6"></x-double>
  </body>
</html>
In my tests, I have been dynamically adding those during test setup:
var script = document.createElement("script");
script.src = "/base/bower_components/platform/platform.js";
document.getElementsByTagName("head")[0].appendChild(script);

var link = document.createElement("link");
link.rel = 'import';
link.href = "/base/bower_components/polymer/polymer.html";
document.getElementsByTagName("head")[0].appendChild(link);
And indeed, if switch my sample page to use those dynamically built <script> and <link> tags, then I see the same failure in my browser.

I cannot hard-code those tags in Karma tests -- Karma decides which tags get added first and how. What ends up working is to assign an onload function to the Polymer platform's <script> tag:
var script = document.createElement("script");
script.src = "/base/bower_components/platform/platform.js";
script.onload = defineTestPolymerElements;
document.getElementsByTagName("head")[0].appendChild(script);

var link = document.createElement("link");
link.rel = 'import';
link.href = "/base/bower_components/polymer/polymer.html";
document.getElementsByTagName("head")[0].appendChild(link);
With that, my full test setup for a dynamically defined Polymer element looks like:
var script = document.createElement("script");
script.src = "/base/bower_components/platform/platform.js";
script.onload = defineTestPolymerElements;
document.getElementsByTagName("head")[0].appendChild(script);

var link = document.createElement("link");
link.rel = 'import';
link.href = "/base/bower_components/polymer/polymer.html";
document.getElementsByTagName("head")[0].appendChild(link);

function defineTestPolymerElements() {
  var double_el = document.createElement('polymer-element');
  double_el.setAttribute('name', 'x-double');
  double_el.setAttribute('attributes', 'in out');

  var script = document.createElement('script');
  script.innerHTML = '    Polymer("x-double", {\n'
                   + '      publish: {\n'
                   + '        out: {value: 0, reflect: true}\n'
                   + '      },\n'
                   + '      ready: function(){\n'
                   + '        this.inChanged();\n'
                   + '      },\n'
                   + '      inChanged: function(){\n'
                   + '        this.out = parseInt(this.in) * 2;\n'
                   + '      }\n'
                   + '    });\n';
  double_el.appendChild(script);
  document.getElementsByTagName("head")[0].appendChild(double_el);
}

// Delay Jasmine specs until polymer-ready
var POLYMER_READY = false;
beforeEach(function(done) {
  window.addEventListener('polymer-ready', function(){
    POLYMER_READY = true;
  });
  waitsFor(function(){return POLYMER_READY;});
});
With that, I think I have finally answered all of the little questions that occurred to me while trying to test code that interacts with any Polymer elements. I did not strictly need to answer these questions to get my task done—testing AngularJS interaction in angular-bind-polymer. Even so, it is good to have a better understanding of all of this. Maybe tomorrow I can actually write the angular-bind-polymer test that I started on 7 days ago!



Day #47

Saturday, April 26, 2014

Dynamically Generating Polymer Definitions (for real)


I remain unable to dynamically add Polymer definitions to my Karma / Jasmine tests. So the question tonight is, does it work in regular pages?

I start in a test page. Rather than jumping right into dynamically defining a Polymer element, I try hard coding the Polymer definition directly in the test page's HTML first. Instead of <link> importing the Polymer definition, I add it in the <head> of my page:
<!doctype html>
<html>
  <head>
    <!-- 1. Load Polymer before any code that touches the DOM. -->
    <script src="bower_components/platform/platform.js"></script>
    <!-- 2. Load component(s) -->
    <!-- <link rel="import" href="test/x-double.html"> -->
    <link rel="import" href="../bower_components/polymer/polymer.html">
    <polymer-element name="x-double" attributes="in out">
      <script>
        Polymer("x-double", { /* ... */ });
      </script>
    </polymer-element>
  </head>
  <body>
    <x-double in="6"></x-double>
  </body>
  <script>
    var container = document.createElement('div');
    container.innerHTML = '<x-double in=42></x-double>';
    document.body.appendChild(container);
  </script>
</html>
That works right away without any problems. The <x-double> elements—both the hard-coded one and the <script> generated one—behave like an <x-double> element should (the out attribute is double the in attribute).

If that works just fine, then perhaps I can generate that same code dynamically, add it to the <head> element, and have it work? The answer, it would seems, is no. I verify that the following does generate the exact same <polymer-element> as the hard-coded value:
<script>
var double_el = document.createElement('polymer-element');
double_el.setAttribute('name', 'x-double');
double_el.setAttribute('attributes', 'in out');
double_el.innerHTML = '  <script>\n'
                  + '    Polymer("x-double", { /* ... */ });\n'
                  + '  <\/script>'
document.getElementsByTagName("head")[0].appendChild(double_el);
</script>
Unfortunately, it is not evaluated. If I add a console.log() statement to the Polymer element's ready() lifecycle method, it is not called. All of the <x-double> elements on the page behaves like HTMLUndefinedElement elements—as if Polymer has not been applied.

After a bit more digging, I finally realize that nothing inside the dynamically generated <polymer-element>'s <script> tag is being evaluated. If I revert to the hard-code version of the <polymer-element>, then leading console.log() statements are shown in the JavaScript console:
    <polymer-element name="x-double" attributes="in out">
      <script>
        console.log('yo');
        Polymer("x-double", { /* ... */ });
      </script>
    </polymer-element>
So it seems that my dynamically generated <script> is not being evaluated as a real <script> tag. This turns out to be due to my including the <script> tags inside the string that I build for the double_el. If I switch to document.createElement('script'):
<script>
var double_el = document.createElement('polymer-element');
double_el.setAttribute('name', 'x-double');
double_el.setAttribute('attributes', 'in out');

var script = document.createElement('script');
script.innerHTML = '    Polymer("x-double", { /* ... /* });';
double_el.appendChild(script);

document.getElementsByTagName("head")[0].appendChild(double_el);
</script>
The everything starts working! Any console.log() statements in there (either before the Polymer definition or inside the ready() callback) are now seen.

This still does not quite solve all of my Karma / Jasmine problems, but it narrows the remaining scope. So, hopefully, I will have a working solution tomorrow.


Day #46

Friday, April 25, 2014

Dynamically Generating Polymer Definitions (not working)


In my ongoing efforts to test angular-bind-polymer, I would like to define test Polymer definition right inside the tests. Currently, I have to define the template and class definition in separate files, then import them into the Karma Jasmine test setup:
var script = document.createElement("script");
script.src = "/base/bower_components/platform/platform.js";
document.getElementsByTagName("head")[0].appendChild(script);

var link = document.createElement("link");
link.rel = "import";
link.href = "/base/test/x-double.html";
document.getElementsByTagName("head")[0].appendChild(link);
// ...
As limitations go, this is hardly severe—heck it may even be appropriate in this case. Still, I would like to know that I can generate custom Polymer element definitions on the fly if I need them.

When I first tried this, it failed badly. Or at least it seemed to. I say “seemed to” because I tried to do too much at the time. I tried to do this, dynamically add the custom element to the page, and test things—all in one night. It went badly. Now that I have a handle on testing and dynamically adding custom elements to the DOM, perhaps dynamically defining element will just work?

To answer that question, I start SMALL. Despite the awful pain of the other night, my first instinct remains to just copy the test Polymer element in its entirety into JavaScript strings that can be appended to the test DOM. I resist that first instinct and instead start by commenting out the link to the Polymer library from the Polymer definition:
<!-- <link rel="import" href="../bower_components/polymer/polymer.html"> -->
<polymer-element name="x-double" attributes="in out">
  <template></template>
  <script>
    Polymer("x-double", { /* ... */ });
  </script>
</polymer-element>
With that, my test fails:
Chrome 34.0.1847 (Linux) Double binding works FAILED
        timeout: timed out after 5000 msec waiting for something to happen
I can restore it to green by dynamically linking to the Polymer library after doing the same for the platform in the test setup:
var script = document.createElement("script");
script.src = "/base/bower_components/platform/platform.js";
document.getElementsByTagName("head")[0].appendChild(script);

var link = document.createElement("link");
link.rel = "import";
link.href = "/base/test/x-double.html";
document.getElementsByTagName("head")[0].appendChild(link);

var link = document.createElement("link");
link.rel = 'import';
link.href = "/base/bower_components/polymer/polymer.html";
document.getElementsByTagName("head")[0].appendChild(link);
That is some progress over the night before, but I still need to dynamically define the element and backing class. I try:
var double_el = document.createElement('polymer-element');
double_el.setAttribute('name', 'x-double');
double_el.setAttribute('attributes', 'in out');
double_el.innerHTML = '  <template></template>\n<script>\n'
                  + '    Polymer("x-double", { /* ... */  });\n'
                  + '  </script>\n';

document.body.appendChild(double_el);
Sadly, that does not work. I am not getting any errors, the test just fails as if the <x-double> element is undefined:
Expected null to equal '84'.
Error: Expected null to equal '84'.
    at new jasmine.ExpectationResult (http://localhost:9876/absolute/home/chris/local/node-v0.10.20/lib/node_modules/karma-jasmine/lib/jasmine.js:114:32)
Unfortunately, this has me stumped for tonight.

I try adding the dynamically generated <polymer-element> definition at the top the document <body>. I try adding it to the document <head>. I even try adding the definition after the polymer-ready event. But my test always fails as if my definition is completely ignored.

So it would seem that there is some magic necessary to get Polymer elements that are dynamically added to the document. I am happy to have made some progress tonight with the dynamic import of the Polymer library. Hopefully tomorrow I can figure out how to get the dynamic Polymer element definition working—either by reading other tests or the Polymer code itself.




Day #45

Thursday, April 24, 2014

Dynamically Generating Sloppy Custom Elements


Up tonight, I would like to see if I can add Polymer elements to the DOM with innerHTML.

This is still part of an effort to write a Karma / Jasmine test for the angular-bind-polymer directive that enables angular elements to bind to Polymer attributes. I actually think that I know enough to write that test at this point, but my adventures have raised questions along the way that are just as important as the end goal of testing that directive.

After last night, I can dynamically add a Polymer to the DOM and have it behave as a Polymer custom element. I can do so as part of a simple test to verify that the Polymer element works:
  it('works', function(){
    var container = document.createElement('div');
    var el = document.createElement('x-double');
    el.setAttribute('in', '42');
    container.appendChild(el);
    document.body.appendChild(container);
    //  Async wait not shown.
    runs(function(){
      expect(el.getAttribute('out')).toEqual('84');
    });
  });
That works fine thanks to document.createElement(). But that is not an entirely satisfactory test because there is a good amount of noise associated with creating the element, setting the attribute and adding the element to the DOM. I would rather use innerHTML:
  it('works', function(){
    var container = document.createElement('div');
    container.innerHTML = '<x-double in=42></x-double>';
    document.body.appendChild(container);
    var el = container.children[0];

    runs(function(){
      expect(el.getAttribute('out')).toEqual('84');
    });
  });
I think that is a little more explicit about what I am testing. But unfortunately, it does not work:
Expected null to equal '84'.
        Error: Expected null to equal '84'.
            at null.<anonymous> (/home/chris/repos/angular-bind-polymer/test/BindingSpec.js:25:38)
This really seems like something that, while not ideal DOM coding (if there is such a thing), ought to work. Over the course of trying to get my Angular directive test working, I had originally tried this (including various setTimeout() statements), but to not avail.

First, I try this is a regular web page:
<!doctype html>
<html>
  <head>
    <!-- 1. Load Polymer before any code that touches the DOM. -->
    <script src="bower_components/platform/platform.js"></script>
    <!-- 2. Load component(s) -->
    <link rel="import" href="test/x-double.html">
  </head>
  <body><!-- ... --></body>
  <script>
    var container = document.createElement('div');
    container.innerHTML = '<x-double in=42></x-double>';
    document.body.appendChild(container);
  </script>
</html>
That works fine. The element shows up, along with the out attribute that correctly doubles the in attribute:



So what gives with using it in my tests? It really seems like giving the Polymer library some time to process the new HTML ought to work:
  it('works', function(){
    var container = document.createElement('div');
    document.body.appendChild(container);

    container.innerHTML = '<x-double in=42></x-double>';
    var el = container.children[0];

    // Wait for one browser event loop so Polymer can do its thing...
    var done = false;
    waitsFor(function(){ return done; });
    setTimeout(function(){ done = true; }, 0);

    runs(function(){
      expect(el.getAttribute('out')).toEqual('84');
    });
  });
And, in fact… it does work:
➜  angular-bind-polymer git:(master) ✗ karma start --single-run                   
INFO [karma]: Karma v0.12.4 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
.
Chrome 34.0.1847 (Linux): Executed 1 of 1 SUCCESS (0.333 secs / 0.328 secs)
D'oh!

I have no good explanation for why I could not make that work earlier. The best I can figure is that I was (as usual) trying to bite of more than I could chew with my initial tests. So many things were going wrong that I wound up mistaking another cause for my inability to get an innerHTML test to work. But work it does and just as expected. I only need to wait for one browser event loop so that Polymer can process the new element. After that, things just work.


Day #33

Wednesday, April 23, 2014

The Simplest Explanations (Polymer Testing)


I feel like my grip on reality is slightly less than it was yesterday. I can make a Polymer pass, but I have no idea why this makes it pass.

I have a very simply <x-double> Polymer element that takes the input of one attribute and doubles it on another attribute:
<polymer-element name="x-double" attributes="in out">
  <template></template>
  <script>
    Polymer("x-double", {
      ready: function(){
        this.inChanged();
      },
      inChanged: function(){
        this.out = parseInt(this.in) * 2;
      }
    });
  </script>
</polymer-element>
I have a correspondingly simple Karma / Jasmine test that tries to verify the element behaves as expected:
  it('works', function(){
    // Container element
    var container = document.createElement('div');

    // Polymer element for testing
    var el = document.createElement('x-double');
    el.setAttribute('out', '');
    el.setAttribute('in', '42');
    container.appendChild(el);
    document.body.appendChild(container);

    // Wait for the Polymer element to update itself...
    var done = false;
    waitsFor(function(){ return done; });
    el.async(function(){ done = true; });

    // Check expectations...
    runs(function(){
      expect(el.getAttribute('out')).toEqual('84');
    });
  });
Well, maybe not that simple, but it ought to work based on all of the research that I have done for Patterns in Polymer. So, naturally, the test fails:
Expected '' to equal '84'.
        Error: Expected '' to equal '84'.
            at null.<anonymous> (/home/chris/repos/angular-bind-polymer/test/BindingSpec.js:20:38)
As I mentioned, I can make this pass, but only if I add a hack that I do not understand. I change the inChanged() watcher method to set the out attribute in addition to setting the out property:
      inChanged: function(){
        this.out = parseInt(this.in) * 2;
        this.setAttribute('out', parseInt(this.in) * 2);
      }
But why is that necessary? Setting the property should set the attribute. Or am I crazy? I go back to read the documentation and, yes, “property values are reflected back into their attribute counterpart.” So what absolutely stupid thing am I doing tonight to prevent that from happening?

Well, it turns out that what I am doing was not absolutely stupid, but it was certainly a little stupid. I failed to read the mailing list which including a message about a breaking change to—you guessed it—attribute binding. It seems that binding like this, is not a typical use-case. It is still supported, but it is opt-in now. I can live with that (though it would be nice if the docs were updated).

Anyhow, I remove the explicit setting of the attribute, and instead opt-in for property reflection. To do so, I add reflect: true to the published definition for my element:
<polymer-element name="x-double" attributes="in out">
  <template></template>
  <script>
    Polymer("x-double", {
      publish: {
        out: {value: 0, reflect: true}
      },
      ready: function(){
        this.inChanged();
      },
      inChanged: function(){
        this.out = parseInt(this.in) * 2;
      }
    });
  </script>
</polymer-element>
With that, I have my test passing. And a little of my sanity restored.



Day #43

Tuesday, April 22, 2014

A Woeful Approach to Dynamically Defining Polymer Elements


Some posts really get away from me. Like last night's fiasco.

Invariably, the posts that get away from me suffer from the same problem: me trying to do too much at once. Last night I tried setting up a Karma test suite from scratch, to test an AngularJS directive with a Polymer web component. Individually, I can do any of those things relatively quickly (as long as I remember that Angular tests need angular-mock, sheesh). Put them together, however, and 2 or 3 three things that don't quite work might as well be 20.

So tonight, I do what I really ought to know by now: bite off smaller chunks. Even though I have my Angular tests running again, there seems to be something not quite right with my Polymer tests. Thanks to my work in Patterns in Polymer, I have a fairly reliable Karma, Jasmine, and Polymer setup, so this annoys me. To eliminate this annoyance, I focus solely on it.

I comment out the various setup blocks from my tests that initialize Angular and the stuff that gets injected to work with directives. I update my karma.conf.js so that only the tests and Polymer setup are loaded on the test page:
    // ...
    // list of files / patterns to load in the browser
    files: [
      'test/PolymerSetup.js',
      // 'bower_components/angular/angular.js',
      // 'bower_components/angular*/angular-*.js',
      // 'angular_bind_polymer.js',
      {pattern: 'bower_components/**', included: false, served: true},
      'test/*Spec.js'
    ],
    // ...
I will pull back in the Angular stuff once I have Polymer sorted out. As for Polymer, my setup remains as it had been in Patterns in Polymer. I dynamically add a <script> tag to load in the polymer platform and then wait for the polymer-ready event before proceeding with tests in the test/*Spec.js files:
script.src = "/base/bower_components/platform/platform.js";
document.getElementsByTagName("head")[0].appendChild(script);

// Delay Jasmine specs until polymer-ready
var POLYMER_READY = false;
beforeEach(function(done) {
  window.addEventListener('polymer-ready', function(){
    POLYMER_READY = true;
  });
  waitsFor(function(){return POLYMER_READY;});
});
I assume that works since I copied and pasted it from book tests that still pass. What I am unsure about is how to add dynamic Polymer element definitions to the page. When testing real polymer elements, I can dynamically create the <link> import in PolymerSetup (after the platform is initialized):
var link = document.createElement("link");
link.rel = "import";
link.href = "/base/elements/x-pizza.html";
document.getElementsByTagName("head")[0].appendChild(link);
I do not have a real element in this case. I just need a dummy Polymer element against which I can run my data-binding test. So instead of putting those files in a separate Polymer definition file and dynamically creating the <link> import element, I try to dynamically add the definition right to the page:
var double_el = document.createElement('polymer-element');
double_el.setAttribute('name', 'x-double');
double_el.setAttribute('attributes', 'in out');
double_el.innerHTML = '<script>\n'
                       + 'Polymer("x-double", {\n'
                       + '  in: 13,\n'
                       + '  ready: function(){ '
                       + '    console.log("+++++")'
                       + '    this.inChanged();'
                       + '    console.log("+++++")'
                       + '  },\n'
                       + '  inChanged: function(){\n'
                       + '    console.log("u0000000");\n'
                       + '    this.out = parseInt(this.in) * 2;\n'
                       + '  }\n'
                       + '});\n'
                       + '</script>\n';
document.body.appendChild(double_el);
For good measure, I also pull in the Polymer library itself (only the platform was loaded previously):
var link = document.createElement("link");
link.rel = 'import';
link.href = "/base/bower_components/polymer/polymer.html";
document.getElementsByTagName("head")[0].appendChild(link);
With that, I get… nothing. No console statements printed and no errors.

Still, that tells me something. Since there are no errors, the call to Polymer inside the <script> tags is working. The element definition even shows up in the DOM inspector:



Rather than try to solve multiple issues tonight, I really am just going to do the simplest thing that should work: moving the Polymer element definition out into a separate file. First, I will serve this fixture definition in Karma (but not load it by default):
    // ...
    // list of files / patterns to load in the browser
    files: [
      'test/PolymerSetup.js',
      // 'bower_components/angular/angular.js',
      // 'bower_components/angular*/angular-*.js',
      // 'angular_bind_polymer.js',
      {pattern: 'bower_components/**', included: false, served: true},
      {pattern: 'test/*.html', included: false, served: true},
      'test/*Spec.js'
    ],
    // ...
Then, I create my dummy Polymer element as test/x-double.html:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="x-double" attributes="in out">
  <script>
    Polymer("x-double", {
      in: 13,
      ready: function(){
        console.log("+++++")
        this.inChanged();
        console.log("+++++")
      },
      inChanged: function(){
        console.log("u0000000");
        this.out = parseInt(this.in) * 2;
      }
    });
  </script>
</polymer-element>
And that work just fine. All of those debug console statements pass. My test checking the doubling passed. Everything.

So it seems that something about the way that I am dynamically adding the element definition to my test page is wrong. Armed with the knowledge that the Polymer definition itself is sound, I am ready to try the next small steps.

Tomorrow.


Day #42

Monday, April 21, 2014

Failing to Test Angular + Polymer


Gah! Finally got the 1.1 edition of Patterns in Polymer out the door and there is still much to do on it. Most of that will have go on the back burner until I return from FITC next week, but I must keep the chain unbroken. So...

Tonight, I try to write a test for angular-bind-polymer, which is all the more relevant after discovering that much of the overhead with which I had burdened that AngularJS directive is no longer needed. Since this is an Angular directive test, Karma is the way to go for testing.

I run through karma init accepting defaults, save for file loading “globs” that make my code in the top-level of the repository and the tests in the test subdirectory:
What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> "*.js"
> test/*Spec.js
> 
In order to test that this Angular directive works with Polymer, I will have to install Polymer as a development dependency. So I modify the Bower package bower.json configuration file, so that Polymer is listed in devDependencies:
{
  "name": "angular-bind-polymer",
  "version": "0.0.2",
  // ...
  "dependencies": {
    "angular": "~1.2.16"
  },
  "devDependencies": {
    "polymer": "Polymer/polymer"
  }
}
After a quick bower update, I am ready to go.

Except not quite. I need to add a few more dependencies:

    // ...
    files: [
      'bower_components/angular/angular.js',
      'bower_components/platform/platform.js',
      'angular_bind_polymer.js',
      {pattern: 'bower_components/**', included: false, served: true},
      'test/*Spec.js'
    ],
    // ...
And even then I am not seeing any of the angualar functions. Bother.

I eventually track this down to the lack of angular-mocks. I never remember that this is an inviolate requirement for testing AngularJS code—I see the name “mock” and assume that it is just for mocks and spies. Ah well, I update bower.json again:
{
  "name": "angular-bind-polymer",
  "version": "0.0.2",
  // ...
  "dependencies": {
    "angular": "~1.2.16"
  },
  "devDependencies": {
    "polymer": "Polymer/polymer",
    "angular-mocks": "~1.2.16"
  }
}
With that, I have my tests running… but failing. I am unsure if this is due to Polymer load order, Angular load order, or something else. But that seems like a good puzzle for another day.




Day #41

Sunday, April 20, 2014

Watching Polymer Attributes Is Now a Whole Lot Easier



With the 1.1 edition of Patterns in Polymer due in but a few hours, I do not have much time for blog posts, but the gods of my chain must have their daily sacrifice.

So I try out my AngularJS application that uses Polymer web components. I would like to see if, as I found last night, some of the infrastructure that I have created around it is really still necessary.

Of course the application has to still work for that investigation. Naturally, it does not. After upgrading to the latest Polymer and AngularJS versions (0.2.2 and 1.2.16, respectively, at the time of this writing), I get a big ol' injector error:
Error {stack: "Error: [$injector:modulerr] Failed to instantiate …:8000/bower_components/angular/angular.js:21459:5", message: "[$injector:modulerr] Failed to instantiate module …er_components%2Fangular%2Fangular.js%3A1379%3A20)"}
 "Error: [$injector:modulerr] Failed to instantiate module pizzaStoreApp due to:
Error: [$injector:nomod] Module 'pizzaStoreApp' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
http://errors.angularjs.org/1.2.16/$injector/nomod?p0=pizzaStoreApp
    at http://localhost:8000/bower_components/angular/angular.js:78:12
    at http://localhost:8000/bower_components/angular/angular.js:1611:17
    ...
Almost lost in there is the root cause, which is that the pizzaStoreApp module is not available for injection. It is required by the root element of my single page application's HTML:
<!doctype html>
<html ng-app="pizzaStoreApp">
  <!- ... -->
</html>
This is one of those things where I have to wonder how it ever worked. My module is, in fact, named PizzaStoreApp, not pizzaStoreApp:
var PizzaStoreApp = angular.module('PizzaStoreApp', [
  'ngRoute',
  'eee-c.angularBindPolymer'
]);
Mercifully, that is an easy enough fix. After making the first letter in the module's name lowercase, everything is working fine. Everything including my angular-bind-polymer directive, which still ensures two-way binding of Angular and Polymer attributes so that Angular can see the current value of the <x-pizza>'s internal pizza state:



That is pretty nice to see. But what about all of the ceremony through which I went to ensure that Polymer was loaded and applied before processing my Angular directive?

Well, it seems that it is no longer necessary. I comment out my onPolymerReady() function whose returned promise completes when the observe property is seen:
      // When Polymer is ready, establish the bound variable watch
      // onPolymerReady().
      //   then(function(){
          // When Polymer sees a change to the bound variable,
          // $apply / $digest the changes here in Angular
          var observer = new MutationObserver(function() {
            scope.$apply();
          });
          observer.observe(polymer(), {attributes: true});

          for (var _attr in attrMap) {
            scope.$watch(
              function() {return element.attr(_attr);},
              function(value) {
                scope[attrMap[_attr]] = value;
              }
            );
          }
        // });
I am not even listening for a polymer-ready event here, but running the mutation observer code immediately upon evaluating the directive. And it works just fine. Angular-bind-polymer is still able to see the Polymer change and set the corresponding property in Angular.

That is very exciting.

I am a little skeptical that it works, but I try moving the various angular and polymer script/template load order around in the containing document—and the bound variables continue to update. I even try this in Firefox and Internet Explorer—and it still works.

I am unsure when this got fixed—or even if the fix was in Polymer or Angular. It matters only if there is a chance that the condition might return, but that is what tests are for—and it is easier to test that something continues to work than for a broken thing to start working. So yay!

Now back to editing...


Day #40

Saturday, April 19, 2014

Polymer Ready for Outside Changes


What are we doing tonight Brain? The same thing we do every night, Pinky—trying to see if something that didn't work two months back in Polymer works now.

This is proving to be a rough time to release a book on Polymer. My self-imposed deadline for edition 1.1 is tomorrow, but it's looking more and more like a 1.2 may need to follow sooner rather than later. Still, I have a deadline so I need to do what I can to get the book in the best shape possible.

Tonight the thing that didn't work a while back was establishing attribute observers on Polymer elements—from outside the Polymer. I first ran into this issue while trying to get Angular and Polymer attributes playing nicely. Pushing changes from Angular down to Polymer worked just fine, but it took quite a bit of fiddling for Angular to see changes from Polymer (eventually resulting in the angular-bind-polymer package).

I wound up incorporating a similar solution into a general approach to watching from the outside. I may not need it after, though it does help to explain the concept before the Angular chapter so I may keep it regardless.

So the real question is, do I need the onPolymerReady hack under normal circumstances? The onPolymerReady hack provided a callback that would be executed as soon as the element in question was decorated by Polymer:
function onPolymerReady(el, fn) {
  if (el.$) fn();
  else setTimeout(function(){onPolymerReady(el, fn);}, 10);
}
Here, I am waiting 10 milliseconds between checking the target element for Polymer's dollar-sign property. That worked well in angular-bind-polymer, but it seems like overkill on simpler pages like this test page. If I comment out the call to onPolymerReady and just start my watcher function immediately:
document.addEventListener("DOMContentLoaded", function(event) {
  var el = document.querySelector('hello-you');
  // onPolymerReady(el, function(){
    watchPolymer(el);
  // });
});
It still does not work:
unresolved is now: undefined watcher.js:14
your_name is now: a watcher.js:14
your_name is now: as watcher.js:14
your_name is now: asd watcher.js:14
your_name is now: asdf watcher.js:14
The element starts off as undefined, which would result in errors (especially when used with Angular). That said, it does work eventually. After a few changes to the Polymer element, the watcher function eventually does see real changes, which is news to me. I do wonder if this is new with MutationObservers (which are used under the covers) or with Polymer. This might make the Angular solution a little easier. Something to check another day.

What does work with no errors—and is more along the lines of the Polymer Way™—is listening for the polymer-ready event:
document.addEventListener("DOMContentLoaded", function(event) {
  var el = document.querySelector('hello-you');
  // onPolymerReady(el, function(){
  document.addEventListener('polymer-ready', function(){
    watchPolymer(el);
  });
});
Then it works from the outset.

Of course, in the JavaScript world, there is no guarantee of script load order and execution. This is why I ran into trouble with AngularJS and Polymer. Which library loaded first? Had the polymer-ready event already fired by the time the Angular directive was evaluated? Had it fired by the time the directive tried to bind to the Polymer element's attribute?

I have to agree that the feedback that prompted tonight's investigation was correct. I do not need this onPolymerReady hack—at least not to solve the problem presented in the chapter. So I will ease back on some of the claims that I make therein. Still, I think that I will leave this in place—of only to avoid discussing this craziness in the already tight Angular chapter.

And now, on to editing!



Day #39

Friday, April 18, 2014

Submitting Forms with Polymer Elements (Still) Doesn't Work


While working through the remaining errata for Patterns in Polymer, I came across one that states that you can put a vanilla form inside of a Polymer element and expect that it should form-submit to an HTTP server like any other form. I honestly do not know that I have ever thought to do that, let alone try it. Now is as good a time as any.

I did research submitting a Polymer element as part of a normal form. That is, the Polymer element is some kind of super date picker and it is embedded in a vanilla HTML form. The expectation was that <input> fields within the Polymer element would be submitted along with any other form data on the page. As I found, this was not the case—the Polymer data was not submitted. In the end, I came up with a MutationObserver workaround, which served as the basis for a chapter in the book.

So first things first, with the latest versions of the JavaScript version of the library and with Polymer.dart, does submitting forms that contain Polymer elements with <input> elements now work?

I am still working with the sample form that looks like:



The first and last text <input> fields are defined in the HTML of the main page while the middle <input> is defined inside the very simple <hello-you> element:



But, if I enter data in all text <input> fields and submit the form, only the form elements that are part of the main document's HTML are submitted:
plain_old_param:plain
distributed_param:projected
And, loading this up in the JavaScript version, I find the same thing.

So you cannot submit a normal HTML form with a Polymer element that contains its own <input> tags. The MutationObserver approach is still needed. A bit of a bummer, but I confess that I relieved that I do not have to rewrite the chapter!

That said, this was not what the errata report specifically addressed. Said reviewer indicated that, if the Polymer element contained both the <form> and the <input>, then submitting it should work just fine.

And, indeed, that does work. I copy the HTML from the page into the <template> of my Polymer element:
<polymer-element name="hello-you">
  <template>
    <form action="test" method="post">
      <h1>Plain-old Form</h1>
      <input type=text name="plain_old_param" placeholder="Plain-old form field">
      <input type=submit value=Play>
    </form>
  </template>
  <script type="application/dart;component=1" src="hello_you.dart"></script>
</polymer-element>
When I fill out that plain_old_param text <input> and submit the <form> contained entirely inside the Polymer element, I find that it is submitted:
plain_old_param:asdf
And, when I try the same approach in the JavaScript version, I find the same results.

So in the end, not much needs to change in that chapter. In my opinion, Polymer elements that serve as <input> fields are a more likely use-case than a Polymer element that contains an entire <form>, so the chapter seems a very worthwhile one to include in the book. Still, I go back to rework the wording a little to (hopefully) make the use-case clearer.


Day #38

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