diff --git a/lib/addHooks.js b/lib/addHooks.js index 1e3eec4e1..b5a5ac397 100644 --- a/lib/addHooks.js +++ b/lib/addHooks.js @@ -4,6 +4,7 @@ const proxyquire = require('proxyquire').noCallThru(); const Hooks = require('./Hooks'); const HooksWorkerClient = require('./HooksWorkerClient'); const logger = require('./logger'); +const reporterOutputLogger = require('./reporters/reporterOutputLogger'); const resolveHookfiles = require('./resolveHookfiles'); // Note: runner.configuration.options must be defined @@ -24,7 +25,7 @@ Stack: ${error.stack} } if (!runner.logs) { runner.logs = []; } - runner.hooks = new Hooks({ logs: runner.logs, logger }); + runner.hooks = new Hooks({ logs: runner.logs, logger: reporterOutputLogger }); if (!runner.hooks.transactions) { runner.hooks.transactions = {}; } diff --git a/lib/configuration.js b/lib/configuration.js index ef445fc2e..a839265b9 100644 --- a/lib/configuration.js +++ b/lib/configuration.js @@ -2,6 +2,8 @@ const clone = require('clone'); const { EventEmitter } = require('events'); const logger = require('./logger'); +const reporterOutputLogger = require('./reporters/reporterOutputLogger'); + function coerceToArray(value) { if (Array.isArray(value)) { @@ -14,6 +16,7 @@ function coerceToArray(value) { return value; } + function applyLoggingOptions(options) { // Color can be either specified as "stringified bool" or bool (nothing else // is expected valid value). Here we're coercing the value to boolean. @@ -24,13 +27,21 @@ function applyLoggingOptions(options) { } logger.transports.console.colorize = options.color; - logger.transports.console.silent = options.silent; + reporterOutputLogger.transports.console.colorize = options.color; + logger.transports.console.timestamp = options.timestamp; + reporterOutputLogger.transports.console.timestamp = options.timestamp; + + logger.transports.console.silent = options.silent; + reporterOutputLogger.transports.console.silent = options.silent; + logger.transports.console.level = options.level; + reporterOutputLogger.transports.console.level = 'test'; return options; } + function applyConfiguration(config) { const configuration = { server: null, @@ -117,6 +128,7 @@ function applyConfiguration(config) { return configuration; } + module.exports = { applyConfiguration, applyLoggingOptions, diff --git a/lib/logger.js b/lib/logger.js index 42d778a18..9417f1f17 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -5,19 +5,10 @@ module.exports = new (winston.Logger)({ new (winston.transports.Console)({ colorize: true }), ], levels: { - silly: 14, - debug: 13, - verbose: 12, - info: 11, - test: 10, - pass: 9, - fail: 8, - complete: 7, - actual: 6, - expected: 5, - hook: 4, - request: 3, - skip: 2, + silly: 5, + debug: 4, + verbose: 3, + info: 2, warn: 1, error: 0, }, @@ -26,15 +17,6 @@ module.exports = new (winston.Logger)({ debug: 'cyan', verbose: 'magenta', info: 'blue', - test: 'yellow', - pass: 'green', - fail: 'red', - complete: 'green', - actual: 'red', - expected: 'red', - hook: 'green', - request: 'green', - skip: 'yellow', warn: 'yellow', error: 'red', }, diff --git a/lib/reporters/ApiaryReporter.js b/lib/reporters/ApiaryReporter.js index e00bd772d..6d7c2e4c5 100644 --- a/lib/reporters/ApiaryReporter.js +++ b/lib/reporters/ApiaryReporter.js @@ -4,6 +4,7 @@ const os = require('os'); const request = require('request'); const logger = require('../logger'); +const reporterOutputLogger = require('./reporterOutputLogger'); const packageData = require('../../package.json'); @@ -186,7 +187,7 @@ ApiaryReporter.prototype.configureEmitter = function configureEmitter(emitter) { this._performRequestAsync(path, 'PATCH', data, (error) => { if (error) { return callback(error); } const reportUrl = this.reportUrl || `https://app.apiary.io/${this.configuration.apiSuite}/tests/run/${this.remoteId}`; - logger.complete(`See results in Apiary at: ${reportUrl}`); + reporterOutputLogger.complete(`See results in Apiary at: ${reportUrl}`); callback(); }); }); diff --git a/lib/reporters/CLIReporter.js b/lib/reporters/CLIReporter.js index 7339b2a16..517a90a61 100644 --- a/lib/reporters/CLIReporter.js +++ b/lib/reporters/CLIReporter.js @@ -1,6 +1,19 @@ const logger = require('../logger'); +const reporterOutputLogger = require('./reporterOutputLogger'); const prettifyResponse = require('../prettifyResponse'); + +const CONNECTION_ERRORS = [ + 'ECONNRESET', + 'ENOTFOUND', + 'ESOCKETTIMEDOUT', + 'ETIMEDOUT', + 'ECONNREFUSED', + 'EHOSTUNREACH', + 'EPIPE', +]; + + function CLIReporter(emitter, stats, tests, inlineErrors, details) { this.type = 'cli'; this.stats = stats; @@ -22,76 +35,63 @@ CLIReporter.prototype.configureEmitter = function configureEmitter(emitter) { emitter.on('end', (callback) => { if (!this.inlineErrors) { - if (this.errors.length !== 0) { logger.info('Displaying failed tests...'); } - for (const test of this.errors) { - logger.fail(`${test.title} duration: ${test.duration}ms`); - logger.fail(test.message); - if (test.request) { logger.request(`\n${prettifyResponse(test.request)}\n`); } - if (test.expected) { logger.expected(`\n${prettifyResponse(test.expected)}\n`); } - if (test.actual) { logger.actual(`\n${prettifyResponse(test.actual)}\n\n`); } + if (this.errors.length) { + logger.info('Displaying failed tests...'); } + this.errors.forEach((test) => { + reporterOutputLogger.fail(`${test.title} duration: ${test.duration}ms`); + reporterOutputLogger.fail(test.message); + if (test.request) reporterOutputLogger.request(`\n${prettifyResponse(test.request)}\n`); + if (test.expected) reporterOutputLogger.expected(`\n${prettifyResponse(test.expected)}\n`); + if (test.actual) reporterOutputLogger.actual(`\n${prettifyResponse(test.actual)}\n\n`); + }); } if (this.stats.tests > 0) { - logger.complete(`${this.stats.passes} passing, ` + reporterOutputLogger.complete(`${this.stats.passes} passing, ` + `${this.stats.failures} failing, ` + `${this.stats.errors} errors, ` + `${this.stats.skipped} skipped, ` + `${this.stats.tests} total`); } - logger.complete(`Tests took ${this.stats.duration}ms`); + reporterOutputLogger.complete(`Tests took ${this.stats.duration}ms`); callback(); }); emitter.on('test pass', (test) => { - logger.pass(`${test.title} duration: ${test.duration}ms`); + reporterOutputLogger.pass(`${test.title} duration: ${test.duration}ms`); if (this.details) { - logger.request(`\n${prettifyResponse(test.request)}\n`); - logger.expected(`\n${prettifyResponse(test.expected)}\n`); - logger.actual(`\n${prettifyResponse(test.actual)}\n\n`); + reporterOutputLogger.request(`\n${prettifyResponse(test.request)}\n`); + reporterOutputLogger.expected(`\n${prettifyResponse(test.expected)}\n`); + reporterOutputLogger.actual(`\n${prettifyResponse(test.actual)}\n\n`); } }); - emitter.on('test skip', test => logger.skip(test.title)); + emitter.on('test skip', test => reporterOutputLogger.skip(test.title)); emitter.on('test fail', (test) => { - logger.fail(`${test.title} duration: ${test.duration}ms`); + reporterOutputLogger.fail(`${test.title} duration: ${test.duration}ms`); if (this.inlineErrors) { - logger.fail(test.message); - if (test.request) { logger.request(`\n${prettifyResponse(test.request)}\n`); } - if (test.expected) { logger.expected(`\n${prettifyResponse(test.expected)}\n`); } - if (test.actual) { logger.actual(`\n${prettifyResponse(test.actual)}\n\n`); } + reporterOutputLogger.fail(test.message); + if (test.request) { reporterOutputLogger.request(`\n${prettifyResponse(test.request)}\n`); } + if (test.expected) { reporterOutputLogger.expected(`\n${prettifyResponse(test.expected)}\n`); } + if (test.actual) { reporterOutputLogger.actual(`\n${prettifyResponse(test.actual)}\n\n`); } } else { this.errors.push(test); } }); emitter.on('test error', (error, test) => { - const connectionErrors = [ - 'ECONNRESET', - 'ENOTFOUND', - 'ESOCKETTIMEDOUT', - 'ETIMEDOUT', - 'ECONNREFUSED', - 'EHOSTUNREACH', - 'EPIPE', - ]; - - if (connectionErrors.indexOf(error.code) > -1) { + if (CONNECTION_ERRORS.includes(error.code)) { test.message = 'Error connecting to server under test!'; + reporterOutputLogger.error(test.message); + } else { + reporterOutputLogger.error(error.stack); } - if (!this.inlineErrors) { - this.errors.push(test); - } - - logger.error(`${test.title} duration: ${test.duration}ms`); - - if (connectionErrors.indexOf(error.code) > -1) { - return logger.error(test.message); - } - logger.error(error.stack); + reporterOutputLogger.error(`${test.title} duration: ${test.duration}ms`); + if (!this.inlineErrors) { this.errors.push(test); } }); }; diff --git a/lib/reporters/DotReporter.js b/lib/reporters/DotReporter.js index b9bbf1ee4..9d00289ff 100644 --- a/lib/reporters/DotReporter.js +++ b/lib/reporters/DotReporter.js @@ -1,4 +1,5 @@ const logger = require('../logger'); +const reporterOutputLogger = require('./reporterOutputLogger'); const prettifyResponse = require('../prettifyResponse'); function DotReporter(emitter, stats, tests) { @@ -24,20 +25,20 @@ DotReporter.prototype.configureEmitter = function configureEmitter(emitter) { this.write('\n'); logger.info('Displaying failed tests...'); for (const test of this.errors) { - logger.fail(`${test.title} duration: ${test.duration}ms`); - logger.fail(test.message); - logger.request(`\n${prettifyResponse(test.request)}\n`); - logger.expected(`\n${prettifyResponse(test.expected)}\n`); - logger.actual(`\n${prettifyResponse(test.actual)}\n\n`); + reporterOutputLogger.fail(`${test.title} duration: ${test.duration}ms`); + reporterOutputLogger.fail(test.message); + reporterOutputLogger.request(`\n${prettifyResponse(test.request)}\n`); + reporterOutputLogger.expected(`\n${prettifyResponse(test.expected)}\n`); + reporterOutputLogger.actual(`\n${prettifyResponse(test.actual)}\n\n`); } } this.write('\n'); - logger.complete(`\ + reporterOutputLogger.complete(`\ ${this.stats.passes} passing, ${this.stats.failures} failing, \ ${this.stats.errors} errors, ${this.stats.skipped} skipped\ `); - logger.complete(`Tests took ${this.stats.duration}ms`); + reporterOutputLogger.complete(`Tests took ${this.stats.duration}ms`); callback(); } diff --git a/lib/reporters/HTMLReporter.js b/lib/reporters/HTMLReporter.js index 07427aded..9f80bd7ac 100644 --- a/lib/reporters/HTMLReporter.js +++ b/lib/reporters/HTMLReporter.js @@ -8,6 +8,7 @@ const md = require('markdown-it')(); const pathmodule = require('path'); const logger = require('../logger'); +const reporterOutputLogger = require('./reporterOutputLogger'); const prettifyResponse = require('../prettifyResponse'); function HTMLReporter(emitter, stats, tests, path, details) { @@ -29,7 +30,7 @@ function HTMLReporter(emitter, stats, tests, path, details) { HTMLReporter.prototype.sanitizedPath = function sanitizedPath(path = './report.html') { const filePath = pathmodule.resolve(untildify(path)); if (fs.existsSync(filePath)) { - logger.info(`File exists at ${filePath}, will be overwritten...`); + logger.warn(`File exists at ${filePath}, will be overwritten...`); } return filePath; }; @@ -48,12 +49,12 @@ HTMLReporter.prototype.configureEmitter = function configureEmitter(emitter) { makeDir(pathmodule.dirname(this.path)) .then(() => { fs.writeFile(this.path, html, (error) => { - if (error) { logger.error(error); } + if (error) { reporterOutputLogger.error(error); } callback(); }); }) .catch((err) => { - logger.error(err); + reporterOutputLogger.error(err); callback(); }); }); diff --git a/lib/reporters/MarkdownReporter.js b/lib/reporters/MarkdownReporter.js index 2c77c2b66..d7bab1514 100644 --- a/lib/reporters/MarkdownReporter.js +++ b/lib/reporters/MarkdownReporter.js @@ -7,6 +7,7 @@ const makeDir = require('make-dir'); const pathmodule = require('path'); const logger = require('../logger'); +const reporterOutputLogger = require('./reporterOutputLogger'); const prettifyResponse = require('../prettifyResponse'); function MarkdownReporter(emitter, stats, tests, path, details) { @@ -28,7 +29,7 @@ function MarkdownReporter(emitter, stats, tests, path, details) { MarkdownReporter.prototype.sanitizedPath = function sanitizedPath(path = './report.md') { const filePath = pathmodule.resolve(untildify(path)); if (fs.existsSync(filePath)) { - logger.info(`File exists at ${filePath}, will be overwritten...`); + logger.warn(`File exists at ${filePath}, will be overwritten...`); } return filePath; }; @@ -46,12 +47,12 @@ MarkdownReporter.prototype.configureEmitter = function configureEmitter(emitter) makeDir(pathmodule.dirname(this.path)) .then(() => { fs.writeFile(this.path, this.buf, (error) => { - if (error) { logger.error(error); } + if (error) { reporterOutputLogger.error(error); } callback(); }); }) .catch((err) => { - logger.error(err); + reporterOutputLogger.error(err); callback(); }); }); diff --git a/lib/reporters/NyanReporter.js b/lib/reporters/NyanReporter.js index f80eb8e58..5f0f099f6 100644 --- a/lib/reporters/NyanReporter.js +++ b/lib/reporters/NyanReporter.js @@ -2,6 +2,7 @@ const tty = require('tty'); const logger = require('../logger'); const prettifyResponse = require('../prettifyResponse'); +const reporterOutputLogger = require('./reporterOutputLogger'); function NyanCatReporter(emitter, stats, tests) { let windowWidth; @@ -56,16 +57,16 @@ NyanCatReporter.prototype.configureEmitter = function configureEmitter(emitter) this.write('\n'); logger.info('Displaying failed tests...'); for (const test of this.errors) { - logger.fail(`${test.title} duration: ${test.duration}ms`); - logger.fail(test.message); - logger.request(`\n${prettifyResponse(test.request)}\n`); - logger.expected(`\n${prettifyResponse(test.expected)}\n`); - logger.actual(`\n${prettifyResponse(test.actual)}\n\n`); + reporterOutputLogger.fail(`${test.title} duration: ${test.duration}ms`); + reporterOutputLogger.fail(test.message); + reporterOutputLogger.request(`\n${prettifyResponse(test.request)}\n`); + reporterOutputLogger.expected(`\n${prettifyResponse(test.expected)}\n`); + reporterOutputLogger.actual(`\n${prettifyResponse(test.actual)}\n\n`); } } - logger.complete(`${this.stats.passes} passing, ${this.stats.failures} failing, ${this.stats.errors} errors, ${this.stats.skipped} skipped`); - logger.complete(`Tests took ${this.stats.duration}ms`); + reporterOutputLogger.complete(`${this.stats.passes} passing, ${this.stats.failures} failing, ${this.stats.errors} errors, ${this.stats.skipped} skipped`); + reporterOutputLogger.complete(`Tests took ${this.stats.duration}ms`); callback(); }); diff --git a/lib/reporters/XUnitReporter.js b/lib/reporters/XUnitReporter.js index d31b6411a..b5db268fe 100644 --- a/lib/reporters/XUnitReporter.js +++ b/lib/reporters/XUnitReporter.js @@ -8,6 +8,7 @@ const makeDir = require('make-dir'); const pathmodule = require('path'); const logger = require('../logger'); +const reporterOutputLogger = require('./reporterOutputLogger'); const prettifyResponse = require('../prettifyResponse'); function XUnitReporter(emitter, stats, tests, path, details) { @@ -42,14 +43,14 @@ XUnitReporter.prototype.updateSuiteStats = function updateSuiteStats(path, stats }, false); const xmlHeader = ''; fs.writeFile(path, `${xmlHeader}\n${newStats}\n${restOfFile}`, (error) => { - if (error) { logger.error(error); } + if (error) { reporterOutputLogger.error(error); } callback(); }); } else { callback(); } } else { - logger.error(err); + reporterOutputLogger.error(err); callback(); } }); @@ -77,7 +78,7 @@ XUnitReporter.prototype.toTag = function toTag(name, attrs, close, content) { XUnitReporter.prototype.sanitizedPath = function sanitizedPath(path = './report.xml') { const filePath = pathmodule.resolve(untildify(path)); if (fs.existsSync(filePath)) { - logger.info(`File exists at ${filePath}, will be overwritten...`); + logger.warn(`File exists at ${filePath}, will be overwritten...`); fs.unlinkSync(filePath); } return filePath; @@ -99,7 +100,7 @@ XUnitReporter.prototype.configureEmitter = function configureEmitter(emitter) { callback(); }) .catch((err) => { - logger.error(err); + reporterOutputLogger.error(err); callback(); }); }); diff --git a/lib/reporters/reporterOutputLogger.js b/lib/reporters/reporterOutputLogger.js new file mode 100644 index 000000000..5722bb50f --- /dev/null +++ b/lib/reporters/reporterOutputLogger.js @@ -0,0 +1,31 @@ +const winston = require('winston'); + +module.exports = new (winston.Logger)({ + transports: [ + new (winston.transports.Console)({ colorize: true }), + ], + levels: { + test: 9, + pass: 8, + fail: 7, + complete: 6, + actual: 5, + expected: 4, + hook: 3, + request: 2, + skip: 1, + error: 0, + }, + colors: { + test: 'yellow', + pass: 'green', + fail: 'red', + complete: 'green', + actual: 'red', + expected: 'red', + hook: 'green', + request: 'green', + skip: 'yellow', + error: 'red', + }, +});