Workstyle: Add vendor tests before using new library code

Hello again. Here is a snag-resolving article about a maintenance problem. There should be a thick volume called “Web Maintenance Pearls”, which everyone should be forced to read.

Here’s the situation: You want to throw out a dependency. It’s an older JavaScript library, and you have a replacement lined up. Some plugin for a popular framework that you already depend on.

Add a couple of jsTestDriver “VendorTests” test cases: proof that the new library works as advertised, defined without the noise of your application’s behavior. This test will run faster than manually testing your web GUI.

Write the vendor behavior test:


/** Tests of used vendor framework functions. */
var VendorTests = TestCase("VendorTests", {

    /**
     * MochiKit & the jQuery BBQ plugin should have the same URL querystring-
     * parsing functionality.
     */
    testMochiKitAndJqueryBBQWorkTheSame : function(){
        var qs = 'elvis=1977&reagan=2004&lee=1973',
            ob = {elvis:1977, reagan:2004, lee:1973},
            mObj = MochiKit.Base.parseQueryString(qs),
            jObj = jQuery.deparam(qs),
            mQs = '',
            jQs = '';

        //jstestdriver.console.log("JsTestDriver", "Elvis died in 1977:"+jObj.elvis);
        assertEquals(1977, jObj.elvis);
        assertEquals(2004, mObj.reagan);
        assertEquals(mObj, jObj);

        mQs = MochiKit.Base.queryString(ob);
        jQs = jQuery.param(ob);
        assertEquals(mQs, jQs);
    }
});

If you want to learn much more about hard maintenance problems, and how to crawl out from under them, read Michael Feathers.

That was the gist of it.

Snag expansion: Perhaps the jsTestDriver functions get overwritten, by code you import

As you include your target dependency – MochiKit – in your list of imported JavaScript files, all your tests still pass. A good sign. You add an empty test-case, with a fail("Good. If this is NOT seen, it means is trouble!");, just to see if the testing tool picks it up. All tests, including the new one, pass. A bad sign. Good thing you were suspicious. Paranoid, almost.

What just happened, and how to fix it:

  • MochiKit exported all its functions to the global namespace, by default. This includes name-clashing functions that thrash jsTestDriver’s same-name functions. Thanks to brasetvik for noting!
  • You can turn off that default behavior. Add this single line of code to a file, say initMochiKitWithoutGlobals.js: MochiKit = {__export__: false};
  • Include the new JS file in the jsTestDriver.conf “load” section, just before MochiKit:

server: http://localhost:9876

