Sunday, November 30, 2014

Jasime/Karma Testing a Polymer Form Input Element


Tonight, I test. Last night, I was too uncertain to test, but having written some exploratory code, I have a better handle on what I'd like my <a-form-input> Polymer element to do.

The <a-form-input> element is a hack to get Polymer elements to behave like native HTML <form> input elements. The hack is simple enough—it breaks Polymer element encapsulation (admittedly one of the principal tenants of Polymer coding) to inject a hidden <input> into the DOM of the containing document. My hesitation last night was caused by uncertainty over how subclass' shadow DOM would interact with the containing page. This is an important point as <a-form-input> is meant to serve as a Polymer "base class" for other elements that want to work in forms. Thankfully, my exploratory code proved that my initial concerns were unwarranted (though problems may still arise).

So tonight, I test. I have a simple <x-double> test element that extends <a-form-input> for unit testing purposes:
<link rel="import" href="../a-form-input.html">
<polymer-element name="x-double" extends="a-form-input" attributes="in">
  <template></template>
  <script>
    Polymer("x-double", {
      attached: function(){
        this.super();
        this.inChanged();
      },
      inChanged: function(){
        this.value = parseInt(this.in) * 2;
      }
    });
  </script>
</polymer-element>
As a test element, it is intentionally simple. It observes the in attribute for changes and, upon seeing one, doubles the in-value placing the result in the value attribute. To test <a-form-input>, this <x-double> element extends it.

I am using Karma and Jasmine to test. The Karma configuration includes the test and library code files for serving, but does not load it:
    // list of files / patterns to load in the browser
    files: [
      'bower_components/webcomponentsjs/webcomponents.js',
      'test/PolymerSetup.js',

      {pattern: 'bower_components/**', included: false, served: true},
      {pattern: '*.html', included: false, served: true},
      {pattern: '*.js', included: false, served: true},
      {pattern: 'test/*.html', included: false, served: true},
      'test/*Spec.js'
    ],
The testing setup, taken directly from Patterns in Polymer is responsible for loading the elements.

The setup for the tests themselves will also be lifted from the book. I create a container element (making it easier to tear down after each test) which contains the target element being tested:
describe('<a-form-input> element', function(){
  var container, el;

  beforeEach(function(done){
    container = document.createElement("div");
    container.innerHTML = '<x-double></x-double>';
    document.body.appendChild(container);
    el = document.querySelector('x-double');
    setTimeout(done, 0); // One event loop for elements to register in Polymer
  });
  afterEach(function(){
    document.body.removeChild(container);
  });
  // Tests here...
});
As for the tests themselves, I can borrow them from the Dart version. The most useful tests (I think) would be the following, which describe the usual properties and attributes of a form input element:
  describe('form input element', function(){
    it('is created in the parent light DOM', function(){});
    it('updates name when "name" is updated', function() {});
    it('updates value when internal state changes', function() {});
  });

  describe('properties', function(){
    it('updates name when name is set', function() {});
    it('updates value when internal state changes', function() {});
  });
And, thanks to last night's exploratory code, these tests just work when implemented:
  describe('form input element', function(){
    it('is created in the parent light DOM', function(){
      var input = container.querySelector('input');
      expect(input).not.toBe(null);
    });
    it('updates name when "name" is updated', function(done) {
      var input = container.querySelector('input');

      el.name = 'my_input_name';
      el.async(function(){
        expect(input.name).toEqual('my_input_name');
        done();
      });
    });
    it('updates value when internal state changes', function(done) {
      var input = container.querySelector('input');

      el.in = '5';
      el.async(function(){
        expect(input.value).toEqual('10');
        done();
      });
    });
  });
Well all but one of them works:
Chrome 39.0.2171 (Linux) <a-form-input> element form input element updates name when "name" is updated FAILED
        Expected 'null' to equal 'my_input_name'.
        Error: Expected 'null' to equal 'my_input_name'.
            at x-double.<anonymous> (/home/chris/repos/a-form-input/test/AFormInputSpec.js:27:28)
            at x-double.<anonymous> (/home/chris/repos/a-form-input/bower_components/polymer/polymer.js:8958:34)
