321 lines
6.7 KiB
JavaScript
Executable File
321 lines
6.7 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Module dependencies.
|
|
*/
|
|
|
|
var program = require('commander')
|
|
, exec = require('child_process').exec
|
|
, path = require('path')
|
|
, resolve = path.resolve
|
|
, mocha = require('../')
|
|
, utils = mocha.utils
|
|
, reporters = mocha.reporters
|
|
, interfaces = mocha.interfaces
|
|
, Runner = mocha.Runner
|
|
, Suite = mocha.Suite
|
|
, vm = require('vm')
|
|
, fs = require('fs')
|
|
, join = path.join
|
|
, cwd = process.cwd();
|
|
|
|
/**
|
|
* Files.
|
|
*/
|
|
|
|
var files = [];
|
|
|
|
/**
|
|
* Images.
|
|
*/
|
|
|
|
var images = {
|
|
fail: __dirname + '/../images/error.png'
|
|
, pass: __dirname + '/../images/ok.png'
|
|
};
|
|
|
|
// options
|
|
|
|
program
|
|
.version(mocha.version)
|
|
.usage('[options] [files]')
|
|
.option('-r, --require <name>', 'require the given module')
|
|
.option('-R, --reporter <name>', 'specify the reporter to use', 'dot')
|
|
.option('-u, --ui <name>', 'specify user-interface (bdd|tdd|exports)', 'bdd')
|
|
.option('-g, --grep <pattern>', 'only run tests matching <pattern>')
|
|
.option('-t, --timeout <ms>', 'set test-case timeout in milliseconds [2000]')
|
|
.option('-s, --slow <ms>', '"slow" test threshold in milliseconds [75]', parseInt)
|
|
.option('-w, --watch', 'watch files for changes')
|
|
.option('-c, --colors', 'force enabling of colors')
|
|
.option('-C, --no-colors', 'force disabling of colors')
|
|
.option('-G, --growl', 'enable growl notification support')
|
|
.option('-d, --debug', "enable node's debugger")
|
|
.option('-b, --bail', "bail after first test failure")
|
|
.option('--globals <names>', 'allow the given comma-delimited global [names]', list, [])
|
|
.option('--ignore-leaks', 'ignore global variable leaks')
|
|
.option('--interfaces', 'display available interfaces')
|
|
.option('--reporters', 'display available reporters')
|
|
|
|
program.name = 'mocha';
|
|
|
|
// --reporters
|
|
|
|
program.on('reporters', function(){
|
|
console.log();
|
|
console.log(' dot - dot matrix');
|
|
console.log(' doc - html documentation');
|
|
console.log(' spec - hierarchical spec list');
|
|
console.log(' json - single json object');
|
|
console.log(' progress - progress bar');
|
|
console.log(' list - spec-style listing');
|
|
console.log(' tap - test-anything-protocol');
|
|
console.log(' landing - unicode landing strip');
|
|
console.log(' xunit - xunit reportert');
|
|
console.log(' teamcity - teamcity ci support');
|
|
console.log(' json-stream - newline delimited json events');
|
|
console.log();
|
|
process.exit();
|
|
});
|
|
|
|
// --interfaces
|
|
|
|
program.on('interfaces', function(){
|
|
console.log('');
|
|
console.log(' bdd');
|
|
console.log(' tdd');
|
|
console.log(' qunit');
|
|
console.log(' exports');
|
|
console.log('');
|
|
process.exit();
|
|
});
|
|
|
|
// -r, --require
|
|
|
|
module.paths.push(cwd, join(cwd, 'node_modules'));
|
|
|
|
program.on('require', function(mod){
|
|
var abs = path.existsSync(mod)
|
|
|| path.existsSync(mod + '.js');
|
|
|
|
if (abs) mod = join(cwd, mod);
|
|
require(mod);
|
|
});
|
|
|
|
// mocha.opts support
|
|
|
|
try {
|
|
var opts = fs.readFileSync('test/mocha.opts', 'utf8')
|
|
.trim()
|
|
.split(/\s+/);
|
|
|
|
process.argv = process.argv
|
|
.slice(0, 2)
|
|
.concat(opts.concat(process.argv.slice(2)));
|
|
} catch (err) {
|
|
// ignore
|
|
}
|
|
|
|
// parse args
|
|
|
|
program.parse(process.argv);
|
|
|
|
// infinite stack traces
|
|
|
|
Error.stackTraceLimit = Infinity; // TODO: config
|
|
|
|
// reporter
|
|
|
|
var suite = new Suite('')
|
|
, Base = require('../lib/reporters/base')
|
|
, Reporter = require('../lib/reporters/' + program.reporter)
|
|
, ui = interfaces[program.ui](suite);
|
|
|
|
// --no-colors
|
|
|
|
if (!program.colors) Base.useColors = false;
|
|
|
|
// --colors
|
|
|
|
if (~process.argv.indexOf('--colors') ||
|
|
~process.argv.indexOf('-c')) {
|
|
Base.useColors = true;
|
|
}
|
|
|
|
// --slow <ms>
|
|
|
|
if (program.slow) Base.slow = program.slow;
|
|
|
|
// --timeout
|
|
|
|
if (program.timeout) suite.timeout(program.timeout);
|
|
|
|
// --bail
|
|
|
|
suite.bail(program.bail);
|
|
|
|
// files
|
|
|
|
var files = program.args
|
|
, re = /\.js$/;
|
|
|
|
// coffee-script support
|
|
|
|
try {
|
|
require('coffee-script');
|
|
re = /\.(js|coffee)$/;
|
|
} catch (err) {
|
|
// ignore
|
|
}
|
|
|
|
// default files to test/*.{js,coffee}
|
|
|
|
if (!files.length) {
|
|
files = fs.readdirSync('test').filter(function(path){
|
|
return path.match(re);
|
|
}).map(function(path){
|
|
return join('test', path);
|
|
});
|
|
}
|
|
|
|
// resolve
|
|
|
|
files = files.map(function(path){
|
|
return resolve(path);
|
|
});
|
|
|
|
// --watch
|
|
|
|
if (program.watch) {
|
|
console.log();
|
|
hideCursor();
|
|
process.on('SIGINT', function(){
|
|
showCursor();
|
|
console.log('\n');
|
|
process.exit();
|
|
});
|
|
|
|
var frames = [
|
|
' \033[96m◜ \033[90mwatching\033[0m'
|
|
, ' \033[96m◠ \033[90mwatching\033[0m'
|
|
, ' \033[96m◝ \033[90mwatching\033[0m'
|
|
, ' \033[96m◞ \033[90mwatching\033[0m'
|
|
, ' \033[96m◡ \033[90mwatching\033[0m'
|
|
, ' \033[96m◟ \033[90mwatching\033[0m'
|
|
];
|
|
|
|
play(frames);
|
|
|
|
utils.watch(utils.files(cwd), function(){
|
|
stop()
|
|
suite = suite.clone();
|
|
ui = interfaces[program.ui](suite);
|
|
load(files, function(){
|
|
run(suite, function(){
|
|
play(frames);
|
|
});
|
|
});
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
// load
|
|
|
|
load(files, function(){
|
|
run(suite, process.exit);
|
|
});
|
|
|
|
// require test files before
|
|
// running the root suite
|
|
|
|
function load(files, fn) {
|
|
var pending = files.length;
|
|
files.forEach(function(file){
|
|
delete require.cache[file];
|
|
suite.emit('pre-require', global, file);
|
|
suite.emit('require', require(file), file);
|
|
suite.emit('post-require', global, file);
|
|
--pending || fn();
|
|
});
|
|
}
|
|
|
|
// run the given suite
|
|
|
|
function run(suite, fn) {
|
|
suite.emit('run');
|
|
var runner = new Runner(suite);
|
|
var reporter = new Reporter(runner);
|
|
runner.globals(program.globals);
|
|
if (program.ignoreLeaks) runner.ignoreLeaks = true;
|
|
if (program.grep) runner.grep(new RegExp(program.grep));
|
|
if (program.growl) growl(runner, reporter);
|
|
runner.run(fn);
|
|
}
|
|
|
|
// enable growl notifications
|
|
|
|
function growl(runner, reporter) {
|
|
var notify = require('growl');
|
|
|
|
runner.on('end', function(){
|
|
var stats = reporter.stats;
|
|
if (stats.failures) {
|
|
var msg = stats.failures + ' of ' + runner.total + ' tests failed';
|
|
notify(msg, { title: 'Failed', image: images.fail });
|
|
} else {
|
|
notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', {
|
|
title: 'Passed'
|
|
, image: images.pass
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Parse list.
|
|
*/
|
|
|
|
function list(str) {
|
|
return str.split(/ *, */);
|
|
}
|
|
|
|
/**
|
|
* Hide the cursor.
|
|
*/
|
|
|
|
function hideCursor(){
|
|
process.stdout.write('\033[?25l');
|
|
};
|
|
|
|
/**
|
|
* Show the cursor.
|
|
*/
|
|
|
|
function showCursor(){
|
|
process.stdout.write('\033[?25h');
|
|
};
|
|
|
|
/**
|
|
* Stop play()ing.
|
|
*/
|
|
|
|
function stop() {
|
|
process.stdout.write('\033[2K');
|
|
clearInterval(play.timer);
|
|
}
|
|
|
|
/**
|
|
* Play the given array of strings.
|
|
*/
|
|
|
|
function play(arr, interval) {
|
|
var len = arr.length
|
|
, interval = interval || 100
|
|
, i = 0;
|
|
|
|
play.timer = setInterval(function(){
|
|
var str = arr[i++ % len];
|
|
process.stdout.write('\r' + str);
|
|
}, interval);
|
|
}
|