js-algorithms/node_modules/mocha/bin/_mocha
2015-07-24 11:14:18 +04:30

493 lines
12 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Module dependencies.
*/
var program = require('commander'),
path = require('path'),
fs = require('fs'),
resolve = path.resolve,
exists = fs.existsSync || path.existsSync,
Mocha = require('../'),
utils = Mocha.utils,
join = path.join,
cwd = process.cwd(),
getOptions = require('./options'),
mocha = new Mocha;
/**
* Save timer references to avoid Sinon interfering (see GH-237).
*/
var Date = global.Date
, setTimeout = global.setTimeout
, setInterval = global.setInterval
, clearTimeout = global.clearTimeout
, clearInterval = global.clearInterval;
/**
* Files.
*/
var files = [];
/**
* Globals.
*/
var globals = [];
/**
* Requires.
*/
var requires = [];
/**
* Images.
*/
var images = {
fail: __dirname + '/../images/error.png'
, pass: __dirname + '/../images/ok.png'
};
// options
program
.version(JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version)
.usage('[debug] [options] [files]')
.option('-A, --async-only', "force all tests to take a callback (async)")
.option('-c, --colors', 'force enabling of colors')
.option('-C, --no-colors', 'force disabling of colors')
.option('-G, --growl', 'enable growl notification support')
.option('-O, --reporter-options <k=v,k2=v2,...>', 'reporter-specific options')
.option('-R, --reporter <name>', 'specify the reporter to use', 'spec')
.option('-S, --sort', "sort test files")
.option('-b, --bail', "bail after first test failure")
.option('-d, --debug', "enable node's debugger, synonym for node --debug")
.option('-g, --grep <pattern>', 'only run tests matching <pattern>')
.option('-f, --fgrep <string>', 'only run tests containing <string>')
.option('-gc, --expose-gc', 'expose gc extension')
.option('-i, --invert', 'inverts --grep and --fgrep matches')
.option('-r, --require <name>', 'require the given module')
.option('-s, --slow <ms>', '"slow" test threshold in milliseconds [75]')
.option('-t, --timeout <ms>', 'set test-case timeout in milliseconds [2000]')
.option('-u, --ui <name>', 'specify user-interface (bdd|tdd|exports)', 'bdd')
.option('-w, --watch', 'watch files for changes')
.option('--check-leaks', 'check for global variable leaks')
.option('--full-trace', 'display the full stack trace')
.option('--compilers <ext>:<module>,...', 'use the given module(s) to compile files', list, [])
.option('--debug-brk', "enable node's debugger breaking on the first line")
.option('--globals <names>', 'allow the given comma-delimited global [names]', list, [])
.option('--harmony', 'enable all harmony features (except typeof)')
.option('--es_staging', 'enable all staged features')
.option('--harmony-collections', 'enable harmony collections (sets, maps, and weak maps)')
.option('--harmony-generators', 'enable harmony generators')
.option('--harmony-proxies', 'enable harmony proxies')
.option('--harmony_shipping', 'enable all shipped harmony fetaures (iojs)')
.option('--harmony_arrow_functions', 'enable "harmony arrow functions" (iojs)')
.option('--harmony_proxies', 'enable "harmony proxies" (iojs)')
.option('--harmony_classes', 'enable "harmony classes" (iojs)')
.option('--inline-diffs', 'display actual/expected differences inline within each string')
.option('--interfaces', 'display available interfaces')
.option('--no-deprecation', 'silence deprecation warnings')
.option('--no-exit', 'require a clean shutdown of the event loop: mocha will not call process.exit')
.option('--no-timeouts', 'disables timeouts, given implicitly with --debug')
.option('--opts <path>', 'specify opts path', 'test/mocha.opts')
.option('--prof', 'log statistical profiling information')
.option('--recursive', 'include sub directories')
.option('--reporters', 'display available reporters')
.option('--throw-deprecation', 'throw an exception anytime a deprecated function is used')
.option('--trace', 'trace function calls')
.option('--trace-deprecation', 'show stack traces on deprecations')
.option('--watch-extensions <ext>,...', 'additional extensions to monitor with --watch', list, [])
.option('--delay', 'wait for async suite definition')
program.name = 'mocha';
// init command
program
.command('init <path>')
.description('initialize a client-side mocha setup at <path>')
.action(function(path){
var mkdir = require('mkdirp');
mkdir.sync(path);
var css = fs.readFileSync(join(__dirname, '..', 'mocha.css'));
var js = fs.readFileSync(join(__dirname, '..', 'mocha.js'));
var tmpl = fs.readFileSync(join(__dirname, '..', 'lib/template.html'));
fs.writeFileSync(join(path, 'mocha.css'), css);
fs.writeFileSync(join(path, 'mocha.js'), js);
fs.writeFileSync(join(path, 'tests.js'), '');
fs.writeFileSync(join(path, 'index.html'), tmpl);
process.exit(0);
});
// --globals
program.on('globals', function(val){
globals = globals.concat(list(val));
});
// --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 reporter');
console.log(' html-cov - HTML test coverage');
console.log(' json-cov - JSON test coverage');
console.log(' min - minimal reporter (great with --watch)');
console.log(' json-stream - newline delimited json events');
console.log(' markdown - markdown documentation (github flavour)');
console.log(' nyan - nyan cat!');
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 = exists(mod) || exists(mod + '.js');
if (abs) mod = resolve(mod);
requires.push(mod);
});
// load mocha.opts into process.argv
getOptions();
// parse args
program.parse(process.argv);
// infinite stack traces
Error.stackTraceLimit = Infinity; // TODO: config
// reporter options
var reporterOptions = {};
if (program.reporterOptions !== undefined) {
program.reporterOptions.split(",").forEach(function(opt) {
var L = opt.split("=");
if (L.length != 2) {
throw new Error("invalid reporter option '" + opt + "'");
}
reporterOptions[L[0]] = L[1];
});
}
// reporter
mocha.reporter(program.reporter, reporterOptions);
// interface
mocha.ui(program.ui);
// load reporter
try {
Reporter = require('../lib/reporters/' + program.reporter);
} catch (err) {
try {
Reporter = require(program.reporter);
} catch (err) {
throw new Error('reporter "' + program.reporter + '" does not exist');
}
}
// --no-colors
if (!program.colors) mocha.useColors(false);
// --colors
if (~process.argv.indexOf('--colors') ||
~process.argv.indexOf('-c')) {
mocha.useColors(true);
}
// --inline-diffs
if (program.inlineDiffs) mocha.useInlineDiffs(true);
// --slow <ms>
if (program.slow) mocha.suite.slow(program.slow);
// --no-timeouts
if (!program.timeouts) mocha.enableTimeouts(false);
// --timeout
if (program.timeout) mocha.suite.timeout(program.timeout);
// --bail
mocha.suite.bail(program.bail);
// --grep
if (program.grep) mocha.grep(new RegExp(program.grep));
// --fgrep
if (program.fgrep) mocha.grep(program.fgrep);
// --invert
if (program.invert) mocha.invert();
// --check-leaks
if (program.checkLeaks) mocha.checkLeaks();
// --stack-trace
if(program.fullTrace) mocha.fullTrace();
// --growl
if (program.growl) mocha.growl();
// --async-only
if (program.asyncOnly) mocha.asyncOnly();
// --delay
if (program.delay) mocha.delay();
// --globals
mocha.globals(globals);
// custom compiler support
var extensions = ['js'];
program.compilers.forEach(function(c) {
var compiler = c.split(':')
, ext = compiler[0]
, mod = compiler[1];
if (mod[0] == '.') mod = join(process.cwd(), mod);
require(mod);
extensions.push(ext);
program.watchExtensions.push(ext);
});
// requires
requires.forEach(function(mod) {
require(mod);
});
//args
var args = program.args;
// default files to test/*.{js,coffee}
if (!args.length) args.push('test');
args.forEach(function(arg){
files = files.concat(utils.lookupFiles(arg, extensions, program.recursive));
});
// resolve
files = files.map(function(path){
return resolve(path);
});
if (program.sort) {
files.sort();
}
// --watch
var runner;
if (program.watch) {
console.log();
hideCursor();
process.on('SIGINT', function(){
showCursor();
console.log('\n');
process.exit();
});
var watchFiles = utils.files(cwd, [ 'js' ].concat(program.watchExtensions));
var runAgain = false;
function loadAndRun() {
try {
mocha.files = files;
runAgain = false;
runner = mocha.run(function(){
runner = null;
if (runAgain) {
rerun();
}
});
} catch(e) {
console.log(e.stack);
}
}
function purge() {
watchFiles.forEach(function(file){
delete require.cache[file];
});
}
loadAndRun();
function rerun() {
purge();
stop()
if (!program.grep)
mocha.grep(null);
mocha.suite = mocha.suite.clone();
mocha.suite.ctx = new Mocha.Context;
mocha.ui(program.ui);
loadAndRun();
}
utils.watch(watchFiles, function(){
runAgain = true;
if (runner) {
runner.abort();
} else {
rerun();
}
});
return;
}
// load
mocha.files = files;
runner = mocha.run(program.exit ? exit : exitLater);
function exitLater(code) {
process.on('exit', function() { process.exit(code) })
}
function exit(code) {
// flush output for Node.js Windows pipe bug
// https://github.com/joyent/node/issues/6247 is just one bug example
// https://github.com/visionmedia/mocha/issues/333 has a good discussion
function done() {
if (!(draining--)) process.exit(code);
}
var draining = 0;
var streams = [process.stdout, process.stderr];
streams.forEach(function(stream){
// submit empty write request and wait for completion
draining += 1;
stream.write('', done);
});
done();
}
process.on('SIGINT', function() { runner.abort(); })
// 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, { name: 'mocha', title: 'Failed', image: images.fail });
} else {
notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', {
name: 'mocha'
, title: 'Passed'
, image: images.pass
});
}
});
}
/**
* Parse list.
*/
function list(str) {
return str.split(/ *, */);
}
/**
* Hide the cursor.
*/
function hideCursor(){
process.stdout.write('\u001b[?25l');
}
/**
* Show the cursor.
*/
function showCursor(){
process.stdout.write('\u001b[?25h');
}
/**
* Stop play()ing.
*/
function stop() {
process.stdout.write('\u001b[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('\u001b[0G' + str);
}, interval);
}