ava 3.8.1 → 3.10.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/index.d.ts +13 -2
- package/lib/assert.js +89 -14
- package/lib/cli.js +24 -11
- package/lib/code-excerpt.js +1 -1
- package/lib/concordance-options.js +0 -1
- package/lib/like-selector.js +37 -0
- package/lib/line-numbers.js +2 -2
- package/lib/load-config.js +1 -1
- package/lib/reporters/default.js +849 -0
- package/lib/reporters/tap.js +4 -1
- package/lib/runner.js +14 -5
- package/lib/test.js +27 -6
- package/lib/worker/subprocess.js +5 -3
- package/package.json +27 -26
- package/readme.md +1 -1
- package/lib/reporters/mini.js +0 -619
- package/lib/reporters/verbose.js +0 -463
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, any>, 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;
|
|
@@ -342,7 +353,7 @@ export interface TimeoutFn {
|
|
|
342
353
|
* Set a timeout for the test, in milliseconds. The test will fail if the timeout is exceeded.
|
|
343
354
|
* The timeout is reset each time an assertion is made.
|
|
344
355
|
*/
|
|
345
|
-
(ms: number): void;
|
|
356
|
+
(ms: number, message?: string): void;
|
|
346
357
|
}
|
|
347
358
|
|
|
348
359
|
export interface TeardownFn {
|
|
@@ -422,7 +433,7 @@ export interface CbExecutionContext<Context = unknown> extends ExecutionContext<
|
|
|
422
433
|
end(error?: any): void;
|
|
423
434
|
}
|
|
424
435
|
|
|
425
|
-
export type ImplementationResult = PromiseLike<void> | Subscribable | void;
|
|
436
|
+
export type ImplementationResult = PromiseLike<void> | Subscribable | void; // eslint-disable-line @typescript-eslint/no-invalid-void-type
|
|
426
437
|
export type Implementation<Context = unknown> = (t: ExecutionContext<Context>) => ImplementationResult;
|
|
427
438
|
export type CbImplementation<Context = unknown> = (t: CbExecutionContext<Context>) => ImplementationResult;
|
|
428
439
|
|
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)
|
|
@@ -64,6 +64,21 @@ class AssertionError extends Error {
|
|
|
64
64
|
}
|
|
65
65
|
exports.AssertionError = AssertionError;
|
|
66
66
|
|
|
67
|
+
function checkAssertionMessage(assertion, message) {
|
|
68
|
+
if (typeof message === 'undefined' || typeof message === 'string') {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return new AssertionError({
|
|
73
|
+
assertion,
|
|
74
|
+
improperUsage: true,
|
|
75
|
+
message: 'The assertion message must be a string',
|
|
76
|
+
values: [formatWithLabel('Called with:', message)]
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
exports.checkAssertionMessage = checkAssertionMessage;
|
|
81
|
+
|
|
67
82
|
function getErrorWithLongStackTrace() {
|
|
68
83
|
const limitBefore = Error.stackTraceLimit;
|
|
69
84
|
Error.stackTraceLimit = Infinity;
|
|
@@ -242,7 +257,9 @@ class Assertions {
|
|
|
242
257
|
fail = notImplemented,
|
|
243
258
|
skip = notImplemented,
|
|
244
259
|
compareWithSnapshot = notImplemented,
|
|
245
|
-
powerAssert
|
|
260
|
+
powerAssert,
|
|
261
|
+
experiments = {},
|
|
262
|
+
disableSnapshots = false
|
|
246
263
|
} = {}) {
|
|
247
264
|
const withSkip = assertionFn => {
|
|
248
265
|
assertionFn.skip = skip;
|
|
@@ -267,22 +284,16 @@ class Assertions {
|
|
|
267
284
|
});
|
|
268
285
|
|
|
269
286
|
const checkMessage = (assertion, message, powerAssert = false) => {
|
|
270
|
-
|
|
271
|
-
|
|
287
|
+
const result = checkAssertionMessage(assertion, message);
|
|
288
|
+
if (result === true) {
|
|
289
|
+
return this.true;
|
|
272
290
|
}
|
|
273
291
|
|
|
274
|
-
const error = new AssertionError({
|
|
275
|
-
assertion,
|
|
276
|
-
improperUsage: true,
|
|
277
|
-
message: 'The assertion message must be a string',
|
|
278
|
-
values: [formatWithLabel('Called with:', message)]
|
|
279
|
-
});
|
|
280
|
-
|
|
281
292
|
if (powerAssert) {
|
|
282
|
-
throw
|
|
293
|
+
throw result;
|
|
283
294
|
}
|
|
284
295
|
|
|
285
|
-
fail(
|
|
296
|
+
fail(result);
|
|
286
297
|
return false;
|
|
287
298
|
};
|
|
288
299
|
|
|
@@ -387,6 +398,61 @@ class Assertions {
|
|
|
387
398
|
}
|
|
388
399
|
});
|
|
389
400
|
|
|
401
|
+
this.like = withSkip((actual, selector, message) => {
|
|
402
|
+
if (!experiments.likeAssertion) {
|
|
403
|
+
fail(new AssertionError({
|
|
404
|
+
assertion: 'like',
|
|
405
|
+
improperUsage: true,
|
|
406
|
+
message: 'You must enable the `likeAssertion` experiment in order to use `t.like()`'
|
|
407
|
+
}));
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (!checkMessage('like', message)) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (!isLikeSelector(selector)) {
|
|
416
|
+
fail(new AssertionError({
|
|
417
|
+
assertion: 'like',
|
|
418
|
+
improperUsage: true,
|
|
419
|
+
message: '`t.like()` selector must be a non-empty object',
|
|
420
|
+
values: [formatWithLabel('Called with:', selector)]
|
|
421
|
+
}));
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
let comparable;
|
|
426
|
+
try {
|
|
427
|
+
comparable = selectComparable(actual, selector);
|
|
428
|
+
} catch (error) {
|
|
429
|
+
if (error === CIRCULAR_SELECTOR) {
|
|
430
|
+
fail(new AssertionError({
|
|
431
|
+
assertion: 'like',
|
|
432
|
+
improperUsage: true,
|
|
433
|
+
message: '`t.like()` selector must not contain circular references',
|
|
434
|
+
values: [formatWithLabel('Called with:', selector)]
|
|
435
|
+
}));
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
throw error;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
const result = concordance.compare(comparable, selector, concordanceOptions);
|
|
443
|
+
if (result.pass) {
|
|
444
|
+
pass();
|
|
445
|
+
} else {
|
|
446
|
+
const actualDescriptor = result.actual || concordance.describe(comparable, concordanceOptions);
|
|
447
|
+
const expectedDescriptor = result.expected || concordance.describe(selector, concordanceOptions);
|
|
448
|
+
fail(new AssertionError({
|
|
449
|
+
assertion: 'like',
|
|
450
|
+
message,
|
|
451
|
+
values: [formatDescriptorDiff(actualDescriptor, expectedDescriptor)]
|
|
452
|
+
}));
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
|
|
390
456
|
this.throws = withSkip((...args) => {
|
|
391
457
|
// Since arrow functions do not support 'arguments', we are using rest
|
|
392
458
|
// operator, so we can determine the total number of arguments passed
|
|
@@ -634,6 +700,15 @@ class Assertions {
|
|
|
634
700
|
});
|
|
635
701
|
|
|
636
702
|
this.snapshot = withSkip((expected, ...rest) => {
|
|
703
|
+
if (disableSnapshots && experiments.disableSnapshotsInHooks) {
|
|
704
|
+
fail(new AssertionError({
|
|
705
|
+
assertion: 'snapshot',
|
|
706
|
+
message: '`t.snapshot()` can only be used in tests',
|
|
707
|
+
improperUsage: true
|
|
708
|
+
}));
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
|
|
637
712
|
let message;
|
|
638
713
|
let snapshotOptions;
|
|
639
714
|
if (rest.length > 1) {
|
package/lib/cli.js
CHANGED
|
@@ -182,6 +182,10 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
182
182
|
const chalkOptions = {level: combined.color === false ? 0 : require('chalk').level};
|
|
183
183
|
const chalk = require('./chalk').set(chalkOptions);
|
|
184
184
|
|
|
185
|
+
if (combined.updateSnapshots && combined.match) {
|
|
186
|
+
exit('Snapshots cannot be updated when matching specific tests.');
|
|
187
|
+
}
|
|
188
|
+
|
|
185
189
|
if (confError) {
|
|
186
190
|
if (confError.parent) {
|
|
187
191
|
exit(`${confError.message}\n\n${chalk.gray((confError.parent && confError.parent.stack) || confError.parent)}`);
|
|
@@ -259,8 +263,7 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
259
263
|
|
|
260
264
|
const ciParallelVars = require('ci-parallel-vars');
|
|
261
265
|
const Api = require('./api');
|
|
262
|
-
const
|
|
263
|
-
const MiniReporter = require('./reporters/mini');
|
|
266
|
+
const DefaultReporter = require('./reporters/default');
|
|
264
267
|
const TapReporter = require('./reporters/tap');
|
|
265
268
|
const Watcher = require('./watcher');
|
|
266
269
|
const normalizeExtensions = require('./extensions');
|
|
@@ -357,6 +360,9 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
357
360
|
pattern: normalizePattern(path.relative(projectDir, path.resolve(process.cwd(), pattern))),
|
|
358
361
|
...rest
|
|
359
362
|
}));
|
|
363
|
+
if (combined.updateSnapshots && filter.some(condition => condition.lineNumbers !== null)) {
|
|
364
|
+
exit('Snapshots cannot be updated when selecting specific tests by their line number.');
|
|
365
|
+
}
|
|
360
366
|
|
|
361
367
|
const api = new Api({
|
|
362
368
|
cacheEnabled: combined.cache !== false,
|
|
@@ -391,25 +397,32 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
391
397
|
reportStream: process.stdout,
|
|
392
398
|
stdStream: process.stderr
|
|
393
399
|
});
|
|
394
|
-
} else if (debug !== null || combined.verbose || isCi || !process.stdout.isTTY) {
|
|
395
|
-
reporter = new VerboseReporter({
|
|
396
|
-
projectDir,
|
|
397
|
-
reportStream: process.stdout,
|
|
398
|
-
stdStream: process.stderr,
|
|
399
|
-
watching: combined.watch
|
|
400
|
-
});
|
|
401
400
|
} else {
|
|
402
|
-
reporter = new
|
|
401
|
+
reporter = new DefaultReporter({
|
|
403
402
|
projectDir,
|
|
404
403
|
reportStream: process.stdout,
|
|
405
404
|
stdStream: process.stderr,
|
|
406
|
-
watching: combined.watch
|
|
405
|
+
watching: combined.watch,
|
|
406
|
+
verbose: debug !== null || combined.verbose || isCi || !process.stdout.isTTY
|
|
407
407
|
});
|
|
408
408
|
}
|
|
409
409
|
|
|
410
410
|
api.on('run', plan => {
|
|
411
411
|
reporter.startRun(plan);
|
|
412
412
|
|
|
413
|
+
if (process.env.AVA_EMIT_RUN_STATUS_OVER_IPC === 'I\'ll find a payphone baby / Take some time to talk to you') {
|
|
414
|
+
if (process.versions.node >= '12.16.0') {
|
|
415
|
+
plan.status.on('stateChange', evt => {
|
|
416
|
+
process.send(evt);
|
|
417
|
+
});
|
|
418
|
+
} else {
|
|
419
|
+
const v8 = require('v8');
|
|
420
|
+
plan.status.on('stateChange', evt => {
|
|
421
|
+
process.send([...v8.serialize(evt)]);
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
413
426
|
plan.status.on('stateChange', evt => {
|
|
414
427
|
if (evt.type === 'interrupt') {
|
|
415
428
|
reporter.endRun();
|
package/lib/code-excerpt.js
CHANGED
|
@@ -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;
|
package/lib/line-numbers.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const picomatch = require('picomatch');
|
|
4
4
|
const flatten = require('lodash/flatten');
|
|
5
5
|
|
|
6
6
|
const NUMBER_REGEX = /^\d+$/;
|
|
@@ -56,7 +56,7 @@ exports.splitPatternAndLineNumbers = splitPatternAndLineNumbers;
|
|
|
56
56
|
function getApplicableLineNumbers(normalizedFilePath, filter) {
|
|
57
57
|
return sortNumbersAscending(distinctArray(flatten(
|
|
58
58
|
filter
|
|
59
|
-
.filter(({pattern, lineNumbers}) => lineNumbers &&
|
|
59
|
+
.filter(({pattern, lineNumbers}) => lineNumbers && picomatch.isMatch(normalizedFilePath, pattern))
|
|
60
60
|
.map(({lineNumbers}) => lineNumbers)
|
|
61
61
|
)));
|
|
62
62
|
}
|
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(['disableSnapshotsInHooks', '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 => {
|