Unit testing dynamically-rendered elements in Polymer
Asked Answered
D

1

7

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.
Dewberry answered 29/12, 2015 at 23:23 Comment(0)
R
8

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.

Rushing answered 30/12, 2015 at 5:26 Comment(3)
bingo - thanks, but how is this much different than using setTimeout() in the tests? How would I be able to continue the next tests which depend on this?Dewberry
See for yourself: github.com/Polymer/web-component-tester/blob/… In most cases, there's no real difference compared to just using setTimeout with an arg of 0. The reason you would want to use window.flush instead of setTimeout, however, is that in future versions of web-component-tester, flush's implementation may change to be smarter and/or more useful.Rushing
And as far as the next tests go, those tests won't be executed until the previous test either calls it's done function, fails by assert, or fails by timing out.Rushing

© 2022 - 2024 — McMap. All rights reserved.