mocha-spec-reporter-with-file-names
Version:
Mocha default spec reporter with file names.
256 lines (255 loc) • 10.6 kB
JavaScript
;
/**
* A lot of this code comes from or is based on the "spec" reporter that is built-in to Mocha. That
* code is located here:
* https://github.com/mochajs/mocha/blob/51d4746cf6ccefdcfcbc841c92f70efaa338e34f/lib/reporters/spec.js
*
* That code has the following license:
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SpecReporterWithFileNames = void 0;
/**
* (The MIT License)
*
* Copyright (c) 2011-2022 OpenJS Foundation and contributors, https://openjsf.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the 'Software'), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute,
* sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or
* substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
const mocha_1 = require("mocha");
const ms_1 = __importDefault(require("ms"));
const path_1 = require("path");
const Base = mocha_1.reporters.Base;
const { EVENT_RUN_END, EVENT_TEST_FAIL, EVENT_TEST_PASS, EVENT_SUITE_BEGIN, EVENT_SUITE_END, EVENT_TEST_PENDING, } = mocha_1.Runner.constants;
const { EVENT_SUITE_ADD_TEST } = mocha_1.Suite.constants;
const baseColors = Base.colors;
const debug = process.argv.includes('--mocha-spec-with-name-debug');
function debugLog(...args) {
if (debug) {
console.info(...args);
}
}
const extraColors = {
fileName: 7,
};
function color(type, str) {
var _a;
if (!Base.useColors) {
return String(str);
}
const colorToUse = (_a = baseColors[type]) !== null && _a !== void 0 ? _a : extraColors[type];
return '\u001b[' + colorToUse + 'm' + str + '\u001b[0m';
}
function stringifyDiffObjs(err) {
if (!(typeof err.actual === 'string') || !(typeof err.expected === 'string')) {
err.actual = mocha_1.utils.stringify(err.actual);
err.expected = mocha_1.utils.stringify(err.expected);
}
}
const mochaIdKey = '__mocha_id__';
const fileKey = 'file';
class SpecReporterWithFileNames extends Base {
constructor(runner, options) {
super(runner, options);
let indents = 0;
let n = 0;
function indent() {
return Array(indents + 1).join(' ');
}
const loggedFiles = new Set();
const loggedSuites = new Set();
const suites = new Map();
function logTestSuite(suite, test) {
var _a;
if (!suite[fileKey]) {
suite[fileKey] = test[fileKey];
}
if (!suite.id) {
suite.id = suite[mochaIdKey];
}
const suiteParent = suite.parent && suites.get(suite.parent[mochaIdKey]);
const isTopDescribe = !(suiteParent === null || suiteParent === void 0 ? void 0 : suiteParent.parent);
if (isTopDescribe && !loggedFiles.has(suite[fileKey])) {
loggedFiles.add(suite[fileKey]);
Base.consoleLog();
indents = 0;
Base.consoleLog(color('fileName', '%s%s'), indent(), (0, path_1.relative)(process.cwd(), (_a = suite[fileKey]) !== null && _a !== void 0 ? _a : ''));
indents++;
}
else if (!isTopDescribe && suiteParent) {
logTestSuite(suiteParent, test);
}
if (!loggedSuites.has(suite.id)) {
Base.consoleLog(color('suite', '%s%s'), indent(), suite.title);
loggedSuites.add(suite.id);
indents++;
}
}
runner
.on(EVENT_SUITE_ADD_TEST, (test) => {
var _a, _b;
debugLog(`====================================== EVENT_SUITE_ADD_TEST`);
debugLog({ test, id: test[mochaIdKey] });
const suite = suites.get((_b = (_a = test.parent) === null || _a === void 0 ? void 0 : _a[mochaIdKey]) !== null && _b !== void 0 ? _b : '');
if (!suite) {
return;
}
logTestSuite(suite, test);
})
.on(EVENT_SUITE_BEGIN, (suite) => {
debugLog(`====================================== EVENT_SUITE_BEGIN`);
debugLog({ suite, id: suite[mochaIdKey] });
// only add file names to the top most level suite
suites.set(suite[mochaIdKey], suite);
})
.on(EVENT_SUITE_END, () => {
debugLog(`====================================== EVENT_SUITE_END`);
--indents;
})
.on(EVENT_TEST_PENDING, (test) => {
debugLog(`====================================== EVENT_TEST_PENDING`);
debugLog({ test, id: test[mochaIdKey] });
Base.consoleLog(indent() + color('pending', ' - %s'), test.title);
})
.on(EVENT_TEST_PASS, (test) => {
debugLog(`====================================== EVENT_TEST_PASS`);
debugLog({ test, id: test[mochaIdKey] });
if (test.speed === 'fast') {
const format = indent() + color('checkmark', ' ' + Base.symbols.ok) + color('pass', ' %s');
Base.consoleLog(format, test.title);
}
else {
const format = indent() +
color('checkmark', ' ' + Base.symbols.ok) +
color('pass', ' %s') +
color(test.speed, ' (%dms)');
Base.consoleLog(format, test.title, test.duration);
}
})
.on(EVENT_TEST_FAIL, (test) => {
debugLog(`====================================== EVENT_TEST_FAIL`);
debugLog({ test, id: test[mochaIdKey] });
Base.consoleLog(indent() + color('fail', '%d) %s'), ++n, test.title);
})
.once(EVENT_RUN_END, (...args) => {
debugLog(`====================================== EVENT_RUN_END`);
debugLog({ args, id: args[mochaIdKey] });
this.finalMessage(...args);
});
}
finalMessage() {
var stats = this.stats;
var fmt;
Base.consoleLog();
// passes
fmt = color('bright pass', ' ') + color('green', ' %d passing') + color('light', ' (%s)');
Base.consoleLog(fmt, stats.passes || 0, (0, ms_1.default)(stats.duration));
// pending
if (stats.pending) {
fmt = color('pending', ' ') + color('pending', ' %d pending');
Base.consoleLog(fmt, stats.pending);
}
// failures
if (stats.failures) {
fmt = color('fail', ' %d failing');
Base.consoleLog(fmt, stats.failures);
this.list(this.failures);
Base.consoleLog();
}
Base.consoleLog();
}
list(failures) {
let multipleErr;
let multipleTest;
Base.consoleLog();
failures.forEach(function (test, i) {
// format
let fmt = color('error title', ' %s) %s:') +
color('error message', ' %s') +
color('error stack', '\n%s\n');
// msg
let msg;
let err;
const testErr = test.err;
if (testErr && testErr.multiple) {
if (multipleTest !== test) {
multipleTest = test;
multipleErr = [testErr].concat(testErr.multiple);
}
err = multipleErr.shift();
}
else {
err = test.err;
}
let message;
if (typeof err.inspect === 'function') {
message = err.inspect() + '';
}
else if (err.message && typeof err.message.toString === 'function') {
message = err.message + '';
}
else {
message = '';
}
let stack = err.stack || message;
let index = message ? stack.indexOf(message) : -1;
if (index === -1) {
msg = message;
}
else {
index += message.length;
msg = stack.slice(0, index);
// remove msg from stack
stack = stack.slice(index + 1);
}
// uncaught
if (err.uncaught) {
msg = 'Uncaught ' + msg;
}
// explicitly show diff
if (!exports.hideDiff && Base.showDiff(err)) {
stringifyDiffObjs(err);
fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
let match = message.match(/^([^:]+): expected/);
msg = '\n ' + color('error message', match ? match[1] : msg);
msg += Base.generateDiff(err.actual, err.expected);
}
// indent stack trace
stack = stack.replace(/^/gm, ' ');
// indented test title
let testTitle = '';
test.titlePath().forEach((str, index) => {
if (index !== 0) {
testTitle += '\n ';
}
for (let i = 0; i < index; i++) {
testTitle += ' ';
}
testTitle += str;
if (index === 0 && test[fileKey]) {
testTitle += ' (' + (0, path_1.relative)(process.cwd(), test[fileKey]) + ') ';
}
});
Base.consoleLog(fmt, i + 1, testTitle, msg, stack);
});
}
}
exports.SpecReporterWithFileNames = SpecReporterWithFileNames;
// use this to easily get a path to this file so it can be handed to Mocha in a config file
SpecReporterWithFileNames.pathToThisReporter = __filename;
module.exports = SpecReporterWithFileNames;