...
Chrome 39.0.2171 (Linux): Executed 5 of 5 (1 FAILED) (0.146 secs / 0.141 secs)
I have yet to teach <a-form-input> how to update the name attribute on the input element as I had done with the value attribute. The fix is easy enough—I need only add a corresponding nameChanged() method to react accordingly:
Polymer('a-form-input', {
  publish: {
    name: {value: 0, reflect: true},
    value: {value: 0, reflect: true},

    attached: function(){
      this.lightInput = document.createElement('input');
      this.lightInput.type = 'hidden';
      this.lightInput.name = this.getAttribute('name');

      this.parentElement.appendChild(this.lightInput);
    },

    nameChanged: function(){
      this.lightInput.name = this.name;
    },

    valueChanged: function(){
      this.lightInput.value = this.value;
    }
  }
});
With that, I have all of the tests passing that I hoped to have from the outset:
INFO [watcher]: Changed file "/home/chris/repos/a-form-input/a_form_input.js".
.....
Chrome 39.0.2171 (Linux): Executed 5 of 5 SUCCESS (0.158 secs / 0.152 secs)
I should probably expand on the distinction between attributes and properties, but so far, so good. This seems like a reasonable solution for the book.


Day #10

Saturday, November 29, 2014

Hacking Native Form Inputs with Polymer


Enough fun, let's code JavaScript.

I kid, I kid. I love JavaScript and JavaScript has been very good to me. I might prefer Dart, but I'll always have a special place in my heart for JavaScript. Today, I get to experience some of that specialness by adapting a Polymer.dart approach into the JavaScript flavor of Polymer.

I am rethinking the approach that I suggested in Patterns in Polymer for Polymer elements that behave like normal HTML <form> input elements. The new approach is very much a hack—it injects hidden <input> elements into the containing document, breaking Polymer element encapsulation. But, since Polymer does not (yet) support directly extending native form elements, I am warming to this hack as the best approach to take.

So tonight, I give it a try in JavaScript. I had originally hoped to get this running under test, but a quick review of the documentation for extending custom elements in Polymer scared me off this idea. Mostly, I worry that inheritance in JavaScript Polymer is not as elegant as the Dart counterpart. Working in the "superclass" shadow DOM seems particularly worrisome since the point of this element will be to create hidden <input> elements in the containing document's light DOM.

Still, I can place myself well for testing. So I start by creating a completely new repository for <a-form-input>. In that repository, I make a test directory that will hold a test element / fixture for my tests. I define test/x-double.html as:
<link rel="import" href="../a-form-input.html">
<polymer-element name="x-double" extends="a-form-input" attributes="in">
  <template></template>
  <script>
    Polymer("x-double", {
      attached: function(){
        this.super();
        this.inChanged();
      },
      inChanged: function(){
        this.value = parseInt(this.in) * 2;
      }
    });
  </script>
</polymer-element>
I have previously used this fixture in other Polymer tests, so the bulk of this is copied and pasted. What is new is the extends attribute, indicating that <x-double> will extend the soon-to-be-created <a-form-input>—in other words, <x-double> is a form input element. The attributes listed are also slightly different. In previous fixtures, I have needed an out attribute to reflect the doubling of the input value, but in this case, I will publish on the value attribute, which will come from <a-form-input>. The last difference is the need to call the attached() method in <a-form-input> from <x-double> via this.super()—it is not real inheritance, but it should work.

Next, I create the element that I want define in a-form-input.html:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="a-form-input" attributes="name value">
  <template></template>
  <script src="a_form_input.js"></script>
</polymer-element>
It is a personal preference to keep my JavaScript separate from my HTML. I may wind up combining this when I publish the package. I am unsure of the need to include the <template> tag. For now I do include it, but experimentation will be required.

I define the actual backing class for <a-form-input> in a_form_input.js:
Polymer('a-form-input', {
  publish: {
    name: {value: 0, reflect: true},
    value: {value: 0, reflect: true},

    attached: function(){
      this.lightInput = document.createElement('input');
      this.lightInput.type = 'hidden';
      this.lightInput.name = this.getAttribute('name');

      this.parentElement.appendChild(this.lightInput);
    },

    valueChanged: function(){
      this.lightInput.value = this.value;
    }
  }
});
This is adapted from last night's Dart approach. The name and value attributes need to be published and reflected (i.e. they reflect internal changes in the attributes) so that they behave like normal form input elements. When a-form-input is attached to the DOM, it injects a hidden input into the parent element. It names the hidden input with with the same value as the element itself. Lastly, I establish an attribute watcher on value so that the hidden input in the parent element will always synchronize to the value of the current element.

I can then smoke test this in a page served over a Python simple HTTP server:
<!doctype html>
<html>
  <head>
    <title>Smoke Test</title>

    <!-- 1. Load Polymer before any code that touches the DOM. -->
    <script src="bower_components/webcomponentsjs/webcomponents.js"></script>
    <!-- 2. Load component(s) -->
    <link rel="import" href="test/x-double.html">
  </head>
  <body>
    <div class="container">
      <h1>Smoke Test: a-form-input</h1>

      <form>
        <x-double name="doubled" in=5></x-double>
      </form>
    </div>
  </body>
