// @flow import EventEmitter from 'events'; import chalk from 'chalk'; import type { Stats } from '../webpack/types'; import createStatsFormatter from '../webpack/util/createStatsFormatter'; type ReporterOptions = { eventEmitter: EventEmitter, interactive: boolean, quiet: boolean, cwd: string, }; const log = (...args: Array) => { console.log(...args); // eslint-disable-line no-console console.log();// eslint-disable-line no-console }; const formatTitleInfo = (title) => chalk.inverse('', title, ''); const formatTitleWarn = (title) => chalk.black.bgYellow('', title, ''); const formatTitleError = (title) => chalk.white.bold.bgRed('', title, ''); class Reporter { added: Array; removed: Array; interactive: boolean; quiet: boolean; formatStats: (stats: Stats) => { warnings: Array, errors: Array }; constructor(options: ReporterOptions) { const { eventEmitter, interactive, quiet, cwd, } = options; this.added = []; this.removed = []; this.interactive = interactive; this.quiet = quiet; this.formatStats = createStatsFormatter(cwd); eventEmitter.on('uncaughtException', this.onUncaughtException); eventEmitter.on('exception', this.onLoadingException); eventEmitter.on('webpack:start', this.onWebpackStart); eventEmitter.on('webpack:ready', this.onWebpackReady); eventEmitter.on('mocha:begin', this.onMochaStart); eventEmitter.on('mocha:aborted', this.onMochaAbort); eventEmitter.on('mocha:finished', this.onMochaReady); eventEmitter.on('entry:added', this.onEntryAdded); eventEmitter.on('entry:removed', this.onEntryRemoved); } logInfo(...args: Array) { if (!this.quiet) { log(...args); } } clearConsole() { if (this.interactive) { process.stdout.write(process.platform === 'win32' ? '\x1B[2J\x1B[0f' : '\x1B[2J\x1B[3J\x1B[H'); } } static displayErrors(severity: string, errors: Array) { const errorCount = errors.length; const message = severity === 'error' ? `Failed to compile with ${chalk.red(`${errorCount} ${severity}(s)`)}` : `Compiled with ${chalk.yellow(`${errorCount} ${severity}(s)`)}`; const titleColor = severity === 'error' ? formatTitleError : formatTitleWarn; log(titleColor('WEBPACK'), message); errors.forEach((err) => log(err)); } onUncaughtException = (err: Error) => { log(formatTitleError('UNCAUGHT EXCEPTION'), 'Exception occurred after running tests'); log(err.stack); }; onLoadingException = (err: Error) => { log(formatTitleError('RUNTIME EXCEPTION'), 'Exception occurred while loading your tests'); log(err.stack); }; onWebpackStart = () => { this.clearConsole(); if (this.added.length > 0) { this.logInfo(formatTitleInfo('MOCHA'), 'The following test entry files were added:'); this.logInfo(this.added.map((f) => `+ ${f}`).join('\n')); } if (this.removed.length > 0) { this.logInfo(formatTitleInfo('MOCHA'), 'The following test entry files were removed:'); this.logInfo(this.removed.map((f) => `- ${f}`).join('\n')); } this.logInfo(formatTitleInfo('WEBPACK'), 'Compiling...'); this.added.length = 0; this.removed.length = 0; }; onWebpackReady = (err?: Error, stats?: Stats) => { this.clearConsole(); if (stats != null) { const { errors, warnings } = this.formatStats(stats); if (errors.length === 0 && warnings.length === 0) { const { startTime, endTime } = stats; const compileTime = endTime - startTime; this.logInfo(formatTitleInfo('WEBPACK'), `Compiled successfully in ${chalk.green(`${compileTime}ms`)}`); return; } if (errors.length > 0) { Reporter.displayErrors('error', errors); return; } if (warnings.length > 0) { Reporter.displayErrors('warning', warnings); } } else { Reporter.displayErrors('error', [err]); } }; onMochaStart = () => { this.logInfo(formatTitleInfo('MOCHA'), 'Testing...'); }; onMochaAbort = () => { this.logInfo(formatTitleInfo('MOCHA'), 'Tests aborted'); }; onMochaReady = (failures: number) => { if (failures === 0) { this.logInfo(formatTitleInfo('MOCHA'), `Tests completed ${chalk.green('successfully')}`); } else { this.logInfo(formatTitleInfo('MOCHA'), `Tests completed with ${chalk.red(`${failures} failure(s)`)}`); } }; onEntryAdded = (file: string) => { this.added.push(file); }; onEntryRemoved = (file: string) => { this.removed.push(file); }; } export default function testRunnerReporter(options: ReporterOptions) { new Reporter(options); // eslint-disable-line no-new }