ava 3.14.0 → 3.15.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 +134 -29
- package/lib/api.js +1 -3
- package/lib/cli.js +2 -2
- package/lib/load-config.js +117 -16
- package/lib/reporters/tap.js +12 -2
- package/lib/runner.js +21 -4
- package/lib/snapshot-manager.js +38 -1
- package/lib/test.js +4 -0
- package/package.json +12 -10
- package/readme.md +1 -6
package/eslint-plugin-helper.js
CHANGED
|
@@ -1,26 +1,25 @@
|
|
|
1
1
|
'use strict';
|
|
2
|
-
|
|
2
|
+
let isMainThread = true;
|
|
3
|
+
let supportsWorkers = false;
|
|
4
|
+
try {
|
|
5
|
+
({isMainThread} = require('worker_threads'));
|
|
6
|
+
supportsWorkers = true;
|
|
7
|
+
} catch {}
|
|
8
|
+
|
|
3
9
|
const {classify, hasExtension, isHelperish, matches, normalizeFileForMatching, normalizeGlobs, normalizePatterns} = require('./lib/globs');
|
|
4
|
-
const loadConfig = require('./lib/load-config');
|
|
5
|
-
const providerManager = require('./lib/provider-manager');
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
11
|
+
let resolveGlobs;
|
|
12
|
+
let resolveGlobsSync;
|
|
9
13
|
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
14
|
+
if (!supportsWorkers || !isMainThread) {
|
|
15
|
+
const normalizeExtensions = require('./lib/extensions');
|
|
16
|
+
const {loadConfig, loadConfigSync} = require('./lib/load-config');
|
|
17
|
+
const providerManager = require('./lib/provider-manager');
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
let providers;
|
|
18
|
-
if (configCache.has(projectDir)) {
|
|
19
|
-
({conf, providers} = configCache.get(projectDir));
|
|
20
|
-
} else {
|
|
21
|
-
conf = loadConfig({resolveFrom: projectDir});
|
|
19
|
+
const configCache = new Map();
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
const collectProviders = ({conf, projectDir}) => {
|
|
22
|
+
const providers = [];
|
|
24
23
|
if (Reflect.has(conf, 'babel')) {
|
|
25
24
|
const {level, main} = providerManager.babel(projectDir);
|
|
26
25
|
providers.push({
|
|
@@ -39,12 +38,125 @@ function load(projectDir, overrides) {
|
|
|
39
38
|
});
|
|
40
39
|
}
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
return providers;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const buildGlobs = ({conf, providers, projectDir, overrideExtensions, overrideFiles}) => {
|
|
45
|
+
const extensions = overrideExtensions ?
|
|
46
|
+
normalizeExtensions(overrideExtensions) :
|
|
47
|
+
normalizeExtensions(conf.extensions, providers);
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
cwd: projectDir,
|
|
51
|
+
...normalizeGlobs({
|
|
52
|
+
extensions,
|
|
53
|
+
files: overrideFiles ? overrideFiles : conf.files,
|
|
54
|
+
providers
|
|
55
|
+
})
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
resolveGlobsSync = (projectDir, overrideExtensions, overrideFiles) => {
|
|
60
|
+
if (!configCache.has(projectDir)) {
|
|
61
|
+
const conf = loadConfigSync({resolveFrom: projectDir});
|
|
62
|
+
const providers = collectProviders({conf, projectDir});
|
|
63
|
+
configCache.set(projectDir, {conf, providers});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const {conf, providers} = configCache.get(projectDir);
|
|
67
|
+
return buildGlobs({conf, providers, projectDir, overrideExtensions, overrideFiles});
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
resolveGlobs = async (projectDir, overrideExtensions, overrideFiles) => {
|
|
71
|
+
if (!configCache.has(projectDir)) {
|
|
72
|
+
configCache.set(projectDir, loadConfig({resolveFrom: projectDir}).then(conf => { // eslint-disable-line promise/prefer-await-to-then
|
|
73
|
+
const providers = collectProviders({conf, projectDir});
|
|
74
|
+
return {conf, providers};
|
|
75
|
+
}));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const {conf, providers} = await configCache.get(projectDir);
|
|
79
|
+
return buildGlobs({conf, providers, projectDir, overrideExtensions, overrideFiles});
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (supportsWorkers) {
|
|
84
|
+
const v8 = require('v8');
|
|
85
|
+
|
|
86
|
+
const MAX_DATA_LENGTH_EXCLUSIVE = 100 * 1024; // Allocate 100 KiB to exchange globs.
|
|
87
|
+
|
|
88
|
+
if (isMainThread) {
|
|
89
|
+
const {Worker} = require('worker_threads');
|
|
90
|
+
let data;
|
|
91
|
+
let sync;
|
|
92
|
+
let worker;
|
|
93
|
+
|
|
94
|
+
resolveGlobsSync = (projectDir, overrideExtensions, overrideFiles) => {
|
|
95
|
+
if (worker === undefined) {
|
|
96
|
+
const dataBuffer = new SharedArrayBuffer(MAX_DATA_LENGTH_EXCLUSIVE);
|
|
97
|
+
data = new Uint8Array(dataBuffer);
|
|
98
|
+
|
|
99
|
+
const syncBuffer = new SharedArrayBuffer(4);
|
|
100
|
+
sync = new Int32Array(syncBuffer);
|
|
101
|
+
|
|
102
|
+
worker = new Worker(__filename, {
|
|
103
|
+
workerData: {
|
|
104
|
+
dataBuffer,
|
|
105
|
+
syncBuffer,
|
|
106
|
+
firstMessage: {projectDir, overrideExtensions, overrideFiles}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
worker.unref();
|
|
110
|
+
} else {
|
|
111
|
+
worker.postMessage({projectDir, overrideExtensions, overrideFiles});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
Atomics.wait(sync, 0, 0);
|
|
115
|
+
|
|
116
|
+
const byteLength = Atomics.exchange(sync, 0, 0);
|
|
117
|
+
if (byteLength === MAX_DATA_LENGTH_EXCLUSIVE) {
|
|
118
|
+
throw new Error('Globs are over 100 KiB and cannot be resolved');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const globsOrError = v8.deserialize(data.slice(0, byteLength));
|
|
122
|
+
if (globsOrError instanceof Error) {
|
|
123
|
+
throw globsOrError;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return globsOrError;
|
|
127
|
+
};
|
|
128
|
+
} else {
|
|
129
|
+
const {parentPort, workerData} = require('worker_threads');
|
|
130
|
+
const data = new Uint8Array(workerData.dataBuffer);
|
|
131
|
+
const sync = new Int32Array(workerData.syncBuffer);
|
|
132
|
+
|
|
133
|
+
const handleMessage = async ({projectDir, overrideExtensions, overrideFiles}) => {
|
|
134
|
+
let encoded;
|
|
135
|
+
try {
|
|
136
|
+
const globs = await resolveGlobs(projectDir, overrideExtensions, overrideFiles);
|
|
137
|
+
encoded = v8.serialize(globs);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
encoded = v8.serialize(error);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const byteLength = encoded.length < MAX_DATA_LENGTH_EXCLUSIVE ? encoded.copy(data) : MAX_DATA_LENGTH_EXCLUSIVE;
|
|
143
|
+
Atomics.store(sync, 0, byteLength);
|
|
144
|
+
Atomics.notify(sync, 0);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
parentPort.on('message', handleMessage);
|
|
148
|
+
handleMessage(workerData.firstMessage);
|
|
149
|
+
delete workerData.firstMessage;
|
|
43
150
|
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const helperCache = new Map();
|
|
44
154
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
155
|
+
function load(projectDir, overrides) {
|
|
156
|
+
const cacheKey = `${JSON.stringify(overrides)}\n${projectDir}`;
|
|
157
|
+
if (helperCache.has(cacheKey)) {
|
|
158
|
+
return helperCache.get(cacheKey);
|
|
159
|
+
}
|
|
48
160
|
|
|
49
161
|
let helperPatterns = [];
|
|
50
162
|
if (overrides && overrides.helpers !== undefined) {
|
|
@@ -55,14 +167,7 @@ function load(projectDir, overrides) {
|
|
|
55
167
|
helperPatterns = normalizePatterns(overrides.helpers);
|
|
56
168
|
}
|
|
57
169
|
|
|
58
|
-
const globs =
|
|
59
|
-
cwd: projectDir,
|
|
60
|
-
...normalizeGlobs({
|
|
61
|
-
extensions,
|
|
62
|
-
files: overrides && overrides.files ? overrides.files : conf.files,
|
|
63
|
-
providers
|
|
64
|
-
})
|
|
65
|
-
};
|
|
170
|
+
const globs = resolveGlobsSync(projectDir, overrides && overrides.extensions, overrides && overrides.files);
|
|
66
171
|
|
|
67
172
|
const classifyForESLint = file => {
|
|
68
173
|
const {isTest} = classify(file, globs);
|
package/lib/api.js
CHANGED
|
@@ -111,10 +111,8 @@ class Api extends Emittery {
|
|
|
111
111
|
}
|
|
112
112
|
};
|
|
113
113
|
|
|
114
|
-
let cacheDir;
|
|
115
114
|
let testFiles;
|
|
116
115
|
try {
|
|
117
|
-
cacheDir = this._createCacheDir();
|
|
118
116
|
testFiles = await globs.findTests({cwd: this.options.projectDir, ...apiOptions.globs});
|
|
119
117
|
if (selectedFiles.length === 0) {
|
|
120
118
|
selectedFiles = filter.length === 0 ? testFiles : globs.applyTestFileFilter({
|
|
@@ -189,7 +187,7 @@ class Api extends Emittery {
|
|
|
189
187
|
|
|
190
188
|
const {providers = []} = this.options;
|
|
191
189
|
const providerStates = (await Promise.all(providers.map(async ({type, main}) => {
|
|
192
|
-
const state = await main.compile({cacheDir, files: testFiles});
|
|
190
|
+
const state = await main.compile({cacheDir: this._createCacheDir(), files: testFiles});
|
|
193
191
|
return state === null ? null : {type, state};
|
|
194
192
|
}))).filter(state => state !== null);
|
|
195
193
|
|
package/lib/cli.js
CHANGED
|
@@ -7,7 +7,7 @@ const arrify = require('arrify');
|
|
|
7
7
|
const yargs = require('yargs');
|
|
8
8
|
const readPkg = require('read-pkg');
|
|
9
9
|
const isCi = require('./is-ci');
|
|
10
|
-
const loadConfig = require('./load-config');
|
|
10
|
+
const {loadConfig} = require('./load-config');
|
|
11
11
|
|
|
12
12
|
function exit(message) {
|
|
13
13
|
console.error(`\n ${require('./chalk').get().red(figures.cross)} ${message}`);
|
|
@@ -83,7 +83,7 @@ exports.run = async () => { // eslint-disable-line complexity
|
|
|
83
83
|
let confError = null;
|
|
84
84
|
try {
|
|
85
85
|
const {argv: {config: configFile}} = yargs.help(false);
|
|
86
|
-
conf = loadConfig({configFile});
|
|
86
|
+
conf = await loadConfig({configFile});
|
|
87
87
|
} catch (error) {
|
|
88
88
|
confError = error;
|
|
89
89
|
}
|
package/lib/load-config.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
|
+
const url = require('url');
|
|
4
5
|
const vm = require('vm');
|
|
5
6
|
const {isPlainObject} = require('is-plain-object');
|
|
6
7
|
const pkgConf = require('pkg-conf');
|
|
@@ -11,23 +12,37 @@ const EXPERIMENTS = new Set([
|
|
|
11
12
|
'configurableModuleFormat',
|
|
12
13
|
'disableNullExpectations',
|
|
13
14
|
'disableSnapshotsInHooks',
|
|
15
|
+
'nextGenConfig',
|
|
14
16
|
'reverseTeardowns',
|
|
15
17
|
'sharedWorkers'
|
|
16
18
|
]);
|
|
17
19
|
|
|
18
20
|
// *Very* rudimentary support for loading ava.config.js files containing an `export default` statement.
|
|
19
|
-
const evaluateJsConfig = configFile => {
|
|
20
|
-
const
|
|
21
|
-
const script = new vm.Script(`'use strict';(()=>{let __export__;\n${contents.replace(/export default/g, '__export__ =')};return __export__;})()`, {
|
|
21
|
+
const evaluateJsConfig = (contents, configFile) => {
|
|
22
|
+
const script = new vm.Script(`'use strict';(()=>{let __export__;\n${contents.toString('utf8').replace(/export default/g, '__export__ =')};return __export__;})()`, {
|
|
22
23
|
filename: configFile,
|
|
23
24
|
lineOffset: -1
|
|
24
25
|
});
|
|
25
|
-
return
|
|
26
|
-
default: script.runInThisContext()
|
|
27
|
-
};
|
|
26
|
+
return script.runInThisContext();
|
|
28
27
|
};
|
|
29
28
|
|
|
30
|
-
const
|
|
29
|
+
const importConfig = async ({configFile, fileForErrorMessage}) => {
|
|
30
|
+
let module;
|
|
31
|
+
try {
|
|
32
|
+
module = await import(url.pathToFileURL(configFile)); // eslint-disable-line node/no-unsupported-features/es-syntax
|
|
33
|
+
} catch (error) {
|
|
34
|
+
throw Object.assign(new Error(`Error loading ${fileForErrorMessage}: ${error.message}`), {parent: error});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const {default: config = MISSING_DEFAULT_EXPORT} = module;
|
|
38
|
+
if (config === MISSING_DEFAULT_EXPORT) {
|
|
39
|
+
throw new Error(`${fileForErrorMessage} must have a default export`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return config;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const loadJsConfig = ({projectDir, configFile = path.join(projectDir, 'ava.config.js')}, useImport = false) => {
|
|
31
46
|
if (!configFile.endsWith('.js')) {
|
|
32
47
|
return null;
|
|
33
48
|
}
|
|
@@ -36,7 +51,10 @@ const loadJsConfig = ({projectDir, configFile = path.join(projectDir, 'ava.confi
|
|
|
36
51
|
|
|
37
52
|
let config;
|
|
38
53
|
try {
|
|
39
|
-
|
|
54
|
+
const contents = fs.readFileSync(configFile);
|
|
55
|
+
config = useImport && contents.includes('nonSemVerExperiments') && contents.includes('nextGenConfig') ?
|
|
56
|
+
importConfig({configFile, fileForErrorMessage}) :
|
|
57
|
+
evaluateJsConfig(contents, configFile) || MISSING_DEFAULT_EXPORT;
|
|
40
58
|
} catch (error) {
|
|
41
59
|
if (error.code === 'ENOENT') {
|
|
42
60
|
return null;
|
|
@@ -69,14 +87,17 @@ const loadCjsConfig = ({projectDir, configFile = path.join(projectDir, 'ava.conf
|
|
|
69
87
|
}
|
|
70
88
|
};
|
|
71
89
|
|
|
72
|
-
const loadMjsConfig = ({projectDir, configFile = path.join(projectDir, 'ava.config.mjs')}) => {
|
|
90
|
+
const loadMjsConfig = ({projectDir, configFile = path.join(projectDir, 'ava.config.mjs')}, experimentally = false) => {
|
|
73
91
|
if (!configFile.endsWith('.mjs')) {
|
|
74
92
|
return null;
|
|
75
93
|
}
|
|
76
94
|
|
|
77
95
|
const fileForErrorMessage = path.relative(projectDir, configFile);
|
|
78
96
|
try {
|
|
79
|
-
fs.readFileSync(configFile);
|
|
97
|
+
const contents = fs.readFileSync(configFile);
|
|
98
|
+
if (experimentally && contents.includes('nonSemVerExperiments') && contents.includes('nextGenConfig')) {
|
|
99
|
+
return {config: importConfig({configFile, fileForErrorMessage}), fileForErrorMessage};
|
|
100
|
+
}
|
|
80
101
|
} catch (error) {
|
|
81
102
|
if (error.code === 'ENOENT') {
|
|
82
103
|
return null;
|
|
@@ -88,11 +109,7 @@ const loadMjsConfig = ({projectDir, configFile = path.join(projectDir, 'ava.conf
|
|
|
88
109
|
throw new Error(`AVA cannot yet load ${fileForErrorMessage} files`);
|
|
89
110
|
};
|
|
90
111
|
|
|
91
|
-
function
|
|
92
|
-
let packageConf = pkgConf.sync('ava', {cwd: resolveFrom});
|
|
93
|
-
const filepath = pkgConf.filepath(packageConf);
|
|
94
|
-
const projectDir = filepath === null ? resolveFrom : path.dirname(filepath);
|
|
95
|
-
|
|
112
|
+
function resolveConfigFile(projectDir, configFile) {
|
|
96
113
|
if (configFile) {
|
|
97
114
|
configFile = path.resolve(configFile); // Relative to CWD
|
|
98
115
|
if (path.basename(configFile) !== path.relative(projectDir, configFile)) {
|
|
@@ -104,6 +121,15 @@ function loadConfig({configFile, resolveFrom = process.cwd(), defaults = {}} = {
|
|
|
104
121
|
}
|
|
105
122
|
}
|
|
106
123
|
|
|
124
|
+
return configFile;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function loadConfigSync({configFile, resolveFrom = process.cwd(), defaults = {}} = {}) {
|
|
128
|
+
let packageConf = pkgConf.sync('ava', {cwd: resolveFrom});
|
|
129
|
+
const filepath = pkgConf.filepath(packageConf);
|
|
130
|
+
const projectDir = filepath === null ? resolveFrom : path.dirname(filepath);
|
|
131
|
+
|
|
132
|
+
configFile = resolveConfigFile(projectDir, configFile);
|
|
107
133
|
const allowConflictWithPackageJson = Boolean(configFile);
|
|
108
134
|
|
|
109
135
|
let [{config: fileConf, fileForErrorMessage} = {config: NO_SUCH_FILE, fileForErrorMessage: undefined}, ...conflicting] = [
|
|
@@ -163,4 +189,79 @@ function loadConfig({configFile, resolveFrom = process.cwd(), defaults = {}} = {
|
|
|
163
189
|
return config;
|
|
164
190
|
}
|
|
165
191
|
|
|
166
|
-
|
|
192
|
+
exports.loadConfigSync = loadConfigSync;
|
|
193
|
+
|
|
194
|
+
async function loadConfig({configFile, resolveFrom = process.cwd(), defaults = {}} = {}) {
|
|
195
|
+
let packageConf = await pkgConf('ava', {cwd: resolveFrom});
|
|
196
|
+
const filepath = pkgConf.filepath(packageConf);
|
|
197
|
+
const projectDir = filepath === null ? resolveFrom : path.dirname(filepath);
|
|
198
|
+
|
|
199
|
+
configFile = resolveConfigFile(projectDir, configFile);
|
|
200
|
+
const allowConflictWithPackageJson = Boolean(configFile);
|
|
201
|
+
|
|
202
|
+
// TODO: Refactor resolution logic to implement https://github.com/avajs/ava/issues/2285.
|
|
203
|
+
let [{config: fileConf, fileForErrorMessage} = {config: NO_SUCH_FILE, fileForErrorMessage: undefined}, ...conflicting] = [
|
|
204
|
+
loadJsConfig({projectDir, configFile}, true),
|
|
205
|
+
loadCjsConfig({projectDir, configFile}),
|
|
206
|
+
loadMjsConfig({projectDir, configFile}, true)
|
|
207
|
+
].filter(result => result !== null);
|
|
208
|
+
|
|
209
|
+
if (conflicting.length > 0) {
|
|
210
|
+
throw new Error(`Conflicting configuration in ${fileForErrorMessage} and ${conflicting.map(({fileForErrorMessage}) => fileForErrorMessage).join(' & ')}`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
let sawPromise = false;
|
|
214
|
+
if (fileConf !== NO_SUCH_FILE) {
|
|
215
|
+
if (allowConflictWithPackageJson) {
|
|
216
|
+
packageConf = {};
|
|
217
|
+
} else if (Object.keys(packageConf).length > 0) {
|
|
218
|
+
throw new Error(`Conflicting configuration in ${fileForErrorMessage} and package.json`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (fileConf && typeof fileConf.then === 'function') { // eslint-disable-line promise/prefer-await-to-then
|
|
222
|
+
sawPromise = true;
|
|
223
|
+
fileConf = await fileConf;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!isPlainObject(fileConf) && typeof fileConf !== 'function') {
|
|
227
|
+
throw new TypeError(`${fileForErrorMessage} must export a plain object or factory function`);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (typeof fileConf === 'function') {
|
|
231
|
+
fileConf = fileConf({projectDir});
|
|
232
|
+
if (fileConf && typeof fileConf.then === 'function') { // eslint-disable-line promise/prefer-await-to-then
|
|
233
|
+
sawPromise = true;
|
|
234
|
+
fileConf = await fileConf;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (!isPlainObject(fileConf)) {
|
|
238
|
+
throw new TypeError(`Factory method exported by ${fileForErrorMessage} must return a plain object`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if ('ava' in fileConf) {
|
|
243
|
+
throw new Error(`Encountered ’ava’ property in ${fileForErrorMessage}; avoid wrapping the configuration`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const config = {...defaults, nonSemVerExperiments: {}, ...fileConf, ...packageConf, projectDir};
|
|
248
|
+
|
|
249
|
+
const {nonSemVerExperiments: experiments} = config;
|
|
250
|
+
if (!isPlainObject(experiments)) {
|
|
251
|
+
throw new Error(`nonSemVerExperiments from ${fileForErrorMessage} must be an object`);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
for (const key of Object.keys(experiments)) {
|
|
255
|
+
if (!EXPERIMENTS.has(key)) {
|
|
256
|
+
throw new Error(`nonSemVerExperiments.${key} from ${fileForErrorMessage} is not a supported experiment`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (sawPromise && experiments.nextGenConfig !== true) {
|
|
261
|
+
throw new Error(`${fileForErrorMessage} exported a promise or an asynchronous factory function. You must enable the ’asyncConfigurationLoading’ experiment for this to work.`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return config;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
exports.loadConfig = loadConfig;
|
package/lib/reporters/tap.js
CHANGED
|
@@ -125,12 +125,22 @@ class TapReporter {
|
|
|
125
125
|
this.reportStream.write(`# ${stripAnsi(title)}${os.EOL}`);
|
|
126
126
|
if (evt.logs) {
|
|
127
127
|
for (const log of evt.logs) {
|
|
128
|
-
const logLines = indentString(log, 4).replace(/^ {4}
|
|
128
|
+
const logLines = indentString(log, 4).replace(/^ {4}/gm, '# ');
|
|
129
129
|
this.reportStream.write(`${logLines}${os.EOL}`);
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
writeTimeout(evt) {
|
|
135
|
+
const err = new Error(`Exited because no new tests completed within the last ${evt.period}ms of inactivity`);
|
|
136
|
+
|
|
137
|
+
for (const [testFile, tests] of evt.pendingTests) {
|
|
138
|
+
for (const title of tests) {
|
|
139
|
+
this.writeTest({testFile, title, err}, {passed: false, todo: false, skip: false});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
134
144
|
consumeStateChange(evt) { // eslint-disable-line complexity
|
|
135
145
|
const fileStats = this.stats && evt.testFile ? this.stats.byFile.get(evt.testFile) : null;
|
|
136
146
|
|
|
@@ -172,7 +182,7 @@ class TapReporter {
|
|
|
172
182
|
this.writeTest(evt, {passed: true, todo: false, skip: false});
|
|
173
183
|
break;
|
|
174
184
|
case 'timeout':
|
|
175
|
-
this.
|
|
185
|
+
this.writeTimeout(evt);
|
|
176
186
|
break;
|
|
177
187
|
case 'uncaught-exception':
|
|
178
188
|
this.writeCrash(evt);
|
package/lib/runner.js
CHANGED
|
@@ -29,6 +29,8 @@ class Runner extends Emittery {
|
|
|
29
29
|
|
|
30
30
|
this.activeRunnables = new Set();
|
|
31
31
|
this.boundCompareTestSnapshot = this.compareTestSnapshot.bind(this);
|
|
32
|
+
this.skippedSnapshots = false;
|
|
33
|
+
this.boundSkipSnapshot = this.skipSnapshot.bind(this);
|
|
32
34
|
this.interrupted = false;
|
|
33
35
|
this.snapshots = null;
|
|
34
36
|
this.nextTaskIndex = 0;
|
|
@@ -199,8 +201,19 @@ class Runner extends Emittery {
|
|
|
199
201
|
return this.snapshots.compare(options);
|
|
200
202
|
}
|
|
201
203
|
|
|
204
|
+
skipSnapshot() {
|
|
205
|
+
this.skippedSnapshots = true;
|
|
206
|
+
}
|
|
207
|
+
|
|
202
208
|
saveSnapshotState() {
|
|
203
|
-
if (
|
|
209
|
+
if (
|
|
210
|
+
this.updateSnapshots &&
|
|
211
|
+
(
|
|
212
|
+
this.runOnlyExclusive ||
|
|
213
|
+
this.skippingTests ||
|
|
214
|
+
this.skippedSnapshots
|
|
215
|
+
)
|
|
216
|
+
) {
|
|
204
217
|
return {cannotSave: true};
|
|
205
218
|
}
|
|
206
219
|
|
|
@@ -209,9 +222,11 @@ class Runner extends Emittery {
|
|
|
209
222
|
}
|
|
210
223
|
|
|
211
224
|
if (this.updateSnapshots) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
225
|
+
return {touchedFiles: snapshotManager.cleanSnapshots({
|
|
226
|
+
file: this.file,
|
|
227
|
+
fixedLocation: this.snapshotDir,
|
|
228
|
+
projectDir: this.projectDir
|
|
229
|
+
})};
|
|
215
230
|
}
|
|
216
231
|
|
|
217
232
|
return {};
|
|
@@ -297,6 +312,7 @@ class Runner extends Emittery {
|
|
|
297
312
|
task.implementation :
|
|
298
313
|
t => task.implementation.apply(null, [t].concat(task.args)),
|
|
299
314
|
compareTestSnapshot: this.boundCompareTestSnapshot,
|
|
315
|
+
skipSnapshot: this.boundSkipSnapshot,
|
|
300
316
|
updateSnapshots: this.updateSnapshots,
|
|
301
317
|
metadata: {...task.metadata, associatedTaskIndex},
|
|
302
318
|
powerAssert: this.powerAssert,
|
|
@@ -349,6 +365,7 @@ class Runner extends Emittery {
|
|
|
349
365
|
task.implementation :
|
|
350
366
|
t => task.implementation.apply(null, [t].concat(task.args)),
|
|
351
367
|
compareTestSnapshot: this.boundCompareTestSnapshot,
|
|
368
|
+
skipSnapshot: this.boundSkipSnapshot,
|
|
352
369
|
updateSnapshots: this.updateSnapshots,
|
|
353
370
|
metadata: task.metadata,
|
|
354
371
|
powerAssert: this.powerAssert,
|
package/lib/snapshot-manager.js
CHANGED
|
@@ -449,12 +449,49 @@ const determineSnapshotDir = mem(({file, fixedLocation, projectDir}) => {
|
|
|
449
449
|
|
|
450
450
|
exports.determineSnapshotDir = determineSnapshotDir;
|
|
451
451
|
|
|
452
|
-
function
|
|
452
|
+
function determineSnapshotPaths({file, fixedLocation, projectDir}) {
|
|
453
453
|
const dir = determineSnapshotDir({file, fixedLocation, projectDir});
|
|
454
454
|
const relFile = path.relative(projectDir, resolveSourceFile(file));
|
|
455
455
|
const name = path.basename(relFile);
|
|
456
456
|
const reportFile = `${name}.md`;
|
|
457
457
|
const snapFile = `${name}.snap`;
|
|
458
|
+
|
|
459
|
+
return {
|
|
460
|
+
dir,
|
|
461
|
+
relFile,
|
|
462
|
+
snapFile,
|
|
463
|
+
reportFile
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function cleanFile(file) {
|
|
468
|
+
try {
|
|
469
|
+
fs.unlinkSync(file);
|
|
470
|
+
return [file];
|
|
471
|
+
} catch (error) {
|
|
472
|
+
if (error.code === 'ENOENT') {
|
|
473
|
+
return [];
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
throw error;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Remove snapshot and report if they exist. Returns an array containing the
|
|
481
|
+
// paths of the touched files.
|
|
482
|
+
function cleanSnapshots({file, fixedLocation, projectDir}) {
|
|
483
|
+
const {dir, snapFile, reportFile} = determineSnapshotPaths({file, fixedLocation, projectDir});
|
|
484
|
+
|
|
485
|
+
return [
|
|
486
|
+
...cleanFile(path.join(dir, snapFile)),
|
|
487
|
+
...cleanFile(path.join(dir, reportFile))
|
|
488
|
+
];
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
exports.cleanSnapshots = cleanSnapshots;
|
|
492
|
+
|
|
493
|
+
function load({file, fixedLocation, projectDir, recordNewSnapshots, updating}) {
|
|
494
|
+
const {dir, relFile, snapFile, reportFile} = determineSnapshotPaths({file, fixedLocation, projectDir});
|
|
458
495
|
const snapPath = path.join(dir, snapFile);
|
|
459
496
|
|
|
460
497
|
let appendOnly = !updating;
|
package/lib/test.js
CHANGED
|
@@ -249,6 +249,10 @@ class Test {
|
|
|
249
249
|
};
|
|
250
250
|
|
|
251
251
|
this.skipSnapshot = () => {
|
|
252
|
+
if (typeof options.skipSnapshot === 'function') {
|
|
253
|
+
options.skipSnapshot();
|
|
254
|
+
}
|
|
255
|
+
|
|
252
256
|
if (options.updateSnapshots) {
|
|
253
257
|
this.addFailedAssertion(new Error('Snapshot assertions cannot be skipped when updating snapshots'));
|
|
254
258
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ava",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0",
|
|
4
4
|
"description": "Node.js test runner that lets you develop with confidence.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "avajs/ava",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"node": ">=10.18.0 <11 || >=12.14.0 <12.17.0 || >=12.17.0 <13 || >=14.0.0 <15 || >=15"
|
|
11
11
|
},
|
|
12
12
|
"scripts": {
|
|
13
|
-
"
|
|
13
|
+
"cover": "c8 --report=none tap && c8 --report=none --no-clean test-ava && c8 report",
|
|
14
|
+
"test": "xo && tsd && npm run -s cover"
|
|
14
15
|
},
|
|
15
16
|
"files": [
|
|
16
17
|
"lib",
|
|
@@ -78,7 +79,7 @@
|
|
|
78
79
|
"currently-unhandled": "^0.4.1",
|
|
79
80
|
"debug": "^4.3.1",
|
|
80
81
|
"del": "^6.0.0",
|
|
81
|
-
"emittery": "^0.
|
|
82
|
+
"emittery": "^0.8.0",
|
|
82
83
|
"equal-length": "^1.0.0",
|
|
83
84
|
"figures": "^3.2.0",
|
|
84
85
|
"globby": "^11.0.1",
|
|
@@ -92,8 +93,8 @@
|
|
|
92
93
|
"matcher": "^3.0.0",
|
|
93
94
|
"md5-hex": "^3.0.1",
|
|
94
95
|
"mem": "^8.0.0",
|
|
95
|
-
"ms": "^2.1.
|
|
96
|
-
"ora": "^5.
|
|
96
|
+
"ms": "^2.1.3",
|
|
97
|
+
"ora": "^5.2.0",
|
|
97
98
|
"p-event": "^4.2.0",
|
|
98
99
|
"p-map": "^4.0.0",
|
|
99
100
|
"picomatch": "^2.2.2",
|
|
@@ -106,7 +107,7 @@
|
|
|
106
107
|
"source-map-support": "^0.5.19",
|
|
107
108
|
"stack-utils": "^2.0.3",
|
|
108
109
|
"strip-ansi": "^6.0.0",
|
|
109
|
-
"supertap": "^
|
|
110
|
+
"supertap": "^2.0.0",
|
|
110
111
|
"temp-dir": "^2.0.0",
|
|
111
112
|
"trim-off-newlines": "^1.0.1",
|
|
112
113
|
"update-notifier": "^5.0.1",
|
|
@@ -119,25 +120,26 @@
|
|
|
119
120
|
"@babel/plugin-proposal-do-expressions": "^7.12.1",
|
|
120
121
|
"@sinonjs/fake-timers": "^6.0.1",
|
|
121
122
|
"ansi-escapes": "^4.3.1",
|
|
122
|
-
"c8": "^7.
|
|
123
|
+
"c8": "^7.4.0",
|
|
123
124
|
"delay": "^4.4.0",
|
|
124
125
|
"esm": "^3.2.25",
|
|
125
126
|
"execa": "^5.0.0",
|
|
127
|
+
"fs-extra": "^9.0.1",
|
|
126
128
|
"get-stream": "^6.0.0",
|
|
127
129
|
"it-first": "^1.0.4",
|
|
128
130
|
"proxyquire": "^2.1.3",
|
|
129
131
|
"react": "^16.14.0",
|
|
130
132
|
"react-test-renderer": "^16.14.0",
|
|
131
133
|
"replace-string": "^3.1.0",
|
|
132
|
-
"sinon": "^9.2.
|
|
134
|
+
"sinon": "^9.2.2",
|
|
133
135
|
"source-map-fixtures": "^2.1.0",
|
|
134
136
|
"tap": "^14.11.0",
|
|
135
137
|
"temp-write": "^4.0.0",
|
|
136
138
|
"tempy": "^1.0.0",
|
|
137
139
|
"touch": "^3.1.0",
|
|
138
140
|
"tsd": "^0.14.0",
|
|
139
|
-
"typescript": "^4.1.
|
|
140
|
-
"xo": "^0.
|
|
141
|
+
"typescript": "^4.1.3",
|
|
142
|
+
"xo": "^0.36.1",
|
|
141
143
|
"zen-observable": "^0.8.15"
|
|
142
144
|
}
|
|
143
145
|
}
|
package/readme.md
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
# <img src="media/header.png" title="AVA" alt="AVA logo" width="530">
|
|
2
2
|
|
|
3
|
-
[](https://travis-ci.org/avajs/ava) [](https://codecov.io/gh/avajs/ava/branch/master) [](https://github.com/xojs/xo) [](https://spectrum.chat/ava)
|
|
4
|
-
[](https://github.com/sindresorhus/awesome-nodejs)
|
|
5
|
-
|
|
6
3
|
AVA is a test runner for Node.js with a concise API, detailed error output, embrace of new language features and process isolation that lets you develop with confidence 🚀
|
|
7
4
|
|
|
8
5
|
Follow the [AVA Twitter account](https://twitter.com/ava__js) for updates.
|
|
@@ -188,9 +185,7 @@ It's the [Andromeda galaxy](https://simple.wikipedia.org/wiki/Andromeda_galaxy).
|
|
|
188
185
|
|
|
189
186
|
## Support
|
|
190
187
|
|
|
191
|
-
- [
|
|
192
|
-
- [Spectrum](https://spectrum.chat/ava)
|
|
193
|
-
- [Twitter](https://twitter.com/ava__js)
|
|
188
|
+
- [GitHub Discussions](https://github.com/avajs/ava/discussions)
|
|
194
189
|
|
|
195
190
|
## Related
|
|
196
191
|
|