</html>
Which does the trick:



So it seems that I need not have worried about the shadow DOM. At least in this case. I can use my is-a-form-input <x-double> element as a form input. If I give it a typical input name attribute of something like "doubled", then a corresponding hidden input is added to the containing <form> with the same name attribute. In other words, when I submit this form, it will contain a doubled form value.

There is still some experimentation warranted with all of this, but the overall approach still seems promising. Up tomorrow: tests.



Day #9

Friday, November 28, 2014

Extracting Polymer Behaviors from Existing Polymer Elements


My hack to get Polymer elements working with native forms (breaking encapsulation and inserting hidden <input> elements into the containing document) is growing on me. It was fairly easy to implement as a proof of concept. It was nearly as easy to write a bunch of tests verifying that the 80% use-cases work. So tonight I push ahead.

My normal next step while writing Patterns in Polymer would be to reimplement in the other language in which the book is written. In this case, since I have written the initial implementation in Dart, I would normally redo the implementation in JavaScript. If a solution works similarly as well in both languages, then I have some assurance that I have a solution that transcends languages—that it is a true pattern of the library.

But that's not what I am going to do tonight. I can't help myself—I must refactor. I wrote a handful of tests describing how I'd like Polymer elements to behave when they are used as HTML form elements. Tests are a great way to verify that things do not break over time. That is a necessary, but boring, reason to test. More fun is refactoring. I have all of these tests describing how I expect my element to behave. Unless I have poor tests (always a possibility), these tests will pass no matter how I might rework the implementation.

And the reimplementation that I hope to realize tonight is extracting this is-a-form-input behavior into a separate Polymer class that my custom Polymer elements can extend as subclasses.

The core <input> tests that I would like to continue to pass tonight are:
PASS: <x-pizza> acts like <input> - value property is updated when internal state changes
PASS: <x-pizza> acts like <input> - value attribute is updated when internal state changes
PASS: <x-pizza> acts like <input> - containing form includes input with supplied name attribute
PASS: <x-pizza> acts like <input> - setting the name property updates the name attribute
I should be able to pull all of last night's code out of the <x-pizza> element into <a-form-input> leaving <x-pizza> unchanged from when I began save for a new superclass:
import 'package:polymer/polymer.dart';
import 'a_form_input.dart';

@CustomTag('x-pizza')
class XPizza extends AFormInput {
  // Code reverted back to original implementation
}
That turns out to be quite easy to make happen. I define the AFormInput class as:
import 'package:polymer/polymer.dart';
import 'dart:html';

@CustomTag('a-form-input')
class AFormInput extends PolymerElement {
  @PublishedProperty(reflect: true)
  String name;

  @PublishedProperty(reflect: true)
  String value;

  Element lightInput;

  AFormInput.created(): super.created();

  void attached() {
    super.attached();

    lightInput = new HiddenInputElement();
    if (name != null) lightInput.name = name;
    parent.append(lightInput);
  }

  void attributeChanged(String name, String oldValue, String newValue) {
    if (name == 'name') {
      lightInput.name = newValue;
    }
  }
}
All of that code is a direct copy of the code from last night except for the attached() lifecycle method. Polymer calls this method when the custom element is attached to a live DOM. This was more complicated last night because <x-pizza> had to do some work when attached as did this is-a-form-input code. Now that this code is properly encapsulated, I no longer need to keep these different behaviors in separate methods. Thanks to proper class encapsulation, these behaviors are pushed back up to the direct methods applying them.

Even better, my tests continue to pass:
PASS: <x-pizza> acts like <input> - value property is updated when internal state changes
PASS: <x-pizza> acts like <input> - value attribute is updated when internal state changes
PASS: <x-pizza> acts like <input> - containing form includes input with supplied name attribute
PASS: <x-pizza> acts like <input> - setting the name property updates the name attribute
Better still, is that my Polymer-is-a-form-input hack still works in live code:



Best of all is that I was absolutely certain that it would continue to work—thanks to the tests that I wrote last night.


Day #8


Thursday, November 27, 2014

TDD a Polymer (Dart) Form Element


After last night, I am leaning toward being bad. Well, not too bad, but I have given up on trying to implement custom Polymer form elements the right way. Instead I begin to think the best approach is to break encapsulation of Polymer elements so that they can inject hidden <input> elements into the containing document containing the necessary data.

