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();