UNPKG

ava

Version:

Node.js test runner that lets you develop with confidence.

195 lines (173 loc) 4.3 kB
'use strict'; const Emittery = require('emittery'); const cloneDeep = require('lodash/cloneDeep'); class RunStatus extends Emittery { constructor(files, parallelRuns) { super(); this.pendingTests = new Map(); this.emptyParallelRun = parallelRuns && parallelRuns.currentFileCount === 0 && parallelRuns.totalRuns > 1 && files > 0; this.stats = { byFile: new Map(), declaredTests: 0, failedHooks: 0, failedTests: 0, failedWorkers: 0, files, parallelRuns, finishedWorkers: 0, internalErrors: 0, remainingTests: 0, passedKnownFailingTests: 0, passedTests: 0, selectedTests: 0, skippedTests: 0, timeouts: 0, todoTests: 0, uncaughtExceptions: 0, unhandledRejections: 0 }; } observeWorker(worker, testFile, stats) { this.stats.byFile.set(testFile, { declaredTests: 0, failedHooks: 0, failedTests: 0, internalErrors: 0, remainingTests: 0, passedKnownFailingTests: 0, passedTests: 0, selectedTests: 0, selectingLines: false, skippedTests: 0, todoTests: 0, uncaughtExceptions: 0, unhandledRejections: 0, ...stats }); this.pendingTests.set(testFile, new Set()); worker.onStateChange(data => this.emitStateChange(data)); } emitStateChange(event) { const {stats} = this; const fileStats = stats.byFile.get(event.testFile); let changedStats = true; switch (event.type) { case 'declared-test': stats.declaredTests++; fileStats.declaredTests++; break; case 'hook-failed': stats.failedHooks++; fileStats.failedHooks++; break; case 'internal-error': stats.internalErrors++; if (event.testFile) { fileStats.internalErrors++; } break; case 'selected-test': stats.selectedTests++; fileStats.selectedTests++; if (event.skip) { stats.skippedTests++; fileStats.skippedTests++; } else if (event.todo) { stats.todoTests++; fileStats.todoTests++; } else { stats.remainingTests++; fileStats.remainingTests++; this.addPendingTest(event); } break; case 'test-failed': stats.failedTests++; fileStats.failedTests++; stats.remainingTests--; fileStats.remainingTests--; this.removePendingTest(event); break; case 'test-passed': if (event.knownFailing) { stats.passedKnownFailingTests++; fileStats.passedKnownFailingTests++; } else { stats.passedTests++; fileStats.passedTests++; } stats.remainingTests--; fileStats.remainingTests--; this.removePendingTest(event); break; case 'timeout': event.pendingTests = this.pendingTests; this.pendingTests = new Map(); stats.timeouts++; break; case 'interrupt': event.pendingTests = this.pendingTests; this.pendingTests = new Map(); break; case 'uncaught-exception': stats.uncaughtExceptions++; fileStats.uncaughtExceptions++; break; case 'unhandled-rejection': stats.unhandledRejections++; fileStats.unhandledRejections++; break; case 'worker-failed': stats.failedWorkers++; break; case 'worker-finished': stats.finishedWorkers++; break; default: changedStats = false; break; } if (changedStats) { this.emit('stateChange', {type: 'stats', stats: cloneDeep(stats)}); } this.emit('stateChange', event); } suggestExitCode(circumstances) { if (this.emptyParallelRun) { return 0; } if (circumstances.matching && this.stats.selectedTests === 0) { return 1; } if ( this.stats.declaredTests === 0 || this.stats.internalErrors > 0 || this.stats.failedHooks > 0 || this.stats.failedTests > 0 || this.stats.failedWorkers > 0 || this.stats.timeouts > 0 || this.stats.uncaughtExceptions > 0 || this.stats.unhandledRejections > 0 ) { return 1; } if ([...this.stats.byFile.values()].some(stats => stats.selectingLines && stats.selectedTests === 0)) { return 1; } return 0; } addPendingTest(event) { if (this.pendingTests.has(event.testFile)) { this.pendingTests.get(event.testFile).add(event.title); } } removePendingTest(event) { if (this.pendingTests.has(event.testFile)) { this.pendingTests.get(event.testFile).delete(event.title); } } } module.exports = RunStatus;