ava 3.7.1 → 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,440 +0,0 @@
1
- 'use strict';
2
- const os = require('os');
3
- const path = require('path');
4
- const stream = require('stream');
5
-
6
- const figures = require('figures');
7
- const indentString = require('indent-string');
8
- const plur = require('plur');
9
- const prettyMs = require('pretty-ms');
10
- const trimOffNewlines = require('trim-off-newlines');
11
-
12
- const chalk = require('../chalk').get();
13
- const codeExcerpt = require('../code-excerpt');
14
- const colors = require('./colors');
15
- const formatSerializedError = require('./format-serialized-error');
16
- const improperUsageMessages = require('./improper-usage-messages');
17
- const prefixTitle = require('./prefix-title');
18
- const whileCorked = require('./while-corked');
19
-
20
- class LineWriter extends stream.Writable {
21
- constructor(dest) {
22
- super();
23
-
24
- this.dest = dest;
25
- this.columns = dest.columns || 80;
26
- this.lastLineIsEmpty = false;
27
- }
28
-
29
- _write(chunk, encoding, callback) {
30
- this.dest.write(chunk);
31
- callback();
32
- }
33
-
34
- writeLine(string) {
35
- if (string) {
36
- this.write(indentString(string, 2) + os.EOL);
37
- this.lastLineIsEmpty = false;
38
- } else {
39
- this.write(os.EOL);
40
- this.lastLineIsEmpty = true;
41
- }
42
- }
43
-
44
- ensureEmptyLine() {
45
- if (!this.lastLineIsEmpty) {
46
- this.writeLine();
47
- }
48
- }
49
- }
50
-
51
- class VerboseReporter {
52
- constructor(options) {
53
- this.reportStream = options.reportStream;
54
- this.stdStream = options.stdStream;
55
- this.watching = options.watching;
56
-
57
- this.lineWriter = new LineWriter(this.reportStream);
58
- this.consumeStateChange = whileCorked(this.reportStream, this.consumeStateChange);
59
- this.endRun = whileCorked(this.reportStream, this.endRun);
60
- this.relativeFile = file => path.relative(options.projectDir, file);
61
-
62
- this.reset();
63
- }
64
-
65
- reset() {
66
- if (this.removePreviousListener) {
67
- this.removePreviousListener();
68
- }
69
-
70
- this.failFastEnabled = false;
71
- this.failures = [];
72
- this.filesWithMissingAvaImports = new Set();
73
- this.knownFailures = [];
74
- this.runningTestFiles = new Map();
75
- this.lastLineIsEmpty = false;
76
- this.matching = false;
77
- this.prefixTitle = (testFile, title) => title;
78
- this.previousFailures = 0;
79
- this.removePreviousListener = null;
80
- this.stats = null;
81
- }
82
-
83
- startRun(plan) {
84
- if (plan.bailWithoutReporting) {
85
- return;
86
- }
87
-
88
- this.reset();
89
-
90
- this.failFastEnabled = plan.failFastEnabled;
91
- this.matching = plan.matching;
92
- this.previousFailures = plan.previousFailures;
93
- this.emptyParallelRun = plan.status.emptyParallelRun;
94
-
95
- if (this.watching || plan.files.length > 1) {
96
- this.prefixTitle = (testFile, title) => prefixTitle(plan.filePathPrefix, testFile, title);
97
- }
98
-
99
- this.removePreviousListener = plan.status.on('stateChange', evt => this.consumeStateChange(evt));
100
-
101
- if (this.watching && plan.runVector > 1) {
102
- this.lineWriter.write(chalk.gray.dim('\u2500'.repeat(this.reportStream.columns || 80)) + os.EOL);
103
- }
104
-
105
- this.lineWriter.writeLine();
106
- }
107
-
108
- consumeStateChange(evt) { // eslint-disable-line complexity
109
- const fileStats = this.stats && evt.testFile ? this.stats.byFile.get(evt.testFile) : null;
110
-
111
- switch (evt.type) {
112
- case 'hook-failed':
113
- this.failures.push(evt);
114
- this.writeTestSummary(evt);
115
- break;
116
- case 'internal-error':
117
- if (evt.testFile) {
118
- this.lineWriter.writeLine(colors.error(`${figures.cross} Internal error when running ${this.relativeFile(evt.testFile)}`));
119
- } else {
120
- this.lineWriter.writeLine(colors.error(`${figures.cross} Internal error`));
121
- }
122
-
123
- this.lineWriter.writeLine(colors.stack(evt.err.summary));
124
- this.lineWriter.writeLine(colors.errorStack(evt.err.stack));
125
- this.lineWriter.writeLine();
126
- this.lineWriter.writeLine();
127
- break;
128
- case 'missing-ava-import':
129
- this.filesWithMissingAvaImports.add(evt.testFile);
130
- this.lineWriter.writeLine(colors.error(`${figures.cross} No tests found in ${this.relativeFile(evt.testFile)}, make sure to import "ava" at the top of your test file`));
131
- break;
132
- case 'hook-finished':
133
- if (evt.logs.length > 0) {
134
- this.lineWriter.writeLine(` ${this.prefixTitle(evt.testFile, evt.title)}`);
135
- this.writeLogs(evt);
136
- }
137
-
138
- break;
139
- case 'selected-test':
140
- if (evt.skip) {
141
- this.lineWriter.writeLine(colors.skip(`- ${this.prefixTitle(evt.testFile, evt.title)}`));
142
- } else if (evt.todo) {
143
- this.lineWriter.writeLine(colors.todo(`- ${this.prefixTitle(evt.testFile, evt.title)}`));
144
- }
145
-
146
- break;
147
- case 'stats':
148
- this.stats = evt.stats;
149
- break;
150
- case 'test-failed':
151
- this.failures.push(evt);
152
- this.writeTestSummary(evt);
153
- break;
154
- case 'test-passed':
155
- if (evt.knownFailing) {
156
- this.knownFailures.push(evt);
157
- }
158
-
159
- this.writeTestSummary(evt);
160
- break;
161
- case 'timeout':
162
- this.lineWriter.writeLine(colors.error(`\n${figures.cross} Timed out while running tests`));
163
- this.lineWriter.writeLine('');
164
- this.writePendingTests(evt);
165
- break;
166
- case 'interrupt':
167
- this.lineWriter.writeLine(colors.error(`\n${figures.cross} Exiting due to SIGINT`));
168
- this.lineWriter.writeLine('');
169
- this.writePendingTests(evt);
170
- break;
171
- case 'uncaught-exception':
172
- this.lineWriter.ensureEmptyLine();
173
- this.lineWriter.writeLine(colors.title(`Uncaught exception in ${this.relativeFile(evt.testFile)}`));
174
- this.lineWriter.writeLine();
175
- this.writeErr(evt);
176
- this.lineWriter.writeLine();
177
- break;
178
- case 'unhandled-rejection':
179
- this.lineWriter.ensureEmptyLine();
180
- this.lineWriter.writeLine(colors.title(`Unhandled rejection in ${this.relativeFile(evt.testFile)}`));
181
- this.lineWriter.writeLine();
182
- this.writeErr(evt);
183
- this.lineWriter.writeLine();
184
- break;
185
- case 'worker-failed':
186
- if (!this.filesWithMissingAvaImports.has(evt.testFile)) {
187
- if (evt.nonZeroExitCode) {
188
- this.lineWriter.writeLine(colors.error(`${figures.cross} ${this.relativeFile(evt.testFile)} exited with a non-zero exit code: ${evt.nonZeroExitCode}`));
189
- } else {
190
- this.lineWriter.writeLine(colors.error(`${figures.cross} ${this.relativeFile(evt.testFile)} exited due to ${evt.signal}`));
191
- }
192
- }
193
-
194
- break;
195
- case 'worker-finished':
196
- if (!evt.forcedExit && !this.filesWithMissingAvaImports.has(evt.testFile)) {
197
- if (fileStats.declaredTests === 0) {
198
- this.lineWriter.writeLine(colors.error(`${figures.cross} No tests found in ${this.relativeFile(evt.testFile)}`));
199
- } else if (!this.failFastEnabled && fileStats.remainingTests > 0) {
200
- this.lineWriter.writeLine(colors.error(`${figures.cross} ${fileStats.remainingTests} ${plur('test', fileStats.remainingTests)} remaining in ${this.relativeFile(evt.testFile)}`));
201
- }
202
- }
203
-
204
- break;
205
- case 'worker-stderr':
206
- case 'worker-stdout':
207
- this.stdStream.write(evt.chunk);
208
- // If the chunk does not end with a linebreak, *forcibly* write one to
209
- // ensure it remains visible in the TTY.
210
- // Tests cannot assume their standard output is not interrupted. Indeed
211
- // we multiplex stdout and stderr into a single stream. However as
212
- // long as stdStream is different from reportStream users can read
213
- // their original output by redirecting the streams.
214
- if (evt.chunk[evt.chunk.length - 1] !== 0x0A) {
215
- this.reportStream.write(os.EOL);
216
- }
217
-
218
- break;
219
- default:
220
- break;
221
- }
222
- }
223
-
224
- writeErr(evt) {
225
- if (evt.err.name === 'TSError' && evt.err.object && evt.err.object.diagnosticText) {
226
- this.lineWriter.writeLine(colors.errorStack(trimOffNewlines(evt.err.object.diagnosticText)));
227
- return;
228
- }
229
-
230
- if (evt.err.source) {
231
- this.lineWriter.writeLine(colors.errorSource(`${this.relativeFile(evt.err.source.file)}:${evt.err.source.line}`));
232
- const excerpt = codeExcerpt(evt.err.source, {maxWidth: this.reportStream.columns - 2});
233
- if (excerpt) {
234
- this.lineWriter.writeLine();
235
- this.lineWriter.writeLine(excerpt);
236
- }
237
- }
238
-
239
- if (evt.err.avaAssertionError) {
240
- const result = formatSerializedError(evt.err);
241
- if (result.printMessage) {
242
- this.lineWriter.writeLine();
243
- this.lineWriter.writeLine(evt.err.message);
244
- }
245
-
246
- if (result.formatted) {
247
- this.lineWriter.writeLine();
248
- this.lineWriter.writeLine(result.formatted);
249
- }
250
-
251
- const message = improperUsageMessages.forError(evt.err);
252
- if (message) {
253
- this.lineWriter.writeLine();
254
- this.lineWriter.writeLine(message);
255
- }
256
- } else if (evt.err.nonErrorObject) {
257
- this.lineWriter.writeLine(trimOffNewlines(evt.err.formatted));
258
- } else {
259
- this.lineWriter.writeLine();
260
- this.lineWriter.writeLine(evt.err.summary);
261
- }
262
-
263
- if (evt.err.stack) {
264
- const {stack} = evt.err;
265
- if (stack.includes('\n')) {
266
- this.lineWriter.writeLine();
267
- this.lineWriter.writeLine(colors.errorStack(stack));
268
- }
269
- }
270
- }
271
-
272
- writePendingTests(evt) {
273
- for (const [file, testsInFile] of evt.pendingTests) {
274
- if (testsInFile.size === 0) {
275
- continue;
276
- }
277
-
278
- this.lineWriter.writeLine(`${testsInFile.size} tests were pending in ${this.relativeFile(file)}\n`);
279
- for (const title of testsInFile) {
280
- this.lineWriter.writeLine(`${figures.circleDotted} ${this.prefixTitle(file, title)}`);
281
- }
282
-
283
- this.lineWriter.writeLine('');
284
- }
285
- }
286
-
287
- writeLogs(evt) {
288
- if (evt.logs) {
289
- for (const log of evt.logs) {
290
- const logLines = indentString(colors.log(log), 4);
291
- const logLinesWithLeadingFigure = logLines.replace(
292
- /^ {4}/,
293
- ` ${colors.information(figures.info)} `
294
- );
295
- this.lineWriter.writeLine(logLinesWithLeadingFigure);
296
- }
297
- }
298
- }
299
-
300
- writeTestSummary(evt) {
301
- if (evt.type === 'hook-failed' || evt.type === 'test-failed') {
302
- this.lineWriter.writeLine(`${colors.error(figures.cross)} ${this.prefixTitle(evt.testFile, evt.title)} ${colors.error(evt.err.message)}`);
303
- } else if (evt.knownFailing) {
304
- this.lineWriter.writeLine(`${colors.error(figures.tick)} ${colors.error(this.prefixTitle(evt.testFile, evt.title))}`);
305
- } else {
306
- // Display duration only over a threshold
307
- const threshold = 100;
308
- const duration = evt.duration > threshold ? colors.duration(' (' + prettyMs(evt.duration) + ')') : '';
309
-
310
- this.lineWriter.writeLine(`${colors.pass(figures.tick)} ${this.prefixTitle(evt.testFile, evt.title)}${duration}`);
311
- }
312
-
313
- this.writeLogs(evt);
314
- }
315
-
316
- writeFailure(evt) {
317
- this.lineWriter.writeLine(`${colors.title(this.prefixTitle(evt.testFile, evt.title))}`);
318
- this.writeLogs(evt);
319
- this.lineWriter.writeLine();
320
- this.writeErr(evt);
321
- }
322
-
323
- endRun() { // eslint-disable-line complexity
324
- if (this.emptyParallelRun) {
325
- this.lineWriter.writeLine('No files tested in this parallel run');
326
- this.lineWriter.writeLine();
327
- return;
328
- }
329
-
330
- if (!this.stats) {
331
- this.lineWriter.writeLine(colors.error(`${figures.cross} Couldn’t find any files to test`));
332
- this.lineWriter.writeLine();
333
- return;
334
- }
335
-
336
- if (this.matching && this.stats.selectedTests === 0) {
337
- this.lineWriter.writeLine(colors.error(`${figures.cross} Couldn’t find any matching tests`));
338
- this.lineWriter.writeLine();
339
- return;
340
- }
341
-
342
- this.lineWriter.writeLine();
343
-
344
- if (this.stats.parallelRuns) {
345
- const {currentFileCount, currentIndex, totalRuns} = this.stats.parallelRuns;
346
- this.lineWriter.writeLine(colors.information(`Ran ${currentFileCount} test ${plur('file', currentFileCount)} out of ${this.stats.files} for job ${currentIndex + 1} of ${totalRuns}`));
347
- this.lineWriter.writeLine();
348
- }
349
-
350
- let firstLinePostfix = this.watching ?
351
- ' ' + chalk.gray.dim('[' + new Date().toLocaleTimeString('en-US', {hour12: false}) + ']') :
352
- '';
353
-
354
- if (this.stats.failedHooks > 0) {
355
- this.lineWriter.writeLine(colors.error(`${this.stats.failedHooks} ${plur('hook', this.stats.failedHooks)} failed`) + firstLinePostfix);
356
- firstLinePostfix = '';
357
- }
358
-
359
- if (this.stats.failedTests > 0) {
360
- this.lineWriter.writeLine(colors.error(`${this.stats.failedTests} ${plur('test', this.stats.failedTests)} failed`) + firstLinePostfix);
361
- firstLinePostfix = '';
362
- }
363
-
364
- if (this.stats.failedHooks === 0 && this.stats.failedTests === 0 && this.stats.passedTests > 0) {
365
- this.lineWriter.writeLine(colors.pass(`${this.stats.passedTests} ${plur('test', this.stats.passedTests)} passed`) + firstLinePostfix);
366
- firstLinePostfix = '';
367
- }
368
-
369
- if (this.stats.passedKnownFailingTests > 0) {
370
- this.lineWriter.writeLine(colors.error(`${this.stats.passedKnownFailingTests} ${plur('known failure', this.stats.passedKnownFailingTests)}`));
371
- }
372
-
373
- if (this.stats.skippedTests > 0) {
374
- this.lineWriter.writeLine(colors.skip(`${this.stats.skippedTests} ${plur('test', this.stats.skippedTests)} skipped`));
375
- }
376
-
377
- if (this.stats.todoTests > 0) {
378
- this.lineWriter.writeLine(colors.todo(`${this.stats.todoTests} ${plur('test', this.stats.todoTests)} todo`));
379
- }
380
-
381
- if (this.stats.unhandledRejections > 0) {
382
- this.lineWriter.writeLine(colors.error(`${this.stats.unhandledRejections} unhandled ${plur('rejection', this.stats.unhandledRejections)}`));
383
- }
384
-
385
- if (this.stats.uncaughtExceptions > 0) {
386
- this.lineWriter.writeLine(colors.error(`${this.stats.uncaughtExceptions} uncaught ${plur('exception', this.stats.uncaughtExceptions)}`));
387
- }
388
-
389
- if (this.previousFailures > 0) {
390
- this.lineWriter.writeLine(colors.error(`${this.previousFailures} previous ${plur('failure', this.previousFailures)} in test files that were not rerun`));
391
- }
392
-
393
- if (this.stats.passedKnownFailingTests > 0) {
394
- this.lineWriter.writeLine();
395
- for (const evt of this.knownFailures) {
396
- this.lineWriter.writeLine(colors.error(this.prefixTitle(evt.testFile, evt.title)));
397
- }
398
- }
399
-
400
- const shouldWriteFailFastDisclaimer = this.failFastEnabled && (this.stats.remainingTests > 0 || this.stats.files > this.stats.finishedWorkers);
401
-
402
- if (this.failures.length > 0) {
403
- this.lineWriter.writeLine();
404
-
405
- const lastFailure = this.failures[this.failures.length - 1];
406
- for (const evt of this.failures) {
407
- this.writeFailure(evt);
408
- if (evt !== lastFailure || shouldWriteFailFastDisclaimer) {
409
- this.lineWriter.writeLine();
410
- this.lineWriter.writeLine();
411
- this.lineWriter.writeLine();
412
- }
413
- }
414
- }
415
-
416
- if (shouldWriteFailFastDisclaimer) {
417
- let remaining = '';
418
- if (this.stats.remainingTests > 0) {
419
- remaining += `At least ${this.stats.remainingTests} ${plur('test was', 'tests were', this.stats.remainingTests)} skipped`;
420
- if (this.stats.files > this.stats.finishedWorkers) {
421
- remaining += ', as well as ';
422
- }
423
- }
424
-
425
- if (this.stats.files > this.stats.finishedWorkers) {
426
- const skippedFileCount = this.stats.files - this.stats.finishedWorkers;
427
- remaining += `${skippedFileCount} ${plur('test file', 'test files', skippedFileCount)}`;
428
- if (this.stats.remainingTests === 0) {
429
- remaining += ` ${plur('was', 'were', skippedFileCount)} skipped`;
430
- }
431
- }
432
-
433
- this.lineWriter.writeLine(colors.information(`\`--fail-fast\` is on. ${remaining}.`));
434
- }
435
-
436
- this.lineWriter.writeLine();
437
- }
438
- }
439
-
440
- module.exports = VerboseReporter;