Saturday, February 9, 2013

QUnit Inside Firefox Extensions





I have a fairly large amount of code inside one of my Firefox extensions (Shopping Helper) .  I also have always manually tested this code, which made changes hard to do. I recently did a major refactoring of the code, and have plans to add more features, so it was time to get serious about automating the testing, with the first step of adding a comprehensive unit test suite.  Now, just about every language has a useful framework,  usually called something like "FooUnit" (e.g., JUnit, PyUnit), so that's the sort of thing I wanted for my extension.


Here's the problem: Firefox extensions are JavaScript, but it runs in a very different context that the normal JavaScript inside an HTML page situation.  This is typically called "chrome code", to differentiate it from the code that runs in the HTML document itself.  This alternate run-time environment has always made debugging and development tricky for Firefox extensions.  It gets further complicated by security issues where the chrome code is trusted more than code inserted into HTML, so there is a "wall" between the two worlds.

Looking for JavaScript unit test frameworks, QUnit seemed to be exactly what I was looking for: same basic framework as the FooUnits, relatively mature and well regarded.  It is nicely done, but it is also not done for the purposes of being used as chrome code inside a Firefox extension.  The question is whether it would run as-is inside chrome code (very doubtful) or could be made to run inside chrome code (more hopeful) without too much effort (a bit worrisome).

Short answer is that QUnit does not run as-is in the chrome context, but can be made to do so with a moderate amount of work (it took me about 5 hours). Since I have now done this work, no one else needs to do this, so for you, it will be relatively easy with all the details I give below.

First off, here are the files you need, which are based on version 1.11.0 of QUnit.
Below are the details of the changes I had to make and how to go about using this inside Firefox chrome code.

 

QUnit Javascript Changes

 

Chrome vs. Non-chrome Control


To not have to fork the QUnit code for the modifications, I defined a global variable QU_ISCHROME whose setting can be changed to easily switch to being used inside a Firefox extension in a chrome window instead of a browser window.  This will default to 'false' to give the previously existing behavior and can be changed to 'true' when including in the XUL/chrome Firefox extension context. It should be a simple one value change to get the effect, though I did not check if it ran outside a XUL window.

 

Altering createElement() calls


Since QUnit creates HTML DOM nodes, and since it will live inside an XML/XUL window, when creating these nodes, we have to explicitly define the namespace when creating them because the XUL namespace is not the standard HTML tags.  We do this by replacing all calls like this:

  document.createElement( "tag" );

with something like this:

  document.createElementNS( QU_HTMLNS, "html:tag" );

where 'QU_HTMLNS' is a conveniently defined global variable we put at the top of the Javascript file which is defined as:

  var QU_HTMLNS = "http://www.w3.org/1999/xhtml";

We add a convenience function QU_createElement() and wrap all node creation in it to keep the code cleaner.

 

 Altering innerHTML Content Assignments


While doing an assignment of an HTML fragment to a node via the innerHTML property works fine in a browser window, this does not work well in a chrome window. Thus, we wrap this assignment in a helper function QU_setInnerHTML() so that when QU_ISCHROME is true, it uses a proper parser directly.

 

Altering innerHtml Content Fetching


Similar to the assignment problem of innerHTML, using it as a value does not work that will in the chrome context either, so we also wrap these in a helper function QU_getInnerHTML().

 

Pedantic QUnit Javascript (optional)


In a more pedantic mode, a number of JavaScript warning were generated around functions that did not always return a value and properties that were used but not defined.

 

Extension Development Environment Notes


I package up my extension with a bash script that conditionally includes testing and debug code based upon a command line parameter.  So all the QUnit code is not in the production build.  This requires a bit of a customized extension environment though one could just manually add and remove the QUnit code if you did not want to have to replicate conditional building. When included by the build script, the code also insert a menu option to allow invoking the unit test window.

 

XUL Window Notes

 

CSS Include


Need to have the CSS includes after the XML declaration, but before the window itself.

  <?xml-stylesheet href="chrome://myextension/skin/qunit-1.11.0.css"
          type="text/css"?>

 

Namespace include


