Examining the Scriptaculous Unit Testing Implementation

6: The "parse" Functions

Here they are again.

  parseResultsURLQueryParameter: function() {
    return window.location.search.parseQuery()["resultsURL"];
  },
  parseTestsQueryParameter: function(){
    if (window.location.search.parseQuery()["tests"]){
        return window.location.search.parseQuery()["tests"].split(',');
    };
  }

We know that the first uses toQueryParams (for which parseQuery serves as an alias) to get a hash of query key/value pairs. Then it returns the value for the key "resultsURL". Our previous examination of toQueryParams informs us, now, that this value may be the undefined value (if there is no key "resultsURL"), a string, or an array of strings. But the use of parseResultsURLQueryParameter in the definition of Test.Unit.Runner's initialize function does not seem to expect an array:

this.options.resultsURL = this.parseResultsURLQueryParameter();

Granted, I'm only going by the choice of the variable name, the single noun resultsURL, but this is likely meaningful in source code that has been written with such care. We'll see how resultsURL is used later, but my bet is that the Test.Unit.Runner class expects the undefined value or a string. This is just a convention.

parseTestsQueryParameter, on the other hand, is expected to return an array. Here's how it's used in the definition of initialize:

this.options.tests      = this.parseTestsQueryParameter(); 

Again, I'm taking a cue from the name of the lvalue: the plural noun tests. But it turns out a little examinaton of parseTestsQueryParameter bears this out. The first line of the function checks the value of hash["tests"]. I mentioned that the undefined value may be treated as a boolean false. Well, here's an example of it. (window.location.search.parseQuery()["tests"]) resolves to false if its value is undefined. This test also traps empty strings, which might be a possible value as well.

Then, a split function is applied to window.location.search.parseQuery()["tests"], a split on commas. So if window.location.search.parseQuery()["tests"] is a string, split will return an array. And if it were an array to start with? Well, split is not part of the array prototype. You can type ["Harvey", "Peter"].split(',') as a watch expression in the Firebug Script pane, and it will tell you ["Harvey", "Peter"].split is not a function. Nor is there any addition of split into the Array class or the Enumerable mixin class by which Array is extended in prototype.js. Again, I think it's just a convention that there won't be more than one tests GET value. If there were, it appears that this code would halt. There's no trapping for an array, and there doesn't seem to be any exception handling either.

And, to complete the first stage of initialization, the initialize function checks to see whether there is an element on the page that should receive log information about the tests:

if (this.options.testLog) {
  this.options.testLog = $(this.options.testLog) || null;
}

If there is a value provided in the options object for testLog, this.options.testLog is assigned a reference to an HTML element with an id matching the existing value of this.options.testLog. Or, if there is no such element, this.options.testLog is set to the null value. That's what the $() business is about: it's a shortcut for document.getElementById().

Since the first line of initialize creates an options object with a default value of 'testlog' for the member variable (or hash key, if you prefer, since hashes and objects behave the same way) testLog, the default for Test.Unit.Runner's initialize function is to look for an HTML element, probably a DIV, with the id 'testlog'.