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!
I would like to document an emerging set of programming conventions,
philosophies, and values that I see evolving in the
node.js community.
I call this the node aesthetic.
callback austerity
The very first example of node you are likely to see is on the
node.js home page.
This snippet is exemplary of the the radical simplicity pioneered by
projects like sinatra.
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1337, "127.0.0.1");
console.log('Server running at http://127.0.0.1:1337/');
Instead of the http server being an external service that we
configure to run our code, it becomes just another tool in our arsenal.
Want to spin up a second http server on a different port? Super easy: just call
http.createServer() once more.
Want to print out how many requests per second you're averaging every minute?
Just plop down a counter and a setInterval().
Node makes what was previously merely configurable into something more
programmable, orthogonal, and more powerful.
A big part of what empowers node to make these kinds of interfaces possible is
its asynchronous nature. No longer do you have statelessness imposed from on
high by the likes of apache or rails. You can keep intermediate state around in
memory just like in any ordinary program. You don't need to reason about
multiple instruction pointers or mutexes or re-entrant, interruptible
execution because all the javascript you write just lives in a single thread.
Depending on the event system to already just be there means that the
end-users of modules don't have to think about what hoops they need to jump
through in order to get the event reactor up, running, and plugged into the
component they're trying to use.
The event machinery is all running quietly under the hood without getting in the
way or making a fuss.
limited surface area
Note also that the http snippet from earlier isn't inheriting from an
http.Server base class or anything of the sort.
The primary focus of most node modules is on using, not extending.
In classical object-oriented design, it's customary to define specific custom
functionality by extending more abstract classes. This idea flows pretty
naturally from thinking about how you might write a compiler to efficiently pack
collections of related properties into memory blocks, but is not such a great
approach for writing usable and perhaps more importantly,
re-usable interfaces.
Simple function calls with callback arguments have the superficial but important
benefit of having less exposed surface area and more encapsulation than most
classical designs.
But moreover, when you extend a base class, your class is somewhat
stuck with the interface and internals that the base class uses.
If you want to make things prettier you'll need to shave some yaks to write
wrapper classes just to get around the restrictions of the class system.
Sometimes this classical style of reusability is appropriate, but in node it is
the exception, not the rule, because the implicit consensus is that the
interface is paramount, so usability should trump extensibility.
A big part of what makes node modules so great is how they tend to have really
obvious entry points as a consequence of focusing on usability and limited
surface area.
Many modules on npm export only a single function
by assigning onto module.exports such that
require('modulename') returns just that single
function of interest. It also helps that module names have a direct
correspondence to the string that you put in your call to
require() and that
require() just returns the module
instead of modifying the environment to include the module.
This is known in some circles as doing a qualified import.
In node you can't even do any other kind of import without getting really
hackish about it and this is a very good thing.
This import style means that when you fire up the REPL to play with a new module
when you do require('modulename') you can see
exactly what functionality 'modulename' provides.
Even more importantly, it means that when you read through some new foreign
piece of code that uses many modules, you can tell
exactly where all the module exports came from.
batteries not included
If software ecosystems are like economies then core libraries are at best
government bureaucracies and at worst nationalized state corporations.
That is, modules in the core distribution get an unfair advantage over the
libraries in userspace by virtue of availability and prominance.
Worse still, because these modules are all maintained in the core project,
contributing, experimenting, and iterating is much harder than with a typical
library on npm. This is especially compounded by the fact that core libraries
are pegged to the core project version and don't often have independent
versioning.
Core distributions with too many modules result in neglected code that can't
make meaningful changes without breaking everything. If a neglected library
lives in userspace then at least people can ignore it more easily and the
independent versioning makes necessary breaking changes easier to mitigate for
legacy code.
The "batteries included" approach may have struck a worthwhile tradeoff back
when package managers were primitive and modules were global, but that advantage
evaporates in the face of baked-in concurrent library versioning and
sophisticated package management.
radical reusability
While the limited surface area approach can hurt extensibility,
you can win a great deal of extensibility back by breaking up
problems into lots of tiny modules.
When you write a module for general enough consumption to put up on
npm,
you can't contaminate your own implementation with too many
implementation-specific details. It's also much easier to test and iterate on
code in complete isolation from your application business logic.
Node and npm go to great lengths to help you do
this.
In node the only modules that can be said to be "global" are compiled into the
binary itself. There is not really such a thing as a global, system-level module
anymore. Instead, when you run a script that performs a
require(), node will search for the module in a
"node_modules" directory from the directory that the script is in all the way
down to "/", stopping at the nearest "node_modules" directory.
Similarly, when you do an npm install, node will place
the modules into the nearest "node_modules" directory or "./node_modules".
All of this preferred locality in the module loader has the very important
practical upshot in that you can depend on different versions of modules
in different places in your program's dependency graph and node will pick the
most local dependency for each module.
So if a module "foo" was tested against and depends on "baz@0.1.x" and a module
"bar" depends on "baz@0.2.x", then when you go to use both "foo" and "bar" in
your own program, "foo" and "bar" will both use the version of "baz" that they
were tested and depend against!
This approach to versioning means that nearly all modules should be
interoperable with each other. It also means that module authors can be more
aggressive with iterating and pushing out backwards-incompatible breaking
changes to their APIs, so long as the authors are careful to increment the
module version appropriately.
Concurrent versioning and locality preference drastically accelerate the rate
that we can iterate on new experiments and the levels of reuse that we can
attain both in a single large project and among projects.
the future
I am wildly optimistic about where this emerging aesthetic of radical
reusability and module-driven development will take node and programming in
general.
Introducing bouncy, a
websocket and https-capable http router proxy / load balancer in node.js!
It's super simple to use, just call bounce()!
var bouncy = require('bouncy');
bouncy(function (req, bounce) {
if (req.headers.host === 'bouncy.example.com') {
bounce(8000);
}
else if (req.headers.host === 'trampoline.example.com') {
bounce(8001)
}
}).listen(80);
You can bounce() to a port, a
host, port, or a stream you open yourself.
Since bouncy is just parsing the http headers and sending along the raw tcp
stream, you can use websockets on the place you
bounce() to without writing any special code!
Bouncy uses node's delicious http parsing innards, so the
req
object you get is a bona-fide
http.ServerRequest with all the fixins.
Plus, bouncy comes with a simple command-line tool if you have a static routing
table kicking around in a json file. Just throw a
routes.json like this one:
{
"beep.example.com" : 8000,
"boop.example.com" : 8001
}
at the bouncy command and give it a port to listen on:
bouncy routes.json 80
Super easy!
Check out the code on github
or with npm do:
npm install bouncy
to install the library or
npm install -g bouncy
to install the command-line tool.
This weekend I took part in
node knockout
along with
Peteris Krumins,
David Wee,
and Josh Holbrook
on
team replicants.
Our entry, heatwave,
uses node-heatmap
to draw a heatmap over your code in realtime to show the parts that are most
active as the code executes.
Heatwave accomplishes this trick using
bunker
to add hooks around every expression.
When these hooks get called, the heatmap gets updated!
Check out the live demo
and if you like what you see,
vote for us!
Introducing node-heatmap,
a nifty node.js module for making pretty heatmaps.
You can even
use node-heatmap in the browser
with browserify.
Here's an example of using heatmap in node with some random values biased
towards the center of the canvas:
var heatmap = require('heatmap');
var heat = heatmap(500, 500, { radius : 30 });
for (var i = 0; i < 5000; i++) {
var rho = Math.random() * 2 * Math.PI;
var z = Math.pow(Math.random(), 2) * 200;
var x = 250 + Math.cos(rho) * z;
var y = 250 + Math.sin(rho) * z;
heat.addPoint(x, y);
}
heat.draw();
var fs = require('fs');
fs.writeFileSync('blob.png', heat.canvas.toBuffer());
When the example calls .addPoint(x,y), heatmap draws a
rectangle with a radial gradient and outwardly diminishing alpha mask so that
the alpha values can pile up as nearby points are added. With Canvas this is
surprisingly easy to do:
Heat.prototype.addPoint = function (x, y, radius) {
var ctx = this.alphaCanvas.getContext('2d');
var g = ctx.createRadialGradient(x, y, 0, x, y, radius);
var a = 1 / 10;
g.addColorStop(0, 'rgba(255,255,255,' + a + ')');
g.addColorStop(1, 'rgba(255,255,255,0)');
ctx.fillStyle = g;
ctx.fillRect(x - radius, y - radius, radius * 2, radius * 2);
};
I got the idea to use a secondary canvas's alpha channel with radial gradients
from heatmap.js,
which is yet another heatmap module which may be of interest.
Later when it comes time to .draw() onto the primary
canvas, I just use
color-convert
to convert an
HSL
coordinate to rgb.
HSL
is super neat because the hue is just an angle, so a loop of
i += 30 from 0 to 360 degrees hits all the rainbow
colors along the way.
All .draw() needs to do is
map the composite alpha value as the HSL
hue
at every pixel onto the RGB of the primary canvas.
Easy!
And here's what the final product from the example code at the start looks like:
Radical.
Check out the code on github
and npm install heatmap!
Mr. Color approves of this
module.