Just another Nodetraveller

My Javascript Testing Toolchain

Update (Jan 4th 2014): Since I wrote this post, I’ve switched to using Karma instead of testem. Karma is written and supported by the Angular guys and in the year or so since this post was originally written has matured more than testem. I have a karma skeleton project that I use to setup my frontend testing environment. The readme for that project is pretty extensive and outlines how to set up a karma project.

I’ve gotten very satisfied with the set of tools I’m currently using when writing JS tests, so much so that I’d thought I’d write a blog post about it.

I write my tests either using Chai and Sinon or Jasmine. I also use a forked version of JSCovReporter to show me code coverage information. Whilst writing and developing the tests I’d either run them in a browser (and manually refresh) or via phantomjs. This means that most of the time my tests are being tested in either firefox or a webkit-based browser. Of course before committing, the tests are checked manually in all browsers in case of cross browser issues.

This last step though is now replaced with a tool called testem. Testem is a command line tool that automates the running of your tests in any specified useragents and displays the results in the terminal. It’ll detect changes in the test files and automatically reruns the tests. It can also run the tests in CI mode which means the results are output in TAP format from which Jenkins can parse and generate reports.

So now, instead of having a browser open whilst writing tests, I run a command like this:

[testem simple usage]
1
$ testem -f testem.json

and windows for Chrome, Safari and Firefox are opened, the tests are run and results are displayed (The browsers and tests page(s) are specifed in the testem.json file). Alternatively you can also run it headlessly using phantomjs if you don’t need a browser.

The results looks like this:

The browser windows are kept open so subsequent changes will trigger the re-run of the tests. Though, in this example, only the above browsers were specified, I can add other browsers manually just by opening the same url in a different browser. At work for instance, where I work on an iMac with 8gigs of memory and have access to multiple mobile test devices, I would also have tabs for all the IE’s running via a vm, iOS simulator and a couple of android phones. So now, rather than wait for Jenkins or perform a quick manual check of the tests in each browser before committing, I know pretty much straight away if a test fails because of a cross-browser issue.

The icing on the cake is that only a small change to your spec runner files is required:

[Add testem support to your test runner page]
1
2
3
4
5
6
<!-- Testem support -->
<script type="text/javascript">
    if (location.hash.indexOf('#testem') !== -1) {
        document.write('<script src="/testem.js"></'+'script>');
    }
</script>

It’s a bit ugly but the conditional is there so there the test page can be run manually without testem.

CI mode

Running testem in CI mode is done using the “ci” parameter:

[testem simple usage]
1
$ testem -f testem.json ci -b Chrome

The results are displayed in TAP format which can be piped to a file which Jenkins can be configured to read from. Full configuration of Jenkins can be found on the testem use with jenkins documentation page.

Code Coverage

I use a forked version of JSCovReporter to generate the coverage report. It’s forked to remove the dependency on backbone.

In order for JSCovReporter to render the report the tests need to be run against pre-instrumented code so when each statement in the files are run they can be counted. This is done using CoverJS like so:

[testem simple usage]
1
$ coverjs ./*.js -o ./instrumented

The above command instruments the specified files and outputs them into an directory called instrumented. These files should then be included in the test page in order for the code coverage report to be generated correctly.

Integrated usage

The test page for my very small micro lib preposterous uses both testem and JSCovReporter so should serve as a simple integrated example.

The preposterous project page is a autogenerated composite page of the latest version of the project Readme and chaijs test report. There is also a coverage page that includes the code coverage report (click on the file name on the right or scroll down to see the full report).

The source code for both the tests reports (minus the project Readme) can be found in the test directory and shows how to integrate both testem and JSCovReporter into a test runner page.

Creating Js Files With Rhino (From JSON)

(This is primarily a note to myself)

I’ve been playing with Rhino. Specifically, reading in json objects, turning that into normal, runnable objects and saving that to a file so browsers can run it. More specifically, the code that transforms the JSON to an object in the browser should be exactly the same as the one that runs in Rhino. That way I don’t have to maintain two different versions.

I’m working on an idea that requires reading in JSON objects and creating objects with methods and properties based on that JSON and could be normally done per request of the page. Obviously, if the JSON is pretty much static, it would be best if we could save the resulting object to a file and reference that file in our pages instead. It would save 1) the request for the JSON file and 2) processing to create the final object. There are two steps to do this.

  1. Transform the JSON

  2. Saving the object

Transform the JSON

Easy, enough. Iterate through the JSON object, create new object and add methods and properties to it as you see fit. To save it though you need a String representation of the object. Gecko’s toSource() method helps there.

Saving the object

In Rhino, make use of Java’s FileWriter object and save it to the filesystem. If your object is really simple then that’s probably it and you’ll be able to run the object in your browser. If your final object makes use of closures then you’ll get errors, obviously, as the variables the closures have reference to, aren’t being created at all when the code is run in the browser. Solution (when run in Rhino) is to turn those methods that use closures into strings and add the variables in via regexp or whatever and then eval it!! Remember, it’s only done in Rhino, so it’s only done once. One thing to note, you don’t have to replace every occurence of that variable name, only the first one (or add a initialisation statement for that variable to the function). Now when the code is written out to file (and read back in by the browser) the variables will have a valid value.

For simple types, like String or numbers, this will work fine. For objects or arrays, you could use toSource() but that just dumps out a object literal of the object at that time and not a reference which is what you really need. The other thing is you’ll have a large object dump in the final code which makes the final code much much larger than you need. The solution then is store these objects in an Array or manager object (outside this system) and retrieve them as needed within your final code.

Here’s an example:

[persistJsForRhinoAndBrowsers.js]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.
.
o[sMethodName] = function(varA,varB){
var f = function(var1){
//Use of an objManager to retrieve object we need
//varA and varB have no value in Rhino
objManager.getObj(var1).method(varA,varB);
};
//so for rhino
if ((typeof this["load"] == "function") && (typeof this["Packages"] == "function")) {
//so get string version of function
var fString = f.toString();
//replace variable names with actual variable value.          
fString = fString.replace('varA', '"' + varA + '"');
fString = fString.replace('varB', '"' + varB + '"');
return eval(fString);
}

return f;

}(a,b);
.
.
.

There you go. Ugly, more than probably not robust but it’s the only way I’ve found to do what I need to do for my use case; admittedly probably not a common usage scenario.

Is there a better way?

ActsAsUndoable

With web apps becoming more and more like Desktop apps, it seems like user interfaces need be more like desktop user interfaces too. Some of these web apps can have a complex UI and their users need to be able to feel comfortable with it. Of course, making the UI as simple as possible is key, but if a task is relatively complex then the UI will also be relatively complex. With more complex tasks, users can make mistakes or want to change things and if they do, they’d appreciate a way of doing so. One way to do that is to provide the user a way of undoing their actions; a way to retreat over their history. Just to make clear, this is something along the lines of a user interaction history, not a browser navigation history. Hence ActsAsUndoable, one way of adding undo functionality to your interactive widgets. (Incidentally while I was writing this post, Aza Raskin posted an article on A List Apart about using undo functionality in interaction design, which seems to dovetail well with this post.)

Design Patterns

Whenever I code a widget, there’s two resources I look to. One is Yahoo!’s Design Pattern library and the other is Jennifer Tidwell’s Designing Interfaces book. It’s from this book and more specifically the explanation of the Multi-Level Undo pattern that ActsAsUndo is based on.

Basically the Multi-Level Undo pattern says that if users can navigate through their action history and undo their actions, then they can explore their own work paths quickly and safely. The most obvious desktop example of a navigable history is Photoshop’s history panel.

The pattern states that these kind of actions should be undoable:

  • Text entry for documents or spreadsheets

  • Database transactions

  • Modifications to images or painting canvases

  • Layout changes — position, size, stacking order, or grouping in graphic applications

  • File operations, such as deleting or modifying files

  • Creation, deletion or rearrangement of objects such as email messages or spreadsheet columns

  • Any cut, copy, or paste operation

There are apps like these available on the web and some do have some simple undo/history functionality. These do tend to be only a single step undo and then, only for actions that change and save a new state eg move to trash. But also other actions that users make but perhaps don’t yet want (or need) to save should also be undoable and at a multiple level too. So, how do we as implement something like the Multi Level Undo interaction pattern? We use the Memento software design pattern.

Demos

Two of Jennifer’s examples are text entry and stacking order changes. I thought these would be relatively simple examples to use for demo purposes. Here’s the text entry demo and also the stacking order demo. I used the demo of the YUI’s sortable list which I hope they don’t mind. If you want, take a look at these to see whats going on before reading on.

Text entry

The first example I’ll cover is the text entry one. It’s a simple example of a textarea in which users can enter text and save snaphots. The original class has two properties el and sValue. el represents the textarea and sValue, the textarea’s value. It has a method called setText() whichs sets sValue to the value of the textarea.

Finally we have a snapshotText() method which calls setText(). We wire a button element to this method.

[ActAsUndoable#1.js]
1
2
3
4
5
6
7
8
9
10
11
12
YAHOO.NT.UI.TextUpdate = function(elId,sGroup,sPreviewId) {
  this.el = document.getElementById(elId);
  this.sValue = this.el.value;
}
YAHOO.NT.UI.TextUpdate.prototype.setText = function(sValue) {
  this.sValue = this.el.value = sValue;
}
YAHOO.NT.UI.TextUpdate.prototype.snapshotText = function() {
  if (this.sValue != this.el.value) {
    this.setText(this.el.value);
  }
}

We can add undoable functionality by augmenting it with YAHOO.Acts.as.Undoable. This adds a sCaretakerGroup property and undo(),redo(),revert() and restore() methods. All we need to do is subscribe to the ‘stateLoaded’ event and make a call to saveState() of the Caretaker to record the initial state. We can do this in the constructor of out TextUpdate object or a init() method if we choose to use one.

[ActsAsUndoable#2b.js]
1
2
3
this.sCareTakerGroup = sGroup;
YAHOO.NT.CaretakerRegistry.subscribe(this.sCareTakerGroup,'stateLoaded',this.restore,this,true);
YAHOO.NT.CaretakerRegistry.saveState(this.sCareTakerGroup,'New',{'args':[this.sValue],'execute':this.setText,'oScope':this});

The first line just sets up a property that allows us to identify a caretaker to manage our states (more on this later).

The second line subscribes to an event called ‘stateLoaded’ and sets the callback to our restore() method.

Then, the third line saves our initial state. We need to do this so the revert() method can revert to the initial state. The callback we specify here depends on your object

So now our TextUpdate class looks like this :

[ActsAsUndoable#3.js]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
YAHOO.NT.UI.TextUpdate  = function(elId,sGroup,sPreviewId) {
  this.el = document.getElementById(elId);
  this.sValue = this.el.value;

  //Interact with caretaker 
  this.sCareTakerGroup = sGroup;
  YAHOO.NT.CaretakerRegistry.subscribe(this.sCareTakerGroup,'stateLoaded',this.restore,this,true);
  YAHOO.NT.CaretakerRegistry.saveState(this.sCareTakerGroup,'New',{'args':[this.sValue],'execute':this.setText,'oScope':this});
}

YAHOO.NT.UI.TextUpdate.prototype.setText = function(sValue) {
  this.sValue = this.el.value = sValue;
}

YAHOO.NT.UI.TextUpdate.prototype.snapshotText = function() {
  if (this.sValue != this.el.value) {
     this.setText(this.el.value);
    //saves value to caretaker 
    YAHOO.NT.CaretakerRegistry.saveState(this.sCareTakerGroup,null,{'args':[this.sValue],'execute':this.setText,'oScope':this});
  }
}
//make undoable
YAHOO.augment(YAHOO.NT.UI.TextUpdate,YAHOO.Acts.as.Undoable);

The last line adds the undoable functionality to the TextUpdate object. (see next code snippet)

But what’s all this caretaker stuff? Well, the Memento software design pattern is an established pattern that helps to implement undo functionality. I touched on the Memento pattern in an earlier post about object-oriented Actionscript.

The Memento pattern has three classes; Originator, Caretaker and Memento. Basically using these three classes, the Memento pattern works likes this:

If an Originator wants its state or some of its state to be undoable or redoable, then it must save its state to a Caretaker. This Caretaker will manage the various states (Mementos) of our Originator. (isn’t it lovely terminology?)

Using this as a premise, we can devise a simple ‘framework’ for adding multi level undo functionality to our apps. We don’t need (or want) to create lots of class hierarchy for our apps but the only classes we need to create revolve around the Caretaker object. Each object that we need undo functionality for, we have a corresponding caretaker. To manage multiple Caretaker objects (since perhaps multiple objects need a separate history), we use a CaretakerRegistry singleton object to do so. This allows Caretaker objects to be registered and created, if not done so already. It also acts as proxy to caretaker objects as all calls to Caretakers are made via the CaretakerRegistry. A registry also allows multiple objects to monitor state changes of an Originator. See the HistoryList section for an example of such an object

Interaction is achieved using CustomEvents. Caretaker objects fire ‘stateLoaded’ and ‘stateSaved’ events. Originator objects subscribe to the ‘stateLoaded event. Any other object that is interested in the state of our Originator will subscribe to both ‘stateLoaded’ and ‘stateSaved’ events. Each event passes the Memento object to each listening object.

Everytime the state of our object has changed and needs to be saved, we call the saveState method (see line 19) of our object’s Caretaker which makes a note of the new state and fires off a ‘stateSaved’ event.

Here are the methods that ActsAsUndoable adds.

[ActsAsUndoable#4.js]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
 * redo changes
 */
 undo : function() {
  YAHOO.NT.CaretakerRegistry.loadState(this.sCareTakerGroup);
 },