The window XML object needs the attribute to ensure that HTML elements can be put inside it:

  xmlns:html="http://www.w3.org/1999/xhtml"

 

JavaScript include


Within the window XML, include the QUnit JavaScript.  Here I have renamed it to reflect that it is the version with the QU_ISCHROME=true setting:

  <script type="application/x-javascript"
          src="chrome://shophelper/content/qunit-chrome-1.11.0.js"/>

 

Body Elements


As with the normal QUnit usage, you need the special two "div" elements. The twist here is that the HTML tag names need to be preceded with "html:" to explicitly define their namespace.

   <html:div id="qunit"></html:div>
   <html:div id="qunit-fixture"></html:div>

 

Full Example XUL Window


<?xml version="1.0"?>

<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://myextension/skin/qunit-1.11.0.css" type="text/css"?>

<window id="MyExtensionUnitTestDialog"
        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
        xmlns:html="http://www.w3.org/1999/xhtml"
       onload="SHUTD_onLoad(event);"
        ondialogcancel="return SHUTD_onDialogCancel();"
         title="My Extension Unit Test Console"
       buttons="cancel">

  <script type="application/x-javascript"
          src="chrome://myextension/content/qunit-chrome-1.11.0.js"/>
  <script type="application/x-javascript"
          src="chrome://myextension/content/unit-test-dialog.js"/>
 
  <vbox flex="1">
    <box>
     <html:div id="qunit"></html:div>
    </box>
    <box>
     <html:div id="qunit-fixture"></html:div>
    </box>
  </vbox>
</window>
 

XUL (extension) Javascript Notes


To test functions in the main extension within the QUnit XUl windo, we need access to the main window where the extension chrome code runs.  I am not sure if there are better ways to do this, but this works by iterating over windows.

    var parentWindow = null
    var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator);
    var enumerator = wm.getEnumerator("navigator:browser");
    while(enumerator.hasMoreElements())
    {
       var win = enumerator.getNext();
       if ( ! win.document )
          continue;
       parentWindow = win;
       break;
    }


You then can invoke functions from the extension with:

  parentWindow.someFunctionName( someArg1, someArg2 );

 

QUnit CSS Changes (optional)


Inside the chrome windows, the CSS property "-moz-border-radius" does not apply, so it gives a warning.

Wednesday, August 1, 2012

Ignorant Juries - By Design

I was reading this article about a judge prepping a jury for a patent infringement case between Apple and Samsung.  Given the horrendous state of the U.S. patent system, it seems to me that the more you understood about software patents, the more biased you would be and thus much less likely to ever be selected for this jury.  Additionally, the more you understood of the creative process, which really starts by copying (and then tweaking), the more biased you would be against design patents, Thus, it seems that the people most ignorant, and least qualified on the topic of patents are the ones deciding the verdict.

This made me realize that this phenomenon is probably not limited to software patents. In any field, the more informed you are about the topic, the more likely you would be labeled as biased toward a case.  Therefore, our judicial system would seem to be driven by those least qualified to make judgements.


To keep it in perspective though, what is likely a far worse problem is when attorneys choose jurors they think are the most malleable, rather than most qualified.  The judicial process then boils down to which attorney is the better salesperson and not whether there is actual guilt or innocence.

Wednesday, July 25, 2012

License Plate Sanity Restored

Stupid Design
About 3 years ago, Texas changed their license plate design to something that made them unreadable from more than 10 feet away.  I am sure they looked beautiful in a high-resolution, brightly lit setting to a committee of clueless overseers, but they failed to deliver on what should have been the number one requirement: readable at a distance in a variety of lighting conditions.

How does a group of people get put in charge of redesigning license plates and fail to deliver on the readability requirement?  How is is that at every step of the design and approval process no one ever raised this question? Did they ever even think to consult the number one user of the plates: law enforcement?  Would any police officer fail to spot this fatal design issue immediately?  This was an epic failure of government, committees and common sense.

It may not sound like it, but this is meant to be a positive, uplifting story. We come to that end by noting that Texas has corrected the problem and is (yet again) going to put out a new design, precisely to address the current problem of readability. And it took the Texas government only 3 years to recognize and fix the problem: maybe a new record.

