1 | 'use strict';
|
2 | const os = require('os');
|
3 | const path = require('path');
|
4 | const stream = require('stream');
|
5 |
|
6 | const cliCursor = require('cli-cursor');
|
7 | const figures = require('figures');
|
8 | const indentString = require('indent-string');
|
9 | const ora = require('ora');
|
10 | const plur = require('plur');
|
11 | const trimOffNewlines = require('trim-off-newlines');
|
12 | const beautifyStack = require('./beautify-stack');
|
13 |
|
14 | const chalk = require('../chalk').get();
|
15 | const codeExcerpt = require('../code-excerpt');
|
16 | const colors = require('./colors');
|
17 | const formatSerializedError = require('./format-serialized-error');
|
18 | const improperUsageMessages = require('./improper-usage-messages');
|
19 | const prefixTitle = require('./prefix-title');
|
20 | const whileCorked = require('./while-corked');
|
21 |
|
22 | const nodeInternals = require('stack-utils').nodeInternals();
|
23 |
|
24 | class LineWriter extends stream.Writable {
|
25 | constructor(dest, spinner) {
|
26 | super();
|
27 |
|
28 | this.dest = dest;
|
29 | this.columns = dest.columns || 80;
|
30 | this.spinner = spinner;
|
31 | this.lastSpinnerText = '';
|
32 | }
|
33 |
|
34 | _write(chunk, encoding, callback) {
|
35 |
|
36 |
|
37 | this.spinner.clear();
|
38 |
|
39 | this._writeWithSpinner(chunk.toString('utf8'));
|
40 | callback();
|
41 | }
|
42 |
|
43 | _writev(pieces, callback) {
|
44 |
|
45 |
|
46 | this.spinner.clear();
|
47 |
|
48 | const last = pieces.pop();
|
49 | for (const piece of pieces) {
|
50 | this.dest.write(piece.chunk);
|
51 | }
|
52 |
|
53 | this._writeWithSpinner(last.chunk.toString('utf8'));
|
54 | callback();
|
55 | }
|
56 |
|
57 | _writeWithSpinner(string) {
|
58 | if (!this.spinner.id) {
|
59 | this.dest.write(string);
|
60 | return;
|
61 | }
|
62 |
|
63 | this.lastSpinnerText = string;
|
64 |
|
65 |
|
66 |
|
67 | this.spinner.text = string.trimEnd().slice(2);
|
68 | this.spinner.render();
|
69 | }
|
70 |
|
71 | writeLine(string) {
|
72 | if (string) {
|
73 | this.write(indentString(string, 2) + os.EOL);
|
74 | } else {
|
75 | this.write(os.EOL);
|
76 | }
|
77 | }
|
78 | }
|
79 |
|
80 | class MiniReporter {
|
81 | constructor(options) {
|
82 | this.reportStream = options.reportStream;
|
83 | this.stdStream = options.stdStream;
|
84 | this.watching = options.watching;
|
85 |
|
86 | this.spinner = ora({
|
87 | isEnabled: true,
|
88 | color: options.spinner ? options.spinner.color : 'gray',
|
89 | discardStdin: !options.watching,
|
90 | hideCursor: false,
|
91 | spinner: options.spinner || (process.platform === 'win32' ? 'line' : 'dots'),
|
92 | stream: options.reportStream
|
93 | });
|
94 | this.lineWriter = new LineWriter(this.reportStream, this.spinner);
|
95 |
|
96 | this.consumeStateChange = whileCorked(this.reportStream, whileCorked(this.lineWriter, this.consumeStateChange));
|
97 | this.endRun = whileCorked(this.reportStream, whileCorked(this.lineWriter, this.endRun));
|
98 | this.relativeFile = file => path.relative(options.projectDir, file);
|
99 |
|
100 | this.reset();
|
101 | }
|
102 |
|
103 | reset() {
|
104 | if (this.removePreviousListener) {
|
105 | this.removePreviousListener();
|
106 | }
|
107 |
|
108 | this.failFastEnabled = false;
|
109 | this.failures = [];
|
110 | this.filesWithMissingAvaImports = new Set();
|
111 | this.filesWithoutDeclaredTests = new Set();
|
112 | this.filesWithoutMatchedLineNumbers = new Set();
|
113 | this.internalErrors = [];
|
114 | this.knownFailures = [];
|
115 | this.lineNumberErrors = [];
|
116 | this.matching = false;
|
117 | this.prefixTitle = (testFile, title) => title;
|
118 | this.previousFailures = 0;
|
119 | this.removePreviousListener = null;
|
120 | this.stats = null;
|
121 | this.uncaughtExceptions = [];
|
122 | this.unhandledRejections = [];
|
123 | }
|
124 |
|
125 | startRun(plan) {
|
126 | if (plan.bailWithoutReporting) {
|
127 | return;
|
128 | }
|
129 |
|
130 | this.reset();
|
131 |
|
132 | this.failFastEnabled = plan.failFastEnabled;
|
133 | this.matching = plan.matching;
|
134 | this.previousFailures = plan.previousFailures;
|
135 |
|
136 | if (this.watching || plan.files.length > 1) {
|
137 | this.prefixTitle = (testFile, title) => prefixTitle(plan.filePathPrefix, testFile, title);
|
138 | }
|
139 |
|
140 | this.removePreviousListener = plan.status.on('stateChange', evt => this.consumeStateChange(evt));
|
141 |
|
142 | if (this.watching && plan.runVector > 1) {
|
143 | this.reportStream.write(chalk.gray.dim('\u2500'.repeat(this.lineWriter.columns)) + os.EOL);
|
144 | }
|
145 |
|
146 | cliCursor.hide(this.reportStream);
|
147 | this.lineWriter.writeLine();
|
148 |
|
149 | this.spinner.start();
|
150 | }
|
151 |
|
152 | consumeStateChange(evt) {
|
153 | const fileStats = this.stats && evt.testFile ? this.stats.byFile.get(evt.testFile) : null;
|
154 |
|
155 | switch (evt.type) {
|
156 | case 'declared-test':
|
157 |
|
158 | break;
|
159 | case 'hook-failed':
|
160 | this.failures.push(evt);
|
161 | this.writeTestSummary(evt);
|
162 | break;
|
163 | case 'internal-error':
|
164 | this.internalErrors.push(evt);
|
165 | if (evt.testFile) {
|
166 | this.writeWithCounts(colors.error(`${figures.cross} Internal error when running ${this.relativeFile(evt.testFile)}`));
|
167 | } else {
|
168 | this.writeWithCounts(colors.error(`${figures.cross} Internal error`));
|
169 | }
|
170 |
|
171 | break;
|
172 | case 'line-number-selection-error':
|
173 | this.lineNumberErrors.push(evt);
|
174 | this.writeWithCounts(colors.information(`${figures.warning} Could not parse ${this.relativeFile(evt.testFile)} for line number selection`));
|
175 | break;
|
176 | case 'missing-ava-import':
|
177 | this.filesWithMissingAvaImports.add(evt.testFile);
|
178 | this.writeWithCounts(colors.error(`${figures.cross} No tests found in ${this.relativeFile(evt.testFile)}, make sure to import "ava" at the top of your test file`));
|
179 | break;
|
180 | case 'selected-test':
|
181 |
|
182 | break;
|
183 | case 'stats':
|
184 | this.stats = evt.stats;
|
185 | break;
|
186 | case 'test-failed':
|
187 | this.failures.push(evt);
|
188 | this.writeTestSummary(evt);
|
189 | break;
|
190 | case 'test-passed':
|
191 | if (evt.knownFailing) {
|
192 | this.knownFailures.push(evt);
|
193 | }
|
194 |
|
195 | this.writeTestSummary(evt);
|
196 | break;
|
197 | case 'timeout':
|
198 | this.lineWriter.writeLine(colors.error(`\n${figures.cross} Timed out while running tests`));
|
199 | this.lineWriter.writeLine('');
|
200 | this.writePendingTests(evt);
|
201 | break;
|
202 | case 'interrupt':
|
203 | this.lineWriter.writeLine(colors.error(`\n${figures.cross} Exiting due to SIGINT`));
|
204 | this.lineWriter.writeLine('');
|
205 | this.writePendingTests(evt);
|
206 | break;
|
207 | case 'uncaught-exception':
|
208 | this.uncaughtExceptions.push(evt);
|
209 | break;
|
210 | case 'unhandled-rejection':
|
211 | this.unhandledRejections.push(evt);
|
212 | break;
|
213 | case 'worker-failed':
|
214 | if (fileStats.declaredTests === 0) {
|
215 | this.filesWithoutDeclaredTests.add(evt.testFile);
|
216 | }
|
217 |
|
218 | break;
|
219 | case 'worker-finished':
|
220 | if (fileStats.declaredTests === 0) {
|
221 | this.filesWithoutDeclaredTests.add(evt.testFile);
|
222 | this.writeWithCounts(colors.error(`${figures.cross} No tests found in ${this.relativeFile(evt.testFile)}`));
|
223 | } else if (fileStats.selectingLines && fileStats.selectedTests === 0) {
|
224 | this.filesWithoutMatchedLineNumbers.add(evt.testFile);
|
225 | this.writeWithCounts(colors.error(`${figures.cross} Line numbers for ${this.relativeFile(evt.testFile)} did not match any tests`));
|
226 | }
|
227 |
|
228 | break;
|
229 | case 'worker-stderr':
|
230 | case 'worker-stdout':
|
231 |
|
232 | this.spinner.clear();
|
233 |
|
234 | this.stdStream.write(evt.chunk);
|
235 |
|
236 |
|
237 |
|
238 |
|
239 |
|
240 |
|
241 | if (evt.chunk[evt.chunk.length - 1] !== 0x0A) {
|
242 |
|
243 |
|
244 |
|
245 | this.lineWriter.write(os.EOL);
|
246 | }
|
247 |
|
248 | this.lineWriter.write(this.lineWriter.lastSpinnerText);
|
249 | break;
|
250 | default:
|
251 | break;
|
252 | }
|
253 | }
|
254 |
|
255 | writeWithCounts(string) {
|
256 | if (!this.stats) {
|
257 | return this.lineWriter.writeLine(string);
|
258 | }
|
259 |
|
260 | string = string || '';
|
261 | if (string !== '') {
|
262 | string += os.EOL;
|
263 | }
|
264 |
|
265 | let firstLinePostfix = this.watching ?
|
266 | ' ' + chalk.gray.dim('[' + new Date().toLocaleTimeString('en-US', {hour12: false}) + ']') :
|
267 | '';
|
268 |
|
269 | if (this.stats.passedTests > 0) {
|
270 | string += os.EOL + colors.pass(`${this.stats.passedTests} passed`) + firstLinePostfix;
|
271 | firstLinePostfix = '';
|
272 | }
|
273 |
|
274 | if (this.stats.passedKnownFailingTests > 0) {
|
275 | string += os.EOL + colors.error(`${this.stats.passedKnownFailingTests} ${plur('known failure', this.stats.passedKnownFailingTests)}`);
|
276 | }
|
277 |
|
278 | if (this.stats.failedHooks > 0) {
|
279 | string += os.EOL + colors.error(`${this.stats.failedHooks} ${plur('hook', this.stats.failedHooks)} failed`) + firstLinePostfix;
|
280 | firstLinePostfix = '';
|
281 | }
|
282 |
|
283 | if (this.stats.failedTests > 0) {
|
284 | string += os.EOL + colors.error(`${this.stats.failedTests} ${plur('test', this.stats.failedTests)} failed`) + firstLinePostfix;
|
285 | firstLinePostfix = '';
|
286 | }
|
287 |
|
288 | if (this.stats.skippedTests > 0) {
|
289 | string += os.EOL + colors.skip(`${this.stats.skippedTests} skipped`);
|
290 | }
|
291 |
|
292 | if (this.stats.todoTests > 0) {
|
293 | string += os.EOL + colors.todo(`${this.stats.todoTests} todo`);
|
294 | }
|
295 |
|
296 | this.lineWriter.writeLine(string);
|
297 | }
|
298 |
|
299 | writeErr(evt) {
|
300 | if (evt.err.name === 'TSError' && evt.err.object && evt.err.object.diagnosticText) {
|
301 | this.lineWriter.writeLine(colors.errorStack(trimOffNewlines(evt.err.object.diagnosticText)));
|
302 | return;
|
303 | }
|
304 |
|
305 | if (evt.err.source) {
|
306 | this.lineWriter.writeLine(colors.errorSource(`${this.relativeFile(evt.err.source.file)}:${evt.err.source.line}`));
|
307 | const excerpt = codeExcerpt(evt.err.source, {maxWidth: this.lineWriter.columns - 2});
|
308 | if (excerpt) {
|
309 | this.lineWriter.writeLine();
|
310 | this.lineWriter.writeLine(excerpt);
|
311 | }
|
312 | }
|
313 |
|
314 | if (evt.err.avaAssertionError) {
|
315 | const result = formatSerializedError(evt.err);
|
316 | if (result.printMessage) {
|
317 | this.lineWriter.writeLine();
|
318 | this.lineWriter.writeLine(evt.err.message);
|
319 | }
|
320 |
|
321 | if (result.formatted) {
|
322 | this.lineWriter.writeLine();
|
323 | this.lineWriter.writeLine(result.formatted);
|
324 | }
|
325 |
|
326 | const message = improperUsageMessages.forError(evt.err);
|
327 | if (message) {
|
328 | this.lineWriter.writeLine();
|
329 | this.lineWriter.writeLine(message);
|
330 | }
|
331 | } else if (evt.err.nonErrorObject) {
|
332 | this.lineWriter.writeLine(trimOffNewlines(evt.err.formatted));
|
333 | } else {
|
334 | this.lineWriter.writeLine();
|
335 | this.lineWriter.writeLine(evt.err.summary);
|
336 | }
|
337 |
|
338 | const formatted = this.formatErrorStack(evt.err);
|
339 | if (formatted.length > 0) {
|
340 | this.lineWriter.writeLine();
|
341 | this.lineWriter.writeLine(formatted.join('\n'));
|
342 | }
|
343 | }
|
344 |
|
345 | formatErrorStack(error) {
|
346 | if (!error.stack) {
|
347 | return [];
|
348 | }
|
349 |
|
350 | if (error.shouldBeautifyStack) {
|
351 | return beautifyStack(error.stack).map(line => {
|
352 | if (nodeInternals.some(internal => internal.test(line))) {
|
353 | return colors.errorStackInternal(`${figures.pointerSmall} ${line}`);
|
354 | }
|
355 |
|
356 | return colors.errorStack(`${figures.pointerSmall} ${line}`);
|
357 | });
|
358 | }
|
359 |
|
360 | return [error.stack];
|
361 | }
|
362 |
|
363 | writeLogs(evt) {
|
364 | if (evt.logs) {
|
365 | for (const log of evt.logs) {
|
366 | const logLines = indentString(colors.log(log), 4);
|
367 | const logLinesWithLeadingFigure = logLines.replace(
|
368 | /^ {4}/,
|
369 | ` ${colors.information(figures.info)} `
|
370 | );
|
371 | this.lineWriter.writeLine(logLinesWithLeadingFigure);
|
372 | }
|
373 | }
|
374 | }
|
375 |
|
376 | writeTestSummary(evt) {
|
377 | if (evt.type === 'hook-failed' || evt.type === 'test-failed') {
|
378 | this.writeWithCounts(`${this.prefixTitle(evt.testFile, evt.title)}`);
|
379 | } else if (evt.knownFailing) {
|
380 | this.writeWithCounts(`${colors.error(this.prefixTitle(evt.testFile, evt.title))}`);
|
381 | } else {
|
382 | this.writeWithCounts(`${this.prefixTitle(evt.testFile, evt.title)}`);
|
383 | }
|
384 | }
|
385 |
|
386 | writeFailure(evt) {
|
387 | this.lineWriter.writeLine(`${colors.title(this.prefixTitle(evt.testFile, evt.title))}`);
|
388 | this.writeLogs(evt);
|
389 | this.lineWriter.writeLine();
|
390 | this.writeErr(evt);
|
391 | }
|
392 |
|
393 | writePendingTests(evt) {
|
394 | for (const [file, testsInFile] of evt.pendingTests) {
|
395 | if (testsInFile.size === 0) {
|
396 | continue;
|
397 | }
|
398 |
|
399 | this.lineWriter.writeLine(`${testsInFile.size} tests were pending in ${this.relativeFile(file)}\n`);
|
400 | for (const title of testsInFile) {
|
401 | this.lineWriter.writeLine(`${figures.circleDotted} ${this.prefixTitle(file, title)}`);
|
402 | }
|
403 |
|
404 | this.lineWriter.writeLine('');
|
405 | }
|
406 | }
|
407 |
|
408 | endRun() {
|
409 | this.spinner.stop();
|
410 | cliCursor.show(this.reportStream);
|
411 |
|
412 | if (!this.stats) {
|
413 | this.lineWriter.writeLine(colors.error(`${figures.cross} Couldn’t find any files to test`));
|
414 | this.lineWriter.writeLine();
|
415 | return;
|
416 | }
|
417 |
|
418 | if (this.matching && this.stats.selectedTests === 0) {
|
419 | this.lineWriter.writeLine(colors.error(`${figures.cross} Couldn’t find any matching tests`));
|
420 | this.lineWriter.writeLine();
|
421 | return;
|
422 | }
|
423 |
|
424 | this.lineWriter.writeLine();
|
425 |
|
426 | let firstLinePostfix = this.watching ?
|
427 | ' ' + chalk.gray.dim('[' + new Date().toLocaleTimeString('en-US', {hour12: false}) + ']') :
|
428 | '';
|
429 |
|
430 | if (this.filesWithMissingAvaImports.size > 0) {
|
431 | for (const testFile of this.filesWithMissingAvaImports) {
|
432 | this.lineWriter.writeLine(colors.error(`${figures.cross} No tests found in ${this.relativeFile(testFile)}, make sure to import "ava" at the top of your test file`) + firstLinePostfix);
|
433 | firstLinePostfix = '';
|
434 | }
|
435 | }
|
436 |
|
437 | if (this.filesWithoutDeclaredTests.size > 0) {
|
438 | for (const testFile of this.filesWithoutDeclaredTests) {
|
439 | if (!this.filesWithMissingAvaImports.has(testFile)) {
|
440 | this.lineWriter.writeLine(colors.error(`${figures.cross} No tests found in ${this.relativeFile(testFile)}`) + firstLinePostfix);
|
441 | firstLinePostfix = '';
|
442 | }
|
443 | }
|
444 | }
|
445 |
|
446 | if (this.lineNumberErrors.length > 0) {
|
447 | for (const evt of this.lineNumberErrors) {
|
448 | this.lineWriter.writeLine(colors.information(`${figures.warning} Could not parse ${this.relativeFile(evt.testFile)} for line number selection`));
|
449 | }
|
450 | }
|
451 |
|
452 | if (this.filesWithoutMatchedLineNumbers.size > 0) {
|
453 | for (const testFile of this.filesWithoutMatchedLineNumbers) {
|
454 | if (!this.filesWithMissingAvaImports.has(testFile) && !this.filesWithoutDeclaredTests.has(testFile)) {
|
455 | this.lineWriter.writeLine(colors.error(`${figures.cross} Line numbers for ${this.relativeFile(testFile)} did not match any tests`) + firstLinePostfix);
|
456 | firstLinePostfix = '';
|
457 | }
|
458 | }
|
459 | }
|
460 |
|
461 | if (this.filesWithMissingAvaImports.size > 0 || this.filesWithoutDeclaredTests.size > 0 || this.filesWithoutMatchedLineNumbers.size > 0) {
|
462 | this.lineWriter.writeLine();
|
463 | }
|
464 |
|
465 | if (this.stats.failedHooks > 0) {
|
466 | this.lineWriter.writeLine(colors.error(`${this.stats.failedHooks} ${plur('hook', this.stats.failedHooks)} failed`) + firstLinePostfix);
|
467 | firstLinePostfix = '';
|
468 | }
|
469 |
|
470 | if (this.stats.failedTests > 0) {
|
471 | this.lineWriter.writeLine(colors.error(`${this.stats.failedTests} ${plur('test', this.stats.failedTests)} failed`) + firstLinePostfix);
|
472 | firstLinePostfix = '';
|
473 | }
|
474 |
|
475 | if (this.stats.failedHooks === 0 && this.stats.failedTests === 0 && this.stats.passedTests > 0) {
|
476 | this.lineWriter.writeLine(colors.pass(`${this.stats.passedTests} ${plur('test', this.stats.passedTests)} passed`) + firstLinePostfix);
|
477 | firstLinePostfix = '';
|
478 | }
|
479 |
|
480 | if (this.stats.passedKnownFailingTests > 0) {
|
481 | this.lineWriter.writeLine(colors.error(`${this.stats.passedKnownFailingTests} ${plur('known failure', this.stats.passedKnownFailingTests)}`));
|
482 | }
|
483 |
|
484 | if (this.stats.skippedTests > 0) {
|
485 | this.lineWriter.writeLine(colors.skip(`${this.stats.skippedTests} ${plur('test', this.stats.skippedTests)} skipped`));
|
486 | }
|
487 |
|
488 | if (this.stats.todoTests > 0) {
|
489 | this.lineWriter.writeLine(colors.todo(`${this.stats.todoTests} ${plur('test', this.stats.todoTests)} todo`));
|
490 | }
|
491 |
|
492 | if (this.stats.unhandledRejections > 0) {
|
493 | this.lineWriter.writeLine(colors.error(`${this.stats.unhandledRejections} unhandled ${plur('rejection', this.stats.unhandledRejections)}`));
|
494 | }
|
495 |
|
496 | if (this.stats.uncaughtExceptions > 0) {
|
497 | this.lineWriter.writeLine(colors.error(`${this.stats.uncaughtExceptions} uncaught ${plur('exception', this.stats.uncaughtExceptions)}`));
|
498 | }
|
499 |
|
500 | if (this.previousFailures > 0) {
|
501 | this.lineWriter.writeLine(colors.error(`${this.previousFailures} previous ${plur('failure', this.previousFailures)} in test files that were not rerun`));
|
502 | }
|
503 |
|
504 | if (this.stats.passedKnownFailingTests > 0) {
|
505 | this.lineWriter.writeLine();
|
506 | for (const evt of this.knownFailures) {
|
507 | this.lineWriter.writeLine(colors.error(this.prefixTitle(evt.testFile, evt.title)));
|
508 | }
|
509 | }
|
510 |
|
511 | const shouldWriteFailFastDisclaimer = this.failFastEnabled && (this.stats.remainingTests > 0 || this.stats.files > this.stats.finishedWorkers);
|
512 |
|
513 | if (this.failures.length > 0) {
|
514 | const writeTrailingLines = shouldWriteFailFastDisclaimer || this.internalErrors.length > 0 || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
|
515 | this.lineWriter.writeLine();
|
516 |
|
517 | const last = this.failures[this.failures.length - 1];
|
518 | for (const evt of this.failures) {
|
519 | this.writeFailure(evt);
|
520 | if (evt !== last || writeTrailingLines) {
|
521 | this.lineWriter.writeLine();
|
522 | this.lineWriter.writeLine();
|
523 | this.lineWriter.writeLine();
|
524 | }
|
525 | }
|
526 | }
|
527 |
|
528 | if (this.internalErrors.length > 0) {
|
529 | const writeLeadingLine = this.failures.length === 0;
|
530 | const writeTrailingLines = shouldWriteFailFastDisclaimer || this.uncaughtExceptions.length > 0 || this.unhandledRejections.length > 0;
|
531 |
|
532 | if (writeLeadingLine) {
|
533 | this.lineWriter.writeLine();
|
534 | }
|
535 |
|
536 | const last = this.internalErrors[this.internalErrors.length - 1];
|
537 | for (const evt of this.internalErrors) {
|
538 | if (evt.testFile) {
|
539 | this.lineWriter.writeLine(colors.error(`${figures.cross} Internal error when running ${this.relativeFile(evt.testFile)}`));
|
540 | } else {
|
541 | this.lineWriter.writeLine(colors.error(`${figures.cross} Internal error`));
|
542 | }
|
543 |
|
544 | this.lineWriter.writeLine(colors.stack(evt.err.summary));
|
545 | this.lineWriter.writeLine(colors.errorStack(evt.err.stack));
|
546 | if (evt !== last || writeTrailingLines) {
|
547 | this.lineWriter.writeLine();
|
548 | this.lineWriter.writeLine();
|
549 | this.lineWriter.writeLine();
|
550 | }
|
551 | }
|
552 | }
|
553 |
|
554 | if (this.uncaughtExceptions.length > 0) {
|
555 | const writeLeadingLine = this.failures.length === 0 && this.internalErrors.length === 0;
|
556 | const writeTrailingLines = shouldWriteFailFastDisclaimer || this.unhandledRejections.length > 0;
|
557 |
|
558 | if (writeLeadingLine) {
|
559 | this.lineWriter.writeLine();
|
560 | }
|
561 |
|
562 | const last = this.uncaughtExceptions[this.uncaughtExceptions.length - 1];
|
563 | for (const evt of this.uncaughtExceptions) {
|
564 | this.lineWriter.writeLine(colors.title(`Uncaught exception in ${this.relativeFile(evt.testFile)}`));
|
565 | this.lineWriter.writeLine();
|
566 | this.writeErr(evt);
|
567 | if (evt !== last || writeTrailingLines) {
|
568 | this.lineWriter.writeLine();
|
569 | this.lineWriter.writeLine();
|
570 | this.lineWriter.writeLine();
|
571 | }
|
572 | }
|
573 | }
|
574 |
|
575 | if (this.unhandledRejections.length > 0) {
|
576 | const writeLeadingLine = this.failures.length === 0 && this.internalErrors.length === 0 && this.uncaughtExceptions.length === 0;
|
577 | const writeTrailingLines = shouldWriteFailFastDisclaimer;
|
578 |
|
579 | if (writeLeadingLine) {
|
580 | this.lineWriter.writeLine();
|
581 | }
|
582 |
|
583 | const last = this.unhandledRejections[this.unhandledRejections.length - 1];
|
584 | for (const evt of this.unhandledRejections) {
|
585 | this.lineWriter.writeLine(colors.title(`Unhandled rejection in ${this.relativeFile(evt.testFile)}`));
|
586 | this.lineWriter.writeLine();
|
587 | this.writeErr(evt);
|
588 | if (evt !== last || writeTrailingLines) {
|
589 | this.lineWriter.writeLine();
|
590 | this.lineWriter.writeLine();
|
591 | this.lineWriter.writeLine();
|
592 | }
|
593 | }
|
594 | }
|
595 |
|
596 | if (shouldWriteFailFastDisclaimer) {
|
597 | let remaining = '';
|
598 | if (this.stats.remainingTests > 0) {
|
599 | remaining += `At least ${this.stats.remainingTests} ${plur('test was', 'tests were', this.stats.remainingTests)} skipped`;
|
600 | if (this.stats.files > this.stats.finishedWorkers) {
|
601 | remaining += ', as well as ';
|
602 | }
|
603 | }
|
604 |
|
605 | if (this.stats.files > this.stats.finishedWorkers) {
|
606 | const skippedFileCount = this.stats.files - this.stats.finishedWorkers;
|
607 | remaining += `${skippedFileCount} ${plur('test file', 'test files', skippedFileCount)}`;
|
608 | if (this.stats.remainingTests === 0) {
|
609 | remaining += ` ${plur('was', 'were', skippedFileCount)} skipped`;
|
610 | }
|
611 | }
|
612 |
|
613 | this.lineWriter.writeLine(colors.information(`\`--fail-fast\` is on. ${remaining}.`));
|
614 | }
|
615 |
|
616 | this.lineWriter.writeLine();
|
617 | }
|
618 | }
|
619 | module.exports = MiniReporter;
|