/**
 * Redo changes
 */
 redo : function() {
  YAHOO.NT.CaretakerRegistry.loadState(this.sCareTakerGroup,-1);
 },
  
/**
 * Revert to initial text value
 * 
 */
revert : function() {
  YAHOO.NT.CaretakerRegistry.loadState(this.sCareTakerGroup,0);
},
  
/**
 * 
 * 
 * @param {Object} oState Object containing callback to call with specified 
 * arguments and scope. Fields are :
 * 
 * {Array}   oState.args Array of arguments to call callback with
 * {fn}          oState.execute Calblack function
 * {Object}  oState.oScope Object to use as scope for callback
 */
restore : function(oState) {
  if (oState.execute) {
  oState.execute.apply(oState.oScope,oState.args);   
  }
}

Our object also has undo(),redo(),revert() methods, all of which call the loadState() method of the Caretaker. This, in turn, fires off a ‘stateLoaded’ event. Because our originating object subscribes to the ‘stateLoaded’ event it is passed the Memento object, which represents the state of the object that it needs to be undone, redone or reverted to. The listening method of our object that the custom event fires to is called restore(). This is the method that sets the state of our object. The restore() method is passed an object with three properties. These are args,execute, and oScope and are defined when we save the state via the saveState() method. Our restore() simply calls the method specified in execute passing as args those specified in args using oScope as the scope for the method.. I restore the state of the object like this (via a method call) rather than simply overwriting some properties with older properties as often some logic needs to be run as well; just resetting the properties probably won’t suffice in anything other than a very simple widget). The best way to do that is via existing methods on our originating object. For the pattern heads out there, this way is similar to the command pattern without actually creating Command objects – the existing methods are the Commands.