This new design harken back to much older designs, and I like them a lot.  Simple, effective and functional.  Now I have to figure out how to trade in my plate for a new style one.
Sensible Design

Tuesday, July 17, 2012

Elevator Insanity

Having been in NYC the last 10 months, I have encountered more elevators than I previously have in the past.  I have grown to have some contempt for elevator manufacturers. I just do not understand why the very small number of manufacturers could not agree on some standards.  You have your "G", which could mean "ground floor" or it could mean "garage". You have the "L" which could mean "Lobby" or "Lower". Then there's the "M", which could mean "mezzanine" or it could mean "main floor".  I have even seen custom letters in some hotels that stand for some special feature on that floor, which of course has no meaning unless you know the secret code. Is "R" a "roof" or a "restaurant"? Do they dare use "G" for "gym"? I do not care what letter they use for what, just the consistency would stop me from pressing the wrong button as I go from one building to the other.

Then there is the inconsistency about where floor numbering starts. Sometimes the first floor it is the same as "main", "lobby" or "ground" floor, but not always.  So if you see a "G" and a "1", you really have no idea what is what.  The one thing that does add a little bit of sanity is the "star" on the button to indicate the "main" floor to get off (which could be "M", "G" , "L"or "1").  For simple buildings with one exit, this is usually satisfactory, but there are a lot of places with multiple levels of exits and one usually does not know exactly what level they entered on.  You can enter from the street and not be on the "ground" floor, or you can enter in way where you have no idea what level you are on. In those circumstances, how I am supposed to know what they consider the "star" level or exactly what button I need to exit the way I came in?

And my biggest gripe comes from the elevator in my apartment building.  The buttons go from "1" to "16", with a"star" on the "1". All this is perfectly good and unambiguous. And what would you expect the LED readout to show when you reach floor "1"?  You would expect to see "G" naturally.  They could not even stay consistent within a single elevator design.

Sunday, January 31, 2010

Shopping Helper Updated

My Firefox add-on, Shopping Helper, has been updated and improved. It's a great tool for helping people shop on-line. You can keep shopping lists and favorite stores and it automatically keeps track of the prices, allowing you to view products in a comparison grid, and alerting you when there is a significant price change. You can get it here:

https://addons.mozilla.org/en-US/firefox/addon/7942

And visit the home page for overview webcasts, screenshots and help:

http://labs.pronto.com/public/shopping-helper/index.html

Sunday, May 31, 2009

Google Wave Thoughts

I've read a bunch of hype about the Google Wave presentation and then actually watched the hour+ long presentation that the Google Wave team gave. Here's some thoughts on the matter.

Is this truly the revolutionary architecture they are hyping? It could be, but only if people buy into it and it does not wind up being too complicated to understand. Thus, its success depends on how well it is hyped, how well they can manage the early-adopted/tech crowd and if they can have enough stuff built on top of it for the average person to get some uptake. Given that, the early developer preview demo and release makes a lot of sense.

The basic fact is that they have the right idea: email is archaic, IM is archaic, and there are all these emerging new interaction patterns: Twitter, Facebook, blogs, etc. Can't we put all these things under one roof? My answer is: maybe. If you design something too general, it fails for being too complex/abstract to comprehend. I wonder if the generality of Google Wave may suffer from trying to be too general.

In some sense, you could give a demo of the TCP/IP stack today and woo the crowd with all the things you could do with it: email, IM, blogs, facebook, twitter, etc. Wow, this thing is amazing!

So is it something the user cares about or something only developers care about? I think it is too complex/abstract for the end-user to consider or care about. But maybe we sit in a time where something as general as TCP/IP is needed, only one level up, and maybe this fits the bill. There's no question that building apps today is a royal pain in the butt, especially handling concurrent events and all that makes up implementing the more collaborative things. If Google Wave winds up being the API that makes all this easier for the developer, maybe it will get uptake and be the "next big thing".

Wednesday, July 16, 2008

Followup 2: Shopping Helper

There's now an on-line video demonstration/tutorial for the Shopping Helper Firefox add-on:

Watch on YouTube