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.
- package/index.d.ts +18 -1
- package/lib/api.js +9 -2
- package/lib/assert.js +62 -6
- package/lib/cli.js +28 -15
- package/lib/code-excerpt.js +1 -1
- package/lib/concordance-options.js +0 -1
- package/lib/fork.js +3 -1
- package/lib/globs.js +17 -13
- package/lib/like-selector.js +37 -0
- package/lib/line-numbers.js +64 -0
- package/lib/load-config.js +1 -1
- package/lib/reporters/beautify-stack.js +73 -0
- package/lib/reporters/colors.js +1 -0
- package/lib/reporters/default.js +834 -0
- package/lib/reporters/tap.js +3 -2
- package/lib/run-status.js +8 -2
- package/lib/runner.js +21 -3
- package/lib/serialize-error.js +30 -17
- package/lib/test.js +43 -1
- package/lib/watcher.js +4 -1
- package/lib/worker/line-numbers.js +90 -0
- package/lib/worker/subprocess.js +21 -6
- package/package.json +27 -72
- package/readme.md +1 -1
- package/lib/beautify-stack.js +0 -75
- package/lib/reporters/mini.js +0 -573
- package/lib/reporters/verbose.js +0 -440
package/index.d.ts
CHANGED
|
@@ -45,6 +45,9 @@ export interface Assertions {
|
|
|
45
45
|
/** Assert that `actual` is [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to `expected`. */
|
|
46
46
|
deepEqual: DeepEqualAssertion;
|
|
47
47
|
|
|
48
|
+
/** Assert that `actual` is like `expected`. */
|
|
49
|
+
like: LikeAssertion;
|
|
50
|
+
|
|
48
51
|
/** Fail the test. */
|
|
49
52
|
fail: FailAssertion;
|
|
50
53
|
|
|
@@ -125,6 +128,14 @@ export interface DeepEqualAssertion {
|
|
|
125
128
|
skip(actual: any, expected: any, message?: string): void;
|
|
126
129
|
}
|
|
127
130
|
|
|
131
|
+
export interface LikeAssertion {
|
|
132
|
+
/** Assert that `value` is like `selector`. */
|
|
133
|
+
(value: any, selector: Record<string, unknown>, message?: string): void;
|
|
134
|
+
|
|
135
|
+
/** Skip this assertion. */
|
|
136
|
+
skip(value: any, selector: any, message?: string): void;
|
|
137
|
+
}
|
|
138
|
+
|
|
128
139
|
export interface FailAssertion {
|
|
129
140
|
/** Fail the test. */
|
|
130
141
|
(message?: string): void;
|
|
@@ -313,6 +324,7 @@ export interface ExecutionContext<Context = unknown> extends Assertions {
|
|
|
313
324
|
|
|
314
325
|
log: LogFn;
|
|
315
326
|
plan: PlanFn;
|
|
327
|
+
teardown: TeardownFn;
|
|
316
328
|
timeout: TimeoutFn;
|
|
317
329
|
try: TryFn<Context>;
|
|
318
330
|
}
|
|
@@ -344,6 +356,11 @@ export interface TimeoutFn {
|
|
|
344
356
|
(ms: number): void;
|
|
345
357
|
}
|
|
346
358
|
|
|
359
|
+
export interface TeardownFn {
|
|
360
|
+
/** Declare a function to be run after the test has ended. */
|
|
361
|
+
(fn: () => void): void;
|
|
362
|
+
}
|
|
363
|
+
|
|
347
364
|
export interface TryFn<Context = unknown> {
|
|
348
365
|
/**
|
|
349
366
|
* Attempt to run some assertions. The result must be explicitly committed or discarded or else
|
|
@@ -416,7 +433,7 @@ export interface CbExecutionContext<Context = unknown> extends ExecutionContext<
|
|
|
416
433
|
end(error?: any): void;
|
|
417
434
|
}
|
|
418
435
|
|
|
419
|
-
export type ImplementationResult = PromiseLike<void> | Subscribable | void;
|
|
436
|
+
export type ImplementationResult = PromiseLike<void> | Subscribable | void; // eslint-disable-line @typescript-eslint/no-invalid-void-type
|
|
420
437
|
export type Implementation<Context = unknown> = (t: ExecutionContext<Context>) => ImplementationResult;
|
|
421
438
|
export type CbImplementation<Context = unknown> = (t: CbExecutionContext<Context>) => ImplementationResult;
|
|
422
439
|
|
package/lib/api.js
CHANGED
|
@@ -16,6 +16,7 @@ const isCi = require('./is-ci');
|
|
|
16
16
|
const RunStatus = require('./run-status');
|
|
17
17
|
const fork = require('./fork');
|
|
18
18
|
const serializeError = require('./serialize-error');
|
|
19
|
+
const {getApplicableLineNumbers} = require('./line-numbers');
|
|
19
20
|
|
|
20
21
|
function resolveModules(modules) {
|
|
21
22
|
return arrify(modules).map(name => {
|
|
@@ -118,7 +119,11 @@ class Api extends Emittery {
|
|
|
118
119
|
if (filter.length === 0) {
|
|
119
120
|
selectedFiles = testFiles;
|
|
120
121
|
} else {
|
|
121
|
-
selectedFiles = globs.applyTestFileFilter({
|
|
122
|
+
selectedFiles = globs.applyTestFileFilter({
|
|
123
|
+
cwd: this.options.projectDir,
|
|
124
|
+
filter: filter.map(({pattern}) => pattern),
|
|
125
|
+
testFiles
|
|
126
|
+
});
|
|
122
127
|
}
|
|
123
128
|
}
|
|
124
129
|
} catch (error) {
|
|
@@ -209,9 +214,11 @@ class Api extends Emittery {
|
|
|
209
214
|
return;
|
|
210
215
|
}
|
|
211
216
|
|
|
217
|
+
const lineNumbers = getApplicableLineNumbers(globs.normalizeFileForMatching(apiOptions.projectDir, file), filter);
|
|
212
218
|
const options = {
|
|
213
219
|
...apiOptions,
|
|
214
220
|
providerStates,
|
|
221
|
+
lineNumbers,
|
|
215
222
|
recordNewSnapshots: !isCi,
|
|
216
223
|
// If we're looking for matches, run every single test process in exclusive-only mode
|
|
217
224
|
runOnlyExclusive: apiOptions.match.length > 0 || runtimeOptions.runOnlyExclusive === true
|
|
@@ -223,7 +230,7 @@ class Api extends Emittery {
|
|
|
223
230
|
}
|
|
224
231
|
|
|
225
232
|
const worker = fork(file, options, apiOptions.nodeArguments);
|
|
226
|
-
runStatus.observeWorker(worker, file);
|
|
233
|
+
runStatus.observeWorker(worker, file, {selectingLines: lineNumbers.length > 0});
|
|
227
234
|
|
|
228
235
|
pendingWorkers.add(worker);
|
|
229
236
|
worker.promise.then(() => {
|
package/lib/assert.js
CHANGED
|
@@ -3,11 +3,11 @@ const concordance = require('concordance');
|
|
|
3
3
|
const isError = require('is-error');
|
|
4
4
|
const isPromise = require('is-promise');
|
|
5
5
|
const concordanceOptions = require('./concordance-options').default;
|
|
6
|
-
const
|
|
6
|
+
const {CIRCULAR_SELECTOR, isLikeSelector, selectComparable} = require('./like-selector');
|
|
7
7
|
const snapshotManager = require('./snapshot-manager');
|
|
8
8
|
|
|
9
9
|
function formatDescriptorDiff(actualDescriptor, expectedDescriptor, options) {
|
|
10
|
-
options = {...options, ...
|
|
10
|
+
options = {...options, ...concordanceOptions};
|
|
11
11
|
return {
|
|
12
12
|
label: 'Difference:',
|
|
13
13
|
formatted: concordance.diffDescriptors(actualDescriptor, expectedDescriptor, options)
|
|
@@ -242,7 +242,8 @@ class Assertions {
|
|
|
242
242
|
fail = notImplemented,
|
|
243
243
|
skip = notImplemented,
|
|
244
244
|
compareWithSnapshot = notImplemented,
|
|
245
|
-
powerAssert
|
|
245
|
+
powerAssert,
|
|
246
|
+
experiments = {}
|
|
246
247
|
} = {}) {
|
|
247
248
|
const withSkip = assertionFn => {
|
|
248
249
|
assertionFn.skip = skip;
|
|
@@ -387,6 +388,61 @@ class Assertions {
|
|
|
387
388
|
}
|
|
388
389
|
});
|
|
389
390
|
|
|
391
|
+
this.like = withSkip((actual, selector, message) => {
|
|
392
|
+
if (!experiments.likeAssertion) {
|
|
393
|
+
fail(new AssertionError({
|
|
394
|
+
assertion: 'like',
|
|
395
|
+
improperUsage: true,
|
|
396
|
+
message: 'You must enable the `likeAssertion` experiment in order to use `t.like()`'
|
|
397
|
+
}));
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
if (!checkMessage('like', message)) {
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (!isLikeSelector(selector)) {
|
|
406
|
+
fail(new AssertionError({
|
|
407
|
+
assertion: 'like',
|
|
408
|
+
improperUsage: true,
|
|
409
|
+
message: '`t.like()` selector must be a non-empty object',
|
|
410
|
+
values: [formatWithLabel('Called with:', selector)]
|
|
411
|
+
}));
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
let comparable;
|
|
416
|
+
try {
|
|
417
|
+
comparable = selectComparable(actual, selector);
|
|
418
|
+
} catch (error) {
|
|
419
|
+
if (error === CIRCULAR_SELECTOR) {
|
|
420
|
+
fail(new AssertionError({
|
|
421
|
+
assertion: 'like',
|
|
422
|
+
improperUsage: true,
|
|
423
|
+
message: '`t.like()` selector must not contain circular references',
|
|
424
|
+
values: [formatWithLabel('Called with:', selector)]
|
|
425
|
+
}));
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
throw error;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const result = concordance.compare(comparable, selector, concordanceOptions);
|
|
433
|
+
if (result.pass) {
|
|
434
|
+
pass();
|
|
435
|
+
} else {
|
|
436
|
+
const actualDescriptor = result.actual || concordance.describe(comparable, concordanceOptions);
|
|
437
|
+
const expectedDescriptor = result.expected || concordance.describe(selector, concordanceOptions);
|
|
438
|
+
fail(new AssertionError({
|
|
439
|
+
assertion: 'like',
|
|
440
|
+
message,
|
|
441
|
+
values: [formatDescriptorDiff(actualDescriptor, expectedDescriptor)]
|
|
442
|
+
}));
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
|
|
390
446
|
this.throws = withSkip((...args) => {
|
|
391
447
|
// Since arrow functions do not support 'arguments', we are using rest
|
|
392
448
|
// operator, so we can determine the total number of arguments passed
|
|
@@ -491,16 +547,16 @@ class Assertions {
|
|
|
491
547
|
savedError,
|
|
492
548
|
values: [formatWithLabel(`${wasReturned ? 'Returned promise' : 'Promise'} resolved with:`, value)]
|
|
493
549
|
});
|
|
494
|
-
},
|
|
550
|
+
}, error => {
|
|
495
551
|
assertExpectations({
|
|
496
552
|
assertion: 'throwsAsync',
|
|
497
|
-
actual:
|
|
553
|
+
actual: error,
|
|
498
554
|
expectations,
|
|
499
555
|
message,
|
|
500
556
|
prefix: `${wasReturned ? 'Returned promise' : 'Promise'} rejected with`,
|
|
501
557
|
savedError
|
|
502
558
|
});
|
|
503
|
-
return
|
|
559
|
+
return error;
|
|
504
560
|
});
|
|
505
561
|
|
|
506
562
|
pending(intermediate);
|
package/lib/cli.js
CHANGED
|
@@ -120,7 +120,7 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
120
120
|
})
|
|
121
121
|
.command('* [<pattern>...]', 'Run tests', yargs => yargs.options(FLAGS).positional('pattern', {
|
|
122
122
|
array: true,
|
|
123
|
-
describe: 'Glob patterns to select what test files to run. Leave empty if you want AVA to run all test files instead',
|
|
123
|
+
describe: 'Glob patterns to select what test files to run. Leave empty if you want AVA to run all test files instead. Add a colon and specify line numbers of specific tests to run',
|
|
124
124
|
type: 'string'
|
|
125
125
|
}))
|
|
126
126
|
.command(
|
|
@@ -143,7 +143,7 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
143
143
|
}
|
|
144
144
|
}).positional('pattern', {
|
|
145
145
|
demand: true,
|
|
146
|
-
describe: 'Glob patterns to select a single test file to debug',
|
|
146
|
+
describe: 'Glob patterns to select a single test file to debug. Add a colon and specify line numbers of specific tests to run',
|
|
147
147
|
type: 'string'
|
|
148
148
|
}),
|
|
149
149
|
argv => {
|
|
@@ -163,6 +163,7 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
163
163
|
})
|
|
164
164
|
.example('$0')
|
|
165
165
|
.example('$0 test.js')
|
|
166
|
+
.example('$0 test.js:4,7-9')
|
|
166
167
|
.help();
|
|
167
168
|
|
|
168
169
|
const combined = {...conf};
|
|
@@ -258,14 +259,14 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
258
259
|
|
|
259
260
|
const ciParallelVars = require('ci-parallel-vars');
|
|
260
261
|
const Api = require('./api');
|
|
261
|
-
const
|
|
262
|
-
const MiniReporter = require('./reporters/mini');
|
|
262
|
+
const DefaultReporter = require('./reporters/default');
|
|
263
263
|
const TapReporter = require('./reporters/tap');
|
|
264
264
|
const Watcher = require('./watcher');
|
|
265
265
|
const normalizeExtensions = require('./extensions');
|
|
266
|
-
const {normalizeGlobs,
|
|
266
|
+
const {normalizeGlobs, normalizePattern} = require('./globs');
|
|
267
267
|
const normalizeNodeArguments = require('./node-arguments');
|
|
268
268
|
const validateEnvironmentVariables = require('./environment-variables');
|
|
269
|
+
const {splitPatternAndLineNumbers} = require('./line-numbers');
|
|
269
270
|
const providerManager = require('./provider-manager');
|
|
270
271
|
|
|
271
272
|
let pkg;
|
|
@@ -349,7 +350,12 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
349
350
|
const match = combined.match === '' ? [] : arrify(combined.match);
|
|
350
351
|
|
|
351
352
|
const input = debug ? debug.files : (argv.pattern || []);
|
|
352
|
-
const filter =
|
|
353
|
+
const filter = input
|
|
354
|
+
.map(pattern => splitPatternAndLineNumbers(pattern))
|
|
355
|
+
.map(({pattern, ...rest}) => ({
|
|
356
|
+
pattern: normalizePattern(path.relative(projectDir, path.resolve(process.cwd(), pattern))),
|
|
357
|
+
...rest
|
|
358
|
+
}));
|
|
353
359
|
|
|
354
360
|
const api = new Api({
|
|
355
361
|
cacheEnabled: combined.cache !== false,
|
|
@@ -384,25 +390,32 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
384
390
|
reportStream: process.stdout,
|
|
385
391
|
stdStream: process.stderr
|
|
386
392
|
});
|
|
387
|
-
} else if (debug !== null || combined.verbose || isCi || !process.stdout.isTTY) {
|
|
388
|
-
reporter = new VerboseReporter({
|
|
389
|
-
projectDir,
|
|
390
|
-
reportStream: process.stdout,
|
|
391
|
-
stdStream: process.stderr,
|
|
392
|
-
watching: combined.watch
|
|
393
|
-
});
|
|
394
393
|
} else {
|
|
395
|
-
reporter = new
|
|
394
|
+
reporter = new DefaultReporter({
|
|
396
395
|
projectDir,
|
|
397
396
|
reportStream: process.stdout,
|
|
398
397
|
stdStream: process.stderr,
|
|
399
|
-
watching: combined.watch
|
|
398
|
+
watching: combined.watch,
|
|
399
|
+
verbose: debug !== null || combined.verbose || isCi || !process.stdout.isTTY
|
|
400
400
|
});
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
api.on('run', plan => {
|
|
404
404
|
reporter.startRun(plan);
|
|
405
405
|
|
|
406
|
+
if (process.env.AVA_EMIT_RUN_STATUS_OVER_IPC === 'I\'ll find a payphone baby / Take some time to talk to you') {
|
|
407
|
+
if (process.versions.node >= '12.16.0') {
|
|
408
|
+
plan.status.on('stateChange', evt => {
|
|
409
|
+
process.send(evt);
|
|
410
|
+
});
|
|
411
|
+
} else {
|
|
412
|
+
const v8 = require('v8');
|
|
413
|
+
plan.status.on('stateChange', evt => {
|
|
414
|
+
process.send([...v8.serialize(evt)]);
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
406
419
|
plan.status.on('stateChange', evt => {
|
|
407
420
|
if (evt.type === 'interrupt') {
|
|
408
421
|
reporter.endRun();
|
package/lib/code-excerpt.js
CHANGED
package/lib/fork.js
CHANGED
|
@@ -48,7 +48,9 @@ module.exports = (file, options, execArgv = process.execArgv) => {
|
|
|
48
48
|
let forcedExit = false;
|
|
49
49
|
const send = evt => {
|
|
50
50
|
if (subprocess.connected && !finished && !forcedExit) {
|
|
51
|
-
subprocess.send({ava: evt})
|
|
51
|
+
subprocess.send({ava: evt}, () => {
|
|
52
|
+
// Disregard errors.
|
|
53
|
+
});
|
|
52
54
|
}
|
|
53
55
|
};
|
|
54
56
|
|
package/lib/globs.js
CHANGED
|
@@ -4,7 +4,7 @@ const globby = require('globby');
|
|
|
4
4
|
const ignoreByDefault = require('ignore-by-default');
|
|
5
5
|
const picomatch = require('picomatch');
|
|
6
6
|
const slash = require('slash');
|
|
7
|
-
const
|
|
7
|
+
const providerManager = require('./provider-manager');
|
|
8
8
|
|
|
9
9
|
const defaultIgnorePatterns = [...ignoreByDefault.directories(), '**/node_modules'];
|
|
10
10
|
const defaultPicomatchIgnorePatterns = [
|
|
@@ -23,23 +23,27 @@ const defaultIgnoredByWatcherPatterns = [
|
|
|
23
23
|
|
|
24
24
|
const buildExtensionPattern = extensions => extensions.length === 1 ? extensions[0] : `{${extensions.join(',')}}`;
|
|
25
25
|
|
|
26
|
-
function
|
|
26
|
+
function normalizePattern(pattern) {
|
|
27
27
|
// Always use `/` in patterns, harmonizing matching across platforms
|
|
28
28
|
if (process.platform === 'win32') {
|
|
29
|
-
|
|
29
|
+
pattern = slash(pattern);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
32
|
+
if (pattern.startsWith('./')) {
|
|
33
|
+
return pattern.slice(2);
|
|
34
|
+
}
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
if (pattern.startsWith('!./')) {
|
|
37
|
+
return `!${pattern.slice(3)}`;
|
|
38
|
+
}
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
return pattern;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
exports.normalizePattern = normalizePattern;
|
|
44
|
+
|
|
45
|
+
function normalizePatterns(patterns) {
|
|
46
|
+
return patterns.map(pattern => normalizePattern(pattern));
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
exports.normalizePatterns = normalizePatterns;
|
|
@@ -85,7 +89,7 @@ function normalizeGlobs({extensions, files: filePatterns, ignoredByWatcher: igno
|
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
for (const {level, main} of providers) {
|
|
88
|
-
if (level >=
|
|
92
|
+
if (level >= providerManager.levels.pathRewrites) {
|
|
89
93
|
({filePatterns, ignoredByWatcherPatterns} = main.updateGlobs({filePatterns, ignoredByWatcherPatterns}));
|
|
90
94
|
}
|
|
91
95
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
function isLikeSelector(selector) {
|
|
3
|
+
return selector !== null &&
|
|
4
|
+
typeof selector === 'object' &&
|
|
5
|
+
Reflect.getPrototypeOf(selector) === Object.prototype &&
|
|
6
|
+
Reflect.ownKeys(selector).length > 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
exports.isLikeSelector = isLikeSelector;
|
|
10
|
+
|
|
11
|
+
const CIRCULAR_SELECTOR = new Error('Encountered a circular selector');
|
|
12
|
+
exports.CIRCULAR_SELECTOR = CIRCULAR_SELECTOR;
|
|
13
|
+
|
|
14
|
+
function selectComparable(lhs, selector, circular = new Set()) {
|
|
15
|
+
if (circular.has(selector)) {
|
|
16
|
+
throw CIRCULAR_SELECTOR;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
circular.add(selector);
|
|
20
|
+
|
|
21
|
+
if (lhs === null || typeof lhs !== 'object') {
|
|
22
|
+
return lhs;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const comparable = {};
|
|
26
|
+
for (const [key, rhs] of Object.entries(selector)) {
|
|
27
|
+
if (isLikeSelector(rhs)) {
|
|
28
|
+
comparable[key] = selectComparable(Reflect.get(lhs, key), rhs, circular);
|
|
29
|
+
} else {
|
|
30
|
+
comparable[key] = Reflect.get(lhs, key);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return comparable;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
exports.selectComparable = selectComparable;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const picomatch = require('picomatch');
|
|
4
|
+
const flatten = require('lodash/flatten');
|
|
5
|
+
|
|
6
|
+
const NUMBER_REGEX = /^\d+$/;
|
|
7
|
+
const RANGE_REGEX = /^(?<startGroup>\d+)-(?<endGroup>\d+)$/;
|
|
8
|
+
const LINE_NUMBERS_REGEX = /^(?:\d+(?:-\d+)?,?)+$/;
|
|
9
|
+
const DELIMITER = ':';
|
|
10
|
+
|
|
11
|
+
const distinctArray = array => [...new Set(array)];
|
|
12
|
+
const sortNumbersAscending = array => {
|
|
13
|
+
const sorted = [...array];
|
|
14
|
+
sorted.sort((a, b) => a - b);
|
|
15
|
+
return sorted;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const parseNumber = string => Number.parseInt(string, 10);
|
|
19
|
+
const removeAllWhitespace = string => string.replace(/\s/g, '');
|
|
20
|
+
const range = (start, end) => new Array(end - start + 1).fill(start).map((element, index) => element + index);
|
|
21
|
+
|
|
22
|
+
const parseLineNumbers = suffix => sortNumbersAscending(distinctArray(flatten(
|
|
23
|
+
suffix.split(',').map(part => {
|
|
24
|
+
if (NUMBER_REGEX.test(part)) {
|
|
25
|
+
return parseNumber(part);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const {groups: {startGroup, endGroup}} = RANGE_REGEX.exec(part);
|
|
29
|
+
const start = parseNumber(startGroup);
|
|
30
|
+
const end = parseNumber(endGroup);
|
|
31
|
+
|
|
32
|
+
if (start > end) {
|
|
33
|
+
return range(end, start);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return range(start, end);
|
|
37
|
+
})
|
|
38
|
+
)));
|
|
39
|
+
|
|
40
|
+
function splitPatternAndLineNumbers(pattern) {
|
|
41
|
+
const parts = pattern.split(DELIMITER);
|
|
42
|
+
if (parts.length === 1) {
|
|
43
|
+
return {pattern, lineNumbers: null};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const suffix = removeAllWhitespace(parts.pop());
|
|
47
|
+
if (!LINE_NUMBERS_REGEX.test(suffix)) {
|
|
48
|
+
return {pattern, lineNumbers: null};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return {pattern: parts.join(DELIMITER), lineNumbers: parseLineNumbers(suffix)};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
exports.splitPatternAndLineNumbers = splitPatternAndLineNumbers;
|
|
55
|
+
|
|
56
|
+
function getApplicableLineNumbers(normalizedFilePath, filter) {
|
|
57
|
+
return sortNumbersAscending(distinctArray(flatten(
|
|
58
|
+
filter
|
|
59
|
+
.filter(({pattern, lineNumbers}) => lineNumbers && picomatch.isMatch(normalizedFilePath, pattern))
|
|
60
|
+
.map(({lineNumbers}) => lineNumbers)
|
|
61
|
+
)));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
exports.getApplicableLineNumbers = getApplicableLineNumbers;
|
package/lib/load-config.js
CHANGED
|
@@ -7,7 +7,7 @@ const pkgConf = require('pkg-conf');
|
|
|
7
7
|
|
|
8
8
|
const NO_SUCH_FILE = Symbol('no ava.config.js file');
|
|
9
9
|
const MISSING_DEFAULT_EXPORT = Symbol('missing default export');
|
|
10
|
-
const EXPERIMENTS = new Set();
|
|
10
|
+
const EXPERIMENTS = new Set(['likeAssertion', 'reverseTeardowns']);
|
|
11
11
|
|
|
12
12
|
// *Very* rudimentary support for loading ava.config.js files containing an `export default` statement.
|
|
13
13
|
const evaluateJsConfig = configFile => {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const StackUtils = require('stack-utils');
|
|
3
|
+
|
|
4
|
+
const stackUtils = new StackUtils({
|
|
5
|
+
ignoredPackages: [
|
|
6
|
+
'@ava/babel',
|
|
7
|
+
'@ava/require-precompiled',
|
|
8
|
+
'@ava/typescript',
|
|
9
|
+
'append-transform',
|
|
10
|
+
'ava',
|
|
11
|
+
'empower-core',
|
|
12
|
+
'esm',
|
|
13
|
+
'nyc'
|
|
14
|
+
],
|
|
15
|
+
internals: [
|
|
16
|
+
// AVA internals, which ignoredPackages don't ignore when we run our own unit tests.
|
|
17
|
+
/\/ava\/(?:lib\/|lib\/worker\/)?[\w-]+\.js:\d+:\d+\)?$/,
|
|
18
|
+
// Only ignore Node.js internals that really are not useful for debugging.
|
|
19
|
+
...StackUtils.nodeInternals().filter(regexp => !/\(internal/.test(regexp.source)),
|
|
20
|
+
/\(internal\/process\/task_queues\.js:\d+:\d+\)$/,
|
|
21
|
+
/\(internal\/modules\/cjs\/.+?\.js:\d+:\d+\)$/,
|
|
22
|
+
/async Promise\.all \(index/,
|
|
23
|
+
/new Promise \(<anonymous>\)/
|
|
24
|
+
]
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
/*
|
|
28
|
+
* Given a string value of the format generated for the `stack` property of a
|
|
29
|
+
* V8 error object, return a string that contains only stack frame information
|
|
30
|
+
* for frames that have relevance to the consumer.
|
|
31
|
+
*
|
|
32
|
+
* For example, given the following string value:
|
|
33
|
+
*
|
|
34
|
+
* ```
|
|
35
|
+
* Error
|
|
36
|
+
* at inner (/home/ava/ex.js:7:12)
|
|
37
|
+
* at /home/ava/ex.js:12:5
|
|
38
|
+
* at outer (/home/ava/ex.js:13:4)
|
|
39
|
+
* at Object.<anonymous> (/home/ava/ex.js:14:3)
|
|
40
|
+
* at Module._compile (module.js:570:32)
|
|
41
|
+
* at Object.Module._extensions..js (module.js:579:10)
|
|
42
|
+
* at Module.load (module.js:487:32)
|
|
43
|
+
* at tryModuleLoad (module.js:446:12)
|
|
44
|
+
* at Function.Module._load (module.js:438:3)
|
|
45
|
+
* at Module.runMain (module.js:604:10)
|
|
46
|
+
* ```
|
|
47
|
+
*
|
|
48
|
+
* ...this function returns the following string value:
|
|
49
|
+
*
|
|
50
|
+
* ```
|
|
51
|
+
* inner (/home/ava/ex.js:7:12)
|
|
52
|
+
* /home/ava/ex.js:12:5
|
|
53
|
+
* outer (/home/ava/ex.js:13:4)
|
|
54
|
+
* Object.<anonymous> (/home/ava/ex.js:14:3)
|
|
55
|
+
* Module._compile (module.js:570:32)
|
|
56
|
+
* Object.Module._extensions..js (module.js:579:10)
|
|
57
|
+
* Module.load (module.js:487:32)
|
|
58
|
+
* tryModuleLoad (module.js:446:12)
|
|
59
|
+
* Function.Module._load (module.js:438:3)
|
|
60
|
+
* Module.runMain (module.js:604:10)
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
module.exports = stack => {
|
|
64
|
+
if (!stack) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return stackUtils.clean(stack)
|
|
69
|
+
.trim()
|
|
70
|
+
.split('\n')
|
|
71
|
+
.map(line => line.trim())
|
|
72
|
+
.filter(line => line !== '');
|
|
73
|
+
};
|