UNPKG

3.95 kBJavaScriptView Raw
1'use strict';
2const Emittery = require('emittery');
3const cloneDeep = require('lodash/cloneDeep');
4
5class RunStatus extends Emittery {
6 constructor(files, parallelRuns) {
7 super();
8
9 this.pendingTests = new Map();
10
11 this.stats = {
12 byFile: new Map(),
13 declaredTests: 0,
14 failedHooks: 0,
15 failedTests: 0,
16 failedWorkers: 0,
17 files,
18 parallelRuns,
19 finishedWorkers: 0,
20 internalErrors: 0,
21 remainingTests: 0,
22 passedKnownFailingTests: 0,
23 passedTests: 0,
24 selectedTests: 0,
25 skippedTests: 0,
26 timeouts: 0,
27 todoTests: 0,
28 uncaughtExceptions: 0,
29 unhandledRejections: 0
30 };
31 }
32
33 observeWorker(worker, testFile) {
34 this.stats.byFile.set(testFile, {
35 declaredTests: 0,
36 failedHooks: 0,
37 failedTests: 0,
38 internalErrors: 0,
39 remainingTests: 0,
40 passedKnownFailingTests: 0,
41 passedTests: 0,
42 selectedTests: 0,
43 skippedTests: 0,
44 todoTests: 0,
45 uncaughtExceptions: 0,
46 unhandledRejections: 0
47 });
48
49 this.pendingTests.set(testFile, new Set());
50 worker.onStateChange(data => this.emitStateChange(data));
51 }
52
53 emitStateChange(event) {
54 const {stats} = this;
55 const fileStats = stats.byFile.get(event.testFile);
56
57 let changedStats = true;
58 switch (event.type) {
59 case 'declared-test':
60 stats.declaredTests++;
61 fileStats.declaredTests++;
62 break;
63 case 'hook-failed':
64 stats.failedHooks++;
65 fileStats.failedHooks++;
66 break;
67 case 'internal-error':
68 stats.internalErrors++;
69 if (event.testFile) {
70 fileStats.internalErrors++;
71 }
72
73 break;
74 case 'selected-test':
75 stats.selectedTests++;
76 fileStats.selectedTests++;
77 if (event.skip) {
78 stats.skippedTests++;
79 fileStats.skippedTests++;
80 } else if (event.todo) {
81 stats.todoTests++;
82 fileStats.todoTests++;
83 } else {
84 stats.remainingTests++;
85 fileStats.remainingTests++;
86 this.addPendingTest(event);
87 }
88
89 break;
90 case 'test-failed':
91 stats.failedTests++;
92 fileStats.failedTests++;
93 stats.remainingTests--;
94 fileStats.remainingTests--;
95 this.removePendingTest(event);
96 break;
97 case 'test-passed':
98 if (event.knownFailing) {
99 stats.passedKnownFailingTests++;
100 fileStats.passedKnownFailingTests++;
101 } else {
102 stats.passedTests++;
103 fileStats.passedTests++;
104 }
105
106 stats.remainingTests--;
107 fileStats.remainingTests--;
108 this.removePendingTest(event);
109 break;
110 case 'timeout':
111 event.pendingTests = this.pendingTests;
112 this.pendingTests = new Map();
113 stats.timeouts++;
114 break;
115 case 'interrupt':
116 event.pendingTests = this.pendingTests;
117 this.pendingTests = new Map();
118 break;
119 case 'uncaught-exception':
120 stats.uncaughtExceptions++;
121 fileStats.uncaughtExceptions++;
122 break;
123 case 'unhandled-rejection':
124 stats.unhandledRejections++;
125 fileStats.unhandledRejections++;
126 break;
127 case 'worker-failed':
128 stats.failedWorkers++;
129 break;
130 case 'worker-finished':
131 stats.finishedWorkers++;
132 break;
133 default:
134 changedStats = false;
135 break;
136 }
137
138 if (changedStats) {
139 this.emit('stateChange', {type: 'stats', stats: cloneDeep(stats)});
140 }
141
142 this.emit('stateChange', event);
143 }
144
145 suggestExitCode(circumstances) {
146 if (circumstances.matching && this.stats.selectedTests === 0) {
147 return 1;
148 }
149
150 if (
151 this.stats.declaredTests === 0 ||
152 this.stats.internalErrors > 0 ||
153 this.stats.failedHooks > 0 ||
154 this.stats.failedTests > 0 ||
155 this.stats.failedWorkers > 0 ||
156 this.stats.timeouts > 0 ||
157 this.stats.uncaughtExceptions > 0 ||
158 this.stats.unhandledRejections > 0
159 ) {
160 return 1;
161 }
162
163 return 0;
164 }
165
166 addPendingTest(event) {
167 if (this.pendingTests.has(event.testFile)) {
168 this.pendingTests.get(event.testFile).add(event.title);
169 }
170 }
171
172 removePendingTest(event) {
173 if (this.pendingTests.has(event.testFile)) {
174 this.pendingTests.get(event.testFile).delete(event.title);
175 }
176 }
177}
178
179module.exports = RunStatus;