ava 4.0.0-alpha.1 → 4.0.1
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.
- package/entrypoints/cli.mjs +4 -0
- package/{eslint-plugin-helper.js → entrypoints/eslint-plugin-helper.cjs} +20 -7
- package/entrypoints/main.cjs +2 -0
- package/entrypoints/main.mjs +1 -0
- package/entrypoints/plugin.cjs +2 -0
- package/entrypoints/plugin.mjs +4 -0
- package/index.d.ts +6 -709
- package/lib/api.js +95 -46
- package/lib/assert.js +122 -173
- package/lib/chalk.js +9 -14
- package/lib/cli.js +105 -97
- package/lib/code-excerpt.js +12 -17
- package/lib/concordance-options.js +30 -31
- package/lib/context-ref.js +3 -6
- package/lib/create-chain.js +32 -4
- package/lib/environment-variables.js +1 -4
- package/lib/eslint-plugin-helper-worker.js +16 -26
- package/lib/extensions.js +2 -2
- package/lib/fork.js +42 -83
- package/lib/glob-helpers.cjs +140 -0
- package/lib/globs.js +136 -163
- package/lib/{ipc-flow-control.js → ipc-flow-control.cjs} +1 -0
- package/lib/is-ci.js +4 -2
- package/lib/like-selector.js +7 -13
- package/lib/line-numbers.js +10 -17
- package/lib/load-config.js +62 -56
- package/lib/module-types.js +3 -3
- package/lib/node-arguments.js +4 -5
- package/lib/{now-and-timers.js → now-and-timers.cjs} +0 -0
- package/lib/parse-test-args.js +22 -11
- package/lib/pkg.cjs +2 -0
- package/lib/plugin-support/shared-worker-loader.js +45 -48
- package/lib/plugin-support/shared-workers.js +24 -43
- package/lib/provider-manager.js +20 -14
- package/lib/reporters/beautify-stack.js +6 -11
- package/lib/reporters/colors.js +40 -15
- package/lib/reporters/default.js +115 -350
- package/lib/reporters/format-serialized-error.js +7 -18
- package/lib/reporters/improper-usage-messages.js +8 -9
- package/lib/reporters/prefix-title.js +17 -15
- package/lib/reporters/tap.js +15 -16
- package/lib/run-status.js +25 -23
- package/lib/runner.js +138 -127
- package/lib/scheduler.js +42 -36
- package/lib/serialize-error.js +34 -34
- package/lib/snapshot-manager.js +83 -76
- package/lib/test.js +114 -195
- package/lib/watcher.js +65 -40
- package/lib/worker/base.js +48 -99
- package/lib/worker/channel.cjs +290 -0
- package/lib/worker/dependency-tracker.js +22 -22
- package/lib/worker/guard-environment.cjs +19 -0
- package/lib/worker/line-numbers.js +57 -19
- package/lib/worker/main.cjs +12 -0
- package/lib/worker/{options.js → options.cjs} +0 -0
- package/lib/worker/{plugin.js → plugin.cjs} +31 -16
- package/lib/worker/state.cjs +5 -0
- package/lib/worker/{utils.js → utils.cjs} +1 -1
- package/package.json +60 -68
- package/plugin.d.ts +51 -53
- package/readme.md +5 -12
- package/types/assertions.d.ts +327 -0
- package/types/subscribable.ts +6 -0
- package/types/test-fn.d.ts +231 -0
- package/types/try-fn.d.ts +58 -0
- package/cli.js +0 -11
- package/index.js +0 -8
- package/lib/worker/channel.js +0 -218
- package/lib/worker/main.js +0 -20
- package/plugin.js +0 -9
package/lib/test.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import concordance from 'concordance';
|
|
2
|
+
import isPromise from 'is-promise';
|
|
3
|
+
import plur from 'plur';
|
|
4
|
+
|
|
5
|
+
import {AssertionError, Assertions, checkAssertionMessage} from './assert.js';
|
|
6
|
+
import concordanceOptions from './concordance-options.js';
|
|
7
|
+
import nowAndTimers from './now-and-timers.cjs';
|
|
8
|
+
import parseTestArgs from './parse-test-args.js';
|
|
9
9
|
|
|
10
10
|
function formatErrorValue(label, error) {
|
|
11
11
|
const formatted = concordance.format(error, concordanceOptions);
|
|
@@ -21,7 +21,7 @@ const captureSavedError = () => {
|
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
const testMap = new WeakMap();
|
|
24
|
-
class ExecutionContext extends
|
|
24
|
+
class ExecutionContext extends Assertions {
|
|
25
25
|
constructor(test) {
|
|
26
26
|
super({
|
|
27
27
|
pass: () => {
|
|
@@ -36,12 +36,9 @@ class ExecutionContext extends assert.Assertions {
|
|
|
36
36
|
skip: () => {
|
|
37
37
|
test.countPassedAssertion();
|
|
38
38
|
},
|
|
39
|
-
compareWithSnapshot: options =>
|
|
40
|
-
return test.compareWithSnapshot(options);
|
|
41
|
-
},
|
|
42
|
-
powerAssert: test.powerAssert,
|
|
39
|
+
compareWithSnapshot: options => test.compareWithSnapshot(options),
|
|
43
40
|
experiments: test.experiments,
|
|
44
|
-
disableSnapshots: test.isHook === true
|
|
41
|
+
disableSnapshots: test.isHook === true,
|
|
45
42
|
});
|
|
46
43
|
testMap.set(this, test);
|
|
47
44
|
|
|
@@ -50,11 +47,9 @@ class ExecutionContext extends assert.Assertions {
|
|
|
50
47
|
};
|
|
51
48
|
|
|
52
49
|
this.log = (...inputArgs) => {
|
|
53
|
-
const args = inputArgs.map(value =>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
concordance.format(value, concordanceOptions);
|
|
57
|
-
});
|
|
50
|
+
const args = inputArgs.map(value => typeof value === 'string'
|
|
51
|
+
? value
|
|
52
|
+
: concordance.format(value, concordanceOptions));
|
|
58
53
|
if (args.length > 0) {
|
|
59
54
|
test.addLog(args.join(' '));
|
|
60
55
|
}
|
|
@@ -81,90 +76,86 @@ class ExecutionContext extends assert.Assertions {
|
|
|
81
76
|
throw error;
|
|
82
77
|
}
|
|
83
78
|
|
|
84
|
-
const {args,
|
|
79
|
+
const {args, implementation, title} = parseTestArgs(attemptArgs);
|
|
85
80
|
|
|
86
|
-
if (
|
|
81
|
+
if (!implementation) {
|
|
87
82
|
throw new TypeError('Expected an implementation.');
|
|
88
83
|
}
|
|
89
84
|
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
if (Array.isArray(implementation)) {
|
|
86
|
+
throw new TypeError('AVA 4 no longer supports t.try() with multiple implementations.');
|
|
87
|
+
}
|
|
92
88
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
89
|
+
let attemptTitle;
|
|
90
|
+
if (!title.isSet || title.isEmpty) {
|
|
91
|
+
attemptTitle = `${test.title} ─ attempt ${test.attemptCount + 1}`;
|
|
92
|
+
} else if (title.isValid) {
|
|
93
|
+
attemptTitle = `${test.title} ─ ${title.value}`;
|
|
94
|
+
} else {
|
|
95
|
+
throw new TypeError('`t.try()` titles must be strings');
|
|
96
|
+
}
|
|
100
97
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
98
|
+
if (!test.registerUniqueTitle(attemptTitle)) {
|
|
99
|
+
throw new Error(`Duplicate test title: ${attemptTitle}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let committed = false;
|
|
103
|
+
let discarded = false;
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (discarded) {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
discarded = true;
|
|
151
|
-
test.finishAttempt({
|
|
152
|
-
assertCount: 0,
|
|
153
|
-
commit: false,
|
|
154
|
-
deferredSnapshotRecordings,
|
|
155
|
-
errors,
|
|
156
|
-
logs,
|
|
157
|
-
passed,
|
|
158
|
-
retainLogs,
|
|
159
|
-
snapshotCount,
|
|
160
|
-
startingSnapshotCount
|
|
161
|
-
});
|
|
105
|
+
const {assertCount, deferredSnapshotRecordings, errors, logs, passed, snapshotCount, startingSnapshotCount} = await test.runAttempt(attemptTitle, t => implementation(t, ...args));
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
errors,
|
|
109
|
+
logs: [...logs], // Don't allow modification of logs.
|
|
110
|
+
passed,
|
|
111
|
+
title: attemptTitle,
|
|
112
|
+
commit: ({retainLogs = true} = {}) => {
|
|
113
|
+
if (committed) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (discarded) {
|
|
118
|
+
test.saveFirstError(new Error('Can’t commit a result that was previously discarded'));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
committed = true;
|
|
123
|
+
test.finishAttempt({
|
|
124
|
+
assertCount,
|
|
125
|
+
commit: true,
|
|
126
|
+
deferredSnapshotRecordings,
|
|
127
|
+
errors,
|
|
128
|
+
logs,
|
|
129
|
+
passed,
|
|
130
|
+
retainLogs,
|
|
131
|
+
snapshotCount,
|
|
132
|
+
startingSnapshotCount,
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
discard: ({retainLogs = false} = {}) => {
|
|
136
|
+
if (committed) {
|
|
137
|
+
test.saveFirstError(new Error('Can’t discard a result that was previously committed'));
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (discarded) {
|
|
142
|
+
return;
|
|
162
143
|
}
|
|
163
|
-
};
|
|
164
|
-
});
|
|
165
144
|
|
|
166
|
-
|
|
167
|
-
|
|
145
|
+
discarded = true;
|
|
146
|
+
test.finishAttempt({
|
|
147
|
+
assertCount: 0,
|
|
148
|
+
commit: false,
|
|
149
|
+
deferredSnapshotRecordings,
|
|
150
|
+
errors,
|
|
151
|
+
logs,
|
|
152
|
+
passed,
|
|
153
|
+
retainLogs,
|
|
154
|
+
snapshotCount,
|
|
155
|
+
startingSnapshotCount,
|
|
156
|
+
});
|
|
157
|
+
},
|
|
158
|
+
};
|
|
168
159
|
};
|
|
169
160
|
}
|
|
170
161
|
|
|
@@ -184,17 +175,9 @@ class ExecutionContext extends assert.Assertions {
|
|
|
184
175
|
const test = testMap.get(this);
|
|
185
176
|
return test.isHook ? test.testPassed : !test.assertError;
|
|
186
177
|
}
|
|
187
|
-
|
|
188
|
-
_throwsArgStart(assertion, file, line) {
|
|
189
|
-
testMap.get(this).trackThrows({assertion, file, line});
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
_throwsArgEnd() {
|
|
193
|
-
testMap.get(this).trackThrows(null);
|
|
194
|
-
}
|
|
195
178
|
}
|
|
196
179
|
|
|
197
|
-
class Test {
|
|
180
|
+
export default class Test {
|
|
198
181
|
constructor(options) {
|
|
199
182
|
this.contextRef = options.contextRef;
|
|
200
183
|
this.experiments = options.experiments || {};
|
|
@@ -202,12 +185,12 @@ class Test {
|
|
|
202
185
|
this.fn = options.fn;
|
|
203
186
|
this.isHook = options.isHook === true;
|
|
204
187
|
this.metadata = options.metadata;
|
|
205
|
-
this.powerAssert = options.powerAssert;
|
|
206
188
|
this.title = options.title;
|
|
207
189
|
this.testPassed = options.testPassed;
|
|
208
190
|
this.registerUniqueTitle = options.registerUniqueTitle;
|
|
209
191
|
this.logs = [];
|
|
210
192
|
this.teardowns = [];
|
|
193
|
+
this.notifyTimeoutUpdate = options.notifyTimeoutUpdate;
|
|
211
194
|
|
|
212
195
|
const {snapshotBelongsTo = this.title, nextSnapshotIndex = 0} = options;
|
|
213
196
|
this.snapshotBelongsTo = snapshotBelongsTo;
|
|
@@ -229,7 +212,7 @@ class Test {
|
|
|
229
212
|
expected,
|
|
230
213
|
index,
|
|
231
214
|
label,
|
|
232
|
-
taskIndex: this.metadata.taskIndex
|
|
215
|
+
taskIndex: this.metadata.taskIndex,
|
|
233
216
|
});
|
|
234
217
|
if (record) {
|
|
235
218
|
this.deferredSnapshotRecordings.push(record);
|
|
@@ -244,7 +227,7 @@ class Test {
|
|
|
244
227
|
belongsTo: snapshotBelongsTo,
|
|
245
228
|
index: this.nextSnapshotIndex,
|
|
246
229
|
deferRecording,
|
|
247
|
-
taskIndex: this.metadata.taskIndex
|
|
230
|
+
taskIndex: this.metadata.taskIndex,
|
|
248
231
|
});
|
|
249
232
|
if (record) {
|
|
250
233
|
this.deferredSnapshotRecordings.push(record);
|
|
@@ -272,7 +255,7 @@ class Test {
|
|
|
272
255
|
contextRef: contextRef.copy(),
|
|
273
256
|
snapshotBelongsTo,
|
|
274
257
|
nextSnapshotIndex,
|
|
275
|
-
title
|
|
258
|
+
title,
|
|
276
259
|
});
|
|
277
260
|
|
|
278
261
|
const {deferredSnapshotRecordings, error, logs, passed, assertCount, snapshotCount} = await attempt.run();
|
|
@@ -285,13 +268,11 @@ class Test {
|
|
|
285
268
|
this.attemptCount = 0;
|
|
286
269
|
this.calledEnd = false;
|
|
287
270
|
this.duration = null;
|
|
288
|
-
this.finishDueToAttributedError = null;
|
|
289
271
|
this.finishDueToInactivity = null;
|
|
290
272
|
this.finishDueToTimeout = null;
|
|
291
273
|
this.finishing = false;
|
|
292
274
|
this.pendingAssertionCount = 0;
|
|
293
275
|
this.pendingAttemptCount = 0;
|
|
294
|
-
this.pendingThrowsAssertion = null;
|
|
295
276
|
this.planCount = null;
|
|
296
277
|
this.startedAt = 0;
|
|
297
278
|
this.timeoutMs = 0;
|
|
@@ -334,7 +315,7 @@ class Test {
|
|
|
334
315
|
|
|
335
316
|
promise
|
|
336
317
|
.catch(error => this.saveFirstError(error))
|
|
337
|
-
.then(() => {
|
|
318
|
+
.then(() => {
|
|
338
319
|
this.pendingAssertionCount--;
|
|
339
320
|
this.refreshTimeout();
|
|
340
321
|
});
|
|
@@ -411,7 +392,7 @@ class Test {
|
|
|
411
392
|
}
|
|
412
393
|
|
|
413
394
|
timeout(ms, message) {
|
|
414
|
-
const result =
|
|
395
|
+
const result = checkAssertionMessage('timeout', message);
|
|
415
396
|
if (result !== true) {
|
|
416
397
|
this.saveFirstError(result);
|
|
417
398
|
// Allow the timeout to be set even when the message is invalid.
|
|
@@ -431,6 +412,8 @@ class Test {
|
|
|
431
412
|
this.finishDueToTimeout();
|
|
432
413
|
}
|
|
433
414
|
}, ms);
|
|
415
|
+
|
|
416
|
+
this.notifyTimeoutUpdate(this.timeoutMs);
|
|
434
417
|
}
|
|
435
418
|
|
|
436
419
|
refreshTimeout() {
|
|
@@ -482,11 +465,11 @@ class Test {
|
|
|
482
465
|
|
|
483
466
|
verifyPlan() {
|
|
484
467
|
if (!this.assertError && this.planCount !== null && this.planCount !== this.assertCount) {
|
|
485
|
-
this.saveFirstError(new
|
|
468
|
+
this.saveFirstError(new AssertionError({
|
|
486
469
|
assertion: 'plan',
|
|
487
470
|
message: `Planned for ${this.planCount} ${plur('assertion', this.planCount)}, but got ${this.assertCount}.`,
|
|
488
471
|
operator: '===',
|
|
489
|
-
savedError: this.planError
|
|
472
|
+
savedError: this.planError,
|
|
490
473
|
}));
|
|
491
474
|
}
|
|
492
475
|
}
|
|
@@ -517,70 +500,16 @@ class Test {
|
|
|
517
500
|
}
|
|
518
501
|
}
|
|
519
502
|
|
|
520
|
-
trackThrows(pending) {
|
|
521
|
-
this.pendingThrowsAssertion = pending;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
detectImproperThrows(error) {
|
|
525
|
-
if (!this.pendingThrowsAssertion) {
|
|
526
|
-
return false;
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
const pending = this.pendingThrowsAssertion;
|
|
530
|
-
this.pendingThrowsAssertion = null;
|
|
531
|
-
|
|
532
|
-
const values = [];
|
|
533
|
-
if (error) {
|
|
534
|
-
values.push(formatErrorValue(`The following error was thrown, possibly before \`t.${pending.assertion}()\` could be called:`, error));
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
this.saveFirstError(new assert.AssertionError({
|
|
538
|
-
assertion: pending.assertion,
|
|
539
|
-
fixedSource: {file: pending.file, line: pending.line},
|
|
540
|
-
improperUsage: true,
|
|
541
|
-
message: `Improper usage of \`t.${pending.assertion}()\` detected`,
|
|
542
|
-
savedError: error instanceof Error && error,
|
|
543
|
-
values
|
|
544
|
-
}));
|
|
545
|
-
return true;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
waitForPendingThrowsAssertion() {
|
|
549
|
-
return new Promise(resolve => {
|
|
550
|
-
this.finishDueToAttributedError = () => {
|
|
551
|
-
resolve(this.finish());
|
|
552
|
-
};
|
|
553
|
-
|
|
554
|
-
this.finishDueToInactivity = () => {
|
|
555
|
-
this.detectImproperThrows();
|
|
556
|
-
resolve(this.finish());
|
|
557
|
-
};
|
|
558
|
-
|
|
559
|
-
// Wait up to a second to see if an error can be attributed to the
|
|
560
|
-
// pending assertion.
|
|
561
|
-
nowAndTimers.setTimeout(() => this.finishDueToInactivity(), 1000).unref();
|
|
562
|
-
});
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
attributeLeakedError(error) {
|
|
566
|
-
if (!this.detectImproperThrows(error)) {
|
|
567
|
-
return false;
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
this.finishDueToAttributedError();
|
|
571
|
-
return true;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
503
|
callFn() {
|
|
575
504
|
try {
|
|
576
505
|
return {
|
|
577
506
|
ok: true,
|
|
578
|
-
retval: this.fn.call(null, this.createExecutionContext())
|
|
507
|
+
retval: this.fn.call(null, this.createExecutionContext()),
|
|
579
508
|
};
|
|
580
509
|
} catch (error) {
|
|
581
510
|
return {
|
|
582
511
|
ok: false,
|
|
583
|
-
error
|
|
512
|
+
error,
|
|
584
513
|
};
|
|
585
514
|
}
|
|
586
515
|
}
|
|
@@ -590,13 +519,11 @@ class Test {
|
|
|
590
519
|
|
|
591
520
|
const result = this.callFn();
|
|
592
521
|
if (!result.ok) {
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
}));
|
|
599
|
-
}
|
|
522
|
+
this.saveFirstError(new AssertionError({
|
|
523
|
+
message: 'Error thrown in test',
|
|
524
|
+
savedError: result.error instanceof Error && result.error,
|
|
525
|
+
values: [formatErrorValue('Error thrown in test:', result.error)],
|
|
526
|
+
}));
|
|
600
527
|
|
|
601
528
|
return this.finish();
|
|
602
529
|
}
|
|
@@ -609,7 +536,7 @@ class Test {
|
|
|
609
536
|
promise = new Promise((resolve, reject) => {
|
|
610
537
|
result.retval.subscribe({
|
|
611
538
|
error: reject,
|
|
612
|
-
complete: () => resolve()
|
|
539
|
+
complete: () => resolve(),
|
|
613
540
|
});
|
|
614
541
|
});
|
|
615
542
|
} else if (returnedPromise) {
|
|
@@ -628,24 +555,22 @@ class Test {
|
|
|
628
555
|
};
|
|
629
556
|
|
|
630
557
|
this.finishDueToInactivity = () => {
|
|
631
|
-
const error = returnedObservable
|
|
632
|
-
new Error('Observable returned by test never completed')
|
|
633
|
-
new Error('Promise returned by test never resolved');
|
|
558
|
+
const error = returnedObservable
|
|
559
|
+
? new Error('Observable returned by test never completed')
|
|
560
|
+
: new Error('Promise returned by test never resolved');
|
|
634
561
|
this.saveFirstError(error);
|
|
635
562
|
resolve(this.finish());
|
|
636
563
|
};
|
|
637
564
|
|
|
638
565
|
promise
|
|
639
566
|
.catch(error => {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
}));
|
|
646
|
-
}
|
|
567
|
+
this.saveFirstError(new AssertionError({
|
|
568
|
+
message: 'Rejected promise returned by test',
|
|
569
|
+
savedError: error instanceof Error && error,
|
|
570
|
+
values: [formatErrorValue('Rejected promise returned by test. Reason:', error)],
|
|
571
|
+
}));
|
|
647
572
|
})
|
|
648
|
-
.then(() => resolve(this.finish()));
|
|
573
|
+
.then(() => resolve(this.finish()));
|
|
649
574
|
});
|
|
650
575
|
}
|
|
651
576
|
|
|
@@ -655,10 +580,6 @@ class Test {
|
|
|
655
580
|
async finish() {
|
|
656
581
|
this.finishing = true;
|
|
657
582
|
|
|
658
|
-
if (!this.assertError && this.pendingThrowsAssertion) {
|
|
659
|
-
return this.waitForPendingThrowsAssertion();
|
|
660
|
-
}
|
|
661
|
-
|
|
662
583
|
this.clearTimeout();
|
|
663
584
|
this.verifyPlan();
|
|
664
585
|
this.verifyAssertions();
|
|
@@ -684,9 +605,7 @@ class Test {
|
|
|
684
605
|
passed,
|
|
685
606
|
snapshotCount: this.snapshotCount,
|
|
686
607
|
assertCount: this.assertCount,
|
|
687
|
-
title: this.title
|
|
608
|
+
title: this.title,
|
|
688
609
|
};
|
|
689
610
|
}
|
|
690
611
|
}
|
|
691
|
-
|
|
692
|
-
module.exports = Test;
|
package/lib/watcher.js
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import nodePath from 'node:path';
|
|
2
|
+
|
|
3
|
+
import chokidar_ from 'chokidar';
|
|
4
|
+
import createDebug from 'debug';
|
|
5
|
+
|
|
6
|
+
import {chalk} from './chalk.js';
|
|
7
|
+
import {applyTestFileFilter, classify, getChokidarIgnorePatterns} from './globs.js';
|
|
8
|
+
|
|
9
|
+
let chokidar = chokidar_;
|
|
10
|
+
export function _testOnlyReplaceChokidar(replacement) {
|
|
11
|
+
chokidar = replacement;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let debug = createDebug('ava:watcher');
|
|
15
|
+
export function _testOnlyReplaceDebug(replacement) {
|
|
16
|
+
debug = replacement('ava:watcher');
|
|
17
|
+
}
|
|
10
18
|
|
|
11
19
|
function rethrowAsync(error) {
|
|
12
20
|
// Don't swallow exceptions. Note that any
|
|
@@ -77,7 +85,7 @@ class TestDependency {
|
|
|
77
85
|
}
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
class Watcher {
|
|
88
|
+
export default class Watcher {
|
|
81
89
|
constructor({api, filter = [], globs, projectDir, providers, reporter}) {
|
|
82
90
|
this.debouncer = new Debouncer(this);
|
|
83
91
|
|
|
@@ -88,7 +96,7 @@ class Watcher {
|
|
|
88
96
|
|
|
89
97
|
const patternFilters = filter.map(({pattern}) => pattern);
|
|
90
98
|
|
|
91
|
-
this.providers = providers
|
|
99
|
+
this.providers = providers;
|
|
92
100
|
this.run = (specificFiles = [], updateSnapshots = false) => {
|
|
93
101
|
const clearLogOnNextRun = this.clearLogOnNextRun && this.runVector > 0;
|
|
94
102
|
if (this.runVector > 0) {
|
|
@@ -104,12 +112,18 @@ class Watcher {
|
|
|
104
112
|
if (runOnlyExclusive) {
|
|
105
113
|
// The test files that previously contained exclusive tests are always
|
|
106
114
|
// run, together with the remaining specific files.
|
|
107
|
-
const remainingFiles =
|
|
115
|
+
const remainingFiles = specificFiles.filter(file => !exclusiveFiles.includes(file));
|
|
108
116
|
specificFiles = [...this.filesWithExclusiveTests, ...remainingFiles];
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
if (filter.length > 0) {
|
|
112
|
-
specificFiles = applyTestFileFilter({
|
|
120
|
+
specificFiles = applyTestFileFilter({
|
|
121
|
+
cwd: projectDir,
|
|
122
|
+
expandDirectories: false,
|
|
123
|
+
filter: patternFilters,
|
|
124
|
+
testFiles: specificFiles,
|
|
125
|
+
treatFilterPatternsAsFiles: false,
|
|
126
|
+
});
|
|
113
127
|
}
|
|
114
128
|
|
|
115
129
|
this.pruneFailures(specificFiles);
|
|
@@ -125,21 +139,21 @@ class Watcher {
|
|
|
125
139
|
previousFailures: this.sumPreviousFailures(this.runVector),
|
|
126
140
|
runOnlyExclusive,
|
|
127
141
|
runVector: this.runVector,
|
|
128
|
-
updateSnapshots: updateSnapshots === true
|
|
129
|
-
}
|
|
142
|
+
updateSnapshots: updateSnapshots === true,
|
|
143
|
+
},
|
|
130
144
|
})
|
|
131
|
-
.then(runStatus => {
|
|
145
|
+
.then(runStatus => {
|
|
132
146
|
reporter.endRun();
|
|
133
147
|
reporter.lineWriter.writeLine(END_MESSAGE);
|
|
134
148
|
|
|
135
149
|
if (this.clearLogOnNextRun && (
|
|
136
|
-
runStatus.stats.failedHooks > 0
|
|
137
|
-
runStatus.stats.failedTests > 0
|
|
138
|
-
runStatus.stats.failedWorkers > 0
|
|
139
|
-
runStatus.stats.internalErrors > 0
|
|
140
|
-
runStatus.stats.timeouts > 0
|
|
141
|
-
runStatus.stats.uncaughtExceptions > 0
|
|
142
|
-
runStatus.stats.unhandledRejections > 0
|
|
150
|
+
runStatus.stats.failedHooks > 0
|
|
151
|
+
|| runStatus.stats.failedTests > 0
|
|
152
|
+
|| runStatus.stats.failedWorkers > 0
|
|
153
|
+
|| runStatus.stats.internalErrors > 0
|
|
154
|
+
|| runStatus.stats.timeouts > 0
|
|
155
|
+
|| runStatus.stats.uncaughtExceptions > 0
|
|
156
|
+
|| runStatus.stats.unhandledRejections > 0
|
|
143
157
|
)) {
|
|
144
158
|
this.clearLogOnNextRun = false;
|
|
145
159
|
}
|
|
@@ -150,6 +164,7 @@ class Watcher {
|
|
|
150
164
|
this.testDependencies = [];
|
|
151
165
|
this.trackTestDependencies(api);
|
|
152
166
|
|
|
167
|
+
this.temporaryFiles = new Set();
|
|
153
168
|
this.touchedFiles = new Set();
|
|
154
169
|
this.trackTouchedFiles(api);
|
|
155
170
|
|
|
@@ -168,7 +183,7 @@ class Watcher {
|
|
|
168
183
|
chokidar.watch(['**/*'], {
|
|
169
184
|
cwd: this.globs.cwd,
|
|
170
185
|
ignored: getChokidarIgnorePatterns(this.globs),
|
|
171
|
-
ignoreInitial: true
|
|
186
|
+
ignoreInitial: true,
|
|
172
187
|
}).on('all', (event, path) => {
|
|
173
188
|
if (event === 'add' || event === 'change' || event === 'unlink') {
|
|
174
189
|
debug('Detected %s of %s', event, path);
|
|
@@ -231,9 +246,13 @@ class Watcher {
|
|
|
231
246
|
return;
|
|
232
247
|
}
|
|
233
248
|
|
|
234
|
-
for (const file of evt.files) {
|
|
249
|
+
for (const file of evt.files.changedFiles) {
|
|
235
250
|
this.touchedFiles.add(file);
|
|
236
251
|
}
|
|
252
|
+
|
|
253
|
+
for (const file of evt.files.temporaryFiles) {
|
|
254
|
+
this.temporaryFiles.add(file);
|
|
255
|
+
}
|
|
237
256
|
});
|
|
238
257
|
});
|
|
239
258
|
}
|
|
@@ -307,7 +326,7 @@ class Watcher {
|
|
|
307
326
|
this.filesWithFailures.push({
|
|
308
327
|
file,
|
|
309
328
|
vector,
|
|
310
|
-
count: 1
|
|
329
|
+
count: 1,
|
|
311
330
|
});
|
|
312
331
|
}
|
|
313
332
|
}
|
|
@@ -379,6 +398,14 @@ class Watcher {
|
|
|
379
398
|
return false;
|
|
380
399
|
}
|
|
381
400
|
|
|
401
|
+
// Unlike touched files, temporary files are never cleared. We may see
|
|
402
|
+
// adds and unlinks detected separately, so we track the temporary files
|
|
403
|
+
// as long as AVA is running.
|
|
404
|
+
if (this.temporaryFiles.has(path)) {
|
|
405
|
+
debug('Ignoring known temporary file %s', path);
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
|
|
382
409
|
return true;
|
|
383
410
|
});
|
|
384
411
|
|
|
@@ -394,21 +421,23 @@ class Watcher {
|
|
|
394
421
|
}
|
|
395
422
|
|
|
396
423
|
const dirtyHelpersAndSources = [];
|
|
397
|
-
const
|
|
424
|
+
const addedOrChangedTests = [];
|
|
425
|
+
const unlinkedTests = [];
|
|
398
426
|
for (const filePath of dirtyPaths) {
|
|
399
427
|
const {isIgnoredByWatcher, isTest} = classify(filePath, this.globs);
|
|
400
428
|
if (!isIgnoredByWatcher) {
|
|
401
429
|
if (isTest) {
|
|
402
|
-
|
|
430
|
+
if (dirtyStates[filePath] === 'unlink') {
|
|
431
|
+
unlinkedTests.push(filePath);
|
|
432
|
+
} else {
|
|
433
|
+
addedOrChangedTests.push(filePath);
|
|
434
|
+
}
|
|
403
435
|
} else {
|
|
404
436
|
dirtyHelpersAndSources.push(filePath);
|
|
405
437
|
}
|
|
406
438
|
}
|
|
407
439
|
}
|
|
408
440
|
|
|
409
|
-
const addedOrChangedTests = dirtyTests.filter(path => dirtyStates[path] !== 'unlink');
|
|
410
|
-
const unlinkedTests = diff(dirtyTests, addedOrChangedTests);
|
|
411
|
-
|
|
412
441
|
this.cleanUnlinkedTests(unlinkedTests);
|
|
413
442
|
|
|
414
443
|
// No need to rerun tests if the only change is that tests were deleted
|
|
@@ -423,12 +452,10 @@ class Watcher {
|
|
|
423
452
|
}
|
|
424
453
|
|
|
425
454
|
// Try to find tests that depend on the changed source files
|
|
426
|
-
const testsByHelpersOrSource = dirtyHelpersAndSources.map(path => {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
});
|
|
431
|
-
}, this).filter(tests => tests.length > 0);
|
|
455
|
+
const testsByHelpersOrSource = dirtyHelpersAndSources.map(path => this.testDependencies.filter(dep => dep.contains(path)).map(dep => {
|
|
456
|
+
debug('%s is a dependency of %s', path, dep.file);
|
|
457
|
+
return dep.file;
|
|
458
|
+
})).filter(tests => tests.length > 0);
|
|
432
459
|
|
|
433
460
|
// Rerun all tests if source files were changed that could not be traced to
|
|
434
461
|
// specific tests
|
|
@@ -440,8 +467,6 @@ class Watcher {
|
|
|
440
467
|
}
|
|
441
468
|
|
|
442
469
|
// Run all affected tests
|
|
443
|
-
this.run([...new Set([
|
|
470
|
+
this.run([...new Set([addedOrChangedTests, testsByHelpersOrSource].flat(2))]);
|
|
444
471
|
}
|
|
445
472
|
}
|
|
446
|
-
|
|
447
|
-
module.exports = Watcher;
|