Stacking Order

The stacking order demo is similar. I rewrote the example to reflect the changes needed to make the reordering done by single method (orderLi). Also I haven’t augmented it with ActsAsUndoable as I don’t use the undo, redo or revert functionality in the UI. The source for YAHOO.example.DDApp is probably a good example to view if you want to see how to add undoable functionality without using augmentation.

History List

The History List object is one that listens to state changes of a given object and renders them in a list. It shows how other objects besides the Originator can monitor changes of state. Clicking on each item in the list, rolls back the state of that object to the state. You just initialise it with the id of the container and the CaretakerGroup that the object you’re providing a history for is using.

Each link in the history list is a named anchor. However in the update() method, we stop the Event so the browser doesn’t actually add it to its own history. We don’t want the act of navigating through the history of our own actions within our task to interfere with the history of our browser.

We can create a new HistoryList object that monitors changes to our object by this :

[ActAsUndoable#5.js]
1
hl = new YAHOO.NT.UI.HistoryList('hlistCont','textArea');

The first is the id of an element in which to render the history list. The second is the name of the caretaker group to monitor. The list is rendered to the document and any saves, reverts, undo etc are shown in this list.

Resources and notes :

  • The ActsAsUndoable was an interim tongue-in-cheek working name but I kinda like it. The Acts.as namespace is a bit overkill but it fits pretty well and will work out nicely in future things I want to post about.

  • Accessibility – we should set the tabindex of our textarea or list to -1 and focus() them when a history list item is clicked or pressed. See this making ajax work with screen readers article from Juicystudio *

  • For the textUpdate example I’ve used the word ‘snapshot’ rather than ‘save’ as ‘save’ would probably mean save permanently to most people. Another button called save could easily be added that would do a true save via normal form functionality. It could even be done using AJAX and the Browser History manager from the YUI library, which would add an item to the history list. Having said that, we don’t want to confuse user with two histories; browser history and what I call User Interaction History.

  • I used the terms as specified as in the Memento Design Pattern. These aren’t the clearest names and I was in two minds of calling Caretaker and CaretakerRegistry, UserActionManager and UserActionManagerRegistry. But I suppose these objects can be used for things other than user actions so I kept the design pattern language.

You can download the examples and js files. One of these days I’ll make my subversion repo public but for now old fashioned zip files will have to do.

Finally, thanks to Tony Kabalan for being a sound board and inputting some useful ideas.