ava 3.8.2 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,619 +0,0 @@
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
- // Discard the current spinner output. Any lines that were meant to be
36
- // preserved should be rewritten.
37
- this.spinner.clear();
38
-
39
- this._writeWithSpinner(chunk.toString('utf8'));
40
- callback();
41
- }
42
-
43
- _writev(pieces, callback) {
44
- // Discard the current spinner output. Any lines that were meant to be
45
- // preserved should be rewritten.
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
- // Ignore whitespace at the end of the chunk. We're continiously rewriting
65
- // the last line through the spinner. Also be careful to remove the indent
66
- // as the spinner adds its own.
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) { // eslint-disable-line complexity
153
- const fileStats = this.stats && evt.testFile ? this.stats.byFile.get(evt.testFile) : null;
154
-
155
- switch (evt.type) {
156
- case 'declared-test':
157
- // Ignore
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
- // Ignore
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
- // Forcibly clear the spinner, writing the chunk corrupts the TTY.
232
- this.spinner.clear();
233
-
234
- this.stdStream.write(evt.chunk);
235
- // If the chunk does not end with a linebreak, *forcibly* write one to
236
- // ensure it remains visible in the TTY.
237
- // Tests cannot assume their standard output is not interrupted. Indeed
238
- // we multiplex stdout and stderr into a single stream. However as
239
- // long as stdStream is different from reportStream users can read
240
- // their original output by redirecting the streams.
241
- if (evt.chunk[evt.chunk.length - 1] !== 0x0A) {
242
- // Use write() rather than writeLine() so the (presumably corked)
243
- // line writer will actually write the empty line before re-rendering
244
- // the last spinner text below.
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() { // eslint-disable-line complexity
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;