load:
  - web/scripts/jquery.js
  - web/scripts/tk/mochikit/initMochiKitWithoutGlobals.js
  - web/scripts/tk/mochikit/packed/MochiKit/MochiKit.js
  - web/scripts/jquery.ba-bbq.js
  - web/scripts/your_web_application.js
  - tests/js/your_web_application/*.js

Update: Currently, my way of making MochiKit.js be non-export configured, is to add the MochiKit = {__export__: false}; line instead of the initial MochiKit = {}; in the MochiKit.js file itself. (Since I’m moving away from that library, I figured it’s a cost I can take. It’s not like I’ll upgrade it, anyway.) Researching why that variable does not seem to get persisted, perhaps it’s a load order snag of some kind. window.MochiKit did not work. Hm. I’ll let you know when I nail it.

So, now your testing library runs correctly again. Cursed be global namespace pollution.

Upcoming meetup poster: active redesigning and customizing

Prettify your web apps

Indulge me. I allowed myself to make a little poster.

Öresund JavaScript Meetup #10 on June 14, in Malmö, at Hypergene’s offices.

I guess there are a couple of things wrong with it. The date and time are lacking. Monday June 14. At 19.00. The URL says those things. (But people looking at a poster can’t click it.)

These guys would disapprove.

Update: Now, I went and added that stuff.

Still, the workshop will be great.

Change ID of a Component in ExtJS

ExtJS: The Component manager instance’s all collection does not let you change an ID of a component.

Let’s say… you wanted to, anyway. Add this:

Ext.ComponentMgr.all.changeKey = function(oldkey, newkey) {
    var comp = this.map[oldkey];
    var index = this.indexOfKey(oldkey);
    this.keys[index] = newkey;
    this.map[newkey] = comp;
    delete this.map[oldkey];
};

Is there anything really wrong with this approach?

7th Öresund JavaScript Meetup (and the next)

So that was the 7th meetup about JavaScript. We learnt stuff about extending Chrome, that WebKit browser. Mark Wubben taught us.

Thanks, Mark! You provided much more than a glorified walkthrough of the extant documentation, you gave us insight into how the workflow feels, and how a finished product can look.

That went well.

When one participant muttered “Anyone interested in… pizza?” we trudged throught the snow to Drottningtorget, around ten Open Source enthusiasts had gathered for the bi-weekly MOSIG social event. Yep, that’s short for Malmö Open Source Interest Group. Affectionately known as “Linux pizza”.

Next meetup date, in Copenhagen, is at 23′s headquarters in Vesterbro.

4th Øresund JavaScript Meetup report

The 4th Øresund JavaScript Meetup was just held at Hypergene‘s Malmö offices, yesterday night. Eight brave souls made the journey to Malmö C and came to Hypergene’s quite fancy offices. After a little pizza snack and introductory conversation, we repaired to a meeting room. Networking was in full swing, in at least three languages, when Jacob announced that he’d run his introductory JSpec presentation.

It turns out that JSpec is a BDD specification tool, which uses some Ruby to run its specs. The syntax was a mix of Ruby and JavaScript in the same file.

Mats Bryntse had quit his job to make an ExtJS web app, and he’ll be on his way to San Francisco in a few months. The app he made, Ext.ux.Scheduler is impressive.

Later, I got fiddly. Programming languages had come up in conversation. Scala! Clojure! Erlang! OCaml! So, I installed a few of them. We deciphered a bit of Scala, and tried it out. Quite entrancing, getting it to run, when four people stand around and shout suggestions. The Erlang Eclipse extension Erlide was hastily installed, but we ran out of time before we got anything done with it. And the title of the meetup is “Öresund JavaScript Meetup”. The irony was not lost on us.

“It’s dead easy to make Erlang web backends!”, I later exclaimed. “Can I quote you on that?” David retorted.

So, there’s my challenge. Using ErlyWeb, MochiWeb, and others, and perhaps Mnesia (the database), I’ll have to work hard until the next meetup to get an environment running – and make a web application.

Speaking of the next meetup, David, who’s working for streaming outfit XStream in Copenhagen, will try and convince his people that a JavaScript Meetup is just what they need to host, so we’ll probably be in Copenhagen next time.

What’s more? There were shoutouts to the Copenhagen PHP Meetup, and talk about coding dojos.

Oresund JavaScript Meetup tonight

Quoth the Öresund JS meetup locally here:

18.00 - 18.15 Arrive, have some free pizza and soda
18.15 - 18.30 Introduction of members and talk about the future of the group
18.30 - 18.40 Mats Bryntse - How "global" are the modern Ajax frameworks
18.50 - 19.10 Olle Jonsson - Building a Dojo application using Bespin (by Mozilla Labs)
19.10 - 19-20 Fredric Berling - "Hello world" with ExtJS
----------------------------------------------------------------

It’s tonight. Wanna come? I go from Malmö after work.

So, my title there, it’s a bit off, I’m going to show you how to become a Bespin hacker. What steps to take. Where to fix things. (Since there’s an ample list of things to fix – as in every healthy FOSS project.)

Hackmeetup on Javascript mini-report: RFID on Rhino

This past Tuesday, we ran another Hackmeetup. Same great location, same great time.

Mark ran a talk about Typography on the Web for an audience of three.

For the hardware experimentation session, Carl had left, so it was Mark, me, and David, and we all worked on the same project. Mark had brought a USB-connected RFID reader of the Phidgets brand, and the idea: Use Rhino to wrap the Phidgets Java API to the RFID reader in a JavaScript, so we could… script the thing.

Update: Physical.js is the software we wrote, now published under MIT license.

We printed out the Javadocs for the API, and began laughing at some of the names: getLEDOn(), getAntennaOn(). Then we whiteboard-sessioned up some simpler, JS-idiomatic names, and had our wishlist. Almost cheating: Rhino has __defineGetter__ and __defineSetter__. Then we scrambled to find out how Rhino’s Java-adapting mechanism really worked. Great fun! Mark was the only one with the Phidgets hardware and software platform installed, so he was typing what me and David sent to him.

Prototyping with Rhino is quick. Soon, we were able to read the RFID card that Mark brought, and also a keychain fob I had with me. When we had 30 minutes until the trains went, Mark said: Let’s also hook it up to chat Jabber! He installed an OpenFire XMPP server on his machine, created a “rfid” account, and had it send a message onTagEnter and onTagLeave (enter/leave sounds better than gain/loss: we’re web developers…). Then he took an extra step and made chat commands to enable/disable the LED and antenna.

We cheered. We’d done it all.

Conclusion: Working on the same project is very productive. This was the first time we had a theme, two speakers, a dropped talk, and gotten stuff Done and Ready and the end of the night. Also: snacks. Healthier snacks are better, so next time, let’s up the ante.

Crosscheck wheel spin

At the last meeting with Copenhagen.rb, I said I’d test out Crosscheck – a JavaScript testing framework, suited for continuous integration or command-line testing.

I had just tried getting it to run, following the slightly outdated Add Event tutorial. At last I got it to work, but I had to actually read the text, not just paste the examples. Better grab the example source code than read the slightly-outdated docs.

Tonight I have tested it in a little more real setting: I had a hunch of how I would be able to do a filtering function that worked like in Quicksilver or Textmate. A case-insensitive, very inclusive search that just narrows down based on the sequence of the characters, not on their immediate order. (Did that make sense?)

Expressed as a positive test case: “jy8″ should match “Jyri is happy to be 8 years of age”.

Anyway, I thought I could construct a regex on the fly, somehow. I itched to try. And I wanted to try it using tests along the way.

So, I entered the first, simplest case I could think of: equality. A list of one element should return only a single-element array. That was simple to do, so I could take on the next (that I could think of). And the rest was just implementation, until I hit something I had to test in Firebug.

The cool thing was that my experimentation was over as soon as I’d answered the question I had. Focus, the programmer’s holy grail. The promise of focus, at least. We’ll see about how that holds up in practice.

Here is the quite redundant, stupid list of test cases that I accumulated while trying things on:


crosscheck.onSetup(function(){
    crosscheck.load("scripts/quicksilverSearch.js");
});
crosscheck.addTest({
    testShouldFindSameMatchingString : function () {
        var outPut = quicksilverMatch('johnny', ['johnny']);
        var exp = ['johnny'];
        assertEquals( exp[0], outPut[0],
            'The same word could not be found in the simple array. Shame.' );
    },
    testShouldNotFindANonMatchingString : function () {
        var outPut = quicksilverMatch('jx', ['johnny']);
        assertTrue( outPut.length == 0, 'The word jx was found in johnny?! Shame.' );
    },
    testShouldMatchAllOnFirstLetter : function() {
        var outPut = quicksilverMatch('jo', ['johnny', 'jools', 'june']);
        assertTrue( outPut.length == 2,
            'Unable to match with indexOf on the first two letters. Shame.' );
    },
    testShouldMatchAcrossWords : function() {
        var outPut = quicksilverMatch('jy', ['jyri', 'johnny', 'june']);
        assertTrue( outPut.length == 2,
            'Unable to match across words. Shame.' );
    },
    testShouldMatchAcrossWordsCaseInsensitively : function() {
        var outPut = quicksilverMatch('jy8', ['Jyri 8 years', 'Johnny turns 8', 'june']);
        assertTrue( outPut.length == 2,
            'Unable to match case-ins. across words. Shame.' );
    }
});

So, the first thing that gets done before execution of each test case: load my script. That is specified in crosscheck.onSetup.

The least interesting part of this is my resulting code. After pulling it through JSLint and mulling over it a couple more times, it just shrunk. Which is the end goal of testing, I guess: to have me own as little code as possible. (We’ll come back to that.)


/**
 * "Quicksilver" filter search: returns an array of the haystack elements that
 * match case-insensitively the given term.
 * 2007-10-05, Olle
 */
