ava 2.0.0 → 2.4.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.
@@ -4,15 +4,34 @@ const normalizeExtensions = require('./lib/extensions');
4
4
  const {hasExtension, normalizeGlobs, classify} = require('./lib/globs');
5
5
  const loadConfig = require('./lib/load-config');
6
6
 
7
- const cache = new Map();
7
+ const configCache = new Map();
8
+ const helperCache = new Map();
8
9
 
9
- function load(projectDir) {
10
- if (cache.has(projectDir)) {
11
- return cache.get(projectDir);
10
+ function load(projectDir, overrides) {
11
+ const cacheKey = `${JSON.stringify(overrides)}\n${projectDir}`;
12
+ if (helperCache.has(cacheKey)) {
13
+ return helperCache.get(cacheKey);
14
+ }
15
+
16
+ let conf;
17
+ let babelConfig;
18
+ if (configCache.has(projectDir)) {
19
+ ({conf, babelConfig} = configCache.get(projectDir));
20
+ } else {
21
+ conf = loadConfig({resolveFrom: projectDir});
22
+ babelConfig = babelPipeline.validate(conf.babel);
23
+ configCache.set(projectDir, {conf, babelConfig});
24
+ }
25
+
26
+ if (overrides) {
27
+ conf = {...conf, ...overrides};
28
+ if (overrides.extensions) {
29
+ // Ignore extensions from the Babel config. Assume all extensions are
30
+ // provided in the override.
31
+ babelConfig = null;
32
+ }
12
33
  }
13
34
 
14
- const conf = loadConfig(projectDir);
15
- const babelConfig = babelPipeline.validate(conf.babel);
16
35
  const extensions = normalizeExtensions(conf.extensions || [], babelConfig);
17
36
  const globs = {cwd: projectDir, ...normalizeGlobs(conf.files, conf.helpers, conf.sources, extensions.all)};
18
37
 
@@ -30,7 +49,7 @@ function load(projectDir) {
30
49
  return classify(`${importPath}.${globs.extensions[0]}`, globs);
31
50
  }
32
51
  });
33
- cache.set(projectDir, helper);
52
+ helperCache.set(cacheKey, helper);
34
53
  return helper;
35
54
  }
36
55
 