I am not entirely sure how this is going to work in practice, but I think I can describe some of the functionality as:
  var _el, _form, _container;
  group("<x-pizza>", (){
    setUp((){ /* ... */ });
    group('acts like <input>', (){
      test('value property is updated when internal state changes', (){});
      test('value attribute is updated when internal state changes', (){});
      test('containing form includes input with supplied name attribute', (){});

      test('setting the value property updates the value attribute', (){});
      test('setting the name property updates the name attribute', (){});
    });
  });
Most of those come for free from Polymer. In the <input> group, I setup as follows:
    group('acts like <input>', (){
      setUp((){
        _el.name = 'my_field_name';
        _el.model.firstHalfToppings.add('pepperoni');

        var completer = new Completer();
        _el.async(completer.complete);
        return completer.future;
      });
      // Tests here...
    });
This setup is mostly from last night. It sets the name of the "input" and updates the internal state such that my <x-pizza> element has pepperoni on the first half. The completer dance around Polymer's async() ensures that the following tests will not run until Polymer has updated the internal state of my custom element.

The first two tests just work™:
  var _el, _form, _container;
  group("<x-pizza>", (){
    setUp((){ /* ... */ });
    group('acts like <input>', (){
      test('value property is updated when internal state changes', (){
        expect(
          _el.value,
          startsWith('First Half: [pepperoni]')
        );
      });
      test('value attribute is updated when internal state changes', (){
        expect(
          _el.getAttribute('value'),
          startsWith('First Half: [pepperoni]')
        );
      });
      // More tests...
    });
  });
There is not much test driven development in this case. All that <x-pizza> needs is published, reflectable properties:
@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // ...
  @PublishedProperty(reflect: true)
  String name;

  @PublishedProperty(reflect: true)
  String value;

  updatePizzaState([_]) {
    value = 'First Half: ${model.firstHalfToppings}\n'
      'Second Half: ${model.secondHalfToppings}\n'
      'Whole: ${model.wholeToppings}';
    // ...
  }
  // ...
}
The next test is a little trickier mostly because Dart lacks the normal elements property on <form> elements. So instead of iterating across all form elements, I have to hack an approximation for it:
  var _el, _form, _container;
  group("<x-pizza>", (){
    setUp((){ /* ... */ });
    group('acts like <input>', (){
      setUp((){
        _el.name = 'my_field_name';
        // ...
      });
      // ...
      test('containing form includes input with supplied name attribute', (){
        var inputNames = _form.
          children.
          where((i)=> i.tagName == 'INPUT').
          map((i)=> i.name);
        expect(inputNames, contains('my_field_name'));
      });
    });
  });
Happily the test turns out to be the hardest part of this because updating the <input> that is injected into the light DOM is as easy as updating the element from last night when attributes change:
@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // ...
  Element lightInput;
  // ...
  void attributeChanged(String name, String oldValue, String newValue) {
    if (name == 'name') {
      lightInput.name = newValue;
    }
  }
  // ...
}
With that, I have my <x-pizza> Polymer element approximating the behavior of a native form element. I am likely overlooking an edge case or two—ah, who I am kidding? This is HTML so I am certainly missing at least a score of edge cases. Still, as proofs of concepts go, this seems reasonable to me. I can create custom Polymer elements that will work as expected with native <form> elements. I should double check that this works in JavaScript (where I'll have access to form.elements). That aside this is definitely promising.


Day #7

Wednesday, November 26, 2014

Native Form Inputs with Polymer


Polymer's inability to define form elements still bugs me. Recent work to support this in Paper Elements is progress, but sometimes you just don't want an input element that looks like a Google property.

I have a solution in Patterns in Polymer. Said solution works with minimal overhead in both JavaScript and Dart. Still, there is overhead and the penalty is paid by the developer using my awesome custom element... which makes my awesome custom element slightly less awesome.

The current approach in Patterns in Polymer requires additional code to sync a hidden <input>'s value with that of the Polymer custom element. So why not have the Polymer element be responsible for synchronizing with the hidden input? And why not take that a step further by making the Polymer element responsible for creating the hidden input in the containing light DOM?

The answer to the last question is encapsulation. But darn it, if Polymer can't make native form elements then screw encapsulation. This turns out to be surprisingly easy to implement. To add support to the <x-pizza> element for a "toppings" form input value, I can add a hidden input when <x-pizza> is attached to the DOM:
@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  Element lightInput;
  // ...
  void attached() {
    super.attached();
    // ...
    lightInput = new HiddenInputElement()..name = 'toppings';
    parent.append(lightInput);
  }
  // ...
}
With that element in the light DOM, I can keep it in sync with the internal pizza state by making the already defined _updatePizzaState() method responsible for updating the hidden field as well:
@CustomTag('x-pizza')
class XPizza extends PolymerElement {
  // ...
  updatePizzaState([_]) {
    // ...
    if (lightInput == null) return;
    lightInput.value = state;
  }
  // ...
}
And that works perfectly fine:



