ava 5.3.1 → 6.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/internal.d.mts +7 -0
- package/lib/api-event-iterator.js +12 -0
- package/lib/api.js +14 -23
- package/lib/assert.js +289 -444
- package/lib/cli.js +95 -61
- package/lib/code-excerpt.js +2 -2
- package/lib/eslint-plugin-helper-worker.js +3 -3
- package/lib/fork.js +3 -13
- package/lib/glob-helpers.cjs +1 -9
- package/lib/globs.js +7 -3
- package/lib/line-numbers.js +1 -1
- package/lib/load-config.js +3 -3
- package/lib/parse-test-args.js +3 -3
- package/lib/plugin-support/shared-workers.js +4 -4
- package/lib/provider-manager.js +11 -13
- package/lib/reporters/beautify-stack.js +0 -1
- package/lib/reporters/default.js +92 -45
- package/lib/reporters/format-serialized-error.js +6 -6
- package/lib/reporters/improper-usage-messages.js +5 -5
- package/lib/reporters/tap.js +30 -30
- package/lib/run-status.js +9 -0
- package/lib/runner.js +7 -7
- package/lib/scheduler.js +14 -1
- package/lib/serialize-error.js +44 -116
- package/lib/slash.cjs +1 -1
- package/lib/snapshot-manager.js +14 -8
- package/lib/test.js +90 -81
- package/lib/watcher.js +494 -365
- package/lib/worker/base.js +90 -51
- package/lib/worker/channel.cjs +9 -53
- package/license +1 -1
- package/package.json +36 -42
- package/readme.md +6 -12
- package/types/assertions.d.cts +107 -49
- package/types/shared-worker.d.cts +0 -2
- package/types/state-change-events.d.cts +143 -0
- package/types/test-fn.d.cts +10 -5
- package/lib/worker/dependency-tracker.js +0 -48
- /package/entrypoints/{main.d.ts → main.d.mts} +0 -0
- /package/entrypoints/{plugin.d.ts → plugin.d.mts} +0 -0
package/lib/cli.js
CHANGED
|
@@ -1,30 +1,25 @@
|
|
|
1
|
-
|
|
2
1
|
import fs from 'node:fs';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import process from 'node:process';
|
|
4
|
+
import v8 from 'node:v8';
|
|
5
5
|
|
|
6
6
|
import arrify from 'arrify';
|
|
7
|
-
import ciParallelVars from 'ci-parallel-vars';
|
|
8
7
|
import figures from 'figures';
|
|
9
8
|
import yargs from 'yargs';
|
|
10
|
-
import {hideBin} from 'yargs/helpers';
|
|
9
|
+
import {hideBin} from 'yargs/helpers';
|
|
11
10
|
|
|
11
|
+
import {asyncEventIteratorFromApi} from './api-event-iterator.js';
|
|
12
12
|
import Api from './api.js';
|
|
13
13
|
import {chalk} from './chalk.js';
|
|
14
14
|
import validateEnvironmentVariables from './environment-variables.js';
|
|
15
15
|
import normalizeExtensions from './extensions.js';
|
|
16
16
|
import {normalizeGlobs, normalizePattern} from './globs.js';
|
|
17
|
-
import {controlFlow} from './ipc-flow-control.cjs';
|
|
18
17
|
import isCi from './is-ci.js';
|
|
19
18
|
import {splitPatternAndLineNumbers} from './line-numbers.js';
|
|
20
19
|
import {loadConfig} from './load-config.js';
|
|
21
20
|
import normalizeModuleTypes from './module-types.js';
|
|
22
21
|
import normalizeNodeArguments from './node-arguments.js';
|
|
23
22
|
import pkg from './pkg.cjs';
|
|
24
|
-
import providerManager from './provider-manager.js';
|
|
25
|
-
import DefaultReporter from './reporters/default.js';
|
|
26
|
-
import TapReporter from './reporters/tap.js';
|
|
27
|
-
import Watcher from './watcher.js';
|
|
28
23
|
|
|
29
24
|
function exit(message) {
|
|
30
25
|
console.error(`\n ${chalk.red(figures.cross)} ${message}`);
|
|
@@ -174,7 +169,7 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
174
169
|
type: 'string',
|
|
175
170
|
}), argv => {
|
|
176
171
|
if (activeInspector) {
|
|
177
|
-
debug.files = argv.pattern
|
|
172
|
+
debug.files = argv.pattern ?? [];
|
|
178
173
|
}
|
|
179
174
|
})
|
|
180
175
|
.command(
|
|
@@ -224,7 +219,7 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
224
219
|
const combined = {...conf};
|
|
225
220
|
|
|
226
221
|
for (const flag of Object.keys(FLAGS)) {
|
|
227
|
-
if (flag === 'no-worker-threads' &&
|
|
222
|
+
if (flag === 'no-worker-threads' && Object.hasOwn(argv, 'worker-threads')) {
|
|
228
223
|
combined.workerThreads = argv['worker-threads'];
|
|
229
224
|
continue;
|
|
230
225
|
}
|
|
@@ -284,7 +279,7 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
284
279
|
|
|
285
280
|
process.exit(0); // eslint-disable-line unicorn/no-process-exit
|
|
286
281
|
} catch (error) {
|
|
287
|
-
exit(`Error removing AVA cache files in ${cacheDir}\n\n${chalk.gray(
|
|
282
|
+
exit(`Error removing AVA cache files in ${cacheDir}\n\n${chalk.gray(error?.stack ?? error)}`);
|
|
288
283
|
}
|
|
289
284
|
}
|
|
290
285
|
|
|
@@ -297,7 +292,7 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
297
292
|
exit('Watch mode is not available in CI, as it prevents AVA from terminating.');
|
|
298
293
|
}
|
|
299
294
|
|
|
300
|
-
if (debug !== null) {
|
|
295
|
+
if (debug !== null && !process.env.TEST_AVA) {
|
|
301
296
|
exit('Watch mode is not available when debugging.');
|
|
302
297
|
}
|
|
303
298
|
}
|
|
@@ -316,32 +311,24 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
316
311
|
}
|
|
317
312
|
}
|
|
318
313
|
|
|
319
|
-
if (
|
|
320
|
-
exit('The --concurrency or -c flag must be provided with a
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
if (!combined.tap && Object.keys(experiments).length > 0) {
|
|
324
|
-
console.log(chalk.magenta(` ${figures.warning} Experiments are enabled. These are unsupported and may change or be removed at any time.`));
|
|
314
|
+
if (Object.hasOwn(combined, 'concurrency') && (!Number.isInteger(combined.concurrency) || combined.concurrency < 0)) {
|
|
315
|
+
exit('The --concurrency or -c flag must be provided with a non-negative integer.');
|
|
325
316
|
}
|
|
326
317
|
|
|
327
|
-
if (
|
|
328
|
-
exit('
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (Reflect.has(conf, 'compileEnhancements')) {
|
|
332
|
-
exit('Enhancement compilation must be configured in AVA’s Babel options.');
|
|
318
|
+
if (Object.hasOwn(conf, 'sortTestFiles') && typeof conf.sortTestFiles !== 'function') {
|
|
319
|
+
exit('’sortTestFiles’ must be a comparator function.');
|
|
333
320
|
}
|
|
334
321
|
|
|
335
|
-
if (
|
|
336
|
-
exit('
|
|
322
|
+
if (Object.hasOwn(conf, 'watch')) {
|
|
323
|
+
exit('’watch’ must not be configured, use the --watch CLI flag instead.');
|
|
337
324
|
}
|
|
338
325
|
|
|
339
|
-
if (
|
|
340
|
-
exit('’
|
|
326
|
+
if (Object.hasOwn(conf, 'ignoredByWatcher')) {
|
|
327
|
+
exit('’ignoredByWatcher’ has moved to ’watchMode.ignoreChanges’.');
|
|
341
328
|
}
|
|
342
329
|
|
|
343
|
-
if (
|
|
344
|
-
|
|
330
|
+
if (!combined.tap && Object.keys(experiments).length > 0) {
|
|
331
|
+
console.log(chalk.magenta(` ${figures.warning} Experiments are enabled. These are unsupported and may change or be removed at any time.`));
|
|
345
332
|
}
|
|
346
333
|
|
|
347
334
|
let projectPackageObject;
|
|
@@ -353,14 +340,16 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
353
340
|
}
|
|
354
341
|
}
|
|
355
342
|
|
|
356
|
-
const {type: defaultModuleType = 'commonjs'} = projectPackageObject
|
|
343
|
+
const {type: defaultModuleType = 'commonjs'} = projectPackageObject ?? {};
|
|
357
344
|
|
|
358
345
|
const providers = [];
|
|
359
|
-
if (
|
|
346
|
+
if (Object.hasOwn(conf, 'typescript')) {
|
|
347
|
+
const {default: providerManager} = await import('./provider-manager.js');
|
|
360
348
|
try {
|
|
361
|
-
const {level, main} = await providerManager.typescript(projectDir);
|
|
349
|
+
const {identifier: protocol, level, main} = await providerManager.typescript(projectDir);
|
|
362
350
|
providers.push({
|
|
363
351
|
level,
|
|
352
|
+
protocol,
|
|
364
353
|
main: main({config: conf.typescript}),
|
|
365
354
|
type: 'typescript',
|
|
366
355
|
});
|
|
@@ -392,7 +381,7 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
392
381
|
|
|
393
382
|
let globs;
|
|
394
383
|
try {
|
|
395
|
-
globs = normalizeGlobs({files: conf.files, ignoredByWatcher: conf.
|
|
384
|
+
globs = normalizeGlobs({files: conf.files, ignoredByWatcher: conf.watchMode?.ignoreChanges, extensions, providers});
|
|
396
385
|
} catch (error) {
|
|
397
386
|
exit(error.message);
|
|
398
387
|
}
|
|
@@ -405,14 +394,17 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
405
394
|
}
|
|
406
395
|
|
|
407
396
|
let parallelRuns = null;
|
|
408
|
-
if (isCi &&
|
|
409
|
-
const {
|
|
410
|
-
|
|
397
|
+
if (isCi && combined.utilizeParallelBuilds !== false) {
|
|
398
|
+
const {default: ciParallelVars} = await import('ci-parallel-vars');
|
|
399
|
+
if (ciParallelVars) {
|
|
400
|
+
const {index: currentIndex, total: totalRuns} = ciParallelVars;
|
|
401
|
+
parallelRuns = {currentIndex, totalRuns};
|
|
402
|
+
}
|
|
411
403
|
}
|
|
412
404
|
|
|
413
405
|
const match = combined.match === '' ? [] : arrify(combined.match);
|
|
414
406
|
|
|
415
|
-
const input = debug ? debug.files : (argv.pattern
|
|
407
|
+
const input = debug ? debug.files : (argv.pattern ?? []);
|
|
416
408
|
const filter = input
|
|
417
409
|
.map(pattern => splitPatternAndLineNumbers(pattern))
|
|
418
410
|
.map(({pattern, ...rest}) => ({
|
|
@@ -423,7 +415,7 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
423
415
|
const api = new Api({
|
|
424
416
|
cacheEnabled: combined.cache !== false,
|
|
425
417
|
chalkOptions,
|
|
426
|
-
concurrency: combined.concurrency
|
|
418
|
+
concurrency: combined.concurrency ?? 0,
|
|
427
419
|
workerThreads: combined.workerThreads !== false,
|
|
428
420
|
debug,
|
|
429
421
|
environmentVariables,
|
|
@@ -443,36 +435,58 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
443
435
|
require: arrify(combined.require),
|
|
444
436
|
serial: combined.serial,
|
|
445
437
|
snapshotDir: combined.snapshotDir ? path.resolve(projectDir, combined.snapshotDir) : null,
|
|
446
|
-
timeout: combined.timeout
|
|
438
|
+
timeout: combined.timeout ?? '10s',
|
|
447
439
|
updateSnapshots: combined.updateSnapshots,
|
|
448
440
|
workerArgv: argv['--'],
|
|
449
441
|
});
|
|
450
442
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
443
|
+
let reporter;
|
|
444
|
+
if (combined.tap && !argv.watch && debug === null) {
|
|
445
|
+
const {default: TapReporter} = await import('./reporters/tap.js');
|
|
446
|
+
reporter = new TapReporter({
|
|
447
|
+
extensions: globs.extensions,
|
|
448
|
+
projectDir,
|
|
449
|
+
reportStream: process.stdout,
|
|
450
|
+
stdStream: process.stderr,
|
|
451
|
+
});
|
|
452
|
+
} else {
|
|
453
|
+
const {default: Reporter} = await import('./reporters/default.js');
|
|
454
|
+
reporter = new Reporter({
|
|
455
|
+
extensions: globs.extensions,
|
|
456
|
+
projectDir,
|
|
457
|
+
reportStream: process.stdout,
|
|
458
|
+
stdStream: process.stderr,
|
|
459
|
+
watching: argv.watch,
|
|
460
|
+
});
|
|
461
|
+
}
|
|
466
462
|
|
|
467
|
-
|
|
468
|
-
|
|
463
|
+
if (process.env.TEST_AVA) {
|
|
464
|
+
const {controlFlow} = await import('./ipc-flow-control.cjs');
|
|
465
|
+
const bufferedSend = controlFlow(process);
|
|
469
466
|
|
|
467
|
+
api.on('run', plan => {
|
|
470
468
|
plan.status.on('stateChange', evt => {
|
|
471
469
|
bufferedSend(evt);
|
|
472
470
|
});
|
|
473
|
-
}
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
if (combined.observeRun && experiments.observeRunsFromConfig) {
|
|
475
|
+
combined.observeRun({
|
|
476
|
+
events: asyncEventIteratorFromApi(api),
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
api.on('run', plan => {
|
|
481
|
+
reporter.startRun(plan);
|
|
474
482
|
|
|
475
483
|
plan.status.on('stateChange', evt => {
|
|
484
|
+
if (evt.type === 'end' || evt.type === 'interrupt') {
|
|
485
|
+
// Write out code coverage data when the run ends, lest a process
|
|
486
|
+
// interrupt causes it to be lost.
|
|
487
|
+
v8.takeCoverage();
|
|
488
|
+
}
|
|
489
|
+
|
|
476
490
|
if (evt.type === 'interrupt') {
|
|
477
491
|
reporter.endRun();
|
|
478
492
|
process.exit(1); // eslint-disable-line unicorn/no-process-exit
|
|
@@ -480,16 +494,36 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
480
494
|
});
|
|
481
495
|
});
|
|
482
496
|
|
|
483
|
-
if (
|
|
484
|
-
const
|
|
497
|
+
if (argv.watch) {
|
|
498
|
+
const {available, start} = await import('./watcher.js');
|
|
499
|
+
if (!available(projectDir)) {
|
|
500
|
+
exit('Watch mode requires support for recursive fs.watch()');
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
let abortController;
|
|
505
|
+
if (process.env.TEST_AVA) {
|
|
506
|
+
const {takeCoverage} = await import('node:v8');
|
|
507
|
+
abortController = new AbortController();
|
|
508
|
+
process.on('message', message => {
|
|
509
|
+
if (message === 'abort-watcher') {
|
|
510
|
+
abortController.abort();
|
|
511
|
+
takeCoverage();
|
|
512
|
+
}
|
|
513
|
+
});
|
|
514
|
+
process.channel?.unref();
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
start({
|
|
485
518
|
api,
|
|
486
519
|
filter,
|
|
487
520
|
globs,
|
|
488
521
|
projectDir,
|
|
489
522
|
providers,
|
|
490
523
|
reporter,
|
|
524
|
+
stdin: process.stdin,
|
|
525
|
+
signal: abortController.signal,
|
|
491
526
|
});
|
|
492
|
-
watcher.observeStdin(process.stdin);
|
|
493
527
|
} else {
|
|
494
528
|
let debugWithoutSpecificFile = false;
|
|
495
529
|
api.on('run', plan => {
|
package/lib/code-excerpt.js
CHANGED
|
@@ -8,13 +8,13 @@ import {chalk} from './chalk.js';
|
|
|
8
8
|
const formatLineNumber = (lineNumber, maxLineNumber) =>
|
|
9
9
|
' '.repeat(Math.max(0, String(maxLineNumber).length - String(lineNumber).length)) + lineNumber;
|
|
10
10
|
|
|
11
|
-
export default function
|
|
11
|
+
export default function excerptCode(source, options = {}) {
|
|
12
12
|
if (!source.isWithinProject || source.isDependency) {
|
|
13
13
|
return null;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
const {file, line} = source;
|
|
17
|
-
const maxWidth = options.maxWidth || 80;
|
|
17
|
+
const maxWidth = (options.maxWidth ?? 0) || 80;
|
|
18
18
|
|
|
19
19
|
let contents;
|
|
20
20
|
try {
|
|
@@ -12,8 +12,8 @@ const configCache = new Map();
|
|
|
12
12
|
|
|
13
13
|
const collectProviders = async ({conf, projectDir}) => {
|
|
14
14
|
const providers = [];
|
|
15
|
-
if (
|
|
16
|
-
const {level, main} = await providerManager.typescript(projectDir);
|
|
15
|
+
if (Object.hasOwn(conf, 'typescript')) {
|
|
16
|
+
const {level, main} = await providerManager.typescript(projectDir, {fullConfig: conf});
|
|
17
17
|
providers.push({
|
|
18
18
|
level,
|
|
19
19
|
main: main({config: conf.typescript}),
|
|
@@ -33,7 +33,7 @@ const buildGlobs = ({conf, providers, projectDir, overrideExtensions, overrideFi
|
|
|
33
33
|
cwd: projectDir,
|
|
34
34
|
...normalizeGlobs({
|
|
35
35
|
extensions,
|
|
36
|
-
files: overrideFiles
|
|
36
|
+
files: overrideFiles ?? conf.files,
|
|
37
37
|
providers,
|
|
38
38
|
}),
|
|
39
39
|
};
|
package/lib/fork.js
CHANGED
|
@@ -4,10 +4,9 @@ import {fileURLToPath} from 'node:url';
|
|
|
4
4
|
import {Worker} from 'node:worker_threads';
|
|
5
5
|
|
|
6
6
|
import Emittery from 'emittery';
|
|
7
|
-
import {pEvent} from 'p-event';
|
|
8
7
|
|
|
9
8
|
import {controlFlow} from './ipc-flow-control.cjs';
|
|
10
|
-
import serializeError from './serialize-error.js';
|
|
9
|
+
import serializeError, {tagWorkerError} from './serialize-error.js';
|
|
11
10
|
|
|
12
11
|
let workerPath = new URL('worker/base.js', import.meta.url);
|
|
13
12
|
export function _testOnlyReplaceWorkerPath(replacement) {
|
|
@@ -35,13 +34,8 @@ const createWorker = (options, execArgv) => {
|
|
|
35
34
|
});
|
|
36
35
|
postMessage = worker.postMessage.bind(worker);
|
|
37
36
|
|
|
38
|
-
// Ensure we've seen this event before we terminate the worker thread, as a
|
|
39
|
-
// workaround for https://github.com/nodejs/node/issues/38418.
|
|
40
|
-
const starting = pEvent(worker, 'message', ({ava}) => ava && ava.type === 'starting');
|
|
41
|
-
|
|
42
37
|
close = async () => {
|
|
43
38
|
try {
|
|
44
|
-
await starting;
|
|
45
39
|
await worker.terminate();
|
|
46
40
|
} finally {
|
|
47
41
|
// No-op
|
|
@@ -53,6 +47,7 @@ const createWorker = (options, execArgv) => {
|
|
|
53
47
|
silent: true,
|
|
54
48
|
env: {NODE_ENV: 'test', ...process.env, ...options.environmentVariables},
|
|
55
49
|
execArgv: [...execArgv, ...additionalExecArgv],
|
|
50
|
+
serialization: 'advanced',
|
|
56
51
|
});
|
|
57
52
|
postMessage = controlFlow(worker);
|
|
58
53
|
close = async () => worker.kill();
|
|
@@ -127,11 +122,6 @@ export default function loadFork(file, options, execArgv = process.execArgv) {
|
|
|
127
122
|
break;
|
|
128
123
|
}
|
|
129
124
|
|
|
130
|
-
case 'ping': {
|
|
131
|
-
send({type: 'pong'});
|
|
132
|
-
break;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
125
|
default: {
|
|
136
126
|
emitStateChange(message.ava);
|
|
137
127
|
}
|
|
@@ -139,7 +129,7 @@ export default function loadFork(file, options, execArgv = process.execArgv) {
|
|
|
139
129
|
});
|
|
140
130
|
|
|
141
131
|
worker.on('error', error => {
|
|
142
|
-
emitStateChange({type: 'worker-failed', err: serializeError(
|
|
132
|
+
emitStateChange({type: 'worker-failed', err: serializeError(tagWorkerError(error))});
|
|
143
133
|
finish();
|
|
144
134
|
});
|
|
145
135
|
|
package/lib/glob-helpers.cjs
CHANGED
|
@@ -16,8 +16,6 @@ const defaultPicomatchIgnorePatterns = [
|
|
|
16
16
|
...defaultIgnorePatterns.map(pattern => `${pattern}/**/*`),
|
|
17
17
|
];
|
|
18
18
|
|
|
19
|
-
const defaultMatchNoIgnore = picomatch(defaultPicomatchIgnorePatterns);
|
|
20
|
-
|
|
21
19
|
const matchingCache = new WeakMap();
|
|
22
20
|
const processMatchingPatterns = input => {
|
|
23
21
|
let result = matchingCache.get(input);
|
|
@@ -46,15 +44,9 @@ const processMatchingPatterns = input => {
|
|
|
46
44
|
|
|
47
45
|
exports.processMatchingPatterns = processMatchingPatterns;
|
|
48
46
|
|
|
49
|
-
|
|
50
|
-
const {matchNoIgnore} = processMatchingPatterns(patterns);
|
|
51
|
-
return matchNoIgnore(file) || defaultMatchNoIgnore(file);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
function classify(file, {cwd, extensions, filePatterns, ignoredByWatcherPatterns}) {
|
|
47
|
+
function classify(file, {cwd, extensions, filePatterns}) {
|
|
55
48
|
file = normalizeFileForMatching(cwd, file);
|
|
56
49
|
return {
|
|
57
|
-
isIgnoredByWatcher: matchesIgnorePatterns(file, ignoredByWatcherPatterns),
|
|
58
50
|
isTest: hasExtension(extensions, file) && !isHelperish(file) && filePatterns.length > 0 && matches(file, filePatterns),
|
|
59
51
|
};
|
|
60
52
|
}
|
package/lib/globs.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
4
|
import {globby, globbySync} from 'globby';
|
|
5
|
+
import picomatch from 'picomatch';
|
|
5
6
|
|
|
6
7
|
import {
|
|
7
8
|
defaultIgnorePatterns,
|
|
@@ -26,6 +27,7 @@ const defaultIgnoredByWatcherPatterns = [
|
|
|
26
27
|
'**/*.snap.md', // No need to rerun tests when the Markdown files change.
|
|
27
28
|
'ava.config.js', // Config is not reloaded so avoid rerunning tests when it changes.
|
|
28
29
|
'ava.config.cjs', // Config is not reloaded so avoid rerunning tests when it changes.
|
|
30
|
+
'ava.config.mjs', // Config is not reloaded so avoid rerunning tests when it changes.
|
|
29
31
|
];
|
|
30
32
|
|
|
31
33
|
const buildExtensionPattern = extensions => extensions.length === 1 ? extensions[0] : `{${extensions.join(',')}}`;
|
|
@@ -36,7 +38,7 @@ export function normalizeGlobs({extensions, files: filePatterns, ignoredByWatche
|
|
|
36
38
|
}
|
|
37
39
|
|
|
38
40
|
if (ignoredByWatcherPatterns !== undefined && (!Array.isArray(ignoredByWatcherPatterns) || ignoredByWatcherPatterns.length === 0)) {
|
|
39
|
-
throw new Error('The ’
|
|
41
|
+
throw new Error('The ’watchMode.ignoreChanges’ configuration must be an array containing glob patterns.');
|
|
40
42
|
}
|
|
41
43
|
|
|
42
44
|
const extensionPattern = buildExtensionPattern(extensions);
|
|
@@ -125,11 +127,13 @@ export async function findTests({cwd, extensions, filePatterns}) {
|
|
|
125
127
|
return files.filter(file => !path.basename(file).startsWith('_'));
|
|
126
128
|
}
|
|
127
129
|
|
|
128
|
-
export function
|
|
129
|
-
|
|
130
|
+
export function buildIgnoreMatcher({ignoredByWatcherPatterns}) {
|
|
131
|
+
const patterns = [
|
|
130
132
|
...defaultIgnorePatterns.map(pattern => `${pattern}/**/*`),
|
|
131
133
|
...ignoredByWatcherPatterns.filter(pattern => !pattern.startsWith('!')),
|
|
132
134
|
];
|
|
135
|
+
|
|
136
|
+
return picomatch(patterns, {dot: true});
|
|
133
137
|
}
|
|
134
138
|
|
|
135
139
|
export function applyTestFileFilter({ // eslint-disable-line complexity
|
package/lib/line-numbers.js
CHANGED
|
@@ -13,7 +13,7 @@ const sortNumbersAscending = array => {
|
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
const parseNumber = string => Number.parseInt(string, 10);
|
|
16
|
-
const removeAllWhitespace = string => string.
|
|
16
|
+
const removeAllWhitespace = string => string.replaceAll(/\s/g, '');
|
|
17
17
|
const range = (start, end) => Array.from({length: end - start + 1}).fill(start).map((element, index) => element + index);
|
|
18
18
|
|
|
19
19
|
const parseLineNumbers = suffix => sortNumbersAscending(distinctArray(
|
package/lib/load-config.js
CHANGED
|
@@ -4,11 +4,11 @@ import process from 'node:process';
|
|
|
4
4
|
import url from 'node:url';
|
|
5
5
|
|
|
6
6
|
import {isPlainObject} from 'is-plain-object';
|
|
7
|
-
import {packageConfig, packageJsonPath} from '
|
|
7
|
+
import {packageConfig, packageJsonPath} from 'package-config';
|
|
8
8
|
|
|
9
9
|
const NO_SUCH_FILE = Symbol('no ava.config.js file');
|
|
10
10
|
const MISSING_DEFAULT_EXPORT = Symbol('missing default export');
|
|
11
|
-
const EXPERIMENTS = new Set();
|
|
11
|
+
const EXPERIMENTS = new Set(['observeRunsFromConfig']);
|
|
12
12
|
|
|
13
13
|
const importConfig = async ({configFile, fileForErrorMessage}) => {
|
|
14
14
|
const {default: config = MISSING_DEFAULT_EXPORT} = await import(url.pathToFileURL(configFile));
|
|
@@ -41,7 +41,7 @@ function resolveConfigFile(configFile) {
|
|
|
41
41
|
return configFile;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const gitScmFile = process.env.AVA_FAKE_SCM_ROOT
|
|
44
|
+
const gitScmFile = process.env.AVA_FAKE_SCM_ROOT ?? '.git';
|
|
45
45
|
|
|
46
46
|
async function findRepoRoot(fromDir) {
|
|
47
47
|
const {root} = path.parse(fromDir);
|
package/lib/parse-test-args.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const buildTitle = (raw, implementation, args) => {
|
|
2
|
-
let value = implementation
|
|
2
|
+
let value = implementation?.title?.(raw, ...args) ?? raw;
|
|
3
3
|
const isValid = typeof value === 'string';
|
|
4
4
|
if (isValid) {
|
|
5
|
-
value = value.trim().
|
|
5
|
+
value = value.trim().replaceAll(/\s+/g, ' ');
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
return {
|
|
@@ -20,7 +20,7 @@ export default function parseTestArgs(args) {
|
|
|
20
20
|
|
|
21
21
|
return {
|
|
22
22
|
args,
|
|
23
|
-
implementation: implementation
|
|
23
|
+
implementation: implementation?.exec ?? implementation,
|
|
24
24
|
title: buildTitle(rawTitle, implementation, args),
|
|
25
25
|
};
|
|
26
26
|
}
|
|
@@ -2,7 +2,7 @@ import events from 'node:events';
|
|
|
2
2
|
import {pathToFileURL} from 'node:url';
|
|
3
3
|
import {Worker} from 'node:worker_threads';
|
|
4
4
|
|
|
5
|
-
import serializeError from '../serialize-error.js';
|
|
5
|
+
import serializeError, {tagWorkerError} from '../serialize-error.js';
|
|
6
6
|
|
|
7
7
|
const LOADER = new URL('shared-worker-loader.js', import.meta.url);
|
|
8
8
|
|
|
@@ -34,7 +34,7 @@ function launchWorker(filename, initialData) {
|
|
|
34
34
|
const launched = {
|
|
35
35
|
statePromises: {
|
|
36
36
|
available: waitForAvailable(worker),
|
|
37
|
-
error: events.once(worker, 'error').then(([error]) => error),
|
|
37
|
+
error: events.once(worker, 'error').then(([error]) => tagWorkerError(error)),
|
|
38
38
|
},
|
|
39
39
|
exited: false,
|
|
40
40
|
worker,
|
|
@@ -97,14 +97,14 @@ export async function observeWorkerProcess(fork, runStatus) {
|
|
|
97
97
|
launched.statePromises.error.then(error => {
|
|
98
98
|
launched.worker.off('message', handleWorkerMessage);
|
|
99
99
|
removeAllInstances();
|
|
100
|
-
runStatus.emitStateChange({type: 'shared-worker-error', err: serializeError(
|
|
100
|
+
runStatus.emitStateChange({type: 'shared-worker-error', err: serializeError(error)});
|
|
101
101
|
signalError();
|
|
102
102
|
});
|
|
103
103
|
|
|
104
104
|
try {
|
|
105
105
|
await launched.statePromises.available;
|
|
106
106
|
|
|
107
|
-
port.postMessage({type: 'ready'});
|
|
107
|
+
port.postMessage({ava: {type: 'ready'}});
|
|
108
108
|
|
|
109
109
|
launched.worker.postMessage({
|
|
110
110
|
type: 'register-test-worker',
|
package/lib/provider-manager.js
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import * as globs from './globs.js';
|
|
2
2
|
import pkg from './pkg.cjs';
|
|
3
3
|
|
|
4
|
-
const levels = {
|
|
4
|
+
export const levels = {
|
|
5
5
|
// As the protocol changes, comparing levels by integer allows AVA to be
|
|
6
|
-
// compatible with different versions.
|
|
7
|
-
|
|
8
|
-
// future use.
|
|
9
|
-
levelIntegersAreCurrentlyUnused: 0,
|
|
6
|
+
// compatible with different versions.
|
|
7
|
+
ava6: 1,
|
|
10
8
|
};
|
|
11
9
|
|
|
12
|
-
const levelsByProtocol = {
|
|
13
|
-
'ava-
|
|
14
|
-
};
|
|
10
|
+
const levelsByProtocol = Object.assign(Object.create(null), {
|
|
11
|
+
'ava-6': levels.ava6,
|
|
12
|
+
});
|
|
15
13
|
|
|
16
|
-
async function load(providerModule, projectDir) {
|
|
14
|
+
async function load(providerModule, projectDir, selectProtocol = () => true) {
|
|
17
15
|
const ava = {version: pkg.version};
|
|
18
16
|
const {default: makeProvider} = await import(providerModule);
|
|
19
17
|
|
|
@@ -21,7 +19,8 @@ async function load(providerModule, projectDir) {
|
|
|
21
19
|
let level;
|
|
22
20
|
const provider = makeProvider({
|
|
23
21
|
negotiateProtocol(identifiers, {version}) {
|
|
24
|
-
const identifier = identifiers
|
|
22
|
+
const identifier = identifiers
|
|
23
|
+
.find(identifier => selectProtocol(identifier) && Object.hasOwn(levelsByProtocol, identifier));
|
|
25
24
|
|
|
26
25
|
if (identifier === undefined) {
|
|
27
26
|
fatal = new Error(`This version of AVA (${ava.version}) is not compatible with ${providerModule}@${version}`);
|
|
@@ -50,9 +49,8 @@ async function load(providerModule, projectDir) {
|
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
const providerManager = {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return load('@ava/typescript', projectDir);
|
|
52
|
+
async typescript(projectDir, {protocol} = {}) {
|
|
53
|
+
return load('@ava/typescript', projectDir, identifier => protocol === undefined || identifier === protocol);
|
|
56
54
|
},
|
|
57
55
|
};
|
|
58
56
|
|