package/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ /// <reference types="node"/>
2
+
1
3
  export interface ObservableLike {
2
4
  subscribe(observer: (value: unknown) => void): void;
3
5
  [Symbol.observable](): ObservableLike;
@@ -23,6 +25,13 @@ export type ThrowsExpectation = {
23
25
  name?: string;
24
26
  };
25
27
 
28
+ export type CommitDiscardOptions = {
29
+ /**
30
+ * Whether the logs should be included in those of the parent test.
31
+ */
32
+ retainLogs?: boolean
33
+ }
34
+
26
35
  /** Options that can be passed to the `t.snapshot()` assertion. */
27
36
  export type SnapshotOptions = {
28
37
  /** If provided and not an empty string, used to select the snapshot to compare the `expected` value against. */
@@ -351,7 +360,7 @@ export interface TruthyAssertion {
351
360
  }
352
361
 
353
362
  /** The `t` value passed to test & hook implementations. */
354
- export interface ExecutionContext<Context = {}> extends Assertions {
363
+ export interface ExecutionContext<Context = unknown> extends Assertions {
355
364
  /** Test context, shared with hooks. */
356
365
  context: Context;
357
366
 
@@ -361,6 +370,7 @@ export interface ExecutionContext<Context = {}> extends Assertions {
361
370
  log: LogFn;
362
371
  plan: PlanFn;
363
372
  timeout: TimeoutFn;
373
+ try: TryFn<Context>;
364
374
  }
365
375
 
366
376
  export interface LogFn {
@@ -390,8 +400,71 @@ export interface TimeoutFn {
390
400
  (ms: number): void;
391
401
  }
392
402
 
403
+ export interface TryFn<Context = unknown> {
404
+ /**
405
+ * Requires opt-in. Attempt to run some assertions. The result must be explicitly committed or discarded or else
406
+ * the test will fail. A macro may be provided. The title may help distinguish attempts from
407
+ * one another.
408
+ */
409
+ <Args extends any[]>(title: string, fn: EitherMacro<Args, Context>, ...args: Args): Promise<TryResult>;
410
+
411
+ /**
412
+ * Requires opt-in. Attempt to run some assertions. The result must be explicitly committed or discarded or else
413
+ * the test will fail. A macro may be provided. The title may help distinguish attempts from
414
+ * one another.
415
+ */
416
+ <Args extends any[]>(title: string, fn: [EitherMacro<Args, Context>, ...EitherMacro<Args, Context>[]], ...args: Args): Promise<TryResult[]>;
417
+
418
+ /**
419
+ * Requires opt-in. Attempt to run some assertions. The result must be explicitly committed or discarded or else
420
+ * the test will fail. A macro may be provided.
421
+ */
422
+ <Args extends any[]>(fn: EitherMacro<Args, Context>, ...args: Args): Promise<TryResult>;
423
+
424
+ /**
425
+ * Requires opt-in. Attempt to run some assertions. The result must be explicitly committed or discarded or else
426
+ * the test will fail. A macro may be provided.
427
+ */
428
+ <Args extends any[]>(fn: [EitherMacro<Args, Context>, ...EitherMacro<Args, Context>[]], ...args: Args): Promise<TryResult[]>;
429
+ }
430
+
431
+ export interface AssertionError extends Error {}
432
+
433
+ export interface TryResult {
434
+ /**
435
+ * Title of the attempt, helping you tell attempts aparts.
436
+ */
437
+ title: string;
438
+
439
+ /**
440
+ * Indicates whether all assertions passed, or at least one failed.
441
+ */
442
+ passed: boolean;
443
+
444
+ /**
445
+ * Errors raised for each failed assertion.
446
+ */
447
+ errors: AssertionError[];
448
+
449
+ /**
450
+ * Logs created during the attempt using `t.log()`. Contains formatted values.
451
+ */
452
+ logs: string[];
453
+
454
+ /**
455
+ * Commit the attempt. Counts as one assertion for the plan count. If the
456
+ * attempt failed, calling this will also cause your test to fail.
457
+ */
458
+ commit(options?: CommitDiscardOptions): void;
459
+
460
+ /**
461
+ * Discard the attempt.
462
+ */
463
+ discard(options?: CommitDiscardOptions): void;
464
+ }
465
+
393
466
  /** The `t` value passed to implementations for tests & hooks declared with the `.cb` modifier. */
394
- export interface CbExecutionContext<Context = {}> extends ExecutionContext<Context> {
467
+ export interface CbExecutionContext<Context = unknown> extends ExecutionContext<Context> {
395
468
  /**
396
469
  * End the test. If `error` is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) the test or hook
397
470
  * will fail.
@@ -400,14 +473,14 @@ export interface CbExecutionContext<Context = {}> extends ExecutionContext<Conte
400
473
  }
401
474
 
402
475
  export type ImplementationResult = PromiseLike<void> | ObservableLike | void;
403
- export type Implementation<Context = {}> = (t: ExecutionContext<Context>) => ImplementationResult;
404
- export type CbImplementation<Context = {}> = (t: CbExecutionContext<Context>) => ImplementationResult;
476
+ export type Implementation<Context = unknown> = (t: ExecutionContext<Context>) => ImplementationResult;
477
+ export type CbImplementation<Context = unknown> = (t: CbExecutionContext<Context>) => ImplementationResult;
405
478
 
406
479
  /** A reusable test or hook implementation. */
407
- export type UntitledMacro<Args extends any[], Context = {}> = (t: ExecutionContext<Context>, ...args: Args) => ImplementationResult;
480
+ export type UntitledMacro<Args extends any[], Context = unknown> = (t: ExecutionContext<Context>, ...args: Args) => ImplementationResult;
408
481
 
409
482
  /** A reusable test or hook implementation. */
410
- export type Macro<Args extends any[], Context = {}> = UntitledMacro<Args, Context> & {
483
+ export type Macro<Args extends any[], Context = unknown> = UntitledMacro<Args, Context> & {
411
484
  /**
412
485
  * Implement this function to generate a test (or hook) title whenever this macro is used. `providedTitle` contains
413
486
  * the title provided when the test or hook was declared. Also receives the remaining test arguments.
@@ -421,10 +494,10 @@ export type EitherMacro<Args extends any[], Context> = Macro<Args, Context> | Un
421
494
  export type OneOrMoreMacros<Args extends any[], Context> = EitherMacro<Args, Context> | [EitherMacro<Args, Context>, ...EitherMacro<Args, Context>[]];
422
495
 
423
496
  /** A reusable test or hook implementation, for tests & hooks declared with the `.cb` modifier. */
424
- export type UntitledCbMacro<Args extends any[], Context = {}> = (t: CbExecutionContext<Context>, ...args: Args) => ImplementationResult
497
+ export type UntitledCbMacro<Args extends any[], Context = unknown> = (t: CbExecutionContext<Context>, ...args: Args) => ImplementationResult
425
498
 
426
499
  /** A reusable test or hook implementation, for tests & hooks declared with the `.cb` modifier. */
427
- export type CbMacro<Args extends any[], Context = {}> = UntitledCbMacro<Args, Context> & {
500
+ export type CbMacro<Args extends any[], Context = unknown> = UntitledCbMacro<Args, Context> & {
428
501
  title?: (providedTitle: string | undefined, ...args: Args) => string;
429
502
  }
430
503
 
@@ -433,7 +506,7 @@ export type EitherCbMacro<Args extends any[], Context> = CbMacro<Args, Context>
433
506
  /** Alias for a single macro, or an array of macros, used for tests & hooks declared with the `.cb` modifier. */
434
507
  export type OneOrMoreCbMacros<Args extends any[], Context> = EitherCbMacro<Args, Context> | [EitherCbMacro<Args, Context>, ...EitherCbMacro<Args, Context>[]];
435
508
 
436
- export interface TestInterface<Context = {}> {
509
+ export interface TestInterface<Context = unknown> {
437
510
  /** Declare a concurrent test. */
438
511
  (title: string, implementation: Implementation<Context>): void;
439
512
 
@@ -470,7 +543,7 @@ export interface TestInterface<Context = {}> {
470
543
  meta: MetaInterface;
471
544
  }
472
545
 
473
- export interface AfterInterface<Context = {}> {
546
+ export interface AfterInterface<Context = unknown> {
474
547
  /** Declare a hook that is run once, after all tests have passed. */
475
548
  (implementation: Implementation<Context>): void;
476
549
 
@@ -492,7 +565,7 @@ export interface AfterInterface<Context = {}> {
492
565
  skip: HookSkipInterface<Context>;
493
566
  }
494
567
 
495
- export interface AlwaysInterface<Context = {}> {
568
+ export interface AlwaysInterface<Context = unknown> {
496
569
  /** Declare a hook that is run once, after all tests are done. */
497
570
  (implementation: Implementation<Context>): void;
498
571
 
@@ -511,7 +584,7 @@ export interface AlwaysInterface<Context = {}> {
511
584
  skip: HookSkipInterface<Context>;
512
585
  }
513
586
 
514
- export interface BeforeInterface<Context = {}> {
587
+ export interface BeforeInterface<Context = unknown> {
515
588
  /** Declare a hook that is run once, before all tests. */
516
589
  (implementation: Implementation<Context>): void;
517
590
 
@@ -530,7 +603,7 @@ export interface BeforeInterface<Context = {}> {
530
603
  skip: HookSkipInterface<Context>;
531
604
  }
532
605
 
533
- export interface CbInterface<Context = {}> {
606
+ export interface CbInterface<Context = unknown> {
534
607
  /** Declare a test that must call `t.end()` when it's done. */
535
608
  (title: string, implementation: CbImplementation<Context>): void;
536
609
 
@@ -553,7 +626,7 @@ export interface CbInterface<Context = {}> {
553
626
  skip: CbSkipInterface<Context>;
554
627
  }
555
628
 
556
- export interface CbFailingInterface<Context = {}> {
629
+ export interface CbFailingInterface<Context = unknown> {
557
630
  /** Declare a test that must call `t.end()` when it's done. The test is expected to fail. */
558
631
  (title: string, implementation: CbImplementation<Context>): void;
559
632
 
@@ -573,7 +646,7 @@ export interface CbFailingInterface<Context = {}> {
573
646
  skip: CbSkipInterface<Context>;
574
647
  }
575
648
 
576
- export interface CbOnlyInterface<Context = {}> {
649
+ export interface CbOnlyInterface<Context = unknown> {
577
650
  /**
578
651
  * Declare a test that must call `t.end()` when it's done. Only this test and others declared with `.only()` are run.
579
652
  */
@@ -592,7 +665,7 @@ export interface CbOnlyInterface<Context = {}> {
592
665
  <T extends any[]>(macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;
593
666
  }
594
667
 
595
- export interface CbSkipInterface<Context = {}> {
668
+ export interface CbSkipInterface<Context = unknown> {
596
669
  /** Skip this test. */
597
670
  (title: string, implementation: CbImplementation<Context>): void;
598
671
 
@@ -603,7 +676,7 @@ export interface CbSkipInterface<Context = {}> {
603
676
  <T extends any[]>(macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;
604
677
  }
605
678
 
606
- export interface FailingInterface<Context = {}> {
679
+ export interface FailingInterface<Context = unknown> {
607
680
  /** Declare a concurrent test. The test is expected to fail. */
608
681
  (title: string, implementation: Implementation<Context>): void;
609
682
 
@@ -623,7 +696,7 @@ export interface FailingInterface<Context = {}> {
623
696
  skip: SkipInterface<Context>;
624
697
  }
625
698
 
626
- export interface HookCbInterface<Context = {}> {
699
+ export interface HookCbInterface<Context = unknown> {
627
700
  /** Declare a hook that must call `t.end()` when it's done. */
628
701
  (implementation: CbImplementation<Context>): void;
629
702
 
@@ -644,7 +717,7 @@ export interface HookCbInterface<Context = {}> {
644
717
  skip: HookCbSkipInterface<Context>;
645
718
  }
646
719
 
647
- export interface HookCbSkipInterface<Context = {}> {
720
+ export interface HookCbSkipInterface<Context = unknown> {
648
721
  /** Skip this hook. */
649
722
  (implementation: CbImplementation<Context>): void;
650
723
 
@@ -658,7 +731,7 @@ export interface HookCbSkipInterface<Context = {}> {
658
731
  <T extends any[]>(macros: OneOrMoreCbMacros<T, Context>, ...rest: T): void;
659
732
  }
660
733
 
661
- export interface HookSkipInterface<Context = {}> {
734
+ export interface HookSkipInterface<Context = unknown> {
662
735
  /** Skip this hook. */
663
736
  (implementation: Implementation<Context>): void;
664
737
 
@@ -672,7 +745,7 @@ export interface HookSkipInterface<Context = {}> {
672
745
  <T extends any[]>(macros: OneOrMoreMacros<T, Context>, ...rest: T): void;
673
746
  }
674
747
 
675
- export interface OnlyInterface<Context = {}> {
748
+ export interface OnlyInterface<Context = unknown> {
676
749
  /** Declare a test. Only this test and others declared with `.only()` are run. */
677
750
  (title: string, implementation: Implementation<Context>): void;
678
751
 
@@ -689,7 +762,7 @@ export interface OnlyInterface<Context = {}> {
689
762
  <T extends any[]>(macros: OneOrMoreMacros<T, Context>, ...rest: T): void;
690
763
  }
691
764
 
692
- export interface SerialInterface<Context = {}> {
765
+ export interface SerialInterface<Context = unknown> {
693
766
  /** Declare a serial test. */
694
767
  (title: string, implementation: Implementation<Context>): void;
695
768
 
@@ -724,7 +797,7 @@ export interface SerialInterface<Context = {}> {
724
797
  todo: TodoDeclaration;
725
798
  }
726
799
 
727
- export interface SkipInterface<Context = {}> {
800
+ export interface SkipInterface<Context = unknown> {
728
801
  /** Skip this test. */
729
802
  (title: string, implementation: Implementation<Context>): void;
730
803
 
package/lib/api.js CHANGED
@@ -51,6 +51,7 @@ class Api extends Emittery {
51
51
  }
52
52
 
53
53
  async run(files = [], runtimeOptions = {}) {
54
+ let setupOrGlobError;
54
55
  files = files.map(file => path.resolve(this.options.resolveTestsFrom, file));
55
56
 
56
57
  const apiOptions = this.options;
@@ -106,9 +107,12 @@ class Api extends Emittery {
106
107
  }
107
108
  };
108
109
 
110
+ let precompiler;
111
+ let helpers;
112
+
109
113
  try {
110
- const precompiler = await this._setupPrecompiler();
111
- let helpers = [];
114
+ precompiler = await this._setupPrecompiler(); // eslint-disable-line require-atomic-updates
115
+ helpers = [];
112
116
  if (files.length === 0 || precompiler.enabled) {
113
117
  let found;
114
118
  if (precompiler.enabled) {
@@ -122,7 +126,12 @@ class Api extends Emittery {
122
126
  ({tests: files} = found);
123
127
  }
124
128
  }
129
+ } catch (error) {
130
+ files = [];
131
+ setupOrGlobError = error;
132
+ }
125
133
 
134
+ try {
126
135
  if (this.options.parallelRuns) {
127
136
  const {currentIndex, totalRuns} = this.options.parallelRuns;
128
137
  const fileCount = files.length;
@@ -140,6 +149,7 @@ class Api extends Emittery {
140
149
 
141
150
  await this.emit('run', {
142
151
  clearLogOnNextRun: runtimeOptions.clearLogOnNextRun === true,
152
+ experiments: Object.keys(apiOptions.experiments),
143
153
  failFastEnabled: failFast,
144
154
  filePathPrefix: commonPathPrefix(files),
145
155
  files,
@@ -150,6 +160,10 @@ class Api extends Emittery {
150
160
  status: runStatus
151
161
  });
152
162
 
163
+ if (setupOrGlobError) {
164
+ throw setupOrGlobError;
165
+ }
166
+
153
167
  // Bail out early if no files were found.
154
168
  if (files.length === 0) {
155
169
  return runStatus;
@@ -310,7 +324,7 @@ class Api extends Emittery {
310
324
  const forkExecArgv = execArgv.slice();
311
325
  let flagName = '--inspect';
312
326
  const oldValue = forkExecArgv[inspectArgIndex];
313
- if (oldValue.indexOf('brk') > 0) {
327
+ if (oldValue.includes('brk')) {
314
328
  flagName += '-brk';
315
329
  }
316
330
 
package/lib/assert.js CHANGED
@@ -53,25 +53,21 @@ class AssertionError extends Error {
53
53
  // Reserved for power-assert statements
54
54
  this.statements = [];
55
55
 
56
- if (opts.stack) {
57
- this.stack = opts.stack;
56
+ if (opts.savedError) {
57
+ this.savedError = opts.savedError;
58
58
  } else {
59
- const limitBefore = Error.stackTraceLimit;
60
- Error.stackTraceLimit = Infinity;
61
- Error.captureStackTrace(this);
62
- Error.stackTraceLimit = limitBefore;
59
+ this.savedError = getErrorWithLongStackTrace();
63
60
  }
64
61
  }
65
62
  }
66
63
  exports.AssertionError = AssertionError;
67
64
 
68
- function getStack() {
65
+ function getErrorWithLongStackTrace() {
69
66
  const limitBefore = Error.stackTraceLimit;
70
67
  Error.stackTraceLimit = Infinity;
71
- const obj = {};
72
- Error.captureStackTrace(obj, getStack);
68
+ const err = new Error();
73
69
  Error.stackTraceLimit = limitBefore;
74
- return obj.stack;
70
+ return err;
75
71
  }
76
72
 
77
73
  function validateExpectations(assertion, expectations, numArgs) { // eslint-disable-line complexity
@@ -143,12 +139,12 @@ function validateExpectations(assertion, expectations, numArgs) { // eslint-disa
143
139
 
144
140
  // Note: this function *must* throw exceptions, since it can be used
145
141
  // as part of a pending assertion for promises.
146
- function assertExpectations({assertion, actual, expectations, message, prefix, stack}) {
142
+ function assertExpectations({assertion, actual, expectations, message, prefix, savedError}) {
147
143
  if (!isError(actual)) {
148
144
  throw new AssertionError({
149
145
  assertion,
150
146
  message,
151
- stack,
147
+ savedError,
152
148
  values: [formatWithLabel(`${prefix} exception that is not an error:`, actual)]
153
149
  });
154
150
  }
@@ -159,7 +155,7 @@ function assertExpectations({assertion, actual, expectations, message, prefix, s
159
155
  throw new AssertionError({
160
156
  assertion,
161
157
  message,
162
- stack,
158
+ savedError,
163
159
  actualStack,
164
160
  values: [
165
161
  formatWithLabel(`${prefix} unexpected exception:`, actual),
@@ -172,7 +168,7 @@ function assertExpectations({assertion, actual, expectations, message, prefix, s
172
168
  throw new AssertionError({
173
169
  assertion,
174
170
  message,
175
- stack,
171
+ savedError,
176
172
  actualStack,
177
173
  values: [
178
174
  formatWithLabel(`${prefix} unexpected exception:`, actual),
@@ -185,7 +181,7 @@ function assertExpectations({assertion, actual, expectations, message, prefix, s
185
181
  throw new AssertionError({
186
182
  assertion,
187
183
  message,
188
- stack,
184
+ savedError,
189
185
  actualStack,
190
186
  values: [
191
187
  formatWithLabel(`${prefix} unexpected exception:`, actual),
@@ -198,7 +194,7 @@ function assertExpectations({assertion, actual, expectations, message, prefix, s
198
194
  throw new AssertionError({
199
195
  assertion,
200
196
  message,
201
- stack,
197
+ savedError,
202
198
  actualStack,
203
199
  values: [
204
200
  formatWithLabel(`${prefix} unexpected exception:`, actual),
@@ -211,7 +207,7 @@ function assertExpectations({assertion, actual, expectations, message, prefix, s
211
207
  throw new AssertionError({
212
208
  assertion,
213
209
  message,
214
- stack,
210
+ savedError,
215
211
  actualStack,
216
212
  values: [
217
213
  formatWithLabel(`${prefix} unexpected exception:`, actual),
@@ -224,7 +220,7 @@ function assertExpectations({assertion, actual, expectations, message, prefix, s
224
220
  throw new AssertionError({
225
221
  assertion,
226
222
  message,
227
- stack,
223
+ savedError,
228
224
  actualStack,
229
225
  values: [
230
226
  formatWithLabel(`${prefix} unexpected exception:`, actual),
@@ -480,14 +476,14 @@ class Assertions {
480
476
  }
481
477
 
482
478
  const handlePromise = (promise, wasReturned) => {
483
- // Record stack before it gets lost in the promise chain.
484
- const stack = getStack();
479
+ // Create an error object to record the stack before it gets lost in the promise chain.
480
+ const savedError = getErrorWithLongStackTrace();
485
481
  // Handle "promise like" objects by casting to a real Promise.
486
482
  const intermediate = Promise.resolve(promise).then(value => { // eslint-disable-line promise/prefer-await-to-then
487
483
  throw new AssertionError({
488
484
  assertion: 'throwsAsync',
489
485
  message,
490
- stack,
486
+ savedError,
491
487
  values: [formatWithLabel(`${wasReturned ? 'Returned promise' : 'Promise'} resolved with:`, value)]
492
488
  });
493
489
  }, reason => {
@@ -497,7 +493,7 @@ class Assertions {
497
493
  expectations,
498
494
  message,
499
495
  prefix: `${wasReturned ? 'Returned promise' : 'Promise'} rejected with`,
500
- stack
496
+ savedError
501
497
  });
502
498
  return reason;
503
499
  });
@@ -587,14 +583,14 @@ class Assertions {
587
583
  }
588
584
 
589
585
  const handlePromise = (promise, wasReturned) => {
590
- // Record stack before it gets lost in the promise chain.
591
- const stack = getStack();
586
+ // Create an error object to record the stack before it gets lost in the promise chain.
587
+ const savedError = getErrorWithLongStackTrace();
592
588
  // Handle "promise like" objects by casting to a real Promise.
593
589
  const intermediate = Promise.resolve(promise).then(noop, error => { // eslint-disable-line promise/prefer-await-to-then
594
590
  throw new AssertionError({
595
591
  assertion: 'notThrowsAsync',
596
592
  message,
597
- actualStack: stack,
593
+ savedError,
598
594
  values: [formatWithLabel(`${wasReturned ? 'Returned promise' : 'Promise'} rejected with:`, error)]
599
595
  });
600
596
  });
@@ -58,6 +58,26 @@ function validate(conf) {
58
58
  };
59
59
  }
60
60
 
61
+ function enableParserPlugins(api) {
62
+ api.assertVersion(7);
63
+
64
+ return {
65
+ name: 'ava-babel-pipeline-enable-parser-plugins',
66
+ manipulateOptions(_, parserOpts) {
67
+ parserOpts.plugins.push(
68
+ 'asyncGenerators',
69
+ 'bigInt',
70
+ 'classPrivateProperties',
71
+ 'classProperties',
72
+ 'dynamicImport',
73
+ 'numericSeparator',
74
+ 'objectRestSpread',
75
+ 'optionalCatchBinding'
76
+ );
77
+ }
78
+ };
79
+ }
80
+
61
81
  // Compare actual values rather than file paths, which should be
62
82
  // more reliable.
63
83
  function makeValueChecker(ref) {
@@ -118,7 +138,12 @@ function hashPartialTestConfig({babelrc, config, options: {plugins, presets}}, p
118
138
  inputs.push(config, stripBomBuf(fs.readFileSync(config)));
119
139
  }
120
140
 
121
- for (const {file: {resolved: filename}} of [...plugins, ...presets]) {
141
+ for (const item of [...plugins, ...presets]) {
142
+ if (!item.file) {
143
+ continue;
144
+ }
145
+
146
+ const {file: {resolved: filename}} = item;
122
147
  if (pluginAndPresetHashes.has(filename)) {
123
148
  inputs.push(pluginAndPresetHashes.get(filename));
124
149
  continue;
@@ -168,9 +193,6 @@ function build(projectDir, cacheDir, userOptions, compileEnhancements) {
168
193
  const pluginAndPresetHashes = new Map();
169
194
 
170
195
  const ensureStage4 = wantsStage4(userOptions, projectDir);
171
- const containsAsyncGenerators = makeValueChecker('@babel/plugin-syntax-async-generators');
172
- const containsObjectRestSpread = makeValueChecker('@babel/plugin-syntax-object-rest-spread');
173
- const containsOptionalCatchBinding = makeValueChecker('@babel/plugin-syntax-optional-catch-binding');
174
196
  const containsStage4 = makeValueChecker('../stage-4');
175
197
  const containsTransformTestFiles = makeValueChecker('@ava/babel-preset-transform-test-files');
176
198
 
@@ -193,17 +215,7 @@ function build(projectDir, cacheDir, userOptions, compileEnhancements) {
193
215
  }
194
216
 
195
217
  const {options: testOptions} = partialTestConfig;
196
- if (!testOptions.plugins.some(containsAsyncGenerators)) { // TODO: Remove once Babel can parse this syntax unaided.
197
- testOptions.plugins.unshift(createConfigItem('@babel/plugin-syntax-async-generators', 'plugin'));
198
- }
199
-
200
- if (!testOptions.plugins.some(containsObjectRestSpread)) { // TODO: Remove once Babel can parse this syntax unaided.
201
- testOptions.plugins.unshift(createConfigItem('@babel/plugin-syntax-object-rest-spread', 'plugin'));
202
- }
203
-
204
- if (!testOptions.plugins.some(containsOptionalCatchBinding)) { // TODO: Remove once Babel can parse this syntax unaided.
205
- testOptions.plugins.unshift(createConfigItem('@babel/plugin-syntax-optional-catch-binding', 'plugin'));
206
- }
218
+ testOptions.plugins.push(enableParserPlugins);
207
219
 
208
220
  if (ensureStage4 && !testOptions.presets.some(containsStage4)) {
209
221
  // Apply last.
package/lib/cli.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict';
2
2
  const path = require('path');
3
+ const fs = require('fs');
3
4
  const del = require('del');
4
5
  const updateNotifier = require('update-notifier');
5
6
  const figures = require('figures');
@@ -7,7 +8,7 @@ const arrify = require('arrify');
7
8
  const meow = require('meow');
8
9
  const Promise = require('bluebird');
9
10
  const isCi = require('is-ci');
10
- const loadConf = require('./load-config');
11
+ const loadConfig = require('./load-config');
11
12
 
12
13
  // Bluebird specific
13
14
  Promise.longStackTraces();
@@ -18,10 +19,17 @@ function exit(message) {
18
19
  }
19
20
 
20
21
  exports.run = async () => { // eslint-disable-line complexity
22
+ const {flags: {config: configFile}} = meow({ // Process the --config flag first
23
+ autoHelp: false, // --help should get picked up by the next meow invocation.
24
+ flags: {
25
+ config: {type: 'string'}
26
+ }
27
+ });
28
+
21
29
  let conf = {};
22
30
  let confError = null;
23
31
  try {
24
- conf = loadConf();
32
+ conf = loadConfig({configFile});
25
33
  } catch (error) {
26
34
  confError = error;
27
35
  }
@@ -35,7 +43,7 @@ exports.run = async () => { // eslint-disable-line complexity
35
43
  --match, -m Only run tests with matching title (Can be repeated)
36
44
  --update-snapshots, -u Update snapshots
37
45
  --fail-fast Stop after first test failure
38
- --timeout, -T Set global timeout
46
+ --timeout, -T Set global timeout (milliseconds or human-readable, e.g. 10s, 2m)
39
47
  --serial, -s Run tests serially
40
48
  --concurrency, -c Max number of test files running at the same time (Default: CPU cores)
41
49
  --verbose, -v Enable verbose output
@@ -43,6 +51,8 @@ exports.run = async () => { // eslint-disable-line complexity
43
51
  --color Force color output
44
52
  --no-color Disable color output
45
53
  --reset-cache Reset AVA's compilation cache and exit
54
+ --config JavaScript file for AVA to read its config from, instead of using package.json
55
+ or ava.config.js files
46
56
 
47
57
  Examples
48
58
  ava
@@ -204,12 +214,31 @@ exports.run = async () => { // eslint-disable-line complexity
204
214
 
205
215
  const match = arrify(conf.match);
206
216
  const resolveTestsFrom = cli.input.length === 0 ? projectDir : process.cwd();
217
+
218
+ const files = cli.input.map(file => path.relative(resolveTestsFrom, path.resolve(process.cwd(), file)));
219
+
220
+ for (const file of cli.input) {
221
+ try {
222
+ const stats = fs.statSync(file);
223
+ if (!stats.isFile()) {
224
+ exit(`${file} is not a test file.`);
225
+ }
226
+ } catch (error) {
227
+ if (error.code === 'ENOENT') {
228
+ exit(`${file} does not exist.`);
229
+ } else {
230
+ exit(`Error accessing ${file}\n\n${chalk.gray((error && error.stack) || error)}`);
231
+ }
232
+ }
233
+ }
234
+
207
235
  const api = new Api({
208
236
  babelConfig,
209
237
  cacheEnabled: conf.cache !== false,
210
238
  color: conf.color,
211
239
  compileEnhancements: conf.compileEnhancements !== false,
212
240
  concurrency: conf.concurrency ? parseInt(conf.concurrency, 10) : 0,
241
+ experiments: conf.nonSemVerExperiments,
213
242
  extensions,
214
243
  failFast: conf.failFast,
215
244
  failWithoutAssertions: conf.failWithoutAssertions !== false,
@@ -259,8 +288,6 @@ exports.run = async () => { // eslint-disable-line complexity
259
288
  });
260
289
  });
261
290
 
262
- const files = cli.input.map(file => path.relative(resolveTestsFrom, path.resolve(process.cwd(), file)));
263
-
264
291
  if (conf.watch) {
265
292
  const watcher = new Watcher({
266
293
  api,