function quicksilverMatch(term, haystack) {
    var matches = [];
    var straw; // The individual piece of hay
    // "t.*e.*r.*m" regular expression for loose matching
    var re = new RegExp(term.split("").join('.*'), "i");
    for (var idx = 0, len = haystack.length; idx < len; ++idx) {
        straw = haystack[idx];
        // Direct match inside word? Or: a case-ins. loose match
        if (straw.indexOf(term) > -1 || re.test(straw)) {
            matches.push(straw);
        }
    }
    return matches;
}

The take-home piece I got from this sitting was how to run Crosscheck. I created a wrapper shell script that I christened “crosschecka”, where I set my target browsers (“hosts”) and paths:


#!/bin/bash
#
# Run the crosscheck jar on given test path, or default
#
# We run on Firefox 1.5 and IE6: moz-1.8:ie-6
#
# Author: olle, 2007-09-26

CROSSCHECK_JAR=/Users/olle/lib/crosscheck-0.2.1/crosscheck.jar
# Colon-separated. Possible values are moz-1.7:moz-1.8:ie-6
HOSTS_TO_CHECK=moz-1.8:ie-6    

# Our test path is tests/eosweb/js, relative to the web app root
if [[ $1 == '' ]]; then
{
    java -jar $CROSSCHECK_JAR -hosts=$HOSTS_TO_CHECK tests/eosweb/js
    exit 0;
}
else
{
    java -jar $CROSSCHECK_JAR -hosts=$HOSTS_TO_CHECK $1
    exit 0;
}
fi

(I never got the file test in the else clause right, so I let Crosscheck complain for me.) The Rails-type win here is having a pre-set default, so I never have to think about it.

Thought: Maybe this whole testing thing is a way to get me to reduce the scope of my methods? To be testable, they have to be decomposed to parts that are cheap to test.

I said I’d come back to owning less code: now that I know these things about the behaviour of my code, do you think I should revisit and sharpen up my test-cases? For one thing, some of them might not even hit right. The first one, it seems very weak and situationally dependent.

Would you go back and change such stuff? Or delete such cases?