commit 0a5d683e0c293cae717b22fdafa7ca14decc1154
Author: James Halliday
Date: Mon Jan 23 11:23:13 2012 +0000

Testling is a browserling product that pkrumins and I put together to make running automated browser tests super easy.

Tests usually look something like this:

var test = require('testling');

test('json parse', function (t) {
    t.deepEqual(
        Object.keys({ a : 1, b : 2 }),
        [ 'a', 'b' ]
    );
    t.end();
});

Then you can run the tests on all the browsers we run using a curl one-liner like this one:

curl -sSNT test.js -u mail@substack.net \
  'http://testling.com/?browsers=chrome/16.0,firefox/9.0,safari/5.1,ie/9.0,ie/7.0'

and when the code blows up (in IE7 in this case because it doesn't have Object.keys), you get a full stack trace!

$ curl -sSNT test.js -u mail@substack.net \
  'http://testling.com/?browsers=chrome/16.0,firefox/9.0,safari/5.1,ie/9.0,ie/7.0'
Enter host password for user 'mail@substack.net':
Bundling...  done

chrome/16.0         1/1  100 % ok
firefox/9.0         1/1  100 % ok
safari/5.1          1/1  100 % ok
iexplore/9.0        1/1  100 % ok
iexplore/7.0        0/1    0 % ok
  Error: [object Error]
    at [anonymous]() in /test.js : line: 4, column: 5
    at keys() in /test.js : line: 5, column: 9
    at [anonymous]() in /test.js : line: 3, column: 29
    at test() in /test.js : line: 3, column: 1

  > t.deepEqual(
  >     Object.keys({ a : 1, b : 2 }),
  >     [ 'a', 'b' ]
  > );


total               4/5   80 % ok

Wow super great! Except perhaps you don't like how the standard test API looks and want something more jasmine-esque and bdd-ish.

Just write a little wrapper like this:

var testling = require('testling');

module.exports = function describe (dname, cb) {
    if (typeof dname === 'function') {
        dcb = dname;
        dname = undefined;
    }

    var ix = 0;
    cb(function it (iname, cb) {
        var name = (dname ? dname + ' :: ' : '') + (iname || 'test #' + ix++);

        testling(name, function (t) {
            var waiting = false;
            t.wait = function () { waiting = true };
            cb(t);
            if (!waiting) t.end();
        });
    });
};

And now you can write your tests like so, require()ing the bdd wrapper node.js-style:

var describe = require('./bdd');

describe('arrays', function (it) {
    it('should map', function (t) {
        t.deepEqual(
            [ 97, 98, 99 ].map(function (c) { return String.fromCharCode(c) }),
            [ 'a', 'b', 'c' ]
        );
    });

    it('can do indexOf', function (t) {
        t.equal([ 'a', 'b', 'c', 'd' ].indexOf('c'), 2);
    });
});

describe('tests', function (it) {
    it('can wait', function (t) {
        t.wait();

        var start = Date.now();
        setTimeout(function () {
            var elapsed = Date.now() - start;
            t.log(elapsed);
            t.ok(elapsed >= 100);
            t.end();
        }, 100);
    });
});

Now you have 2 files but you can still use a one-liner to stitch everything together:

$ tar -cf- bdd.js test.js | curl -sSNT- -u mail@substack.net \
  'testling.com/?browsers=chrome/16.0,firefox/9.0,iexplore/9.0'
Enter host password for user 'mail@substack.net':
Bundling...  done

chrome/16.0         3/3  100 % ok
  Log: 101
firefox/9.0         3/3  100 % ok
  Log: 115
iexplore/9.0        2/3   66 % ok
  Log: 83
  Assertion Error: not ok
    at [anonymous]() in /test.js : line: 24, column: 13
    at ok() in /test.js : line: 24, column: 13
    at [anonymous]() in /test.js : line: 21, column: 29
    at setTimeout() in /test.js : line: 21, column: 9
    at [anonymous]() in /test.js : line: 17, column: 29
    at it() in /test.js : line: 17, column: 5
    at [anonymous]() in /test.js : line: 16, column: 28

  > t.ok(elapsed >= 100);


total               8/9   88 % ok

...and strangely enough, IE9 only sleeps for 83 milliseconds when you tell it to sleep for 100! TYPICAL.

Check out the testling documentation, create a browserling account to use with testling, and hack up some crazy browser tests and test runners!

more
git clone http://substack.net/blog.git