Source: reporter/stdout.js

"use strict";
/**
 * `GlaceJS` stdout reporter.
 *
 * @module
 */

const fs = require("fs");
const path = require("path");
const util = require("util");

const _ = require("lodash");
const colors = require("colors");
const fse = require("fs-extra");
const MochaReporter = require("mocha").reporters.base;
const prettyms = require("pretty-ms");

const CONF = require("../config");
const TestCase = require("../testing").TestCase;
const utils = require("../utils");

let report;
let indents = 0;

/**
 * Calculate message indentation.
 * @ignore
 */
const indent = () => Array(indents).join("  ");

/**
 * Write report to stdout.
 * @ignore
 */
const stdout = function() {
    report.write(colors.strip(util.format.apply(util, arguments)) + "\n");
    console.log.apply(console, arguments);
};

/**
 * Print epilogue results.
 * @ignore
 */
const epilogue = () => {
    const passedTests = [], failedTests = [], skippedTests = [];

    for (const testCase of CONF.test.cases) {
        if (testCase.status === TestCase.FAILED) {
            failedTests.push(testCase);
        }
        if (testCase.status === TestCase.PASSED) {
            passedTests.push(testCase);
        }
        if (testCase.status === TestCase.SKIPPED) {
            skippedTests.push(testCase);
        }
    }

    printStatistics(passedTests.length, failedTests.length);
    if (skippedTests.length) printSkippedTests(skippedTests);

    utils.printTestErrors(failedTests, stdout);
    utils.printSessionErrors(stdout);
};

/**
 * Prints tests statistics.
 * @ignore
 */
const printStatistics = (passedNum, failedNum) => {
    let msg;
    const indent = "  ";

    if (passedNum) {
        msg = "passed test" + (passedNum === 1 ? "" : "s");
        stdout((indent + MochaReporter.symbols.ok + " " +
                String(passedNum).bold + " " + msg).green);
    }

    if (failedNum) {
        msg = "failed test" + (failedNum === 1 ? "" : "s");
        stdout((indent + MochaReporter.symbols.err + " " +
                String(failedNum).bold + " " + msg).red);
    }

    const chunksNum = CONF.test.cases.reduce((a, b) => a + b.chunks.length, 0);
    if (chunksNum) {
        msg = "executed chunk" + (chunksNum === 1 ? "" : "s");
        stdout(indent + chunksNum + " " + msg);
    }

    let execTime = CONF.test.cases
        .map(t => t.duration)
        .reduce((a, b) => a + b, 0);

    if (execTime > 0) {
        execTime = (execTime < 60000 ? `${execTime / 1000} sec` : prettyms(execTime)).bold;
        stdout();
        stdout(indent + "Summary tests time is", execTime);
    }
};

/**
 * Prints skipped tests.
 * @ignore
 */
const printSkippedTests = skippedTests => {
    let msg;
    const indent = "  ";
    msg = "skipped test" + (skippedTests.length === 1 ? "" : "s");
    stdout();
    // HACK https://github.com/glacejs/glace-core/issues/136
    stdout(indent + "# ".gray + String(skippedTests.length).gray.bold + " " + msg.gray);

    for (const skip of skippedTests) {
        msg = `* '${skip.name}'`;
        if (skip.rawInfo[0]) {
            msg += " - " + skip.rawInfo[0].bold;
        }
        stdout(indent + indent + msg.gray);
    }
};

module.exports = {
    /**
     * Called before tests start.
     *
     * @method
     * @instance
     */
    start: () => {
        fse.mkdirsSync(CONF.report.dir);
        report = fs.createWriteStream(
            path.resolve(CONF.report.dir, "stdout.log"), { flags : "w" });
    },
    /**
     * Called before tests end.
     *
     * @method
     * @instance
     */
    end: () => {
        epilogue();
        stdout();
        const reportMsg = "Local report is " + CONF.report.dir;
        stdout(Array(reportMsg.length + 1).join("-").yellow);
        stdout(reportMsg.yellow);
    },
    /**
     * Called on scope start.
     *
     * @method
     * @instance
     * @arg {object} scope - `MochaJS` suite.
     */
    scope: scope => {
        ++indents;
        if (indents) {
            stdout();
            stdout((indent() + "scope: " + scope.title).cyan);
        }
    },
    /**
     * Called before scope end.
     *
     * @method
     * @instance
     */
    scopeEnd: () => {
        --indents;
        if (!indents) stdout();
    },
    /**
     * Called on suite start.
     *
     * @method
     * @instance
     * @arg {object} suite - `MochaJS` suite.
     */
    suite: suite => {
        ++indents;
        if (indents) {
            stdout();
            stdout((indent() + "suite: " + suite.title).cyan);
        }
    },
    /**
     * Called before suite end.
     *
     * @method
     * @instance
     */
    suiteEnd: () => {
        --indents;
        if (!indents) stdout();
    },
    /**
     * Called on test start.
     *
     * @method
     * @instance
     * @arg {object} test - `MochaJS` suite.
     */
    test: test => {
        ++indents;
        if (indents) {
            stdout();
            stdout((indent() + "test: " + test.title).cyan.bold);
        }
    },
    /**
     * Called on test end.
     *
     * @method
     * @instance
     */
    testEnd: () => {
        --indents;
        if (!indents) stdout();
    },
    /**
     * Called on chunk passed.
     *
     * @method
     * @instance
     * @arg {object} chunk - `MochaJS` test.
     */
    pass: chunk => {
        let msg = indent() + "  " + MochaReporter.symbols.ok + " chunk";
        if (chunk.title) msg += ": " + chunk.title;
        stdout(msg.green);
    },
    /**
     * Called on chunk skipped.
     *
     * @method
     * @instance
     * @arg {object} chunk - `MochaJS` test.
     */
    skip: chunk => {
        let msg = indent() + "  # chunk";
        if (chunk.title) msg += ": " + chunk.title;
        stdout(msg.gray);
    },
    /**
     * Called on chunk or hook failed.
     *
     * @method
     * @instance
     * @arg {object} chunk - `MochaJS` test.
     */
    fail: chunk => {
        let suffix = chunk.type === "hook" ? " hook" : " chunk";
        let msg = indent() + "  " + MochaReporter.symbols.err + suffix;
        if (chunk.title) msg += ": " + chunk.title;
        stdout(msg.red);

        if (CONF.report.errorsNow) {
            let errMsg;
            if (CONF.test.curCase) {
                errMsg = _.last(CONF.test.curCase.errors);
            } else {
                errMsg = _.last(CONF.session.errors);
            }
            stdout(errMsg.red.bold);
        }
    },
    /**
     * Called on report finalizing.
     *
     * @method
     * @instance
     */
    done: () => new Promise(resolve => report.end(resolve)),
};