Skip to content

Writing tests for a live extension

chemerisuk edited this page Oct 7, 2014 · 1 revision

One requirement for a high quality widget is test coverage. New elements are captured by a live extension asynchronously, so it’s not easy just to make them in memory. To solve the problem better-dom has the DOM.mock[All] function.

DOM.mock

var myButton = DOM.mock("button.my-button");

DOM.mock creates elements, just like DOM.create. On top of that, it synchronously applies the registered live extensions to the newly created elements. For even more convenience all wrapper objects created by DOM.mock preserve event handlers (e.g. onClick), so you can test them.

From time to time you might need to create a “fake” instance of an element. Use DOM.mock without arguments to make such kind of object.

console.log(DOM.mock().length); // => 0

A test for the modal dialog live extension can look like below (Jasmine is used):

describe(".modal-dlg", function() {
  var dlg, backdrop;

  beforeEach(function() {
    dlg = DOM.mock("div.modal-dlg");
    backdrop = DOM.mock();
  });

  it("should hide itself and backdrop on close", function() {
    var dlgSpy = spyOn(dlg, "hide"),
      backdropSpy = spyOn(backdrop, "hide");

    dlg.onClose(backdrop);
    expect(dlgSpy).toHaveBeenCalled();
    expect(backdropSpy).toHaveBeenCalled();
  });

  it("should show itself and backdrop on show", function() {
    var dlgSpy = spyOn(dlg, "show"),
      backdropSpy = spyOn(backdrop, "show");

    dlg.showModal(backdrop);
    expect(dlgSpy).toHaveBeenCalled();
    expect(backdropSpy).toHaveBeenCalled();
  });
});

Feature detection

There are some cases when filtering by a CSS selector is not flexible enough. For instance you want to declare a Live Extension but only for browsers that support (or not support) a particular feature. Simple if check doesn’t work well here: you may need to run tests in a headless browser like PhantomJS that might support the feature natively. Starting with better-dom 1.7 DOM.extend supports the optional argument condition.

Assume we need to create a polyfill for the placeholder attribute. It doesn’t make sense to implement it for browsers that have built-in support. Below is how the feature detection could be:

var supportsPlaceholder = typeof DOM.create("input").get("placeholder") === "string";

By using just a simple If statement like below:

if (!supportsPlaceholder) {
  DOM.extend("[placeholder]", {
    // implement placeholder support
  };
}

we won’t have an ability to test the widget, because PhantomJS for instance supports the placeholder attribute and the Live Extension will never be declared. To solve the problem you can use an extra condition argument in DOM.extend, that might be boolean or a function:

DOM.extend("[placeholder]", !supportsPlaceholder, {
  constructor: function() {  },
  onFocus: function() {  },
  onBlur: function() {  }
});

DOM.mock ignores the condition argument, so you can access all methods of the [placeholder] extension even if current browser passes the check:

var input = DOM.mock("input[placeholder=test]");

typeof input.onFocus; // => "function"