Template Test Case

A Template test case allows to test an entire template (or module) in the DOM. A template is loaded into the page just as if it was loaded in a whole applcation, the test class however provides methods to check what is displayed in the page and to interact with it simulating user behavior.

It makes use of the class aria.jsunit.helpers.OutObj to mock a TemplateCtxt and provide a test area in the DOM. OutObj is available as the property this.outObj of the test class.

Test Structure

The structure of a Template test case is different from other unit tests. Due to the test complexity, a single TemplateTestCase class should be used only for one asynchronous test (usually corresponding to one template).

The skeleton is as follow:

Aria.classDefinition({
    $classpath : "MyTemplateTest",
    $extends : "aria.jsunit.TemplateTestCase",
    $prototype : {
        runTemplateTest : function () {
            // Do some assertions

            this.end();
        }
     }
});

There's only one entry point, runTemplateTest and the test can only be asynchronous, for this reason it's necessary to call this.end(); at the end of testing.

If the test class is called MyTemplateTest, by default, the test runner will load the template MyTemplateTestTpl inside the page and initialize it with an empty data-model. It is possible to change this behavior by configuring the environment as explained in the paragraph below.

Environment

The environment configuration allows to change the default Template test case behavior.

Aria.classDefinition({
    $classpath : "MyTemplateTest",
    $extends : "aria.jsunit.TemplateTestCase",
    $constructor : function () {
        this.$TemplateTestCase.constructor.call(this);
        this.setTestEnv({
            template : "another.path.TemplateToTest"
        });
    },
    $prototype : {
        runTemplateTest : function () {
            // Do some assertions

            this.end();
        }
     }
});

The environment can be changed in the test constructor calling this.setTestEnv();, this method takes a configuration object with the following properties:

  • template Template classpath, defaults to this.$classpath + "Tpl"
  • moduleCtrl Module controller description. It should match the ModuleCtrl bean, defaults to null
  • data Initial data model, default to an empty object
  • iframe Whether the template should be loaded inside an iframe or directly in the test page. Using an iframe gives more isolation but makes the test slower as the framework must be loaded twice. Defaults to false
  • css and baseCss CSS text around the test area or iframe. It is useful to change for instance the window size and assert what happens on viewport resize. Use css if you want to append to default CSS text, or baseCss to change it completely.

As the test might run in different environments (in the test page or inside an iframe) TemplateTestCase class provide shortcuts to useful these objects:

  • this.templateCtxt Reference to the template context
  • this.testWindow Reference to the window object containing the template under test.
  • this.testDocument Reference to the document object containing the template under test
  • this.testDiv Element containing the template output

The template context is particularly useful to test the internals of the template. It is an instance of aria.templates.TemplateCtxt so it has the interesting properties:

  • this.templateCtxt.data Template's data model.
  • this.templateCtxt.moduleCtrl Public interface of the module controller.
  • this.templateCtxt.moduleCtrlPrivate Private instance of the module controller.
  • this.templateCtxt._tpl Template instance, it has all the methods defined in the template script.

Through the templateCtxt is possible to call the methods (among many others):

  • this.templateCtxt.$refresh() Refresh the whole template of a specific section.
  • this.templateCtxt.$focus(id) Give focus to the element with the specified id.
  • this.templateCtxt.$getElementById(id) Returns an HTMLElement wrapped in DomElementWrapper.

DOM Assertions

Usual assertion for this type of test case are the presence of DOM elements on the page, their attributes or properties when the template is loaded or after user interaction or template refresh.

The following utilities might be of help when asserting DOM element properties:

The above utilities are useful once you have a DOM element, to retrieve it you can use any of the following methods of the test class.

  • getElementById(id) Return the DOM element in the current template with the specified id (the id should have been given with the {id .../} statement).
  • getElementsByClassName(dom, classname) Get the element with the given class, child of a dom element.
  • getWidgetInstance(id) Get the instance of a widget from the template, given its ID.
  • getWidgetDomElement(id, tagName) Get the first DOM element with the given tag name from the given widget in the template.
  • getInputField(id) Get the input DOM element of an input based widget.
  • getWidgetDropDownPopup(id) Get the DOM element of the dropdown popup associated to a DropDownTrait widget (MultiSelect, AutoComplete, ...).
  • getExpandButton(id) Get the expand button of a dropdown-based widget (MultiSelect, AutoComplete, ...).
  • getLink(id) Get the a DOM element of a Link widget.

