diff --git a/.eslintrc.yml b/.eslintrc.yml index f696b45ca8..a668139868 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -79,3 +79,12 @@ overrides: parserOptions: ecmaVersion: 6 sourceType: module + + - files: + - lib/reporters/*.js + rules: + no-restricted-syntax: + - error + # disallow Reporters using `console.log()` + - selector: 'CallExpression[callee.object.name=console][callee.property.name=log]' + message: &GH-3604 See https://github.com/mochajs/mocha/issues/3604 diff --git a/lib/reporters/base.js b/lib/reporters/base.js index 3736c018ca..670a160fa8 100644 --- a/lib/reporters/base.js +++ b/lib/reporters/base.js @@ -27,6 +27,11 @@ exports = module.exports = Base; var isatty = tty.isatty(1) && tty.isatty(2); +/** + * Save log references to avoid tests interfering (see GH-3604). + */ +var consoleLog = console.log; + /** * Enable coloring by default, except in the browser interface. */ @@ -192,7 +197,7 @@ var generateDiff = (exports.generateDiff = function(actual, expected) { * Error property */ exports.list = function(failures) { - console.log(); + Base.consoleLog(); failures.forEach(function(test, i) { // format var fmt = @@ -253,7 +258,7 @@ exports.list = function(failures) { testTitle += str; }); - console.log(fmt, i + 1, testTitle, msg, stack); + Base.consoleLog(fmt, i + 1, testTitle, msg, stack); }); }; @@ -308,7 +313,7 @@ Base.prototype.epilogue = function() { var stats = this.stats; var fmt; - console.log(); + Base.consoleLog(); // passes fmt = @@ -316,26 +321,26 @@ Base.prototype.epilogue = function() { color('green', ' %d passing') + color('light', ' (%s)'); - console.log(fmt, stats.passes || 0, milliseconds(stats.duration)); + Base.consoleLog(fmt, stats.passes || 0, milliseconds(stats.duration)); // pending if (stats.pending) { fmt = color('pending', ' ') + color('pending', ' %d pending'); - console.log(fmt, stats.pending); + Base.consoleLog(fmt, stats.pending); } // failures if (stats.failures) { fmt = color('fail', ' %d failing'); - console.log(fmt, stats.failures); + Base.consoleLog(fmt, stats.failures); Base.list(this.failures); - console.log(); + Base.consoleLog(); } - console.log(); + Base.consoleLog(); }; /** @@ -488,4 +493,6 @@ function sameType(a, b) { return objToString.call(a) === objToString.call(b); } +Base.consoleLog = consoleLog; + Base.abstract = true; diff --git a/lib/reporters/doc.js b/lib/reporters/doc.js index efcb2d0caf..5a6af8fb42 100644 --- a/lib/reporters/doc.js +++ b/lib/reporters/doc.js @@ -44,41 +44,45 @@ function Doc(runner, options) { return; } ++indents; - console.log('%s
', indent()); + Base.consoleLog('%s
', indent()); ++indents; - console.log('%s

%s

', indent(), utils.escape(suite.title)); - console.log('%s
', indent()); + Base.consoleLog('%s

%s

', indent(), utils.escape(suite.title)); + Base.consoleLog('%s
', indent()); }); runner.on(EVENT_SUITE_END, function(suite) { if (suite.root) { return; } - console.log('%s
', indent()); + Base.consoleLog('%s
', indent()); --indents; - console.log('%s
', indent()); + Base.consoleLog('%s
', indent()); --indents; }); runner.on(EVENT_TEST_PASS, function(test) { - console.log('%s
%s
', indent(), utils.escape(test.title)); + Base.consoleLog('%s
%s
', indent(), utils.escape(test.title)); var code = utils.escape(utils.clean(test.body)); - console.log('%s
%s
', indent(), code); + Base.consoleLog('%s
%s
', indent(), code); }); runner.on(EVENT_TEST_FAIL, function(test, err) { - console.log( + Base.consoleLog( '%s
%s
', indent(), utils.escape(test.title) ); var code = utils.escape(utils.clean(test.body)); - console.log( + Base.consoleLog( '%s
%s
', indent(), code ); - console.log('%s
%s
', indent(), utils.escape(err)); + Base.consoleLog( + '%s
%s
', + indent(), + utils.escape(err) + ); }); } diff --git a/lib/reporters/dot.js b/lib/reporters/dot.js index c4c5ce5d92..3913f0c679 100644 --- a/lib/reporters/dot.js +++ b/lib/reporters/dot.js @@ -68,7 +68,7 @@ function Dot(runner, options) { }); runner.once(EVENT_RUN_END, function() { - console.log(); + process.stdout.write('\n'); self.epilogue(); }); } diff --git a/lib/reporters/landing.js b/lib/reporters/landing.js index b124c7789c..a6af946c42 100644 --- a/lib/reporters/landing.js +++ b/lib/reporters/landing.js @@ -95,7 +95,7 @@ function Landing(runner, options) { runner.once(EVENT_RUN_END, function() { cursor.show(); - console.log(); + process.stdout.write('\n'); self.epilogue(); }); } diff --git a/lib/reporters/list.js b/lib/reporters/list.js index cdf4279644..c7ff8c4ea8 100644 --- a/lib/reporters/list.js +++ b/lib/reporters/list.js @@ -41,7 +41,7 @@ function List(runner, options) { var n = 0; runner.on(EVENT_RUN_BEGIN, function() { - console.log(); + Base.consoleLog(); }); runner.on(EVENT_TEST_BEGIN, function(test) { @@ -50,7 +50,7 @@ function List(runner, options) { runner.on(EVENT_TEST_PENDING, function(test) { var fmt = color('checkmark', ' -') + color('pending', ' %s'); - console.log(fmt, test.fullTitle()); + Base.consoleLog(fmt, test.fullTitle()); }); runner.on(EVENT_TEST_PASS, function(test) { @@ -59,12 +59,12 @@ function List(runner, options) { color('pass', ' %s: ') + color(test.speed, '%dms'); cursor.CR(); - console.log(fmt, test.fullTitle(), test.duration); + Base.consoleLog(fmt, test.fullTitle(), test.duration); }); runner.on(EVENT_TEST_FAIL, function(test) { cursor.CR(); - console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); + Base.consoleLog(color('fail', ' %d) %s'), ++n, test.fullTitle()); }); runner.once(EVENT_RUN_END, self.epilogue.bind(self)); diff --git a/lib/reporters/progress.js b/lib/reporters/progress.js index 0432e4fb0a..0211122a9d 100644 --- a/lib/reporters/progress.js +++ b/lib/reporters/progress.js @@ -58,7 +58,7 @@ function Progress(runner, options) { // tests started runner.on(EVENT_RUN_BEGIN, function() { - console.log(); + process.stdout.write('\n'); cursor.hide(); }); @@ -91,7 +91,7 @@ function Progress(runner, options) { // and the failures if any runner.once(EVENT_RUN_END, function() { cursor.show(); - console.log(); + process.stdout.write('\n'); self.epilogue(); }); } diff --git a/lib/reporters/spec.js b/lib/reporters/spec.js index e1ae95e92d..e51ed80ac4 100644 --- a/lib/reporters/spec.js +++ b/lib/reporters/spec.js @@ -46,24 +46,24 @@ function Spec(runner, options) { } runner.on(EVENT_RUN_BEGIN, function() { - console.log(); + Base.consoleLog(); }); runner.on(EVENT_SUITE_BEGIN, function(suite) { ++indents; - console.log(color('suite', '%s%s'), indent(), suite.title); + Base.consoleLog(color('suite', '%s%s'), indent(), suite.title); }); runner.on(EVENT_SUITE_END, function() { --indents; if (indents === 1) { - console.log(); + Base.consoleLog(); } }); runner.on(EVENT_TEST_PENDING, function(test) { var fmt = indent() + color('pending', ' - %s'); - console.log(fmt, test.title); + Base.consoleLog(fmt, test.title); }); runner.on(EVENT_TEST_PASS, function(test) { @@ -73,19 +73,19 @@ function Spec(runner, options) { indent() + color('checkmark', ' ' + Base.symbols.ok) + color('pass', ' %s'); - console.log(fmt, test.title); + Base.consoleLog(fmt, test.title); } else { fmt = indent() + color('checkmark', ' ' + Base.symbols.ok) + color('pass', ' %s') + color(test.speed, ' (%dms)'); - console.log(fmt, test.title, test.duration); + Base.consoleLog(fmt, test.title, test.duration); } }); runner.on(EVENT_TEST_FAIL, function(test) { - console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); + Base.consoleLog(indent() + color('fail', ' %d) %s'), ++n, test.title); }); runner.once(EVENT_RUN_END, self.epilogue.bind(self)); diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js index 09b32f1ca7..6c9c937be8 100644 --- a/lib/reporters/xunit.js +++ b/lib/reporters/xunit.js @@ -142,7 +142,7 @@ XUnit.prototype.write = function(line) { } else if (typeof process === 'object' && process.stdout) { process.stdout.write(line + '\n'); } else { - console.log(line); + Base.consoleLog(line); } }; diff --git a/test/reporters/base.spec.js b/test/reporters/base.spec.js index 739063f32a..80957c39aa 100644 --- a/test/reporters/base.spec.js +++ b/test/reporters/base.spec.js @@ -5,7 +5,6 @@ var chai = require('chai'); var sinon = require('sinon'); var helpers = require('./helpers'); var reporters = require('../../').reporters; - var AssertionError = assert.AssertionError; var Base = reporters.Base; var chaiExpect = chai.expect; @@ -417,4 +416,27 @@ describe('Base reporter', function() { var errOut = stdout.join('\n').trim(); expect(errOut, 'to be', '1) test title:\n Error\n foo\n bar'); }); + + describe('when reporter output immune to user test changes', function() { + var sandbox; + var baseConsoleLog; + + beforeEach(function() { + sandbox = sinon.createSandbox(); + sandbox.stub(console, 'log'); + baseConsoleLog = sandbox.stub(Base, 'consoleLog'); + }); + + it('should let you stub out console.log without effecting reporters output', function() { + Base.list([]); + baseConsoleLog.restore(); + + expect(baseConsoleLog, 'was called'); + expect(console.log, 'was not called'); + }); + + afterEach(function() { + sandbox.restore(); + }); + }); }); diff --git a/test/reporters/xunit.spec.js b/test/reporters/xunit.spec.js index 26fce4a1c4..2d05312fae 100644 --- a/test/reporters/xunit.spec.js +++ b/test/reporters/xunit.spec.js @@ -277,14 +277,14 @@ describe('XUnit reporter', function() { }); describe('when output directed to console', function() { - it("should call 'console.log' with line", function() { + it("should call 'Base.consoleLog' with line", function() { // :TODO: XUnit needs a trivially testable means to force console.log() var realProcess = process; process = false; // eslint-disable-line no-native-reassign, no-global-assign var xunit = new XUnit(runner); var fakeThis = {fileStream: false}; - var consoleLogStub = sinon.stub(console, 'log'); + var consoleLogStub = sinon.stub(Base, 'consoleLog'); xunit.write.call(fakeThis, expectedLine); consoleLogStub.restore();