ava 3.0.0 → 3.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.
- package/eslint-plugin-helper.js +23 -7
- package/index.d.ts +1 -8
- package/lib/api.js +6 -3
- package/lib/assert.js +2 -2
- package/lib/beautify-stack.js +2 -2
- package/lib/cli.js +27 -8
- package/lib/extensions.js +4 -4
- package/lib/globs.js +8 -1
- package/lib/load-config.js +5 -34
- package/lib/provider-manager.js +53 -0
- package/lib/test.js +6 -8
- package/lib/watcher.js +25 -2
- package/lib/worker/subprocess.js +42 -11
- package/package.json +9 -10
- package/lib/babel-manager.js +0 -33
package/eslint-plugin-helper.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
const babelManager = require('./lib/babel-manager');
|
|
3
2
|
const normalizeExtensions = require('./lib/extensions');
|
|
4
3
|
const {classify, hasExtension, isHelperish, matches, normalizeFileForMatching, normalizeGlobs, normalizePatterns} = require('./lib/globs');
|
|
5
4
|
const loadConfig = require('./lib/load-config');
|
|
5
|
+
const providerManager = require('./lib/provider-manager');
|
|
6
6
|
|
|
7
7
|
const configCache = new Map();
|
|
8
8
|
const helperCache = new Map();
|
|
@@ -14,22 +14,37 @@ function load(projectDir, overrides) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
let conf;
|
|
17
|
-
let
|
|
17
|
+
let providers;
|
|
18
18
|
if (configCache.has(projectDir)) {
|
|
19
|
-
({conf,
|
|
19
|
+
({conf, providers} = configCache.get(projectDir));
|
|
20
20
|
} else {
|
|
21
21
|
conf = loadConfig({resolveFrom: projectDir});
|
|
22
22
|
|
|
23
|
+
providers = [];
|
|
23
24
|
if (Reflect.has(conf, 'babel')) {
|
|
24
|
-
|
|
25
|
+
const {level, main} = providerManager.babel(projectDir);
|
|
26
|
+
providers.push({
|
|
27
|
+
level,
|
|
28
|
+
main: main({config: conf.babel}),
|
|
29
|
+
type: 'babel'
|
|
30
|
+
});
|
|
25
31
|
}
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+
if (Reflect.has(conf, 'typescript')) {
|
|
34
|
+
const {level, main} = providerManager.typescript(projectDir);
|
|
35
|
+
providers.push({
|
|
36
|
+
level,
|
|
37
|
+
main: main({config: conf.typescript}),
|
|
38
|
+
type: 'typescript'
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
configCache.set(projectDir, {conf, providers});
|
|
28
43
|
}
|
|
29
44
|
|
|
30
45
|
const extensions = overrides && overrides.extensions ?
|
|
31
46
|
normalizeExtensions(overrides.extensions) :
|
|
32
|
-
normalizeExtensions(conf.extensions,
|
|
47
|
+
normalizeExtensions(conf.extensions, providers);
|
|
33
48
|
|
|
34
49
|
let helperPatterns = [];
|
|
35
50
|
if (overrides && overrides.helpers !== undefined) {
|
|
@@ -44,7 +59,8 @@ function load(projectDir, overrides) {
|
|
|
44
59
|
cwd: projectDir,
|
|
45
60
|
...normalizeGlobs({
|
|
46
61
|
extensions,
|
|
47
|
-
files: overrides && overrides.files ? overrides.files : conf.files
|
|
62
|
+
files: overrides && overrides.files ? overrides.files : conf.files,
|
|
63
|
+
providers
|
|
48
64
|
})
|
|
49
65
|
};
|
|
50
66
|
|
package/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
/// <reference types="node"/>
|
|
2
|
-
|
|
3
1
|
export interface Subscribable {
|
|
4
2
|
subscribe(observer: {
|
|
5
3
|
error(err: any): void,
|
|
@@ -247,16 +245,11 @@ export interface SnapshotAssertion {
|
|
|
247
245
|
}
|
|
248
246
|
|
|
249
247
|
export interface ThrowsAssertion {
|
|
250
|
-
/**
|
|
251
|
-
* Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value.
|
|
252
|
-
*/
|
|
253
|
-
<ThrownError extends Error>(fn: () => any, expectations?: null, message?: string): ThrownError;
|
|
254
|
-
|
|
255
248
|
/**
|
|
256
249
|
* Assert that the function throws [an error](https://www.npmjs.com/package/is-error). If so, returns the error value.
|
|
257
250
|
* The error must satisfy all expectations.
|
|
258
251
|
*/
|
|
259
|
-
<ThrownError extends Error>(fn: () => any, expectations
|
|
252
|
+
<ThrownError extends Error>(fn: () => any, expectations?: ThrowsExpectation | null, message?: string): ThrownError;
|
|
260
253
|
|
|
261
254
|
/** Skip this assertion. */
|
|
262
255
|
skip(fn: () => any, expectations?: any, message?: string): void;
|
package/lib/api.js
CHANGED
|
@@ -185,8 +185,11 @@ class Api extends Emittery {
|
|
|
185
185
|
}
|
|
186
186
|
});
|
|
187
187
|
|
|
188
|
-
const {
|
|
189
|
-
const
|
|
188
|
+
const {providers = []} = this.options;
|
|
189
|
+
const providerStates = (await Promise.all(providers.map(async ({type, main}) => {
|
|
190
|
+
const state = await main.compile({cacheDir, files: testFiles});
|
|
191
|
+
return state === null ? null : {type, state};
|
|
192
|
+
}))).filter(state => state !== null);
|
|
190
193
|
|
|
191
194
|
// Resolve the correct concurrency value.
|
|
192
195
|
let concurrency = Math.min(os.cpus().length, isCi ? 2 : Infinity);
|
|
@@ -208,7 +211,7 @@ class Api extends Emittery {
|
|
|
208
211
|
|
|
209
212
|
const options = {
|
|
210
213
|
...apiOptions,
|
|
211
|
-
|
|
214
|
+
providerStates,
|
|
212
215
|
recordNewSnapshots: !isCi,
|
|
213
216
|
// If we're looking for matches, run every single test process in exclusive-only mode
|
|
214
217
|
runOnlyExclusive: apiOptions.match.length > 0 || runtimeOptions.runOnlyExclusive === true
|
package/lib/assert.js
CHANGED
|
@@ -73,7 +73,7 @@ function getErrorWithLongStackTrace() {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function validateExpectations(assertion, expectations, numArgs) { // eslint-disable-line complexity
|
|
76
|
-
if (numArgs === 1 || expectations === null) {
|
|
76
|
+
if (numArgs === 1 || expectations === null || expectations === undefined) {
|
|
77
77
|
expectations = {};
|
|
78
78
|
} else if (
|
|
79
79
|
typeof expectations === 'function' ||
|
|
@@ -85,7 +85,7 @@ function validateExpectations(assertion, expectations, numArgs) { // eslint-disa
|
|
|
85
85
|
) {
|
|
86
86
|
throw new AssertionError({
|
|
87
87
|
assertion,
|
|
88
|
-
message: `The second argument to \`t.${assertion}()\` must be an expectation object or \`
|
|
88
|
+
message: `The second argument to \`t.${assertion}()\` must be an expectation object, \`null\` or \`undefined\``,
|
|
89
89
|
values: [formatWithLabel('Called with:', expectations)]
|
|
90
90
|
});
|
|
91
91
|
} else {
|
package/lib/beautify-stack.js
CHANGED
|
@@ -7,8 +7,8 @@ const debug = require('debug')('ava');
|
|
|
7
7
|
let ignoreStackLines = [];
|
|
8
8
|
|
|
9
9
|
const avaInternals = /\/ava\/(?:lib\/|lib\/worker\/)?[\w-]+\.js:\d+:\d+\)?$/;
|
|
10
|
-
const avaDependencies = /\/node_modules\/(?:@ava\/babel|append-transform|empower-core|nyc|require-precompiled|(?:ava\/node_modules\/)?(?:babel-runtime|core-js))\//;
|
|
11
|
-
const stackFrameLine = /^.+( \(.+:\d+:\d+\)|:\d+:\d+)$/;
|
|
10
|
+
const avaDependencies = /\/node_modules\/(?:@ava\/babel|@ava\/require-precompiled|append-transform|empower-core|nyc|require-precompiled|(?:ava\/node_modules\/)?(?:babel-runtime|core-js))\//;
|
|
11
|
+
const stackFrameLine = /^.+( \(.+:\d+:\d+\)|:\d+:\d+)$/;
|
|
12
12
|
|
|
13
13
|
if (!debug.enabled) {
|
|
14
14
|
ignoreStackLines = StackUtils.nodeInternals();
|
package/lib/cli.js
CHANGED
|
@@ -256,11 +256,11 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
256
256
|
const MiniReporter = require('./reporters/mini');
|
|
257
257
|
const TapReporter = require('./reporters/tap');
|
|
258
258
|
const Watcher = require('./watcher');
|
|
259
|
-
const babelManager = require('./babel-manager');
|
|
260
259
|
const normalizeExtensions = require('./extensions');
|
|
261
260
|
const {normalizeGlobs, normalizePatterns} = require('./globs');
|
|
262
261
|
const normalizeNodeArguments = require('./node-arguments');
|
|
263
262
|
const validateEnvironmentVariables = require('./environment-variables');
|
|
263
|
+
const providerManager = require('./provider-manager');
|
|
264
264
|
|
|
265
265
|
let pkg;
|
|
266
266
|
try {
|
|
@@ -279,10 +279,28 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
279
279
|
js: defaultModuleType
|
|
280
280
|
};
|
|
281
281
|
|
|
282
|
-
|
|
282
|
+
const providers = [];
|
|
283
283
|
if (Reflect.has(conf, 'babel')) {
|
|
284
284
|
try {
|
|
285
|
-
|
|
285
|
+
const {level, main} = providerManager.babel(projectDir);
|
|
286
|
+
providers.push({
|
|
287
|
+
level,
|
|
288
|
+
main: main({config: conf.babel}),
|
|
289
|
+
type: 'babel'
|
|
290
|
+
});
|
|
291
|
+
} catch (error) {
|
|
292
|
+
exit(error.message);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (Reflect.has(conf, 'typescript')) {
|
|
297
|
+
try {
|
|
298
|
+
const {level, main} = providerManager.typescript(projectDir);
|
|
299
|
+
providers.push({
|
|
300
|
+
level,
|
|
301
|
+
main: main({config: conf.typescript}),
|
|
302
|
+
type: 'typescript'
|
|
303
|
+
});
|
|
286
304
|
} catch (error) {
|
|
287
305
|
exit(error.message);
|
|
288
306
|
}
|
|
@@ -297,14 +315,14 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
297
315
|
|
|
298
316
|
let extensions;
|
|
299
317
|
try {
|
|
300
|
-
extensions = normalizeExtensions(conf.extensions,
|
|
318
|
+
extensions = normalizeExtensions(conf.extensions, providers);
|
|
301
319
|
} catch (error) {
|
|
302
320
|
exit(error.message);
|
|
303
321
|
}
|
|
304
322
|
|
|
305
323
|
let globs;
|
|
306
324
|
try {
|
|
307
|
-
globs = normalizeGlobs({files: conf.files, ignoredByWatcher: conf.ignoredByWatcher, extensions});
|
|
325
|
+
globs = normalizeGlobs({files: conf.files, ignoredByWatcher: conf.ignoredByWatcher, extensions, providers});
|
|
308
326
|
} catch (error) {
|
|
309
327
|
exit(error.message);
|
|
310
328
|
}
|
|
@@ -328,22 +346,22 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
328
346
|
const filter = normalizePatterns(input.map(fileOrPattern => path.relative(projectDir, path.resolve(process.cwd(), fileOrPattern))));
|
|
329
347
|
|
|
330
348
|
const api = new Api({
|
|
331
|
-
babelProvider,
|
|
332
349
|
cacheEnabled: combined.cache !== false,
|
|
333
350
|
chalkOptions,
|
|
334
351
|
concurrency: combined.concurrency || 0,
|
|
335
352
|
debug,
|
|
353
|
+
environmentVariables,
|
|
336
354
|
experiments,
|
|
337
355
|
extensions,
|
|
338
356
|
failFast: combined.failFast,
|
|
339
357
|
failWithoutAssertions: combined.failWithoutAssertions !== false,
|
|
340
358
|
globs,
|
|
341
|
-
moduleTypes,
|
|
342
|
-
environmentVariables,
|
|
343
359
|
match,
|
|
360
|
+
moduleTypes,
|
|
344
361
|
nodeArguments,
|
|
345
362
|
parallelRuns,
|
|
346
363
|
projectDir,
|
|
364
|
+
providers,
|
|
347
365
|
ranFromCli: true,
|
|
348
366
|
require: arrify(combined.require),
|
|
349
367
|
serial: combined.serial,
|
|
@@ -393,6 +411,7 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
393
411
|
filter,
|
|
394
412
|
globs,
|
|
395
413
|
projectDir,
|
|
414
|
+
providers,
|
|
396
415
|
reporter
|
|
397
416
|
});
|
|
398
417
|
watcher.observeStdin(process.stdin);
|
package/lib/extensions.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module.exports = (configuredExtensions,
|
|
1
|
+
module.exports = (configuredExtensions, providers = []) => {
|
|
2
2
|
// Combine all extensions possible for testing. Remove duplicate extensions.
|
|
3
3
|
const duplicates = new Set();
|
|
4
4
|
const seen = new Set();
|
|
@@ -16,15 +16,15 @@ module.exports = (configuredExtensions, babelProvider) => {
|
|
|
16
16
|
combine(configuredExtensions);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
combine(
|
|
19
|
+
for (const {main} of providers) {
|
|
20
|
+
combine(main.extensions);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
if (duplicates.size > 0) {
|
|
24
24
|
throw new Error(`Unexpected duplicate extensions in options: '${[...duplicates].join('\', \'')}'.`);
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
// Unless the default was used by
|
|
27
|
+
// Unless the default was used by providers, as long as the extensions aren't explicitly set, set the default.
|
|
28
28
|
if (configuredExtensions === undefined) {
|
|
29
29
|
if (!seen.has('cjs')) {
|
|
30
30
|
seen.add('cjs');
|
package/lib/globs.js
CHANGED
|
@@ -4,6 +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 {levels: providerLevels} = require('./provider-manager');
|
|
7
8
|
|
|
8
9
|
const defaultIgnorePatterns = [...ignoreByDefault.directories(), '**/node_modules'];
|
|
9
10
|
const defaultPicomatchIgnorePatterns = [
|
|
@@ -43,7 +44,7 @@ function normalizePatterns(patterns) {
|
|
|
43
44
|
|
|
44
45
|
exports.normalizePatterns = normalizePatterns;
|
|
45
46
|
|
|
46
|
-
function normalizeGlobs({extensions, files: filePatterns, ignoredByWatcher: ignoredByWatcherPatterns}) {
|
|
47
|
+
function normalizeGlobs({extensions, files: filePatterns, ignoredByWatcher: ignoredByWatcherPatterns, providers}) {
|
|
47
48
|
if (filePatterns !== undefined && (!Array.isArray(filePatterns) || filePatterns.length === 0)) {
|
|
48
49
|
throw new Error('The \'files\' configuration must be an array containing glob patterns.');
|
|
49
50
|
}
|
|
@@ -83,6 +84,12 @@ function normalizeGlobs({extensions, files: filePatterns, ignoredByWatcher: igno
|
|
|
83
84
|
ignoredByWatcherPatterns = [...defaultIgnoredByWatcherPatterns];
|
|
84
85
|
}
|
|
85
86
|
|
|
87
|
+
for (const {level, main} of providers) {
|
|
88
|
+
if (level >= providerLevels.pathRewrites) {
|
|
89
|
+
({filePatterns, ignoredByWatcherPatterns} = main.updateGlobs({filePatterns, ignoredByWatcherPatterns}));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
86
93
|
return {extensions, filePatterns, ignoredByWatcherPatterns};
|
|
87
94
|
}
|
|
88
95
|
|
package/lib/load-config.js
CHANGED
|
@@ -7,42 +7,17 @@ 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(
|
|
11
|
-
|
|
12
|
-
class LegacyCommonJsAccessError extends Error {
|
|
13
|
-
constructor(thing, fileForErrorMessage) {
|
|
14
|
-
super(`${thing} is not available in ${fileForErrorMessage}. Use a .cjs file instead`);
|
|
15
|
-
this.name = 'LegacyCommonJsAccessError';
|
|
16
|
-
}
|
|
17
|
-
}
|
|
10
|
+
const EXPERIMENTS = new Set();
|
|
18
11
|
|
|
19
12
|
// *Very* rudimentary support for loading ava.config.js files containing an `export default` statement.
|
|
20
|
-
const evaluateJsConfig =
|
|
13
|
+
const evaluateJsConfig = configFile => {
|
|
21
14
|
const contents = fs.readFileSync(configFile, 'utf8');
|
|
22
15
|
const script = new vm.Script(`'use strict';(()=>{let __export__;\n${contents.replace(/export default/g, '__export__ =')};return __export__;})()`, {
|
|
23
16
|
filename: configFile,
|
|
24
17
|
lineOffset: -1
|
|
25
18
|
});
|
|
26
19
|
return {
|
|
27
|
-
default: script.
|
|
28
|
-
console,
|
|
29
|
-
process,
|
|
30
|
-
get __dirname() {
|
|
31
|
-
throw new LegacyCommonJsAccessError('__dirname', fileForErrorMessage);
|
|
32
|
-
},
|
|
33
|
-
get __filename() {
|
|
34
|
-
throw new LegacyCommonJsAccessError('__filename', fileForErrorMessage);
|
|
35
|
-
},
|
|
36
|
-
get module() {
|
|
37
|
-
throw new LegacyCommonJsAccessError('module', fileForErrorMessage);
|
|
38
|
-
},
|
|
39
|
-
get exports() {
|
|
40
|
-
throw new LegacyCommonJsAccessError('exports', fileForErrorMessage);
|
|
41
|
-
},
|
|
42
|
-
get require() {
|
|
43
|
-
throw new LegacyCommonJsAccessError('require()', fileForErrorMessage);
|
|
44
|
-
}
|
|
45
|
-
})
|
|
20
|
+
default: script.runInThisContext()
|
|
46
21
|
};
|
|
47
22
|
};
|
|
48
23
|
|
|
@@ -55,17 +30,13 @@ const loadJsConfig = ({projectDir, configFile = path.join(projectDir, 'ava.confi
|
|
|
55
30
|
|
|
56
31
|
let config;
|
|
57
32
|
try {
|
|
58
|
-
({default: config = MISSING_DEFAULT_EXPORT} = evaluateJsConfig(configFile
|
|
33
|
+
({default: config = MISSING_DEFAULT_EXPORT} = evaluateJsConfig(configFile));
|
|
59
34
|
} catch (error) {
|
|
60
35
|
if (error.code === 'ENOENT') {
|
|
61
36
|
return null;
|
|
62
37
|
}
|
|
63
38
|
|
|
64
|
-
|
|
65
|
-
throw error;
|
|
66
|
-
} else {
|
|
67
|
-
throw Object.assign(new Error(`Error loading ${fileForErrorMessage}`), {parent: error});
|
|
68
|
-
}
|
|
39
|
+
throw Object.assign(new Error(`Error loading ${fileForErrorMessage}: ${error.message}`), {parent: error});
|
|
69
40
|
}
|
|
70
41
|
|
|
71
42
|
if (config === MISSING_DEFAULT_EXPORT) {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const pkg = require('../package.json');
|
|
2
|
+
const globs = require('./globs');
|
|
3
|
+
|
|
4
|
+
const levels = {
|
|
5
|
+
ava3: 1,
|
|
6
|
+
pathRewrites: 2
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
exports.levels = levels;
|
|
10
|
+
|
|
11
|
+
const levelsByProtocol = {
|
|
12
|
+
'ava-3': levels.ava3,
|
|
13
|
+
'ava-3.2': levels.pathRewrites
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function load(providerModule, projectDir) {
|
|
17
|
+
const ava = {version: pkg.version};
|
|
18
|
+
const makeProvider = require(providerModule);
|
|
19
|
+
|
|
20
|
+
let fatal;
|
|
21
|
+
let level;
|
|
22
|
+
const provider = makeProvider({
|
|
23
|
+
negotiateProtocol(identifiers, {version}) {
|
|
24
|
+
const [identifier] = identifiers.filter(identifier => Reflect.has(levelsByProtocol, identifier));
|
|
25
|
+
|
|
26
|
+
if (identifier === undefined) {
|
|
27
|
+
fatal = new Error(`This version of AVA (${ava.version}) is not compatible with ${providerModule}@${version}`);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
level = levelsByProtocol[identifier];
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
ava,
|
|
35
|
+
async findFiles({extensions, patterns}) {
|
|
36
|
+
return globs.findFiles({cwd: projectDir, extensions, filePatterns: patterns});
|
|
37
|
+
},
|
|
38
|
+
identifier,
|
|
39
|
+
normalizeGlobPatterns: globs.normalizePatterns,
|
|
40
|
+
projectDir
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
if (fatal) {
|
|
46
|
+
throw fatal;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return {...provider, level};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
exports.babel = projectDir => load('@ava/babel', projectDir);
|
|
53
|
+
exports.typescript = projectDir => load('@ava/typescript', projectDir);
|
package/lib/test.js
CHANGED
|
@@ -69,22 +69,20 @@ class ExecutionContext extends assert.Assertions {
|
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
this.try = async (...attemptArgs) => {
|
|
72
|
-
if (test.experiments.tryAssertion !== true) {
|
|
73
|
-
throw new Error('t.try() is currently an experiment. Opt in by setting `nonSemVerExperiments.tryAssertion` to `true`.');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
72
|
const {args, buildTitle, implementations, receivedImplementationArray} = parseTestArgs(attemptArgs);
|
|
77
73
|
|
|
78
74
|
if (implementations.length === 0) {
|
|
79
75
|
throw new TypeError('Expected an implementation.');
|
|
80
76
|
}
|
|
81
77
|
|
|
82
|
-
const attemptPromises = implementations.map(implementation => {
|
|
78
|
+
const attemptPromises = implementations.map((implementation, index) => {
|
|
83
79
|
let {title, isSet, isValid, isEmpty} = buildTitle(implementation);
|
|
84
80
|
|
|
85
81
|
if (!isSet || isEmpty) {
|
|
86
|
-
title = `${test.title}
|
|
87
|
-
} else if (
|
|
82
|
+
title = `${test.title} ─ attempt ${test.attemptCount + 1 + index}`;
|
|
83
|
+
} else if (isValid) {
|
|
84
|
+
title = `${test.title} ─ ${title}`;
|
|
85
|
+
} else {
|
|
88
86
|
throw new TypeError('`t.try()` titles must be strings'); // Throw synchronously!
|
|
89
87
|
}
|
|
90
88
|
|
|
@@ -594,7 +592,7 @@ class Test {
|
|
|
594
592
|
if (this.metadata.callback) {
|
|
595
593
|
if (returnedObservable || returnedPromise) {
|
|
596
594
|
const asyncType = returnedObservable ? 'observables' : 'promises';
|
|
597
|
-
this.saveFirstError(new Error(`Do not return ${asyncType} from tests declared via \`test.cb(...)
|
|
595
|
+
this.saveFirstError(new Error(`Do not return ${asyncType} from tests declared via \`test.cb(...)\`. Use \`test.cb(...)\` for legacy callback APIs. When using promises, observables or async functions, use \`test(...)\`.`));
|
|
598
596
|
return this.finishPromised();
|
|
599
597
|
}
|
|
600
598
|
|
package/lib/watcher.js
CHANGED
|
@@ -6,6 +6,7 @@ const diff = require('lodash/difference');
|
|
|
6
6
|
const flatten = require('lodash/flatten');
|
|
7
7
|
const chalk = require('./chalk').get();
|
|
8
8
|
const {applyTestFileFilter, classify, getChokidarIgnorePatterns} = require('./globs');
|
|
9
|
+
const {levels: providerLevels} = require('./provider-manager');
|
|
9
10
|
|
|
10
11
|
function rethrowAsync(err) {
|
|
11
12
|
// Don't swallow exceptions. Note that any
|
|
@@ -77,13 +78,14 @@ class TestDependency {
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
class Watcher {
|
|
80
|
-
constructor({api, filter = [], globs, projectDir, reporter}) {
|
|
81
|
+
constructor({api, filter = [], globs, projectDir, providers, reporter}) {
|
|
81
82
|
this.debouncer = new Debouncer(this);
|
|
82
83
|
|
|
83
84
|
this.clearLogOnNextRun = true;
|
|
84
85
|
this.runVector = 0;
|
|
85
86
|
this.previousFiles = [];
|
|
86
87
|
this.globs = {cwd: projectDir, ...globs};
|
|
88
|
+
this.providers = providers.filter(({level}) => level >= providerLevels.pathRewrites);
|
|
87
89
|
this.run = (specificFiles = [], updateSnapshots = false) => {
|
|
88
90
|
const clearLogOnNextRun = this.clearLogOnNextRun && this.runVector > 0;
|
|
89
91
|
if (this.runVector > 0) {
|
|
@@ -190,6 +192,15 @@ class Watcher {
|
|
|
190
192
|
}
|
|
191
193
|
|
|
192
194
|
updateTestDependencies(file, dependencies) {
|
|
195
|
+
// Ensure the rewritten test file path is included in the dependencies,
|
|
196
|
+
// since changes to non-rewritten paths are ignored.
|
|
197
|
+
for (const {main} of this.providers) {
|
|
198
|
+
const rewritten = main.resolveTestFile(file);
|
|
199
|
+
if (!dependencies.includes(rewritten)) {
|
|
200
|
+
dependencies = [rewritten, ...dependencies];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
193
204
|
if (dependencies.length === 0) {
|
|
194
205
|
this.testDependencies = this.testDependencies.filter(dep => dep.file !== file);
|
|
195
206
|
return;
|
|
@@ -358,7 +369,7 @@ class Watcher {
|
|
|
358
369
|
const {dirtyStates} = this;
|
|
359
370
|
this.dirtyStates = {};
|
|
360
371
|
|
|
361
|
-
|
|
372
|
+
let dirtyPaths = Object.keys(dirtyStates).filter(path => {
|
|
362
373
|
if (this.touchedFiles.has(path)) {
|
|
363
374
|
debug('Ignoring known touched file %s', path);
|
|
364
375
|
this.touchedFiles.delete(path);
|
|
@@ -367,6 +378,18 @@ class Watcher {
|
|
|
367
378
|
|
|
368
379
|
return true;
|
|
369
380
|
});
|
|
381
|
+
|
|
382
|
+
for (const {main} of this.providers) {
|
|
383
|
+
dirtyPaths = dirtyPaths.filter(path => {
|
|
384
|
+
if (main.ignoreChange(path)) {
|
|
385
|
+
debug('Ignoring changed file %s', path);
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return true;
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
370
393
|
const dirtyHelpersAndSources = [];
|
|
371
394
|
const dirtyTests = [];
|
|
372
395
|
for (const filePath of dirtyPaths) {
|
package/lib/worker/subprocess.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
+
const {pathToFileURL} = require('url');
|
|
2
3
|
const currentlyUnhandled = require('currently-unhandled')();
|
|
3
4
|
|
|
4
5
|
require('./ensure-forked'); // eslint-disable-line import/no-unassigned-import
|
|
5
6
|
|
|
6
7
|
const ipc = require('./ipc');
|
|
7
8
|
|
|
9
|
+
const supportsESM = async () => {
|
|
10
|
+
try {
|
|
11
|
+
await import('data:text/javascript,');
|
|
12
|
+
return true;
|
|
13
|
+
} catch {}
|
|
14
|
+
|
|
15
|
+
return false;
|
|
16
|
+
};
|
|
17
|
+
|
|
8
18
|
ipc.send({type: 'ready-for-options'});
|
|
9
19
|
ipc.options.then(async options => {
|
|
10
20
|
require('./options').set(options);
|
|
@@ -15,8 +25,8 @@ ipc.options.then(async options => {
|
|
|
15
25
|
global.console = Object.assign(global.console, new console.Console({stdout, stderr, colorMode: true}));
|
|
16
26
|
}
|
|
17
27
|
|
|
18
|
-
const babelManager = require('../babel-manager');
|
|
19
28
|
const nowAndTimers = require('../now-and-timers');
|
|
29
|
+
const providerManager = require('../provider-manager');
|
|
20
30
|
const Runner = require('../runner');
|
|
21
31
|
const serializeError = require('../serialize-error');
|
|
22
32
|
const dependencyTracking = require('./dependency-tracker');
|
|
@@ -102,7 +112,7 @@ ipc.options.then(async options => {
|
|
|
102
112
|
return runner;
|
|
103
113
|
};
|
|
104
114
|
|
|
105
|
-
// Store value
|
|
115
|
+
// Store value to prevent required modules from modifying it.
|
|
106
116
|
const testPath = options.file;
|
|
107
117
|
|
|
108
118
|
// Install basic source map support.
|
|
@@ -118,25 +128,46 @@ ipc.options.then(async options => {
|
|
|
118
128
|
|
|
119
129
|
// Install before processing options.require, so if helpers are added to the
|
|
120
130
|
// require configuration the *compiled* helper will be loaded.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
131
|
+
const {projectDir, providerStates = []} = options;
|
|
132
|
+
const providers = providerStates.map(({type, state}) => {
|
|
133
|
+
if (type === 'babel') {
|
|
134
|
+
const provider = providerManager.babel(projectDir).worker({extensionsToLoadAsModules, state});
|
|
135
|
+
runner.powerAssert = provider.powerAssert;
|
|
136
|
+
return provider;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (type === 'typescript') {
|
|
140
|
+
return providerManager.typescript(projectDir).worker({extensionsToLoadAsModules, state});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return null;
|
|
144
|
+
}).filter(provider => provider !== null);
|
|
127
145
|
|
|
128
146
|
let requireFn = require;
|
|
147
|
+
let isESMSupported;
|
|
129
148
|
const load = async ref => {
|
|
130
149
|
for (const extension of extensionsToLoadAsModules) {
|
|
131
150
|
if (ref.endsWith(`.${extension}`)) {
|
|
132
|
-
|
|
151
|
+
if (typeof isESMSupported !== 'boolean') {
|
|
152
|
+
// Lazily determine support since this prints an experimental warning.
|
|
153
|
+
// eslint-disable-next-line no-await-in-loop
|
|
154
|
+
isESMSupported = await supportsESM();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (isESMSupported) {
|
|
158
|
+
return import(pathToFileURL(ref));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
ipc.send({type: 'internal-error', err: serializeError('Internal runner error', false, new Error('ECMAScript Modules are not supported in this Node.js version.'))});
|
|
133
162
|
exit(1);
|
|
134
163
|
return;
|
|
135
164
|
}
|
|
136
165
|
}
|
|
137
166
|
|
|
138
|
-
|
|
139
|
-
|
|
167
|
+
for (const provider of providers) {
|
|
168
|
+
if (provider.canLoad(ref)) {
|
|
169
|
+
return provider.load(ref, {requireFn});
|
|
170
|
+
}
|
|
140
171
|
}
|
|
141
172
|
|
|
142
173
|
return requireFn(ref);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ava",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Testing can be a drag. AVA helps you get it done.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "avajs/ava",
|
|
@@ -95,8 +95,8 @@
|
|
|
95
95
|
"p-map": "^3.0.0",
|
|
96
96
|
"picomatch": "^2.2.1",
|
|
97
97
|
"pkg-conf": "^3.1.0",
|
|
98
|
-
"plur": "^
|
|
99
|
-
"pretty-ms": "^
|
|
98
|
+
"plur": "^4.0.0",
|
|
99
|
+
"pretty-ms": "^6.0.0",
|
|
100
100
|
"read-pkg": "^5.2.0",
|
|
101
101
|
"resolve-cwd": "^3.0.0",
|
|
102
102
|
"slash": "^3.0.0",
|
|
@@ -106,33 +106,32 @@
|
|
|
106
106
|
"supertap": "^1.0.0",
|
|
107
107
|
"temp-dir": "^2.0.0",
|
|
108
108
|
"trim-off-newlines": "^1.0.1",
|
|
109
|
-
"update-notifier": "^4.
|
|
109
|
+
"update-notifier": "^4.1.0",
|
|
110
110
|
"write-file-atomic": "^3.0.1",
|
|
111
111
|
"yargs": "^15.1.0"
|
|
112
112
|
},
|
|
113
113
|
"devDependencies": {
|
|
114
|
-
"@ava/babel": "^1.0.
|
|
115
|
-
"@
|
|
114
|
+
"@ava/babel": "^1.0.1",
|
|
115
|
+
"@sinonjs/fake-timers": "^6.0.0",
|
|
116
116
|
"ansi-escapes": "^4.3.0",
|
|
117
117
|
"delay": "^4.3.0",
|
|
118
118
|
"esm": "^3.2.25",
|
|
119
119
|
"execa": "^4.0.0",
|
|
120
120
|
"get-stream": "^5.1.0",
|
|
121
|
-
"lolex": "^5.1.2",
|
|
122
121
|
"p-event": "^4.1.0",
|
|
123
122
|
"proxyquire": "^2.1.3",
|
|
124
123
|
"react": "^16.12.0",
|
|
125
124
|
"react-test-renderer": "^16.12.0",
|
|
126
125
|
"replace-string": "^3.0.0",
|
|
127
|
-
"sinon": "^8.1.
|
|
126
|
+
"sinon": "^8.1.1",
|
|
128
127
|
"source-map-fixtures": "^2.1.0",
|
|
129
128
|
"tap": "^14.10.6",
|
|
130
129
|
"temp-write": "^4.0.0",
|
|
131
|
-
"tempy": "^0.
|
|
130
|
+
"tempy": "^0.4.0",
|
|
132
131
|
"touch": "^3.1.0",
|
|
133
132
|
"ts-node": "^8.6.2",
|
|
134
133
|
"typescript": "^3.7.5",
|
|
135
|
-
"xo": "^0.
|
|
134
|
+
"xo": "^0.26.1",
|
|
136
135
|
"zen-observable": "^0.8.15"
|
|
137
136
|
},
|
|
138
137
|
"xo": {
|
package/lib/babel-manager.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
const pkg = require('../package.json');
|
|
2
|
-
const globs = require('./globs');
|
|
3
|
-
|
|
4
|
-
module.exports = ({projectDir}) => {
|
|
5
|
-
const ava = {version: pkg.version};
|
|
6
|
-
const makeProvider = require('@ava/babel');
|
|
7
|
-
|
|
8
|
-
let fatal;
|
|
9
|
-
const provider = makeProvider({
|
|
10
|
-
negotiateProtocol(identifiers, {version}) {
|
|
11
|
-
if (!identifiers.includes('ava-3')) {
|
|
12
|
-
fatal = new Error(`This version of AVA (${ava.version}) is not compatible with@ava/babel@${version}`);
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
ava,
|
|
18
|
-
async findFiles({extensions, patterns}) {
|
|
19
|
-
return globs.findFiles({cwd: projectDir, extensions, filePatterns: patterns});
|
|
20
|
-
},
|
|
21
|
-
identifier: 'ava-3',
|
|
22
|
-
normalizeGlobPatterns: globs.normalizePatterns,
|
|
23
|
-
projectDir
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
if (fatal) {
|
|
29
|
-
throw fatal;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return provider;
|
|
33
|
-
};
|