Search code examples
polymerpolymer-1.0chaiwct

Unit testing dynamically-rendered elements in Polymer


Overview

DOM elements that are dynamically-rendered within dom-if, dom-repeat <templates> seem to be rendered asynchronously thus making unit-testing a bit of a pain.


The Polymer Component

template(is='dom-if', if='{{!defaultPrintAll}}')
  template(is='dom-repeat', items='{{_pageBucketItems}}')
    button(type='button', class$='{{_computeDefaultClass(item)}}', on-tap='_togglePageClick') {{item}}

The Test

  test("Clicking 'Export All' to off, reveals board-selection tiles", function() {
    $("#export-pdf-checkbox-all").siblings(".checkbox").trigger("click");
    Polymer.dom.flush()
    expect($(".board-range__button")).to.be.visible;
  });

Why it seems to fail:

When clicking a button which triggers the dom-if/dom-repeat the elements don't render in a synchronous order.

  • The dom-if and it's subsequent/nested dom-repeat render asynchronously.

  • To make matters worse, the button itself get's it's class in a computed/binded manner (mind the class$= on the button).

So the question boils down to this:

Is it possible to force render the dom-if, dom-repeat, and the computed-binding of the class in a synchronous order after I simulate the click to the button which activates all 3 of those conditions?


Notes:

  • I'm using Polymer's official WCT as the test harness.
  • I'm also using chai-jquery.
  • I've also used Polymer.dom.flush() but it still doesn't, ahem.. flush.
  • I'm aware that I can use chai-as-promised.js instead but it adds unnecessary complexity to my tests for a trivial matter such as this, so I'd like to avoid it.

Solution

  • Rather than using Polymer.dom.flush(), try using the flush function that WCT puts on the window. This will enqueue a callback function to be executed, in theory, after the template has rendered.

    test("Clicking 'Export All' to off, reveals board-selection tiles", function(done) {
        $("#export-pdf-checkbox-all").siblings(".checkbox").trigger("click");
        flush(function () {
            expect($(".board-range__button")).to.be.visible;
            done();
        }
    });
    

    Important to notice: Asynchronous tests require the done function to be passed into the test callback, and require done to be called after your conditions have been evaluated.