diff --git a/lib/cli.js b/lib/cli.js index e170c49b7..968258725 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -38,6 +38,7 @@ exports.run = () => { --verbose, -v Enable verbose output --no-cache Disable the transpiler cache --no-power-assert Disable Power Assert + --no-color Disable color output --match, -m Only run tests with matching title (Can be repeated) --watch, -w Re-run tests when tests and source files change --timeout, -T Set global timeout @@ -68,9 +69,10 @@ exports.run = () => { 'tap', 'verbose', 'watch', - 'update-snapshots' + 'update-snapshots', + 'color' ], - default: conf, + default: Object.assign({color: true}, conf), alias: { t: 'tap', v: 'verbose', @@ -118,7 +120,8 @@ exports.run = () => { pkgDir, timeout: cli.flags.timeout, concurrency: cli.flags.concurrency ? parseInt(cli.flags.concurrency, 10) : 0, - updateSnapshots: cli.flags.updateSnapshots + updateSnapshots: cli.flags.updateSnapshots, + color: cli.flags.color }); let reporter; @@ -126,9 +129,9 @@ exports.run = () => { if (cli.flags.tap && !cli.flags.watch) { reporter = new TapReporter(); } else if (cli.flags.verbose || isCi) { - reporter = new VerboseReporter({basePath: pkgDir}); + reporter = new VerboseReporter({color: cli.flags.color, basePath: pkgDir}); } else { - reporter = new MiniReporter({watching: cli.flags.watch, basePath: pkgDir}); + reporter = new MiniReporter({color: cli.flags.color, watching: cli.flags.watch, basePath: pkgDir}); } reporter.api = api; diff --git a/lib/fork.js b/lib/fork.js index fa960f106..f728c8290 100644 --- a/lib/fork.js +++ b/lib/fork.js @@ -36,7 +36,9 @@ module.exports = (file, opts, execArgv) => { } : false }, opts); - const ps = childProcess.fork(path.join(__dirname, 'test-worker.js'), [JSON.stringify(opts)], { + const args = [JSON.stringify(opts), opts.color ? '--color' : '--no-color']; + + const ps = childProcess.fork(path.join(__dirname, 'test-worker.js'), args, { cwd: opts.pkgDir, silent: true, env, diff --git a/lib/reporters/mini.js b/lib/reporters/mini.js index 05b92a926..2588f7eaa 100644 --- a/lib/reporters/mini.js +++ b/lib/reporters/mini.js @@ -14,11 +14,6 @@ const extractStack = require('../extract-stack'); const codeExcerpt = require('../code-excerpt'); const colors = require('../colors'); -chalk.enabled = true; -Object.keys(colors).forEach(key => { - colors[key].enabled = true; -}); - // TODO(@jamestalamge): This should be fixed in log-update and ansi-escapes once we are confident it's a good solution. const CSI = '\u001b['; const ERASE_LINE = CSI + '2K'; @@ -42,12 +37,17 @@ function eraseLines(count) { class MiniReporter { constructor(options) { + this.options = Object.assign({}, options); + + chalk.enabled = this.options.color; + for (const key of Object.keys(colors)) { + colors[key].enabled = this.options.color; + } + const spinnerDef = spinners[process.platform === 'win32' ? 'line' : 'dots']; this.spinnerFrames = spinnerDef.frames.map(c => chalk.gray.dim(c)); this.spinnerInterval = spinnerDef.interval; - this.options = Object.assign({}, options); - this.reset(); this.stream = process.stderr; this.stringDecoder = new StringDecoder(); diff --git a/lib/reporters/verbose.js b/lib/reporters/verbose.js index bd7f8cf02..46ca74681 100644 --- a/lib/reporters/verbose.js +++ b/lib/reporters/verbose.js @@ -10,13 +10,14 @@ const extractStack = require('../extract-stack'); const codeExcerpt = require('../code-excerpt'); const colors = require('../colors'); -Object.keys(colors).forEach(key => { - colors[key].enabled = true; -}); - class VerboseReporter { constructor(options) { this.options = Object.assign({}, options); + + chalk.enabled = options.color; + for (const key of Object.keys(colors)) { + colors[key].enabled = options.color; + } } start() { return ''; diff --git a/readme.md b/readme.md index 456e900ba..63f788414 100644 --- a/readme.md +++ b/readme.md @@ -163,6 +163,7 @@ $ ava --help --verbose, -v Enable verbose output --no-cache Disable the transpiler cache --no-power-assert Disable Power Assert + --no-color Disable color output --match, -m Only run tests with matching title (Can be repeated) --watch, -w Re-run tests when tests and source files change --timeout, -T Set global timeout diff --git a/test/cli.js b/test/cli.js index de8eddcd4..7cd6809ca 100644 --- a/test/cli.js +++ b/test/cli.js @@ -12,12 +12,15 @@ const proxyquire = require('proxyquire'); const sinon = require('sinon'); const uniqueTempDir = require('unique-temp-dir'); const execa = require('execa'); +const colors = require('../lib/colors'); const cliPath = path.join(__dirname, '../cli.js'); // For some reason chalk is disabled by default chalk.enabled = true; -const colors = require('../lib/colors'); +for (const key of Object.keys(colors)) { + colors[key].enabled = true; +} function execCli(args, opts, cb) { let dirname; @@ -71,7 +74,7 @@ function execCli(args, opts, cb) { } test('disallow invalid babel config shortcuts', t => { - execCli('es2015.js', {dirname: 'fixture/invalid-babel-config'}, (err, stdout, stderr) => { + execCli(['--color', 'es2015.js'], {dirname: 'fixture/invalid-babel-config'}, (err, stdout, stderr) => { t.ok(err); let expectedOutput = '\n '; diff --git a/test/reporters/mini.js b/test/reporters/mini.js index 804c055d3..d7028f5c4 100644 --- a/test/reporters/mini.js +++ b/test/reporters/mini.js @@ -27,6 +27,9 @@ process.stdout.columns = 5000; const fullWidthLine = chalk.gray.dim('\u2500'.repeat(5000)); function miniReporter(options) { + if (options === undefined) { + options = {color: true}; + } const reporter = new MiniReporter(options); reporter.start = () => ''; return reporter; @@ -35,7 +38,7 @@ function miniReporter(options) { process.stderr.setMaxListeners(50); test('start', t => { - const reporter = new MiniReporter(); + const reporter = new MiniReporter({color: true}); t.is(reporter.start(), ' \n ' + graySpinner + ' '); reporter.clearInterval(); @@ -432,7 +435,7 @@ test('results with errors and disabled code excerpts', t => { err2.expected = JSON.stringify([2]); err2.expectedType = 'array'; - const reporter = miniReporter({basePath: path.dirname(err2Path)}); + const reporter = miniReporter({color: true, basePath: path.dirname(err2Path)}); reporter.failCount = 1; const runStatus = { @@ -495,7 +498,7 @@ test('results with errors and broken code excerpts', t => { err2.expected = JSON.stringify([2]); err2.expectedType = 'array'; - const reporter = miniReporter({basePath: path.dirname(err2Path)}); + const reporter = miniReporter({color: true, basePath: path.dirname(err2Path)}); reporter.failCount = 1; const runStatus = { @@ -559,7 +562,7 @@ test('results with errors and disabled assert output', t => { err2.expected = JSON.stringify([2]); err2.expectedType = 'array'; - const reporter = miniReporter({basePath: path.dirname(err1Path)}); + const reporter = miniReporter({color: true, basePath: path.dirname(err1Path)}); reporter.failCount = 1; const runStatus = { @@ -731,7 +734,7 @@ test('results with watching enabled', t => { lolex.install(new Date(2014, 11, 19, 17, 19, 12, 200).getTime(), ['Date']); const time = ' ' + chalk.grey.dim('[17:19:12]'); - const reporter = miniReporter({watching: true}); + const reporter = miniReporter({color: true, watching: true}); reporter.passCount = 1; reporter.failCount = 0; @@ -905,3 +908,27 @@ test('results when hasExclusive is enabled, but there are multiple remaining tes t.is(actualOutput, expectedOutput); t.end(); }); + +test('result when no-color flag is set', t => { + const reporter = miniReporter({ + color: false + }); + + const runStatus = { + hasExclusive: true, + testCount: 3, + passCount: 1, + failCount: 0, + remainingCount: 2 + }; + + const output = reporter.finish(runStatus); + const expectedOutput = [ + '', + '', + ' The .only() modifier is used in some tests. 2 tests were not run', + '\n' + ].join('\n'); + t.is(output, expectedOutput); + t.end(); +}); diff --git a/test/reporters/verbose.js b/test/reporters/verbose.js index 4286c1696..fce06fd24 100644 --- a/test/reporters/verbose.js +++ b/test/reporters/verbose.js @@ -23,6 +23,9 @@ lolex.install(new Date(2014, 11, 19, 17, 19, 12, 200).getTime(), ['Date']); const time = ' ' + chalk.grey.dim('[17:19:12]'); function createReporter(options) { + if (options === undefined) { + options = {color: true}; + } const reporter = new VerboseReporter(options); return reporter; } @@ -378,7 +381,7 @@ test('results with errors', t => { error2.expected = JSON.stringify([2]); error2.expectedType = 'array'; - const reporter = createReporter({basePath: path.dirname(err1Path)}); + const reporter = createReporter({color: true, basePath: path.dirname(err1Path)}); const runStatus = createRunStatus(); runStatus.failCount = 1; runStatus.tests = [{ @@ -439,7 +442,7 @@ test('results with errors and disabled code excerpts', t => { error2.expected = JSON.stringify([2]); error2.expectedType = 'array'; - const reporter = createReporter({basePath: path.dirname(err2Path)}); + const reporter = createReporter({color: true, basePath: path.dirname(err2Path)}); const runStatus = createRunStatus(); runStatus.failCount = 1; runStatus.tests = [{ @@ -499,7 +502,7 @@ test('results with errors and disabled code excerpts', t => { error2.expected = JSON.stringify([2]); error2.expectedType = 'array'; - const reporter = createReporter({basePath: path.dirname(err2Path)}); + const reporter = createReporter({color: true, basePath: path.dirname(err2Path)}); const runStatus = createRunStatus(); runStatus.failCount = 1; runStatus.tests = [{ @@ -560,7 +563,7 @@ test('results with errors and disabled assert output', t => { error2.expected = JSON.stringify([2]); error2.expectedType = 'array'; - const reporter = createReporter({basePath: path.dirname(err1Path)}); + const reporter = createReporter({color: true, basePath: path.dirname(err1Path)}); const runStatus = createRunStatus(); runStatus.failCount = 1; runStatus.tests = [{ @@ -602,7 +605,7 @@ test('results with errors and disabled assert output', t => { }); test('results when fail-fast is enabled', t => { - const reporter = new VerboseReporter(); + const reporter = createReporter(); const runStatus = createRunStatus(); runStatus.remainingCount = 1; runStatus.failCount = 1; @@ -626,7 +629,7 @@ test('results when fail-fast is enabled', t => { }); test('results without fail-fast if no failing tests', t => { - const reporter = new VerboseReporter(); + const reporter = createReporter(); const runStatus = createRunStatus(); runStatus.remainingCount = 1; runStatus.failCount = 0; @@ -645,7 +648,7 @@ test('results without fail-fast if no failing tests', t => { }); test('results without fail-fast if no skipped tests', t => { - const reporter = new VerboseReporter(); + const reporter = createReporter(); const runStatus = createRunStatus(); runStatus.remainingCount = 0; runStatus.failCount = 1; @@ -715,7 +718,7 @@ test('full-width line when sectioning', t => { test('write calls console.error', t => { const stub = sinon.stub(console, 'error'); - const reporter = new VerboseReporter(); + const reporter = createReporter(); reporter.write('result'); t.true(stub.called); console.error.restore(); @@ -723,7 +726,7 @@ test('write calls console.error', t => { }); test('reporter.stdout and reporter.stderr both use process.stderr.write', t => { - const reporter = new VerboseReporter(); + const reporter = createReporter(); const stub = sinon.stub(process.stderr, 'write'); reporter.stdout('result'); reporter.stderr('result'); @@ -733,7 +736,7 @@ test('reporter.stdout and reporter.stderr both use process.stderr.write', t => { }); test('results when hasExclusive is enabled, but there are no known remaining tests', t => { - const reporter = new VerboseReporter(); + const reporter = createReporter(); const runStatus = createRunStatus(); runStatus.hasExclusive = true; runStatus.passCount = 1; @@ -750,7 +753,7 @@ test('results when hasExclusive is enabled, but there are no known remaining tes }); test('results when hasExclusive is enabled, but there is one remaining tests', t => { - const reporter = new VerboseReporter(); + const reporter = createReporter(); const runStatus = createRunStatus(); runStatus.hasExclusive = true; runStatus.testCount = 2; @@ -773,7 +776,7 @@ test('results when hasExclusive is enabled, but there is one remaining tests', t }); test('results when hasExclusive is enabled, but there are multiple remaining tests', t => { - const reporter = new VerboseReporter(); + const reporter = createReporter(); const runStatus = createRunStatus(); runStatus.hasExclusive = true; runStatus.testCount = 3; @@ -794,3 +797,26 @@ test('results when hasExclusive is enabled, but there are multiple remaining tes t.is(output, expectedOutput); t.end(); }); + +test('result when no-color flag is set', t => { + const reporter = new VerboseReporter({color: false}); + const runStatus = createRunStatus(); + runStatus.hasExclusive = true; + runStatus.testCount = 3; + runStatus.passCount = 1; + runStatus.failCount = 0; + runStatus.remainingCount = 2; + + const output = reporter.finish(runStatus); + const expectedOutput = [ + '', + ' 1 test passed [17:19:12]', + '', + '', + ' The .only() modifier is used in some tests. 2 tests were not run', + '' + ].join('\n'); + + t.is(output, expectedOutput); + t.end(); +});