I still think that the MutationObserver approach is a useful pattern for the book, but I am hard-pressed to think that it is a better solution than a breaks-encapsulation, light-DOM input. I'll give it a day or two for this idea to percolate, but I may need a chapter rewrite shortly.


Day #6

Tuesday, November 25, 2014

Better Polymer.dart Tests with Futures


I lamented Polymer.dart's JavaScript-y callback nature yesterday. But I didn't do much about it. Ultimately I cannot do much until the library matures into more of a Dart library. The Patterns in Polymer book needs to track the library, not my weakly held, strong opinions (and I do admit that it makes sense to track the JavaScript Polymer for now).

That said, I do think it worth exploring Dart futures as a means for cleaning up my tests.

Last night's test looks like:
    group('syncing <input> values', (){
      var input;

      setUp((){
        input = _container.append(createElement('<input>'));
        syncInputFromAttr(input, _el, 'state');
        _el.model.firstHalfToppings.add('pepperoni');
      });

      test('updates the input', (){
        _el.async(expectAsync((_){
          expect(
            input.value,
            startsWith('First Half: [pepperoni]')
          );
        }));
      });
    });
For a small test, that is not too horrible. Problems would arise as the complexity of the Polymer element grows.

The main problem with the above test is in the actual test:
      test('updates the input', (){
        _el.async(expectAsync((_){
          expect(
            input.value,
            startsWith('First Half: [pepperoni]')
          );
        }));
      });
The expectAsync() call in there is a nod to the asynchronous nature of Polymer and my attempt to account for it in my test. I need to wait for Polymer to invoke the supplied callback, which is what wrapping the callback in expectAsync() does. But it obscures the purpose of the test.

The async nature of this particular test is not integral to the functionality. It is merely a necessary evil to ensure that the Polymer element has been updated. Instead, I would much prefer to write:
      test('updates the input', (){
        expect(
          input.value,
          startsWith('First Half: [pepperoni]')
        );
      });
The intent of that test is much clearer.

To make that work, I need a setup that blocks. I had forgotten that vanilla unittest in Dart supports this if the setUp() block returns a Future:
      setUp((){
        input = _container.append(createElement('<input>'));
        syncInputFromAttr(input, _el, 'state');
        _el.model.firstHalfToppings.add('pepperoni');

       var completer = new Completer();
       _el.async(completer.complete);
       return completer.future;
      });
I am none too fond of the completer dance that is necessary. My preference would be to use something along the lines of Ruby's tap(), which in Dart would look something like this:
        return new MyCompleter()
          ..tap((completer){ _el.async(completer.complete); })
          .future;
Alas, Dart does not support tap() or an equivalent. Alas too that Completer is abstract, making it impossible to extend to add this functionality.

When I use the Page Objects testing pattern with my Polymer elements, I typically define a flush() method on the page object. The page object flush() can do the completer dance, making the code pretty nice.

The closest that I can get without page objects is a _flush() test helper:
Future<T> _flush(el) {
  var completer = new Completer();
  el.async(completer.complete);
  return completer.future;
}
This leaves my setup as:
      setUp((){
        input = _container.append(createElement('<input>'));
        syncInputFromAttr(input, _el, 'state');
        _el.model.firstHalfToppings.add('pepperoni');

        return _flush(_el);
      });
That is nice, but ugh, how I hate indirection in tests.

Update: This is one Future method in Polymer: onMutation(). And it works, but there is something of an impedance mismatch as it requires a node argument. I can make the tests pass if I supply the Polymer element's shadow root as the argument:
      setUp((){
        input = _container.append(createElement('<input>'));
        syncInputFromAttr(input, _el, 'state');
        _el.model.firstHalfToppings.add('pepperoni');

        return _el.onMutation(_el.shadowRoot);
      });
While that works, it adds undesirable complexity. Reading that in 6 months I would wonder why I was passing the shadowRoot property—was that a requirement of my test? Of my code? Or was it (as is the case here) just something that made the test work, but has no particular value.

I think I will stick with some variation of the completer dance for now. But it sure would be nice if the Polymer.dart folks would add a proper Future version of async.


Day #5

Monday, November 24, 2014

Testing Polymer.dart Synchronization with Form Input Fields


