Saturday, December 27, 2014

Finally, Testing Polymer in Internet Explorer


I am not fond of Internet Explorer. The reasons are myriad, but not terribly helpful. Despite my dislike, I must admit that it tends to fail in ways that make sense. More often than not, when troubleshooting an “IE bug” I find myself wondering how code works in other browsers. Maybe that will wind up being the case with my Polymer testing woes.

I am running tests for my <x-pizza> Polymer element with Karma. The tests are written with the Jasmine testing library and look like:
describe('<x-pizza>', function(){
  var el, container;
  beforeEach(function(done){
    // Create <x-pizza> el and other setup...
  });
  // ...
  describe('properties', function(){
    it('updates value when internal state changes', function(done) {
      el.model.firstHalfToppings.push('pepperoni');
      el.async(function(){
        expect(el.value).toContain('pepperoni');
        done();
      });
    });
  });
});
The error for the past two nights (with karma-webdriver-launcher and karma-ie-launcher) occurs when I try to access the firstHalfToppings property of model. In the test, it is undefined:
C:\Users\IEUser\plain_old_forms>node node_modules\karma\bin\karma start --single-run --browsers IE
INFO [karma]: Karma v0.12.28 server started at http://localhost:9876/
INFO [launcher]: Starting browser IE
INFO [IE 11.0.0 (Windows 7)]: Connected on socket qNf8JVIEqjDNilL7AQFH with id 9528228
IE 11.0.0 (Windows 7) <x-pizza> properties updates value when internal state changes FAILED
        TypeError: Unable to get property 'firstHalfToppings' of undefined or null reference
           at Anonymous function (C:/Users/IEUser/plain_old_forms/test/XPizzaSpec.js:29:7)
But if I fire this element up in a web page, that property is very definitely defined:



After much futzing, I eventually trace this not to my code, but to Polymer's async() method, which comes on the line after the failure message. Things got so wonky that failures in previous tests would break other tests in non-helpful ways. Only after running single tests — with Karma/Jasmine's ddescribe() / iit() — was I able to identify the culprit as async(), which my test uses:
    it('updates value when internal state changes', function(done) {
      el.model.firstHalfToppings.push('pepperoni');
      el.async(function(){
        expect(el.value).toContain('pepperoni');
        done();
      });
    });
Polymer elements expose this method as a callback mechanism. The supplied callback (my test's assertion in this case), is only invoked once Polymer has updated the UI and all bound variables. This is ideal for testing because the test can be assured that all of the Polymer element's properties and visualizations have been updated—at least in Chrome and Firefox.

In Internet Explorer, it seems that nothing is updated until one more browser event loop. In other words, I have to wait for a setTimeout-0 in addition to the async() callback:
it('updates value when internal state changes', function(done) {
      el.model.firstHalfToppings.push('pepperoni');
      el.async(function(){
        setTimeout(function(){
          expect(el.value).toContain('pepperoni');
          done();
        }, 0);
      });
    });
This quite definitely fixes the test. I have other tests that magically pass with the addition of a setTimeout-0, but reliably fail without.

And once again, after some investigation of an “IE Bug,” I am left with a code error that is not IE's fault. This is almost certainly a Polymer bug. The entire point of async() is that the element is updated when the supplied callback is invoked. That I have to wait for another event loop on top of this seems quite wrong.


Day #37

No comments:

Post a Comment