UNPKG

mocha-iko-reporter

Version:

Mocha reporter for iko assertion library

199 lines (164 loc) 4.87 kB
"use strict"; const chalk = require("chalk"); const ikoChalk = require("iko-error-chalk"); const Colors = { sectionTitle: chalk.underline, suiteName: chalk.bold, testPass: chalk.green, testFail: chalk.red, testSlow: chalk.red, pending: chalk.yellow, errorTitle: chalk.bold.red, errorMessage: chalk, errorStack: chalk.italic.grey, errorUncaught: chalk.bold.red, duration: chalk.dim, }; const Symbols = { suite: "›", info: "ℹ", success: "✔", warning: "⚠", error: "✖", }; if (process.platform === "win32") { Object.assign(Symbols, { info: "i", success: "√", warning: "‼", error: "×", }); } // Save timer references to avoid Sinon interfering. // See: https://github.com/mochajs/mocha/issues/237 const Date = global.Date; const renderAssertionError = ikoChalk.createAssertionErrorRenderer(); const stdout = process.stdout; function formatTime(time) { const mins = Math.floor(time / 60000); const secs = (time - mins * 60000) / 1000; const str = secs + (secs === 1 ? " sec" : " secs"); if (mins) { str = mins + (mins === 1 ? " min " : " mins ") + str; } return str; } function getTestNounFor(testCount) { return testCount === 1 ? "test" : "tests"; } function indent(depth) { return " ".repeat(depth); } function leftPad(p, s) { return s.replace(/^/gm, p); } function write() { for (let i = 0; i < arguments.length; i++) { stdout.write(arguments[i]); } } function fullTestName(node) { const result = []; while (node.parent) { result.push(node.title); node = node.parent; } result.reverse(); return result.join(` ${Symbols.suite} `); } function Reporter(runner) { let passed = 0; let failed = 0; let pending = 0; let slow = 0; const failedTests = []; let depth = 0; let netTime = 0; let startTime; runner.on("start", function () { startTime = new Date(); write("\n"); }); runner.on("suite", function (suite) { if (!suite.root) { depth++; write(Colors.suiteName(`${indent(depth)}${Symbols.suite} ${suite.title}\n`)); } }); runner.on("suite end", function () { depth--; if (depth === 0) { write("\n"); } }); runner.on("test end", function (test) { netTime += test.duration; }); runner.on("pass", function (test) { passed++; write(indent(depth), Colors.testPass(` ${Symbols.success} ${test.title}`)); const duration = test.duration; if (duration > test.slow()) { slow++; write(Colors.testSlow(` (${duration}ms)`)); } write("\n"); }); runner.on("fail", function (test, err) { failed++; test.err = err; failedTests.push(test); write(indent(depth), Colors.testFail(` ${Symbols.error} ${test.title}\n`)); }); runner.on("end", () => { const totalTime = new Date().getUTCMilliseconds() - startTime.getUTCMilliseconds(); const currentTime = new Date().toTimeString(); write(Colors.duration(` Finished in ${formatTime(totalTime)} / ${formatTime(netTime)} @ ${currentTime}\n\n`)); write(Colors.testPass(` ${Symbols.success} ${passed} ${getTestNounFor(passed)} passed\n`)); if (pending) { write(Colors.pending(` ${Symbols.info} ${pending} ${getTestNounFor(pending)} pending\n`)); } if (slow) { write(Colors.testSlow(` ${Symbol.warning} ${slow} ${getTestNounFor(slow)} slow\n`)); } if (failed) { write(Colors.testFail(` ${Symbols.error} ${failed} ${getTestNounFor(failed)} failed\n\n`)); write(Colors.sectionTitle("FAILED TESTS:\n\n")); failedTests.forEach(function (test) { const err = test.err; let rawMessage; let message; if (err.message && typeof err.message.toString === "function") { rawMessage = err.message; if (typeof rawMessage !== "string") { rawMessage = message = rawMessage.toString(); } else { message = renderAssertionError({ text: rawMessage, annotations: err.annotations }); } } else { rawMessage = message = ""; } let stack = err.stack; // remove message from stack trace if (rawMessage) { const index = stack.indexOf(rawMessage); if (index !== -1) { stack = stack.slice(index + rawMessage.length + 1); } } stack = leftPad(" ", stack); write(Colors.errorTitle(` ${Symbols.error} ${fullTestName(test)}\n\n`)); if (err.uncaught) { write(Colors.errorUncaught(` ${Symbols.warning} Uncaught Error:\n`)) } write(leftPad(" ", message), "\n"); write(Colors.errorStack(stack), "\n\n"); }); } }); runner.on("pending", function (test) { pending++; write(Colors.pending(` - ${test.title}`), "\n"); }); } module.exports = Reporter;