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

No comments:

Post a Comment