Surprisingly, none of my Dart tests are failing. Or have failed. It's not that I expect Dart tests to fail with any regularity—far from it. It is just that the tests for the JavaScript version of Patterns in Polymer seem to fail every other week or so.

It probably helps that Polymer.dart has remained at 0.15 for a while now. I could also use some better tests. And since I went to all that trouble last night to write a test describing the JavaScript code from the chapter on synchronizing the plain-old form <input> value with Polymer, it seems only fair that I write the same thing for Dart.

The JavaScript test wound up looking like:
  describe('syncing <input> values', function(){
    var input;

    beforeEach(function(done){
      input = document.createElement('input');
      container.appendChild(input);

      syncInputFromAttr(input, el, 'state');

      el.model.firstHalfToppings.push('pepperoni');
      el.async(done);
    });

    it('updates the input', function(){
      expect(input.value).toEqual('pepperoni');
    });
  });
Cerate an <input>, sync it with the <x-pizza> Polymer element, add a pepperoni to the Polymer element and expect that the <input> now contains pepperoni as well.

As with last night's JavaScript solution, I am using MutationObserver objects to synchronize my elements in Dart:
syncInputFromAttr(input, el, attr) {
   var observer = new MutationObserver((changes, _) {
    changes.forEach((change) {
      if (change.attributeName == attr) {
        input.value = change.target.attributes[change.attributeName];
      }
    });
  });
  observer.observe(el, attributes: true);
}
This creates an observer with the supplied callback that is invoked when changes are seen. It then observes the supplied el, which is my <x-pizza> Polymer element in this case. Not a particularly Darty looking thing, but the callback updates the input when the desired changes are seen.

It works in my smoke tests, but I still need a repeatable unit test. Well, maybe not need, but if this approach failed in JavaScript, then it can also fail in Dart at some point. So it would be good to have such a test.

The test setup is the same as with the JavaScript test—create an <input> which is added to the container from previous setup. Then synchronize the Polymer and input elements. Lastly, add pepperoni as the first half topping:
  group('syncing <input> values', (){
    var input;

    setUp((){
      input = createElement('<input>');
      _container.append(input);

      syncInputFromAttr(input, _el, 'state');

      _el.model.firstHalfToppings.add('pepperoni');
    });

    test('updates the input', (){
      // Test will go here...
    });
  });
