UNPKG

ava

Version:

Futuristic test runner 🚀

245 lines (198 loc) • 4.84 kB
#!/usr/bin/env node 'use strict'; var fs = require('fs'); var path = require('path'); var figures = require('figures'); var flatten = require('arr-flatten'); var globby = require('globby'); var meow = require('meow'); var updateNotifier = require('update-notifier'); var chalk = require('chalk'); var Promise = require('bluebird'); var fork = require('./lib/fork'); var log = require('./lib/logger'); // Bluebird specific Promise.longStackTraces(); var cli = meow({ help: [ 'Usage', ' ava [<file|folder|glob> ...]', '', 'Options', ' --init Add AVA to your project', ' --fail-fast Stop after first test failure', ' --serial Run tests serially', '', 'Examples', ' ava', ' ava test.js test2.js', ' ava test-*.js', ' ava --init', ' ava --init foo.js', '', 'Default patterns when no arguments:', 'test.js test-*.js test/*.js' ] }, { string: ['_'], boolean: [ 'fail-fast', 'serial' ] }); var testCount = 0; var fileCount = 0; var unhandledRejectionCount = 0; var uncaughtExceptionCount = 0; var errors = []; function error(err) { console.error(err.stack); flushIoAndExit(1); } function prefixTitle(file) { var separator = ' ' + chalk.gray.dim(figures.pointerSmall) + ' '; var base = path.dirname(cli.input[0]); if (base === '.') { base = cli.input[0] || 'test'; } base += path.sep; var prefix = path.relative(process.cwd(), file) .replace(base, '') .replace(/\.spec/, '') .replace(/test\-/g, '') .replace(/\.js$/, '') .split(path.sep) .join(separator); if (prefix.length > 0) { prefix += separator; } return prefix; } function stats(stats) { testCount += stats.testCount; } function test(data) { var isError = data.error.message; if (fileCount > 1) { data.title = prefixTitle(data.file) + data.title; } if (isError) { log.error(data.title, chalk.red(data.error.message)); errors.push(data); } else { // if there's only one file and one anonymous test // don't log it if (fileCount === 1 && testCount === 1 && data.title === '[anonymous]') { return; } log.test(data); } } function run(file) { var args = [file]; if (cli.flags.failFast) { args.push('--fail-fast'); } if (cli.flags.serial) { args.push('--serial'); } return fork(args) .on('stats', stats) .on('test', test) .on('unhandledRejections', rejections) .on('uncaughtException', uncaughtException) .on('data', function (data) { process.stdout.write(data); }); } function rejections(data) { var unhandled = data.unhandledRejections; log.unhandledRejections(data.file, unhandled); unhandledRejectionCount += unhandled.length; } function uncaughtException(data) { uncaughtExceptionCount++; log.uncaughtException(data.file, data.uncaughtException); } function sum(arr, key) { var result = 0; arr.forEach(function (item) { result += item[key]; }); return result; } function exit(results) { // assemble stats from all tests var stats = results.map(function (result) { return result.stats; }); var tests = results.map(function (result) { return result.tests; }); var passed = sum(stats, 'passCount'); var failed = sum(stats, 'failCount'); log.write(); log.report(passed, failed, unhandledRejectionCount, uncaughtExceptionCount); log.write(); if (failed > 0) { log.errors(flatten(tests)); } process.stdout.write(''); flushIoAndExit( failed > 0 || unhandledRejectionCount > 0 || uncaughtExceptionCount > 0 ? 1 : 0 ); } function flushIoAndExit(code) { // TODO: figure out why this needs to be here to // correctly flush the output when multiple test files process.stdout.write(''); process.stderr.write(''); // timeout required to correctly flush io on Node 0.10 Windows setTimeout(function () { process.exit(code); }, process.env.AVA_APPVEYOR ? 500 : 0); } function init(files) { log.write(); return handlePaths(files) .map(function (file) { return path.resolve('.', file); }) .then(function (files) { if (files.length === 0) { log.error('Couldn\'t find any files to test\n'); process.exit(1); } fileCount = files.length; var tests = files.map(run); return Promise.all(tests); }); } function handlePaths(files) { if (files.length === 0) { files = [ 'test.js', 'test-*.js', 'test/*.js' ]; } // convert pinkie-promise to Bluebird promise files = Promise.resolve(globby(files)); return files .map(function (file) { if (fs.statSync(file).isDirectory()) { return handlePaths([path.join(file, '*.js')]); } return file; }) .then(flatten) .filter(function (file) { return path.extname(file) === '.js' && path.basename(file)[0] !== '_'; }); } updateNotifier({pkg: cli.pkg}).notify(); if (cli.flags.init) { require('ava-init')().catch(error); } else { init(cli.input).then(exit).catch(error); }