jasmine-console-reporter
Version:
Console Reporter for Jasmine. Outputs detailed test results to the console, with beautiful layout and colors. This is the default reporter of grunt-jasmine-nodejs.
171 lines (145 loc) • 5.86 kB
JavaScript
const path = require('path');
/**
* Activity Logger
* @author Onur Yıldırım (onur@cutepilot.com)
* @license MIT
*/
module.exports = (() => {
const utils = {
// windows returns 'win32' even on 64 bit but we still check for win64,
// just in case...
isWindows: process.platform === 'win32'
|| process.platform === 'win64'
};
utils.type = obj => ({}).toString.call(obj).match(/\s(\w+)/i)[1].toLowerCase();
utils.isNum = value => typeof value === 'number';
utils.log = (...args) => {
process.stdout.write.apply(process.stdout, args);
};
utils.plural = (str, count) => count === 1 ? str : str + 's';
utils.capitalize = string => string.slice(0, 1).toUpperCase() + string.slice(1);
utils.repeat = (string, times) => new Array(times + 1).join(string);
utils.indent = (str, times, indentChar) => {
indentChar = indentChar || ' ';
const newArr = [];
const lines = (str || '').split('\n');
for (let i = 0; i < lines.length; i++) {
newArr.push(utils.repeat(indentChar, times) + lines[i]);
}
return newArr.join('\n');
};
let pStackLine = /[ \t]+at [^\r\n]+/g;
utils.restack = (expectation, cleanLevel, style) => {
if (!expectation) return '';
let stack = String(expectation.stack || '');
let message = typeof expectation.message === 'string'
? expectation.message
: (stack.split(pStackLine)[0] || '');
message = message.replace(/[\r\n]+$/, '');
let stackLines = stack.match(pStackLine);
if (cleanLevel > 0) {
let cleanPath = cleanLevel >= 2
? 'node_modules'
: path.join('node_modules', 'jasmine-core');
cleanPath = path.sep + cleanPath + path.sep;
// remove stack lines with jasmine-core path.
stackLines = stackLines.filter(line => {
const pathCheck = line.indexOf(cleanPath) === -1;
const sepCheck = cleanLevel >= 3
? line.indexOf(path.sep) > -1
: true;
return (pathCheck && sepCheck);
});
}
stackLines = stackLines.map(line => {
// make the file paths CMD+clickable by removing the wrapping parenths.
line = line.replace(/\(([^(]+?)\)/g, '$1'); // '( $1 )'
if (style) {
// add colors/styles to file name, line and column numbers
line = line.replace(/([^:/\\ ]+):(\d+):(\d+)/, (m, $1, $2, $3) => {
return style.yellow($1) + ':' + style.white($2) + ':' + style.white($3);
});
}
return line;
});
// add back the error message and rest of stack
if (style) message = style.red(message);
return message + '\n' + stackLines.join('\n');
};
utils.extend = (defaults, object) => {
object = object || {};
for (let key in defaults) {
if (defaults.hasOwnProperty(key) && object[key] === undefined) {
object[key] = defaults[key];
}
}
return object;
};
// ---------------------------
const defaultOpts = {
colors: 1, // 0 to 2
cleanStack: 1, // 0 to 3
verbosity: { // object or number (0 to 4)
specs: true,
failed: true,
pending: true,
disabled: true,
summary: true
},
activity: false, // bool or string ("dots")
listStyle: 'indent', // "indent"|"flat"
timeUnit: 'ms', // s|ms|ns
timeThreshold: { ok: 500, warn: 1000, ouch: 3000 }, // number|object
emoji: true,
beep: true
};
// Normalizes and tries to get a numeric value. This is used for options
// that support both Boolean and Number values.
function _boolToNum(value, numTrue, numFalse, defaultVal) {
if (typeof value === 'boolean') {
return value ? numTrue : numFalse;
}
return typeof value === 'number' ? value : defaultVal;
}
function _mormalizeVerbosity(verbosity) {
if (utils.type(verbosity) === 'object') {
return Object.assign({}, defaultOpts.verbosity, verbosity);
}
const v = _boolToNum(verbosity, 4, 0, null);
if (v === null) return Object.assign({}, defaultOpts.verbosity);
return {
failed: true, // always true
disabled: v >= 4, // also list disabled specs and some additional info
specs: v >= 3,
pending: v >= 2,
summary: v >= 1
};
}
function _keep(n, min) {
return typeof n === 'number' && n > (min || 0);
}
function _normalizeThreshold(threshold) {
const t = Object.assign({}, threshold);
if (!_keep(t.ok, 0)) delete t.ok;
if (!_keep(t.warn, t.ok)) delete t.warn;
if (!_keep(t.ouch, t.warn)) delete t.ouch;
return t;
}
utils.getOptions = options => {
let opts = options || {};
opts.cleanStack = _boolToNum(opts.cleanStack, 1, 0, defaultOpts.cleanStack);
opts.colors = _boolToNum(opts.colors, 1, 0, defaultOpts.colors);
opts = Object.assign({}, defaultOpts, opts);
opts.verbosity = _mormalizeVerbosity(opts.verbosity);
const threshold = opts.timeThreshold;
const t = utils.type(threshold);
opts.timeThreshold = t === 'number'
? { ouch: threshold }
: t === 'object'
? _normalizeThreshold(threshold)
: Object.assign({}, defaultOpts.timeThreshold);
return opts;
};
// ---------------------------
return utils;
})();