ava 3.8.2 → 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 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;
@@ -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,6 +3,7 @@ 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 {CIRCULAR_SELECTOR, isLikeSelector, selectComparable} = require('./like-selector');
6
7
  const snapshotManager = require('./snapshot-manager');
7
8
 
8
9
  function formatDescriptorDiff(actualDescriptor, expectedDescriptor, options) {
@@ -241,7 +242,8 @@ class Assertions {
241
242
  fail = notImplemented,
242
243
  skip = notImplemented,
243
244
  compareWithSnapshot = notImplemented,
244
- powerAssert
245
+ powerAssert,
246
+ experiments = {}
245
247
  } = {}) {
246
248
  const withSkip = assertionFn => {
247
249
  assertionFn.skip = skip;
@@ -386,6 +388,61 @@ class Assertions {
386
388
  }
387
389
  });
388
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
+
389
446
  this.throws = withSkip((...args) => {
390
447
  // Since arrow functions do not support 'arguments', we are using rest
391
448
  // operator, so we can determine the total number of arguments passed
package/lib/cli.js CHANGED
@@ -259,8 +259,7 @@ exports.run = async () => { // eslint-disable-line complexity
259
259
 
260
260
  const ciParallelVars = require('ci-parallel-vars');
261
261
  const Api = require('./api');
262
- const VerboseReporter = require('./reporters/verbose');
263
- const MiniReporter = require('./reporters/mini');
262
+ const DefaultReporter = require('./reporters/default');
264
263
  const TapReporter = require('./reporters/tap');
265
264
  const Watcher = require('./watcher');
266
265
  const normalizeExtensions = require('./extensions');
@@ -391,19 +390,13 @@ exports.run = async () => { // eslint-disable-line complexity
391
390
  reportStream: process.stdout,
392
391
  stdStream: process.stderr
393
392
  });
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
393
  } else {
402
- reporter = new MiniReporter({
394
+ reporter = new DefaultReporter({
403
395
  projectDir,
404
396
  reportStream: process.stdout,
405
397
  stdStream: process.stderr,
406
- watching: combined.watch
398
+ watching: combined.watch,
399
+ verbose: debug !== null || combined.verbose || isCi || !process.stdout.isTTY
407
400
  });
408
401
  }
409
402
 
@@ -19,7 +19,7 @@ module.exports = (source, options = {}) => {
19
19
  let contents;
20
20
  try {
21
21
  contents = fs.readFileSync(file, 'utf8');
22
- } catch (_) {
22
+ } catch {
23
23
  return null;
24
24
  }
25
25
 
@@ -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;
@@ -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 => {