This test class provides also methods to retrieve information of DOM elements:

  • computeZIndex(element) Compute the z-index of an element.

User Interaction

The greatest advantage of TemplateTestCase class is the ability to simulate user behavior like click, type, mouse move and so on.

The class provides the following methods:

  • this.clickAndType(id, text, callback, blur) Calling this method will click on the element with id id, type the text in text, blur the field if blur is true (default value) and execute the callback after.

clickAndType is convenient shortcut for a common user action, the implementation of this method use the utility class aria.utils.SynEvents that is a wrapper around syn, the Standalone Synthetic Event Library by bitovi and available as shortcut on this.synEvent.

  • this.synEvent.click(element, callback) Emulates a click event on a specific DOM element, it'll also fire in the correct order the events mosedown, mousemove and mouseup.
  • this.synEvent.move(options, from, callback) Emulates a mouse move from a given element from or a point in the screen to another point as described in options. It'll fire the eventsmouseover and mouseout as well as mouseenter and mouseleave on Internet Explorer.
  • this.synEvent.type(element, text, callback) Emulates typing in a specific DOM element. It fires the events keydown, keypress and keyup on the given target.

It is recommended to use the shortcut this.synEvent over the full classpath because the shortcut fires the events in the correct environment, even when the test runs inside an iframe.

For more flexibility it's also possible to use the Syn object directly using this.testWindow.Syn. This allows to call any method provided by Syn, the drawback is that the callback must be a plain function and cannot be Aria Templates callback.

Syn provides the following actions:

  • Syn.click(options, element, callback)
  • Syn.dblclick(options, element, callback)
  • Syn.rightClick(options, element, callback)
  • Syn.move(options, element, callback)
  • Syn.drag(options, element, callback)
  • Syn.key(options, element, callback)
  • Syn.type(options, element, callback)

The actions provided by Syn are always more complex than simply raising an event, in most cases they fire multiple events or simply require different implementation depending on the browser.

A lower level API is exposed by:

  • Syn.trigger(type, options, element) This method allows to create a synthetic event and dispatch it on the element. It also runs any default actions for that element (like following a link on <a> tags).
  • aria.utils.FireDomEvent.fireEvent(type, element, options) Fire any event on a target HTML element. It supports mouse, keyboard and generic HTML events. It can be used as well to simulate touch events.
  • aria.utils.Delegate.delegate(event) Use the internal delegate mechanism to fire any event, even non-standard ones. Events can be created calling aria.DomEvent.getFakeEvent(type, element) and then adding properties on it. This is the lowest level API and only works for framework parts that rely on event delegation.

Examples

Template Test using a module controller

The following example shows how to use a module controller in a template test. Currently it is necessary to set the data object inside the Environment explicitly to null to access the same object from the template/template script and the module controller.

Aria.classDefinition({
  $classpath : "MyTemplateTest",
  $extends : "aria.jsunit.TemplateTestCase",
  $constructor : function() {
    this.$TemplateTestCase.constructor.call(this);
    this.setTestEnv({
      moduleCtrl : {
        classpath : "controller.MyController"
      },
      data: null //necessary to have the same object inside the template/template script and the module controller
    });
  },
  $prototype : {
    runTemplateTest : function() {
      //call module controller
      this.templateCtxt.moduleCtrl.doRequest( //method setting something in the data object.
        "ABC", {
          fn : this.assertModuleCtrlCall, 
          scope: this
        });
    },
    assertModuleCtrlCall : function () {
      // Do some assertions
      this.assertTrue(this.templateCtxt.data.value !== null);
      this.end();
    }
  }
});

.