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

No comments:

Post a Comment