The test then needs to check that the input value is now set to indicate that the first half of the pizza includes pepperoni like the Polymer element:
  group('syncing <input> values', (){
    var input;

    setUp((){ /* ... /* });

    test('updates the input', (){
      expect(
        input.value,
        startsWith('First Half: [pepperoni]')
      );
    });
  });
Except that does not work:
FAIL: <x-pizza> syncing <input> values updates the input
  Expected: a string starting with 'First Half: [pepperoni]'
    Actual: ''
Fortunately, I have gotten to the point in my Polymer testing (JavaScript and Dart) at which I know that I need to wait for Polymer to update all internal and external values before testing. Furthermore, I know that I can supply a callback to Polymer's async() method such that Polymer will invoke this callback when it has updated bound variables and observables alike.

To adapt this into vanilla Dart unittest, I need to wrap the whole thing in an expectAsync() call:
group('syncing <input> values', (){
    var input;

    setUp((){ /* ... /* });

    test('updates the input', (){
      _el.async(expectAsync((_){
        expect(
          input.value,
          startsWith('First Half: [pepperoni]')
        );
      }));
    });
  });
And that actually works. I now have my very basic test working and a test of the actual functionality from this chapter both passing:
PASS: <x-pizza> has a shadowRoot
PASS: <x-pizza> syncing <input> values updates the input
All 2 tests passed.
I must admit that I begin to dislike the callback love in Polymer.dart. Both the MutationObserver and the async() methods require callbacks, making this feel very much like JavaScript code. I can only hope that this changes at some point so that I can use a Future or two. In the meantime, I will likely convert this test into a scheduled_test test. I may have to build my own Completer to make it work, but the end result ought to be a bit easier on the eye.



Day #4

Sunday, November 23, 2014

Replacing MutationObserver with Object.observe() in Polymer


I was able to fix and test a change observer for Polymer
elements. Tonight, I am curious if the Object.observe() approach might offer any benefits over the current MutationObserver approach that I have been using in Patterns in Polymer.

Given a Polymer element (el), a form input object (input), and an attribute to watch on the Polymer element, I can ensure that the value of the form element synchronizes with the Polymer element thusly:
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      if (mutation.attributeName == attr) {
        input.value = mutation.target[attr];
      }
    });
  });
  observer.observe(el, {attributes: true});
The observer watches el (including attribute changes). Each time changes are seen, the callback loops through the changes (mutations) looking for one that matches the attribute attr. Finding such a change, the callback then updates the form input field with the same value as the Polymer element.

This solution works just fine, but is a little cumbersome if only because MutationObserver is a lot to type.

So what about Object.observer()? It turns out that very little need change to support a switch to this new approach:
  Object.observe(el, function(changes){
    changes.forEach(function(change){
      if (change.name == attr) {
        input.value = change.object[attr];
      }
    });
  });
In fact, aside from less haughty "changes" in place of "mutations," almost nothing has changed. I still loop through the list of changes and conditionally update input. The only real difference is that I no longer have to create a mutation object that can then observe the element—the observe() code is baked right into Object.

It would be nice to filter out only those changes in which I am interested. Object.observe() does support filtering, but is limited to filters only the type of change, not the attribute being changed:
  Object.observe(el, function(changes){
    changes.forEach(function(change){
      input.value = change.object[attr];
    });
  }, ['update']);
Since everything in this particular Polymer element is an update, this does not have the desired effect. All changes are sent to the observer callback.

In other words, for my particular use-case, MutationObserver and Object.observe are identical—requiring identical callback handlers to update the form element. And, since MutationObserver (currently) has broader support, I will likely stick with it in the book.


Day #3

Saturday, November 22, 2014

Testing Polymer Updates to Other Elements


Yesterday, I committed a programming cardinal sin. Polymer saw a new release and my code broke. That will happen and is hardly a sin. I fixed my code to address the issue and pushed the fix to the code repository. Again, not a sin.

The sin that I need to confess? I pushed the fix without an accompanying test.

For shame. My penance, of course, is to write a test—and make sure that the test really captures the failures so that I never see them again.

The broken code from last night involved a MutationObserver that synchronizes values from a Polymer element with a plain-old form <input>. The fix was in both the Polymer code (I was not publishing the attribute properly) and in the MutationObserver code which was using some very old wait-for-Polymer setup. The new observer code looks like:
Polymer.whenPolymerReady(function(){
  var input = document.querySelector('#pizza_value');
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      if (mutation.attributeName == 'state') {
    });
  });
  observer.observe(document.querySelector('x-pizza'), {attributes: true});
});
That is nice, but tied to the implementation of my particular code: the #pizza_value, x-pizza, and state values are all hard-coded. That is not great in production code, but for example code in the Patterns in Polymer book, it is fine. Except for testing.

I really want to test the inside of that, so I refactor it as:
Polymer.whenPolymerReady(function(){
  var input = document.querySelector('#pizza_value'),
      el = document.querySelector('x-pizza');
  syncInputFromAttr(input, el, 'state');
});

function syncInputFromAttr(input, el, attr) {
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      if (mutation.attributeName == attr) {
        input.value = mutation.target[attr];
      }
    });
  });
  observer.observe(el, {attributes: true});
}
That should make it easier to test and I think easier to explain in the book (if nothing else, I can split the polymer-ready and observer discussion apart).

Now, let's see if I can test that...

I am still using Karma and Jasmine for testing. I start in my Karma configuration, to which I add this synchronization script to the list of files to load:
    files: [
      'bower_components/webcomponentsjs/webcomponents.js',
      'test/PolymerSetup.js',
      'scripts/sync_polymer_in_form.js',
      {pattern: 'elements/**', included: false, served: true},
      {pattern: 'bower_components/**', included: false, served: true},
      'test/**/*Spec.js'
    ]
That generates an error:
Chrome 39.0.2171 (Linux) ERROR
  Uncaught NotFoundError: Failed to execute 'observe' on 'MutationObserver': The provided node was null.
  at /home/chris/repos/polymer-book/book/code-js/plain_old_forms/scripts/sync_polymer_in_form.js:16
This is because the elements in the when-polymer-ready block do not exist in my tests. I am loath to add conditionals into the actual book code as it is messy to filter out of the book. Instead, I cheat by maybe-adding the <x-pizza> element needed by the smoke test:
document.addEventListener("DOMContentLoaded", function(event) {
  var el = document.querySelector('x-pizza');
  if (el) return;

  document.body.appendChild(document.createElement('x-pizza'));
});
My existing (minimal) test again runs without error, so now I need to write an actual test. I will create an <input> element to synchronize to the value of my Polymer element's state attribute. At the end of the test, the input should have a value of "pepperoni" to reflect the change to the <x-pizza> element:
  describe('syncing <input> values', function(){
    var input;
    // Need setup here....

    it('updates the input', function(){
      expect(input.value).toEqual('pepperoni');
    });
  });
Now, I just need the setup to create the <input> and connect it to the already created <x-pizza> element. Something like the following ought to do the trick:
  describe('syncing <input> values', function(){
    var input;

    beforeEach(function(done){
      input = document.createElement('input');
      container.appendChild(input);

      syncInputFromAttr(input, el, 'state');

      el.model.firstHalfToppings.push('pepperoni');
      el.async(done);
    });

    it('updates the input', function(){
      expect(input.value).toEqual('pepperoni');
    });
  });
Creating the <input> element and appending it to the test's containing <div> is easy enough. Connecting the <input> element's value to the <x-pizza> element's state attribute is done by the newly created syncInputFromAttr(). Lastly, I need to update the internal state of the <x-pizza>, which can be done via the firstHalfTopping property.

The async() callback of Jasmine's done() is the only real trick. When Polymer is done updating the internal state, its bound variables, and the UI, then it calls the callback supplied to async(). By supplying Jasmine's done(), I tell the tests to wait to execute until Polymer has completely updated the element—and allowed the Mutation Observer to see the change and do the same.

Happily, this works as desired:
SUCCESS <x-pizza> element content has a shadow DOM
SUCCESS <x-pizza> syncing <input> values updates the input
More importantly, I can break the test by reverting last night's fixes (not publishing the attribute or using old watch code). Since I can break the test like this, I know that I have successfully captured the error that I previously missed. Better yet, I know that my code will never fail like this again.

Up tomorrow: a little refactoring with Object.observe().



Patterns in Polymer




Day #2

Friday, November 21, 2014

No Idea How That Ever Worked


Again, things are broken in Patterns in Polymer. I would like to get it mostly in order by the end of the month, so having the latest Polymer expose yet more problems is less than ideal. Hopefully these are the easy kinds of problems (they never are).

Actually, there is some hope this time around. All of the book's tests are passing. Even though the tests could be better in places, that still counts for something. The actual problems occur in a handful of the JavaScript smoke tests. In addition to unit and/or acceptance tests, the code for each chapter includes an index.html page that I normally access with the aid of Python's simple HTTP server. Only they stopped working for some reason.

I am unsure if there is a common thread, but some of the pages are just blank. Looking through the code, I see that I am definitely using the correct life-cycle methods:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="normal-changes">
  <template>
  </template>
  <script>
    Polymer('normal-changes', {
      // ...
      attached: function() {
        console.log('yo')
        // ...
    };
  </script>
</polymer-element>
Console debugging verifies that the Polymer element is being created properly. The page is just blank for some reason...

D'oh!

It is blank because the smoke test page contains nothing but a Polymer element wrapping a content-editable field:
  <body>
    <div class=container>
      <normal-changes>
        <div contenteditable>
          <!-- ... -->
        </div>
      </normal-changes>
    </div>
  </body>
Amazingly that worked at one point. The problem is not the page's HTML, but rather the Polymer element's <template>, which lacks a <content> element into which the page can project the wrapped content. The fix is to simply add the tag:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="normal-changes">
  <template>
    <content></content>
  </template>
  <script>
    Polymer('normal-changes', {
      // ...
      attached: function() {
        console.log('yo')
        // ...
    };
  </script>
</polymer-element>
A reload now includes the Polymer wrapped content:



I have no idea how that ever worked, but Polymer must have been OK with it at one point.

Most of the other changes are along the same lines—silly things that I just missed or I cannot figure out how they ever worked. That said, the plain-old forms chapter was just broken. I definitely need to revisit the test code on that tomorrow. For now, I get it working through a combination of reflecting changes in the attribute and better mutation observing in the main page.

I am publishing the current pizza state in the state attribute. For changes to be reflected, I need to explicitly mark it as such:
Polymer('x-pizza', {
  // ...
  publish: {
    state: {
      value: '',
      reflect: true
    }
  },
  // ...
});
The chapter still recommends watching that value so that changes can be synchronized in the containing form. Mutation observing seems to have gotten a little easier thanks to whenPolymerReady():
Polymer.whenPolymerReady(function(){
  var input = document.querySelector('#pizza_value');
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      if (mutation.attributeName == 'state') {
        input.value = mutation.target[mutation.attributeName];
      }
    });
  });
  observer.observe(document.querySelector('x-pizza'), {attributes: true});
});
The previous version of that code required three separate functions and some convoluted wait-for-polymer logic. This seems much nicer (and closer to the Dart solution).

At any rate, I have all of the smoke tests working. I will revisit the tests for the Forms chapter tomorrow.


Day #1