Saturday, December 14, 2013

Dynamically Created Polymers and Bound Variables (Dart)


I now have my parent Polymer talking to my child polymer. I can update the value in the parent, display it, pass it to the child, which displays the same value:



I am doing this in Dart the same way that I did it in JavaScript—by binding parent variables to child attributes:
<link rel="import" href="child.html">
<polymer-element name="x-parent">
  <template>
    <div>
      <h2>Parent</h2>
      <p>Count: {{parent_count}}</p>
      <x-child count="{{parent_count}}"></x-child>
    </div>
  </template>
  <script type="application/dart" src="parent.dart"></script>
</polymer-element>
This seems to work, but nothing really works in this business if you can't test it.

I start with the page context for my web tests. As I have found in Patterns in Polymer, this page needs to import the Polymer being tested and the test script:
<head>
  <!-- Load component(s) -->
  <link rel="import" href="packages/parent_child/parent.html">
  <script type="application/dart" src="test.dart"></script>
</head>
I do not start Polymer, instead I leave that to the test script:
library parent_child_test;
import 'package:unittest/unittest.dart';
import 'dart:html';
import 'dart:async';
import 'package:polymer/polymer.dart';

main() {
  initPolymer();
  // Test here...
}
The test setup that I have been using creates an element, storing it in a variable so that it can be torn down, and adds it to the document. The only thing complicating this is Dart's insistence on sanitizing HTML tags. Since the whole point of Polymer is to create non-standard HTML tags, I have to use a non-sanitizing createElement() helper. Altogether, this looks like:
main() {
  initPolymer();
  var _el;
  setUp((){
    _el = createElement('<x-parent></x-parent>');
    document.body.append(_el);
  });
  tearDown((){
    _el.remove();
  });

  // Test here...
}

createElement(String html) =>
  new Element.html(html, treeSanitizer: new NullTreeSanitizer());

class NullTreeSanitizer implements NodeTreeSanitizer {
  void sanitizeTree(node) {}
}
The tests themselves are straight-forward. I create a timer that waits for the timer in the Polymer itself so that I can test that the Polymer content is being updated:
    solo_test('updates the parent', (){
      new Timer(
        new Duration(milliseconds: 1500),
        expectAsync0((){
          expect(
            query('x-parent').shadowRoot.text,
            contains('Count: 1')
          );
        })
      );
    });
That ought to work. Of course it does not:
FAIL: [updates] updates the parent
  Expected: contains 'Count: 1'
    Actual: '\n'
    '    \n'
    '      Parent\n'
    '      Count: 0\n'
    '      \n'
    '    \n'
    '  '
Regardless of the delay, I find that “Count” text is not updating. In fact, if I do not remove the test element in the teardown, I find that it is not being updated in the browser either. In the end, I find that variable binding does not work unless the element is added before polymer is initialized:
main() {
  _el = createElement('<x-parent></x-parent>');
  document.body.append(_el);
  initPolymer();
  _el.remove();
  // Test here...
}
I am sure that there must be some underlying reason for this. I am just as sure that there is some more direct way of ensuring that bound variables are updated in the UI. But this works. And working in tests is usually good enough. If I ever need this in actual code, I will revisit. But, hopefully, if I do ever need this, the problem will be fixed or better understood. For now, I have two decent tests and am ready to start writing another chapter.


Day #965

1 comment: