ava 3.15.0 → 4.0.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.
- package/entrypoints/cli.mjs +4 -0
- package/entrypoints/eslint-plugin-helper.cjs +109 -0
- 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 -816
- package/lib/api.js +108 -49
- package/lib/assert.js +255 -270
- package/lib/chalk.js +9 -14
- package/lib/cli.js +118 -112
- package/lib/code-excerpt.js +12 -17
- package/lib/concordance-options.js +29 -65
- package/lib/context-ref.js +3 -6
- package/lib/create-chain.js +32 -20
- package/lib/environment-variables.js +1 -4
- package/lib/eslint-plugin-helper-worker.js +73 -0
- package/lib/extensions.js +2 -2
- package/lib/fork.js +81 -84
- 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 +11 -18
- package/lib/load-config.js +56 -180
- package/lib/module-types.js +3 -7
- 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 -46
- package/lib/provider-manager.js +20 -14
- package/lib/reporters/beautify-stack.js +6 -12
- package/lib/reporters/colors.js +40 -15
- package/lib/reporters/default.js +114 -364
- 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 +18 -25
- package/lib/run-status.js +29 -23
- package/lib/runner.js +157 -172
- package/lib/scheduler.js +53 -0
- package/lib/serialize-error.js +61 -64
- package/lib/snapshot-manager.js +271 -289
- package/lib/test.js +135 -291
- package/lib/watcher.js +69 -44
- package/lib/worker/base.js +208 -0
- package/lib/worker/channel.cjs +290 -0
- package/lib/worker/dependency-tracker.js +24 -23
- package/lib/worker/{ensure-forked.js → guard-environment.cjs} +5 -4
- package/lib/worker/line-numbers.js +58 -20
- package/lib/worker/main.cjs +12 -0
- package/lib/worker/{options.js → options.cjs} +0 -0
- package/lib/worker/{plugin.js → plugin.cjs} +30 -21
- package/lib/worker/state.cjs +5 -0
- package/lib/worker/utils.cjs +6 -0
- package/package.json +71 -68
- package/plugin.d.ts +51 -53
- package/readme.md +5 -13
- 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/eslint-plugin-helper.js +0 -201
- package/index.js +0 -8
- package/lib/worker/ipc.js +0 -201
- package/lib/worker/main.js +0 -21
- package/lib/worker/subprocess.js +0 -266
- package/plugin.js +0 -9
package/lib/api.js
CHANGED
|
@@ -1,23 +1,25 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import process from 'node:process';
|
|
5
|
+
|
|
6
|
+
import arrify from 'arrify';
|
|
7
|
+
import chunkd from 'chunkd';
|
|
8
|
+
import commonPathPrefix from 'common-path-prefix';
|
|
9
|
+
import Emittery from 'emittery';
|
|
10
|
+
import ms from 'ms';
|
|
11
|
+
import pMap from 'p-map';
|
|
12
|
+
import resolveCwd from 'resolve-cwd';
|
|
13
|
+
import tempDir from 'temp-dir';
|
|
14
|
+
|
|
15
|
+
import fork from './fork.js';
|
|
16
|
+
import * as globs from './globs.js';
|
|
17
|
+
import isCi from './is-ci.js';
|
|
18
|
+
import {getApplicableLineNumbers} from './line-numbers.js';
|
|
19
|
+
import {observeWorkerProcess} from './plugin-support/shared-workers.js';
|
|
20
|
+
import RunStatus from './run-status.js';
|
|
21
|
+
import scheduler from './scheduler.js';
|
|
22
|
+
import serializeError from './serialize-error.js';
|
|
21
23
|
|
|
22
24
|
function resolveModules(modules) {
|
|
23
25
|
return arrify(modules).map(name => {
|
|
@@ -40,7 +42,40 @@ function getFilePathPrefix(files) {
|
|
|
40
42
|
return commonPathPrefix(files);
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
class
|
|
45
|
+
class TimeoutTrigger {
|
|
46
|
+
constructor(fn, waitMs = 0) {
|
|
47
|
+
this.fn = fn.bind(null);
|
|
48
|
+
this.ignoreUntil = 0;
|
|
49
|
+
this.waitMs = waitMs;
|
|
50
|
+
this.timer = undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
debounce() {
|
|
54
|
+
if (this.timer === undefined) {
|
|
55
|
+
this.timer = setTimeout(() => this.trigger(), this.waitMs);
|
|
56
|
+
} else {
|
|
57
|
+
this.timer.refresh();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
discard() {
|
|
62
|
+
// N.B. this.timer is not cleared so if debounce() is called after it will
|
|
63
|
+
// not run again.
|
|
64
|
+
clearTimeout(this.timer);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
ignoreFor(periodMs) {
|
|
68
|
+
this.ignoreUntil = Math.max(this.ignoreUntil, Date.now() + periodMs);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
trigger() {
|
|
72
|
+
if (Date.now() >= this.ignoreUntil) {
|
|
73
|
+
this.fn();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default class Api extends Emittery {
|
|
44
79
|
constructor(options) {
|
|
45
80
|
super();
|
|
46
81
|
|
|
@@ -55,7 +90,7 @@ class Api extends Emittery {
|
|
|
55
90
|
}
|
|
56
91
|
}
|
|
57
92
|
|
|
58
|
-
async run({files: selectedFiles = [], filter = [], runtimeOptions = {}} = {}) {
|
|
93
|
+
async run({files: selectedFiles = [], filter = [], runtimeOptions = {}} = {}) { // eslint-disable-line complexity
|
|
59
94
|
let setupOrGlobError;
|
|
60
95
|
|
|
61
96
|
const apiOptions = this.options;
|
|
@@ -70,11 +105,11 @@ class Api extends Emittery {
|
|
|
70
105
|
let bailed = false;
|
|
71
106
|
const pendingWorkers = new Set();
|
|
72
107
|
const timedOutWorkerFiles = new Set();
|
|
73
|
-
let
|
|
108
|
+
let timeoutTrigger;
|
|
74
109
|
if (apiOptions.timeout && !apiOptions.debug) {
|
|
75
110
|
const timeout = ms(apiOptions.timeout);
|
|
76
111
|
|
|
77
|
-
|
|
112
|
+
timeoutTrigger = new TimeoutTrigger(() => {
|
|
78
113
|
// If failFast is active, prevent new test files from running after
|
|
79
114
|
// the current ones are exited.
|
|
80
115
|
if (failFast) {
|
|
@@ -89,7 +124,7 @@ class Api extends Emittery {
|
|
|
89
124
|
}
|
|
90
125
|
}, timeout);
|
|
91
126
|
} else {
|
|
92
|
-
|
|
127
|
+
timeoutTrigger = new TimeoutTrigger(() => {});
|
|
93
128
|
}
|
|
94
129
|
|
|
95
130
|
this._interruptHandler = () => {
|
|
@@ -102,7 +137,7 @@ class Api extends Emittery {
|
|
|
102
137
|
bailed = true;
|
|
103
138
|
|
|
104
139
|
// Make sure we don't run the timeout handler
|
|
105
|
-
|
|
140
|
+
timeoutTrigger.discard();
|
|
106
141
|
|
|
107
142
|
runStatus.emitStateChange({type: 'interrupt'});
|
|
108
143
|
|
|
@@ -111,6 +146,8 @@ class Api extends Emittery {
|
|
|
111
146
|
}
|
|
112
147
|
};
|
|
113
148
|
|
|
149
|
+
const {providers = []} = this.options;
|
|
150
|
+
|
|
114
151
|
let testFiles;
|
|
115
152
|
try {
|
|
116
153
|
testFiles = await globs.findTests({cwd: this.options.projectDir, ...apiOptions.globs});
|
|
@@ -118,7 +155,8 @@ class Api extends Emittery {
|
|
|
118
155
|
selectedFiles = filter.length === 0 ? testFiles : globs.applyTestFileFilter({
|
|
119
156
|
cwd: this.options.projectDir,
|
|
120
157
|
filter: filter.map(({pattern}) => pattern),
|
|
121
|
-
|
|
158
|
+
providers,
|
|
159
|
+
testFiles,
|
|
122
160
|
});
|
|
123
161
|
}
|
|
124
162
|
} catch (error) {
|
|
@@ -126,6 +164,13 @@ class Api extends Emittery {
|
|
|
126
164
|
setupOrGlobError = error;
|
|
127
165
|
}
|
|
128
166
|
|
|
167
|
+
const selectionInsights = {
|
|
168
|
+
filter,
|
|
169
|
+
ignoredFilterPatternFiles: selectedFiles.ignoredFilterPatternFiles || [],
|
|
170
|
+
testFileCount: testFiles.length,
|
|
171
|
+
selectionCount: selectedFiles.length,
|
|
172
|
+
};
|
|
173
|
+
|
|
129
174
|
try {
|
|
130
175
|
if (this.options.parallelRuns) {
|
|
131
176
|
const {currentIndex, totalRuns} = this.options.parallelRuns;
|
|
@@ -137,11 +182,13 @@ class Api extends Emittery {
|
|
|
137
182
|
|
|
138
183
|
const currentFileCount = selectedFiles.length;
|
|
139
184
|
|
|
140
|
-
runStatus = new RunStatus(fileCount, {currentFileCount, currentIndex, totalRuns});
|
|
185
|
+
runStatus = new RunStatus(fileCount, {currentFileCount, currentIndex, totalRuns}, selectionInsights);
|
|
141
186
|
} else {
|
|
142
|
-
runStatus = new RunStatus(selectedFiles.length, null);
|
|
187
|
+
runStatus = new RunStatus(selectedFiles.length, null, selectionInsights);
|
|
143
188
|
}
|
|
144
189
|
|
|
190
|
+
selectedFiles = scheduler.failingTestsFirst(selectedFiles, this._getLocalCacheDir(), this.options.cacheEnabled);
|
|
191
|
+
|
|
145
192
|
const debugWithoutSpecificFile = Boolean(this.options.debug) && !this.options.debug.active && selectedFiles.length !== 1;
|
|
146
193
|
|
|
147
194
|
await this.emit('run', {
|
|
@@ -155,7 +202,7 @@ class Api extends Emittery {
|
|
|
155
202
|
previousFailures: runtimeOptions.previousFailures || 0,
|
|
156
203
|
runOnlyExclusive: runtimeOptions.runOnlyExclusive === true,
|
|
157
204
|
runVector: runtimeOptions.runVector || 0,
|
|
158
|
-
status: runStatus
|
|
205
|
+
status: runStatus,
|
|
159
206
|
});
|
|
160
207
|
|
|
161
208
|
if (setupOrGlobError) {
|
|
@@ -169,9 +216,9 @@ class Api extends Emittery {
|
|
|
169
216
|
|
|
170
217
|
runStatus.on('stateChange', record => {
|
|
171
218
|
if (record.testFile && !timedOutWorkerFiles.has(record.testFile)) {
|
|
172
|
-
//
|
|
219
|
+
// Debounce the timer whenever there is activity from workers that
|
|
173
220
|
// haven't already timed out.
|
|
174
|
-
|
|
221
|
+
timeoutTrigger.debounce();
|
|
175
222
|
}
|
|
176
223
|
|
|
177
224
|
if (failFast && (record.type === 'hook-failed' || record.type === 'test-failed' || record.type === 'worker-failed')) {
|
|
@@ -185,14 +232,16 @@ class Api extends Emittery {
|
|
|
185
232
|
}
|
|
186
233
|
});
|
|
187
234
|
|
|
188
|
-
const
|
|
189
|
-
|
|
235
|
+
const providerStates = [];
|
|
236
|
+
await Promise.all(providers.map(async ({type, main}) => {
|
|
190
237
|
const state = await main.compile({cacheDir: this._createCacheDir(), files: testFiles});
|
|
191
|
-
|
|
192
|
-
|
|
238
|
+
if (state !== null) {
|
|
239
|
+
providerStates.push({type, state});
|
|
240
|
+
}
|
|
241
|
+
}));
|
|
193
242
|
|
|
194
243
|
// Resolve the correct concurrency value.
|
|
195
|
-
let concurrency = Math.min(os.cpus().length, isCi ? 2 :
|
|
244
|
+
let concurrency = Math.min(os.cpus().length, isCi ? 2 : Number.POSITIVE_INFINITY);
|
|
196
245
|
if (apiOptions.concurrency > 0) {
|
|
197
246
|
concurrency = apiOptions.concurrency;
|
|
198
247
|
}
|
|
@@ -212,13 +261,15 @@ class Api extends Emittery {
|
|
|
212
261
|
}
|
|
213
262
|
|
|
214
263
|
const lineNumbers = getApplicableLineNumbers(globs.normalizeFileForMatching(apiOptions.projectDir, file), filter);
|
|
264
|
+
// Removing `providers` field because they cannot be transfered to the worker threads.
|
|
265
|
+
const {providers, ...forkOptions} = apiOptions;
|
|
215
266
|
const options = {
|
|
216
|
-
...
|
|
267
|
+
...forkOptions,
|
|
217
268
|
providerStates,
|
|
218
269
|
lineNumbers,
|
|
219
270
|
recordNewSnapshots: !isCi,
|
|
220
271
|
// If we're looking for matches, run every single test process in exclusive-only mode
|
|
221
|
-
runOnlyExclusive: apiOptions.match.length > 0 || runtimeOptions.runOnlyExclusive === true
|
|
272
|
+
runOnlyExclusive: apiOptions.match.length > 0 || runtimeOptions.runOnlyExclusive === true,
|
|
222
273
|
};
|
|
223
274
|
|
|
224
275
|
if (runtimeOptions.updateSnapshots) {
|
|
@@ -227,42 +278,52 @@ class Api extends Emittery {
|
|
|
227
278
|
}
|
|
228
279
|
|
|
229
280
|
const worker = fork(file, options, apiOptions.nodeArguments);
|
|
281
|
+
worker.onStateChange(data => {
|
|
282
|
+
if (data.type === 'test-timeout-configured' && !apiOptions.debug) {
|
|
283
|
+
timeoutTrigger.ignoreFor(data.period);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
230
286
|
runStatus.observeWorker(worker, file, {selectingLines: lineNumbers.length > 0});
|
|
231
|
-
deregisteredSharedWorkers.push(
|
|
287
|
+
deregisteredSharedWorkers.push(observeWorkerProcess(worker, runStatus));
|
|
232
288
|
|
|
233
289
|
pendingWorkers.add(worker);
|
|
234
290
|
worker.promise.then(() => {
|
|
235
291
|
pendingWorkers.delete(worker);
|
|
236
292
|
});
|
|
237
|
-
|
|
293
|
+
timeoutTrigger.debounce();
|
|
238
294
|
|
|
239
295
|
await worker.promise;
|
|
240
296
|
}, {concurrency, stopOnError: false});
|
|
241
297
|
|
|
242
298
|
// Allow shared workers to clean up before the run ends.
|
|
243
299
|
await Promise.all(deregisteredSharedWorkers);
|
|
300
|
+
scheduler.storeFailedTestFiles(runStatus, this.options.cacheEnabled === false ? null : this._createCacheDir());
|
|
244
301
|
} catch (error) {
|
|
245
302
|
if (error && error.name === 'AggregateError') {
|
|
246
|
-
for (const
|
|
247
|
-
runStatus.emitStateChange({type: 'internal-error', err: serializeError('Internal error', false,
|
|
303
|
+
for (const error_ of error.errors) {
|
|
304
|
+
runStatus.emitStateChange({type: 'internal-error', err: serializeError('Internal error', false, error_)});
|
|
248
305
|
}
|
|
249
306
|
} else {
|
|
250
307
|
runStatus.emitStateChange({type: 'internal-error', err: serializeError('Internal error', false, error)});
|
|
251
308
|
}
|
|
252
309
|
}
|
|
253
310
|
|
|
254
|
-
|
|
311
|
+
timeoutTrigger.discard();
|
|
255
312
|
return runStatus;
|
|
256
313
|
}
|
|
257
314
|
|
|
315
|
+
_getLocalCacheDir() {
|
|
316
|
+
return path.join(this.options.projectDir, 'node_modules', '.cache', 'ava');
|
|
317
|
+
}
|
|
318
|
+
|
|
258
319
|
_createCacheDir() {
|
|
259
320
|
if (this._cacheDir) {
|
|
260
321
|
return this._cacheDir;
|
|
261
322
|
}
|
|
262
323
|
|
|
263
|
-
const cacheDir = this.options.cacheEnabled === false
|
|
264
|
-
fs.mkdtempSync(`${tempDir}${path.sep}`)
|
|
265
|
-
|
|
324
|
+
const cacheDir = this.options.cacheEnabled === false
|
|
325
|
+
? fs.mkdtempSync(`${tempDir}${path.sep}`)
|
|
326
|
+
: this._getLocalCacheDir();
|
|
266
327
|
|
|
267
328
|
// Ensure cacheDir exists
|
|
268
329
|
fs.mkdirSync(cacheDir, {recursive: true});
|
|
@@ -272,5 +333,3 @@ class Api extends Emittery {
|
|
|
272
333
|
return cacheDir;
|
|
273
334
|
}
|
|
274
335
|
}
|
|
275
|
-
|
|
276
|
-
module.exports = Api;
|