On Using Jasmine in xpcshell Tests.
Like most JS frameworks, when used for client side development Jasmine expects an environment that meets certain conditions (e.g. it assumes the existence of a global window object).
In Salsita, however, we’re in the business of creating browser add-ons – which happens to mean that most of the time, some of those conditions are not met.
In the world of XUL-based Firefox add-ons, code is (or should be anyway) usually structured in code modules, which import each other and are themselves imported from the XUL window in a manner similar to HTML. This provides a nice way to encapsulate the logic into individual files. Furthemore, Firefox add-ons can use a special console application, xpcshell, that allows for relatively convenient (and more importantly – fast) automated testing of these modules. When it comes to BDD (and Jasmine in particular), however, there are some flies to be found in our agile testing ointment.
First, as has already been said, there is no global window object. That should come as no surprise, since, well, xpcshell is a console application. Luckily enough, when we go through Jasmine’s code, it is clear that we need just a handful of methods from the window object – namely those related to timers: setTimeout, setInterval and the respective clear* counterparts. Hence, the solution is easy – we make a fake window object and fob it off to Jasmine.
We’re going to import this fake window object in the head_init.js fixture file that is run before the tests in our test directory (in the newer Firefox versions, you have to specify the fixtures in the xpcshell.ini file).
An important point to note is that we do not want to use the standard Component.utils.import call to import Jasmine within our tests. Doing that would require changes to the library code, because JS code modules expect exported symbols to be specified explicitly using EXPORTED_SYMBOLS. Instead, we use mozIJSSubScriptLoader and load the library into the test scope (after importing the fake window object).
Next, we create the alias so that resource://<addonid>/ URLs map to the correct path (and the Cu.imports in our modules work when the code is run in xpcshell). The following snippet is taken from a recipe on MDC.
And we’re almost there! The last thing is to make sure the BDD specs are actually run. Jasmine runs all the specs asynchronously, whereas xpcshell expects synchronous tests. That means if we don’t tell xpcshell to wait until the specs have finished, none of the specs will actually be run. Therefore, we use a little trick here.
The runSpecs function is to be called from the test_* files that contain the actual test code (the parameter is a wrapper function for the Jasmine test suite). It sets a “pending” flag that tells xpcshell we’re running something asynchronous here and it should not quit right away but rather wait for us to signal that we are done.
We’re using the reporter’s callback function to check whether all the specs have been run. If there were any failures, we call xpcshell’s do_throw to make the test fail (which quits the tests, so there’s no need to unset the pending flag). Otherwise, we just unset the flag and – we’re done!