217 lines
6.0 KiB
JavaScript
217 lines
6.0 KiB
JavaScript
/*!
|
|
* Nodeunit
|
|
* Copyright (c) 2010 Caolan McMahon
|
|
* MIT Licensed
|
|
*/
|
|
|
|
/**
|
|
* Module dependencies
|
|
*/
|
|
|
|
var async = require('../deps/async'),
|
|
fs = require('fs'),
|
|
util = require('util'),
|
|
Script = require('vm').Script,
|
|
http = require('http');
|
|
|
|
|
|
/**
|
|
* Detect if coffee-script, iced-coffeescript, or streamline are available and
|
|
* the respective file extensions to the search filter in modulePaths if it is.
|
|
*/
|
|
|
|
var extensions = [ 'js' ]; // js is always supported: add it unconditionally
|
|
var extensionPattern;
|
|
|
|
try {
|
|
require('coffee' + '-script/register');
|
|
extensions.push('coffee');
|
|
} catch (e) { }
|
|
|
|
try {
|
|
require('iced-coffee' + '-script/register');
|
|
extensions.push('iced');
|
|
} catch (e) { }
|
|
|
|
try {
|
|
require('stream' + 'line').register();
|
|
extensions.push('_coffee');
|
|
extensions.push('_js');
|
|
} catch (e) { }
|
|
|
|
extensionPattern = new RegExp('\\.(?:' + extensions.join('|') + ')$');
|
|
|
|
|
|
/**
|
|
* Finds all modules at each path in an array, If a path is a directory, it
|
|
* returns all supported file types inside it. This only reads 1 level deep in
|
|
* the directory and does not recurse through sub-directories.
|
|
*
|
|
* The extension (.js, .coffee etc) is stripped from the filenames so they can
|
|
* simply be require()'ed.
|
|
*
|
|
* @param {Array} paths
|
|
* @param {Function} callback
|
|
* @api public
|
|
*/
|
|
|
|
exports.modulePaths = function (paths, callback) {
|
|
async.concat(paths, function (p, cb) {
|
|
fs.stat(p, function (err, stats) {
|
|
if (err) {
|
|
return cb(err);
|
|
}
|
|
if (stats.isFile()) {
|
|
return cb(null, [p]);
|
|
}
|
|
if (stats.isDirectory()) {
|
|
fs.readdir(p, function (err, files) {
|
|
if (err) {
|
|
return cb(err);
|
|
}
|
|
|
|
// filter out any filenames with unsupported extensions
|
|
var modules = files.filter(function (filename) {
|
|
return extensionPattern.exec(filename);
|
|
});
|
|
|
|
// remove extension from module name and prepend the
|
|
// directory path
|
|
var fullpaths = modules.map(function (filename) {
|
|
var mod_name = filename.replace(extensionPattern, '');
|
|
return [p, mod_name].join('/');
|
|
});
|
|
|
|
// sort filenames here, because Array.map changes order
|
|
fullpaths.sort();
|
|
|
|
cb(null, fullpaths);
|
|
});
|
|
}
|
|
});
|
|
}, callback);
|
|
};
|
|
|
|
/**
|
|
* Evaluates JavaScript files in a sandbox, returning the context. The first
|
|
* argument can either be a single filename or an array of filenames. If
|
|
* multiple filenames are given their contents are concatenated before
|
|
* evalution. The second argument is an optional context to use for the sandbox.
|
|
*
|
|
* @param files
|
|
* @param {Object} sandbox
|
|
* @return {Object}
|
|
* @api public
|
|
*/
|
|
|
|
exports.sandbox = function (files, /*optional*/sandbox) {
|
|
var source, script, result;
|
|
if (!(files instanceof Array)) {
|
|
files = [files];
|
|
}
|
|
source = files.map(function (file) {
|
|
return fs.readFileSync(file, 'utf8');
|
|
}).join('');
|
|
|
|
if (!sandbox) {
|
|
sandbox = {};
|
|
}
|
|
script = new Script(source);
|
|
result = script.runInNewContext(sandbox);
|
|
return sandbox;
|
|
};
|
|
|
|
/**
|
|
* Provides a http request, response testing environment.
|
|
*
|
|
* Example:
|
|
*
|
|
* var httputil = require('nodeunit').utils.httputil
|
|
* exports.testSomething = function(test) {
|
|
* httputil(function (req, resp) {
|
|
* resp.writeHead(200, {});
|
|
* resp.end('test data');
|
|
* },
|
|
* function(server, client) {
|
|
* client.fetch('GET', '/', {}, function(resp) {
|
|
* test.equal('test data', resp.body);
|
|
* server.close();
|
|
* test.done();
|
|
* })
|
|
* });
|
|
* };
|
|
*
|
|
* @param {Function} cgi
|
|
* @param {Function} envReady
|
|
* @api public
|
|
*/
|
|
exports.httputil = function (cgi, envReady) {
|
|
var hostname = process.env.HOSTNAME || 'localhost';
|
|
var port = process.env.PORT || 3000;
|
|
|
|
var server = http.createServer(cgi);
|
|
server.listen(port, hostname);
|
|
|
|
var client = http.createClient(port, hostname);
|
|
client.fetch = function (method, path, headers, respReady) {
|
|
var request = this.request(method, path, headers);
|
|
request.end();
|
|
request.on('response', function (response) {
|
|
response.setEncoding('utf8');
|
|
response.on('data', function (chunk) {
|
|
if (response.body) {
|
|
response.body += chunk;
|
|
} else {
|
|
response.body = chunk;
|
|
}
|
|
});
|
|
response.on('end', function () {
|
|
if (response.headers['content-type'] === 'application/json') {
|
|
response.bodyAsObject = JSON.parse(response.body);
|
|
}
|
|
respReady(response);
|
|
});
|
|
});
|
|
};
|
|
|
|
process.nextTick(function () {
|
|
if (envReady && typeof envReady === 'function') {
|
|
envReady(server, client);
|
|
}
|
|
});
|
|
};
|
|
|
|
|
|
/**
|
|
* Improves formatting of AssertionError messages to make deepEqual etc more
|
|
* readable.
|
|
*
|
|
* @param {Object} assertion
|
|
* @return {Object}
|
|
* @api public
|
|
*/
|
|
|
|
exports.betterErrors = function (assertion) {
|
|
if (!assertion.error) {
|
|
return assertion;
|
|
}
|
|
var e = assertion.error;
|
|
if (e.actual && e.expected) {
|
|
var actual = util.inspect(e.actual, false, 10).replace(/\n$/, '');
|
|
var expected = util.inspect(e.expected, false, 10).replace(/\n$/, '');
|
|
var multiline = (
|
|
actual.indexOf('\n') !== -1 ||
|
|
expected.indexOf('\n') !== -1
|
|
);
|
|
var spacing = (multiline ? '\n' : ' ');
|
|
e._message = e.message;
|
|
e.stack = (
|
|
e.name + ':' + spacing +
|
|
actual + spacing + e.operator + spacing +
|
|
expected + '\n' +
|
|
e.stack.split('\n').slice(1).join('\n')
|
|
);
|
|
}
|
|
return assertion;
|
|
};
|