UNPKG

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