Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verbose test results #370

Merged
merged 12 commits into from
May 20, 2015
6 changes: 6 additions & 0 deletions bin/jest.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ var argv = optimist
),
type: 'boolean'
},
verbose: {
description: _wrapDesc(
'Display individual test results with the test suite hierarchy.'
),
type: 'boolean'
}
})
.check(function(argv) {
if (argv.runInBand && argv.hasOwnProperty('maxWorkers')) {
Expand Down
36 changes: 28 additions & 8 deletions src/DefaultTestReporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@

var colors = require('./lib/colors');
var formatFailureMessage = require('./lib/utils').formatFailureMessage;
var formatMsg = require('./lib/utils').formatMsg;
var path = require('path');
var VerboseLogger = require('./lib/testLogger');

var FAIL_COLOR = colors.RED_BG + colors.BOLD;
var PASS_COLOR = colors.GREEN_BG + colors.BOLD;
Expand All @@ -27,6 +29,10 @@ DefaultTestReporter.prototype.onRunStart =
function(config, aggregatedResults) {
this._config = config;
this._printWaitingOn(aggregatedResults);
if (this._config.verbose) {
var verboseLogger = new VerboseLogger(this._config, this._process);
this.verboseLog = verboseLogger.verboseLog.bind(verboseLogger);
}
};

DefaultTestReporter.prototype.onTestResult =
Expand Down Expand Up @@ -62,14 +68,27 @@ function(config, testResult, aggregatedResults) {
}
*/

this.log(this._getResultHeader(allTestsPassed, pathStr, [
testRunTimeString
]));
if (config.verbose) {
this.verboseLog(testResult.testResults);
} else {
this.log(this._getResultHeader(allTestsPassed, pathStr, [
testRunTimeString
]));
}

testResult.logMessages.forEach(this._printConsoleMessage.bind(this));

if (!allTestsPassed) {
this.log(formatFailureMessage(testResult, /*color*/!config.noHighlight));
if (config.verbose) {
aggregatedResults.postSuiteHeaders.push(
this._getResultHeader(allTestsPassed, pathStr, [
testRunTimeString
]),
formatFailureMessage(testResult, /*color*/!config.noHighlight)
);
} else {
this.log(formatFailureMessage(testResult, /*color*/!config.noHighlight));
}
}

this._printWaitingOn(aggregatedResults);
Expand All @@ -86,6 +105,10 @@ function (config, aggregatedResults) {
return;
}

if (config.verbose) {
this.log(aggregatedResults.postSuiteHeaders.join('\n'));
}

var results = '';
if (numFailedTests) {
results += this._formatMsg(
Expand Down Expand Up @@ -135,10 +158,7 @@ DefaultTestReporter.prototype._clearWaitingOn = function() {
};

DefaultTestReporter.prototype._formatMsg = function(msg, color) {
if (this._config.noHighlight) {
return msg;
}
return colors.colorize(msg, color);
return formatMsg(msg, color, this._config);
};

DefaultTestReporter.prototype._getResultHeader =
Expand Down
1 change: 1 addition & 0 deletions src/TestRunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,7 @@ TestRunner.prototype.runTests = function(testPaths, reporter) {
numPassedTests: 0,
numFailedTests: 0,
testResults: [],
postSuiteHeaders: []
};

reporter.onRunStart && reporter.onRunStart(config, aggregatedResults);
Expand Down
6 changes: 2 additions & 4 deletions src/jasmineTestRunner/JasmineReporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

var colors = require('../lib/colors');
var diff = require('diff');
var formatMsg = require('../lib/utils').formatMsg;
var jasmine = require('../../vendor/jasmine/jasmine-1.3.0').jasmine;
var Q = require('q');

Expand Down Expand Up @@ -238,10 +239,7 @@ JasmineReporter.prototype._prettyPrint = function(obj, indent, cycleWeakMap) {
};

JasmineReporter.prototype._formatMsg = function(msg, color) {
if (this._config.noHighlight) {
return msg;
}
return colors.colorize(msg, color);
return formatMsg(msg, color, this._config);
};

module.exports = JasmineReporter;
4 changes: 4 additions & 0 deletions src/jest.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ function _promiseConfig(argv, packageRoot) {
config.noHighlight = argv.noHighlight;
}

if (argv.verbose) {
config.verbose = argv.verbose;
}

return config;
});
}
Expand Down
197 changes: 197 additions & 0 deletions src/lib/testLogger.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/**
* Copyright (c) 2014, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';

var colors = require('./colors');
var formatMsg = require('./utils').formatMsg;

/**
* Creates a VerboseLogger object used to encapsulate verbose logging.
*
* @param {Object} config - configuration options for the test suite.
* @param {Object} customProcess - Optional process.
*
* NOTE: config is being passed in to preserve a users preferences when using
* the CLI option. @example --noHighLight
*/
function VerboseLogger(config, customProcess) {
this._process = customProcess || process;
this._config = config || {};
}

/**
* Kicks off the verbose logging by constructing a Test Hierarchy and then
* printing it with the correct formatting and a trailing newline.
*
* @param {Array} testResults - All information about test results in a test
* run. Given as an Array of objects.
*
* @see {@link _createTestNode}
* @see {@link traverseTestResults}
*/
VerboseLogger.prototype.verboseLog = function(testResults) {
this.traverseTestResults(_createTestTree(testResults));
this.log('');
};


/**
* Prints test titles and their Ancestry with correct indentation.
*
* If the node contains test titles, then the test titles must be printed
* before stepping lower into the hierarchy (`node.childNodes`). Otherwise
* a header has been reached and must be printed before stepping lower into
* the hierarchy.
*
* @param {Object} node - A test node with correct hierarchy for printing.
* @param {String} indentation - Indentation for a given level in the hierarchy.
*
* @note:
* The amount of indentation is determined when stepping lower into the
* hierarchy.
* @see{@link _createTestNode}
* @see{@link _createTestTree}
*
*/
VerboseLogger.prototype.traverseTestResults = function(node, indentation) {
var indentationIncrement;
if (typeof node === 'undefined' || node === null) {
return;
}

indentationIncrement = ' ';
indentation = indentation || '';
if (Object.prototype.toString.call(node.testTitles) === '[object Array]') {
this.printTestTitles(node.testTitles, indentation);
this.traverseTestResults(node.childNodes, indentation);
} else {
for (var key in node) {
this.log(indentation + key);
this.traverseTestResults(node[key], indentation + indentationIncrement);
}
}
};

/**
*
* Pretty print test results to note which are failing and passing.
* Passing tests are prepended with PASS or a check. Failing tests are prepended
* with FAIL or an x.
* @param {object} testTitles - All information about test titles in a test run.
* @param {string} indentation - Indentation used for formatting.
*/
VerboseLogger.prototype.printTestTitles = function(testTitles, indentation) {
var prefixColor, statusPrefix;

for (var i = 0; i < testTitles.length; i++){
if (testTitles[i].failureMessages.length === 0) {
prefixColor = colors.GREEN;
statusPrefix = this._config.noHighlight ? 'PASS - ' : '\u2713 ';
} else {
prefixColor = colors.RED;
statusPrefix = this._config.noHighlight ? 'FAIL - ' : '\u2715 ';
}
this.log(
this._formatMsg(indentation + statusPrefix, prefixColor)
+ this._formatMsg(testTitles[i].title, colors.GRAY)
);
}
};

VerboseLogger.prototype.log = function(str) {
this._process.stdout.write(str + '\n');
};

VerboseLogger.prototype._formatMsg = function(msg, color) {
return formatMsg(msg, color, this._config);
};

/**
* Prepares the test hierarchy for a `test title` by mapping its ancestry.
*
* @example
* Test Structure A -
* describe('HeaderOne', function(){
* describe('HeaderTwo', function(){
* it('quacks like a duck', function(){
* expect(true).toBeTruthy();
* });
* });
* });
*
* Produces Test Node A -
* {
* testTitles: [],
* childNodes: {
* HeaderOne: {
* testTitles: [],
* childNodes: {
* HeaderTwo: {
* testTitles: ['it quacks like a duck'],
* childNodes: {}
* }
* }
* }
* }
* }
*
* @param {Object} testResult - An object containing a test title and its
* pass/fail status.
* @param {Array} ancestorTitles - Ancestor/describe headers associated with the
* given test title.
* @param {Object} currentNode - A parent in the test hierarchy. Contains:
* 1) Test titles associated with the current parent
* 2) The next parent in the hierarchy.
*
* @return {Object} A node mapping the hierarchy of `test titles` with common
* ancestors.
*/
function _createTestNode(testResult, ancestorTitles, currentNode) {
currentNode = currentNode || { testTitles: [], childNodes: {} };
if (ancestorTitles.length === 0) {
currentNode.testTitles.push(testResult);
} else {
if (!currentNode.childNodes[ancestorTitles[0]]) {
currentNode.childNodes[ancestorTitles[0]] = {
testTitles: [],
childNodes: {}
};
}
_createTestNode(
testResult,
ancestorTitles.slice(1,ancestorTitles.length),
currentNode.childNodes[ancestorTitles[0]]
);
}

return currentNode;
}

/**
*
* Constructs a tree representing the hierarchy of a test run.
*
* @param {Array} testResults - Collection of tests.
* @return {Object} Complete test hierarchy for a test run.
*
* @note: Here, a test run refers to a jest file. A tree is used
* to map all common ancestors (describe blocks) to individual
* test titles (it blocks) without repetition.
* @see {@link _createTestNode}
*
*/
function _createTestTree(testResults) {
var tree;
for (var i = 0; i < testResults.length; i++) {
tree = _createTestNode(testResults[i], testResults[i].ancestorTitles, tree);
}

return tree;
}

module.exports = VerboseLogger;
9 changes: 9 additions & 0 deletions src/lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,14 @@ function formatFailureMessage(testResult, color) {
}).join('\n');
}

function formatMsg(msg, color, _config) {
_config = _config || {};
if (_config.noHighlight) {
return msg;
}
return colors.colorize(msg, color);
}

// A RegExp that matches paths that should not be included in error stack traces
// (mostly because these paths represent noisy/unhelpful libs)
var STACK_TRACE_LINE_IGNORE_RE = new RegExp('^(?:' + [
Expand All @@ -434,6 +442,7 @@ var STACK_TRACE_LINE_IGNORE_RE = new RegExp('^(?:' + [


exports.escapeStrForRegex = escapeStrForRegex;
exports.formatMsg = formatMsg;
exports.getLineCoverageFromCoverageInfo = getLineCoverageFromCoverageInfo;
exports.getLinePercentCoverageFromCoverageInfo =
getLinePercentCoverageFromCoverageInfo;
Expand Down