123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443 |
- #!/usr/bin/env node
- /**
- * Module dependencies.
- */
- var program = require('commander')
- , sprintf = require('util').format
- , path = require('path')
- , fs = require('fs')
- , glob = require('glob')
- , resolve = path.resolve
- , exists = fs.existsSync || path.existsSync
- , Mocha = require('../')
- , utils = Mocha.utils
- , reporters = Mocha.reporters
- , interfaces = Mocha.interfaces
- , Base = reporters.Base
- , join = path.join
- , basename = path.basename
- , cwd = process.cwd()
- , 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('-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('-i, --invert', 'inverts --grep matches')
- .option('-t, --timeout <ms>', 'set test-case timeout in milliseconds [2000]')
- .option('-s, --slow <ms>', '"slow" test threshold in milliseconds [75]')
- .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, synonym for node --debug")
- .option('-b, --bail', "bail after first test failure")
- .option('-A, --async-only', "force all tests to take a callback (async)")
- .option('--recursive', 'include sub directories')
- .option('--debug-brk', "enable node's debugger breaking on the first line")
- .option('--globals <names>', 'allow the given comma-delimited global [names]', list, [])
- .option('--check-leaks', 'check for global variable leaks')
- .option('--interfaces', 'display available interfaces')
- .option('--reporters', 'display available reporters')
- .option('--compilers <ext>:<module>,...', 'use the given module(s) to compile files', list, [])
- 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(' teamcity - teamcity ci support');
- 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);
- });
- // 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
- mocha.reporter(program.reporter);
- // 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) Base.useColors = false;
- // --colors
- if (~process.argv.indexOf('--colors') ||
- ~process.argv.indexOf('-c')) {
- Base.useColors = true;
- }
- // --slow <ms>
- if (program.slow) mocha.suite.slow(program.slow);
- // --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));
- // --invert
- if (program.invert) mocha.invert();
- // --check-leaks
- if (program.checkLeaks) mocha.checkLeaks();
- // --growl
- if (program.growl) mocha.growl();
- // --async-only
- if (program.asyncOnly) mocha.asyncOnly();
- // --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);
- });
- var re = new RegExp('\\.(' + extensions.join('|') + ')$');
- // requires
- requires.forEach(function(mod) {
- require(mod);
- });
- // files
- var files = []
- , args = program.args;
- // default files to test/*.{js,coffee}
- if (!args.length) args.push('test');
- args.forEach(function(arg){
- files = files.concat(lookupFiles(arg, program.recursive));
- });
- // 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 spinner = 'win32' == process.platform
- ? ['|','/','-','\\']
- : ['◜','◠','◝','◞','◡','◟'];
- var frames = spinner.map(function(c) {
- return sprintf(' \u001b[96m%s \u001b[90mwatching\u001b[0m', c);
- });
- var watchFiles = utils.files(cwd);
- function loadAndRun() {
- try {
- mocha.files = files;
- mocha.run(function(){
- play(frames);
- });
- } catch(e) {
- console.log(e.stack);
- }
- }
- function purge() {
- watchFiles.forEach(function(file){
- delete require.cache[file];
- });
- }
- loadAndRun();
- utils.watch(watchFiles, function(){
- purge();
- stop()
- mocha.suite = mocha.suite.clone();
- mocha.ui(program.ui);
- loadAndRun();
- });
- return;
- }
- // load
- mocha.files = files;
- mocha.run(process.exit);
- // 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);
- }
- /**
- * Lookup file names at the given `path`.
- */
- function lookupFiles(path, recursive) {
- var files = [];
- if (!exists(path)) {
- if (exists(path + '.js')) {
- path += '.js'
- } else {
- return glob.sync(path);
- }
- }
- var stat = fs.statSync(path);
- if (stat.isFile()) return path;
- fs.readdirSync(path).forEach(function(file){
- file = join(path, file);
- var stat = fs.statSync(file);
- if (stat.isDirectory()) {
- if (recursive) files = files.concat(lookupFiles(file, recursive));
- return
- }
- if (!stat.isFile() || !re.test(file) || basename(file)[0] == '.') return;
- files.push(file);
- });
- return files;
- }
- /**
- * 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);
- }
|