ava 4.0.0-rc.1 → 4.1.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/entrypoints/eslint-plugin-helper.cjs +1 -1
- package/entrypoints/main.mjs +1 -3
- package/lib/api.js +15 -6
- package/lib/chalk.js +4 -5
- package/lib/cli.js +28 -9
- package/lib/code-excerpt.js +2 -8
- package/lib/concordance-options.js +2 -1
- package/lib/eslint-plugin-helper-worker.js +1 -1
- package/lib/fork.js +1 -1
- package/lib/globs.js +8 -10
- package/lib/load-config.js +30 -7
- package/lib/reporters/tap.js +1 -1
- package/lib/runner.js +2 -2
- package/lib/snapshot-manager.js +12 -8
- package/lib/test.js +6 -6
- package/lib/worker/base.js +6 -6
- package/lib/worker/channel.cjs +34 -2
- package/lib/worker/guard-environment.cjs +1 -2
- package/package.json +29 -30
- package/readme.md +8 -10
- package/types/assertions.d.ts +12 -0
|
@@ -90,7 +90,7 @@ function load(projectDir, overrides) {
|
|
|
90
90
|
|
|
91
91
|
const helper = Object.freeze({
|
|
92
92
|
classifyFile: classifyForESLint,
|
|
93
|
-
classifyImport
|
|
93
|
+
classifyImport(importPath) {
|
|
94
94
|
if (hasExtension(globs.extensions, importPath)) {
|
|
95
95
|
// The importPath has one of the test file extensions: we can classify
|
|
96
96
|
// it directly.
|
package/entrypoints/main.mjs
CHANGED
package/lib/api.js
CHANGED
|
@@ -177,13 +177,19 @@ export default class Api extends Emittery {
|
|
|
177
177
|
const fileCount = selectedFiles.length;
|
|
178
178
|
|
|
179
179
|
// The files must be in the same order across all runs, so sort them.
|
|
180
|
-
|
|
180
|
+
const defaultComparator = (a, b) => a.localeCompare(b, [], {numeric: true});
|
|
181
|
+
selectedFiles = selectedFiles.sort(this.options.sortTestFiles || defaultComparator);
|
|
181
182
|
selectedFiles = chunkd(selectedFiles, currentIndex, totalRuns);
|
|
182
183
|
|
|
183
184
|
const currentFileCount = selectedFiles.length;
|
|
184
185
|
|
|
185
186
|
runStatus = new RunStatus(fileCount, {currentFileCount, currentIndex, totalRuns}, selectionInsights);
|
|
186
187
|
} else {
|
|
188
|
+
// If a custom sorter was configured, use it.
|
|
189
|
+
if (this.options.sortTestFiles) {
|
|
190
|
+
selectedFiles = selectedFiles.sort(this.options.sortTestFiles);
|
|
191
|
+
}
|
|
192
|
+
|
|
187
193
|
runStatus = new RunStatus(selectedFiles.length, null, selectionInsights);
|
|
188
194
|
}
|
|
189
195
|
|
|
@@ -232,10 +238,13 @@ export default class Api extends Emittery {
|
|
|
232
238
|
}
|
|
233
239
|
});
|
|
234
240
|
|
|
235
|
-
const providerStates =
|
|
241
|
+
const providerStates = [];
|
|
242
|
+
await Promise.all(providers.map(async ({type, main}) => {
|
|
236
243
|
const state = await main.compile({cacheDir: this._createCacheDir(), files: testFiles});
|
|
237
|
-
|
|
238
|
-
|
|
244
|
+
if (state !== null) {
|
|
245
|
+
providerStates.push({type, state});
|
|
246
|
+
}
|
|
247
|
+
}));
|
|
239
248
|
|
|
240
249
|
// Resolve the correct concurrency value.
|
|
241
250
|
let concurrency = Math.min(os.cpus().length, isCi ? 2 : Number.POSITIVE_INFINITY);
|
|
@@ -258,8 +267,8 @@ export default class Api extends Emittery {
|
|
|
258
267
|
}
|
|
259
268
|
|
|
260
269
|
const lineNumbers = getApplicableLineNumbers(globs.normalizeFileForMatching(apiOptions.projectDir, file), filter);
|
|
261
|
-
// Removing `providers`
|
|
262
|
-
const {providers, ...forkOptions} = apiOptions;
|
|
270
|
+
// Removing `providers` and `sortTestFiles` fields because they cannot be transferred to the worker threads.
|
|
271
|
+
const {providers, sortTestFiles, ...forkOptions} = apiOptions;
|
|
263
272
|
const options = {
|
|
264
273
|
...forkOptions,
|
|
265
274
|
providerStates,
|
package/lib/chalk.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {Chalk} from 'chalk'; // eslint-disable-line unicorn/import-style
|
|
2
2
|
|
|
3
|
-
let
|
|
4
|
-
export default instance;
|
|
3
|
+
let chalk = new Chalk(); // eslint-disable-line import/no-mutable-exports
|
|
5
4
|
|
|
6
|
-
export {
|
|
5
|
+
export {chalk};
|
|
7
6
|
|
|
8
7
|
let configured = false;
|
|
9
8
|
export function set(options) {
|
|
@@ -12,5 +11,5 @@ export function set(options) {
|
|
|
12
11
|
}
|
|
13
12
|
|
|
14
13
|
configured = true;
|
|
15
|
-
|
|
14
|
+
chalk = new Chalk(options);
|
|
16
15
|
}
|
package/lib/cli.js
CHANGED
|
@@ -21,6 +21,7 @@ import {splitPatternAndLineNumbers} from './line-numbers.js';
|
|
|
21
21
|
import {loadConfig} from './load-config.js';
|
|
22
22
|
import normalizeModuleTypes from './module-types.js';
|
|
23
23
|
import normalizeNodeArguments from './node-arguments.js';
|
|
24
|
+
import pkg from './pkg.cjs';
|
|
24
25
|
import providerManager from './provider-manager.js';
|
|
25
26
|
import DefaultReporter from './reporters/default.js';
|
|
26
27
|
import TapReporter from './reporters/tap.js';
|
|
@@ -87,7 +88,7 @@ const FLAGS = {
|
|
|
87
88
|
verbose: {
|
|
88
89
|
alias: 'v',
|
|
89
90
|
coerce: coerceLastValue,
|
|
90
|
-
description: 'Enable verbose output (
|
|
91
|
+
description: 'Enable verbose output (default)',
|
|
91
92
|
type: 'boolean',
|
|
92
93
|
},
|
|
93
94
|
watch: {
|
|
@@ -102,8 +103,15 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
102
103
|
let conf;
|
|
103
104
|
let confError;
|
|
104
105
|
try {
|
|
105
|
-
const {argv: {config: configFile}} = yargs(hideBin(process.argv)).help(false);
|
|
106
|
-
|
|
106
|
+
const {argv: {config: configFile}} = yargs(hideBin(process.argv)).help(false).version(false);
|
|
107
|
+
const loaded = await loadConfig({configFile});
|
|
108
|
+
if (loaded.unsupportedFiles.length > 0) {
|
|
109
|
+
console.log(chalk.magenta(
|
|
110
|
+
` ${figures.warning} AVA does not support JSON config, ignoring:\n\n ${loaded.unsupportedFiles.join('\n ')}`,
|
|
111
|
+
));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
conf = loaded.config;
|
|
107
115
|
if (conf.configFile && path.basename(conf.configFile) !== path.relative(conf.projectDir, conf.configFile)) {
|
|
108
116
|
console.log(chalk.magenta(` ${figures.warning} Using configuration from ${conf.configFile}`));
|
|
109
117
|
}
|
|
@@ -132,6 +140,7 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
132
140
|
|
|
133
141
|
let resetCache = false;
|
|
134
142
|
const {argv} = yargs(hideBin(process.argv))
|
|
143
|
+
.version(pkg.version)
|
|
135
144
|
.parserConfiguration({
|
|
136
145
|
'boolean-negation': true,
|
|
137
146
|
'camel-case-expansion': false,
|
|
@@ -161,7 +170,7 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
161
170
|
})
|
|
162
171
|
.command('* [<pattern>...]', 'Run tests', yargs => yargs.options(FLAGS).positional('pattern', {
|
|
163
172
|
array: true,
|
|
164
|
-
describe: 'Select which test files to run. Leave empty if you want AVA to run all test files as per your configuration. Accepts glob patterns, directories that (recursively) contain test files, and file paths
|
|
173
|
+
describe: 'Select which test files to run. Leave empty if you want AVA to run all test files as per your configuration. Accepts glob patterns, directories that (recursively) contain test files, and file paths optionally suffixed with a colon and comma-separated numbers and/or ranges identifying the 1-based line(s) of specific tests to run',
|
|
165
174
|
type: 'string',
|
|
166
175
|
}), argv => {
|
|
167
176
|
if (activeInspector) {
|
|
@@ -188,7 +197,7 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
188
197
|
},
|
|
189
198
|
}).positional('pattern', {
|
|
190
199
|
demand: true,
|
|
191
|
-
describe: 'Glob
|
|
200
|
+
describe: 'Glob pattern to select a single test file to debug, optionally suffixed with a colon and comma-separated numbers and/or ranges identifying the 1-based line(s) of specific tests to run',
|
|
192
201
|
type: 'string',
|
|
193
202
|
}),
|
|
194
203
|
argv => {
|
|
@@ -231,7 +240,12 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
231
240
|
}
|
|
232
241
|
}
|
|
233
242
|
|
|
234
|
-
const chalkOptions = {level:
|
|
243
|
+
const chalkOptions = {level: 0};
|
|
244
|
+
if (combined.color !== false) {
|
|
245
|
+
const {supportsColor: {level}} = await import('chalk'); // eslint-disable-line node/no-unsupported-features/es-syntax, unicorn/import-style
|
|
246
|
+
chalkOptions.level = level;
|
|
247
|
+
}
|
|
248
|
+
|
|
235
249
|
const {set: setChalk} = await import('./chalk.js'); // eslint-disable-line node/no-unsupported-features/es-syntax
|
|
236
250
|
setChalk(chalkOptions);
|
|
237
251
|
|
|
@@ -314,16 +328,20 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
314
328
|
exit('’sources’ has been removed. Use ’ignoredByWatcher’ to provide glob patterns of files that the watcher should ignore.');
|
|
315
329
|
}
|
|
316
330
|
|
|
317
|
-
|
|
331
|
+
if (Reflect.has(conf, 'sortTestFiles') && typeof conf.sortTestFiles !== 'function') {
|
|
332
|
+
exit('’sortTestFiles’ must be a comparator function.');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
let projectPackageObject;
|
|
318
336
|
try {
|
|
319
|
-
|
|
337
|
+
projectPackageObject = JSON.parse(fs.readFileSync(path.resolve(projectDir, 'package.json')));
|
|
320
338
|
} catch (error) {
|
|
321
339
|
if (error.code !== 'ENOENT') {
|
|
322
340
|
throw error;
|
|
323
341
|
}
|
|
324
342
|
}
|
|
325
343
|
|
|
326
|
-
const {type: defaultModuleType = 'commonjs'} =
|
|
344
|
+
const {type: defaultModuleType = 'commonjs'} = projectPackageObject || {};
|
|
327
345
|
|
|
328
346
|
const providers = [];
|
|
329
347
|
if (Reflect.has(conf, 'typescript')) {
|
|
@@ -406,6 +424,7 @@ export default async function loadCli() { // eslint-disable-line complexity
|
|
|
406
424
|
moduleTypes,
|
|
407
425
|
nodeArguments,
|
|
408
426
|
parallelRuns,
|
|
427
|
+
sortTestFiles: conf.sortTestFiles,
|
|
409
428
|
projectDir,
|
|
410
429
|
providers,
|
|
411
430
|
ranFromCli: true,
|
package/lib/code-excerpt.js
CHANGED
|
@@ -2,7 +2,6 @@ import fs from 'node:fs';
|
|
|
2
2
|
|
|
3
3
|
import truncate from 'cli-truncate';
|
|
4
4
|
import codeExcerpt from 'code-excerpt';
|
|
5
|
-
import equalLength from 'equal-length';
|
|
6
5
|
|
|
7
6
|
import {chalk} from './chalk.js';
|
|
8
7
|
|
|
@@ -34,20 +33,15 @@ export default function exceptCode(source, options = {}) {
|
|
|
34
33
|
value: truncate(item.value, maxWidth - String(line).length - 5),
|
|
35
34
|
}));
|
|
36
35
|
|
|
37
|
-
const
|
|
38
|
-
const extendedLines = equalLength(joinedLines).split('\n');
|
|
36
|
+
const extendedWidth = Math.max(...lines.map(item => item.value.length));
|
|
39
37
|
|
|
40
38
|
return lines
|
|
41
|
-
.map((item, index) => ({
|
|
42
|
-
line: item.line,
|
|
43
|
-
value: extendedLines[index],
|
|
44
|
-
}))
|
|
45
39
|
.map(item => {
|
|
46
40
|
const isErrorSource = item.line === line;
|
|
47
41
|
|
|
48
42
|
const lineNumber = formatLineNumber(item.line, line) + ':';
|
|
49
43
|
const coloredLineNumber = isErrorSource ? lineNumber : chalk.grey(lineNumber);
|
|
50
|
-
const result = ` ${coloredLineNumber} ${item.value}`;
|
|
44
|
+
const result = ` ${coloredLineNumber} ${item.value.padEnd(extendedWidth)}`;
|
|
51
45
|
|
|
52
46
|
return isErrorSource ? chalk.bgRed(result) : result;
|
|
53
47
|
})
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {inspect} from 'node:util';
|
|
2
2
|
|
|
3
3
|
import ansiStyles from 'ansi-styles';
|
|
4
|
+
import {Chalk} from 'chalk'; // eslint-disable-line unicorn/import-style
|
|
4
5
|
import stripAnsi from 'strip-ansi';
|
|
5
6
|
|
|
6
7
|
import {chalk} from './chalk.js';
|
|
7
8
|
|
|
8
|
-
const forceColor = new
|
|
9
|
+
const forceColor = new Chalk({level: Math.max(chalk.level, 1)});
|
|
9
10
|
|
|
10
11
|
const colorTheme = {
|
|
11
12
|
boolean: ansiStyles.yellow,
|
|
@@ -41,7 +41,7 @@ const buildGlobs = ({conf, providers, projectDir, overrideExtensions, overrideFi
|
|
|
41
41
|
|
|
42
42
|
const resolveGlobs = async (projectDir, overrideExtensions, overrideFiles) => {
|
|
43
43
|
if (!configCache.has(projectDir)) {
|
|
44
|
-
configCache.set(projectDir, loadConfig({resolveFrom: projectDir}).then(async conf => {
|
|
44
|
+
configCache.set(projectDir, loadConfig({resolveFrom: projectDir}).then(async ({config: conf}) => {
|
|
45
45
|
const providers = await collectProviders({conf, projectDir});
|
|
46
46
|
return {conf, providers};
|
|
47
47
|
}));
|
package/lib/fork.js
CHANGED
|
@@ -4,7 +4,7 @@ import {fileURLToPath} from 'node:url';
|
|
|
4
4
|
import {Worker} from 'node:worker_threads';
|
|
5
5
|
|
|
6
6
|
import Emittery from 'emittery';
|
|
7
|
-
import pEvent from 'p-event';
|
|
7
|
+
import {pEvent} from 'p-event';
|
|
8
8
|
|
|
9
9
|
import {controlFlow} from './ipc-flow-control.cjs';
|
|
10
10
|
import serializeError from './serialize-error.js';
|
package/lib/globs.js
CHANGED
|
@@ -4,27 +4,23 @@ import path from 'node:path';
|
|
|
4
4
|
import {globby, globbySync} from 'globby';
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
-
classify,
|
|
8
7
|
defaultIgnorePatterns,
|
|
9
8
|
hasExtension,
|
|
10
|
-
isHelperish,
|
|
11
|
-
matches,
|
|
12
9
|
normalizeFileForMatching,
|
|
13
|
-
normalizePattern,
|
|
14
10
|
normalizePatterns,
|
|
15
11
|
processMatchingPatterns,
|
|
16
12
|
} from './glob-helpers.cjs';
|
|
17
13
|
|
|
18
14
|
export {
|
|
19
15
|
classify,
|
|
20
|
-
defaultIgnorePatterns,
|
|
21
|
-
hasExtension,
|
|
22
16
|
isHelperish,
|
|
23
17
|
matches,
|
|
24
|
-
normalizeFileForMatching,
|
|
25
18
|
normalizePattern,
|
|
19
|
+
defaultIgnorePatterns,
|
|
20
|
+
hasExtension,
|
|
21
|
+
normalizeFileForMatching,
|
|
26
22
|
normalizePatterns,
|
|
27
|
-
};
|
|
23
|
+
} from './glob-helpers.cjs';
|
|
28
24
|
|
|
29
25
|
const defaultIgnoredByWatcherPatterns = [
|
|
30
26
|
'**/*.snap.md', // No need to rerun tests when the Markdown files change.
|
|
@@ -120,11 +116,13 @@ const globDirectoriesSync = (cwd, patterns) => {
|
|
|
120
116
|
};
|
|
121
117
|
|
|
122
118
|
export async function findFiles({cwd, extensions, filePatterns}) {
|
|
123
|
-
|
|
119
|
+
const files = await globFiles(cwd, filePatterns);
|
|
120
|
+
return files.filter(file => hasExtension(extensions, file));
|
|
124
121
|
}
|
|
125
122
|
|
|
126
123
|
export async function findTests({cwd, extensions, filePatterns}) {
|
|
127
|
-
|
|
124
|
+
const files = await findFiles({cwd, extensions, filePatterns});
|
|
125
|
+
return files.filter(file => !path.basename(file).startsWith('_'));
|
|
128
126
|
}
|
|
129
127
|
|
|
130
128
|
export function getChokidarIgnorePatterns({ignoredByWatcherPatterns}) {
|
package/lib/load-config.js
CHANGED
|
@@ -20,14 +20,15 @@ const importConfig = async ({configFile, fileForErrorMessage}) => {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
const loadConfigFile = async ({projectDir, configFile}) => {
|
|
23
|
-
if (!fs.existsSync(configFile)) {
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
23
|
const fileForErrorMessage = path.relative(projectDir, configFile);
|
|
28
24
|
try {
|
|
25
|
+
await fs.promises.access(configFile);
|
|
29
26
|
return {config: await importConfig({configFile, fileForErrorMessage}), configFile, fileForErrorMessage};
|
|
30
27
|
} catch (error) {
|
|
28
|
+
if (error.code === 'ENOENT') {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
31
32
|
throw Object.assign(new Error(`Error loading ${fileForErrorMessage}: ${error.message}`), {parent: error});
|
|
32
33
|
}
|
|
33
34
|
};
|
|
@@ -63,6 +64,20 @@ async function findRepoRoot(fromDir) {
|
|
|
63
64
|
return root;
|
|
64
65
|
}
|
|
65
66
|
|
|
67
|
+
async function checkJsonFile(searchDir) {
|
|
68
|
+
const file = path.join(searchDir, 'ava.config.json');
|
|
69
|
+
try {
|
|
70
|
+
await fs.promises.access(file);
|
|
71
|
+
return file;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (error.code === 'ENOENT') {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
66
81
|
export async function loadConfig({configFile, resolveFrom = process.cwd(), defaults = {}} = {}) {
|
|
67
82
|
let packageConf = await packageConfig('ava', {cwd: resolveFrom});
|
|
68
83
|
const filepath = packageJsonPath(packageConf);
|
|
@@ -74,6 +89,7 @@ export async function loadConfig({configFile, resolveFrom = process.cwd(), defau
|
|
|
74
89
|
const allowConflictWithPackageJson = Boolean(configFile);
|
|
75
90
|
configFile = resolveConfigFile(configFile);
|
|
76
91
|
|
|
92
|
+
const unsupportedFiles = [];
|
|
77
93
|
let fileConf = NO_SUCH_FILE;
|
|
78
94
|
let fileForErrorMessage;
|
|
79
95
|
let conflicting = [];
|
|
@@ -86,11 +102,18 @@ export async function loadConfig({configFile, resolveFrom = process.cwd(), defau
|
|
|
86
102
|
let searchDir = projectDir;
|
|
87
103
|
const stopAt = path.dirname(repoRoot);
|
|
88
104
|
do {
|
|
89
|
-
[
|
|
105
|
+
const [jsonFile, ...results] = await Promise.all([ // eslint-disable-line no-await-in-loop
|
|
106
|
+
checkJsonFile(searchDir),
|
|
90
107
|
loadConfigFile({projectDir, configFile: path.join(searchDir, 'ava.config.js')}),
|
|
91
108
|
loadConfigFile({projectDir, configFile: path.join(searchDir, 'ava.config.cjs')}),
|
|
92
109
|
loadConfigFile({projectDir, configFile: path.join(searchDir, 'ava.config.mjs')}),
|
|
93
|
-
])
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
if (jsonFile !== null) {
|
|
113
|
+
unsupportedFiles.push(jsonFile);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
[{config: fileConf, fileForErrorMessage, configFile} = {config: NO_SUCH_FILE, fileForErrorMessage: undefined}, ...conflicting] = results.filter(result => result !== null);
|
|
94
117
|
|
|
95
118
|
searchDir = path.dirname(searchDir);
|
|
96
119
|
} while (fileConf === NO_SUCH_FILE && searchDir !== stopAt);
|
|
@@ -137,5 +160,5 @@ export async function loadConfig({configFile, resolveFrom = process.cwd(), defau
|
|
|
137
160
|
}
|
|
138
161
|
}
|
|
139
162
|
|
|
140
|
-
return config;
|
|
163
|
+
return {config, unsupportedFiles};
|
|
141
164
|
}
|
package/lib/reporters/tap.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'node:path';
|
|
|
4
4
|
import indentString from 'indent-string';
|
|
5
5
|
import plur from 'plur';
|
|
6
6
|
import stripAnsi from 'strip-ansi';
|
|
7
|
-
import supertap from 'supertap';
|
|
7
|
+
import * as supertap from 'supertap';
|
|
8
8
|
|
|
9
9
|
import beautifyStack from './beautify-stack.js';
|
|
10
10
|
import prefixTitle from './prefix-title.js';
|
package/lib/runner.js
CHANGED
|
@@ -224,8 +224,8 @@ export default class Runner extends Emittery {
|
|
|
224
224
|
return this.snapshots.skipSnapshot(options);
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
saveSnapshotState() {
|
|
228
|
-
return {touchedFiles: this.snapshots.save()};
|
|
227
|
+
async saveSnapshotState() {
|
|
228
|
+
return {touchedFiles: await this.snapshots.save()};
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
onRun(runnable) {
|
package/lib/snapshot-manager.js
CHANGED
|
@@ -182,8 +182,8 @@ function sortBlocks(blocksByTitle, blockIndices) {
|
|
|
182
182
|
);
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
function encodeSnapshots(snapshotData) {
|
|
186
|
-
const encoded = cbor.
|
|
185
|
+
async function encodeSnapshots(snapshotData) {
|
|
186
|
+
const encoded = await cbor.encodeAsync(snapshotData, {
|
|
187
187
|
omitUndefinedProperties: true,
|
|
188
188
|
canonical: true,
|
|
189
189
|
});
|
|
@@ -351,7 +351,7 @@ class Manager {
|
|
|
351
351
|
this.recordSerialized({belongsTo, index, ...snapshot});
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
-
save() {
|
|
354
|
+
async save() {
|
|
355
355
|
const {dir, relFile, snapFile, snapPath, reportPath} = this;
|
|
356
356
|
|
|
357
357
|
if (this.updating && this.newBlocksByTitle.size === 0) {
|
|
@@ -371,15 +371,17 @@ class Manager {
|
|
|
371
371
|
),
|
|
372
372
|
};
|
|
373
373
|
|
|
374
|
-
const buffer = encodeSnapshots(snapshots);
|
|
374
|
+
const buffer = await encodeSnapshots(snapshots);
|
|
375
375
|
const reportBuffer = generateReport(relFile, snapFile, snapshots);
|
|
376
376
|
|
|
377
|
-
fs.
|
|
377
|
+
await fs.promises.mkdir(dir, {recursive: true});
|
|
378
378
|
|
|
379
379
|
const temporaryFiles = [];
|
|
380
380
|
const tmpfileCreated = file => temporaryFiles.push(file);
|
|
381
|
-
|
|
382
|
-
|
|
381
|
+
await Promise.all([
|
|
382
|
+
writeFileAtomic(snapPath, buffer, {tmpfileCreated}),
|
|
383
|
+
writeFileAtomic(reportPath, reportBuffer, {tmpfileCreated}),
|
|
384
|
+
]);
|
|
383
385
|
return {
|
|
384
386
|
changedFiles: [snapPath, reportPath],
|
|
385
387
|
temporaryFiles,
|
|
@@ -398,7 +400,9 @@ const resolveSourceFile = mem(file => {
|
|
|
398
400
|
return file;
|
|
399
401
|
}
|
|
400
402
|
|
|
401
|
-
return
|
|
403
|
+
return payload.sources[0].startsWith('file://')
|
|
404
|
+
? fileURLToPath(payload.sources[0])
|
|
405
|
+
: payload.sources[0];
|
|
402
406
|
});
|
|
403
407
|
|
|
404
408
|
export const determineSnapshotDir = mem(({file, fixedLocation, projectDir}) => {
|
package/lib/test.js
CHANGED
|
@@ -24,16 +24,16 @@ const testMap = new WeakMap();
|
|
|
24
24
|
class ExecutionContext extends Assertions {
|
|
25
25
|
constructor(test) {
|
|
26
26
|
super({
|
|
27
|
-
pass
|
|
27
|
+
pass() {
|
|
28
28
|
test.countPassedAssertion();
|
|
29
29
|
},
|
|
30
|
-
pending
|
|
30
|
+
pending(promise) {
|
|
31
31
|
test.addPendingAssertion(promise);
|
|
32
32
|
},
|
|
33
|
-
fail
|
|
33
|
+
fail(error) {
|
|
34
34
|
test.addFailedAssertion(error);
|
|
35
35
|
},
|
|
36
|
-
skip
|
|
36
|
+
skip() {
|
|
37
37
|
test.countPassedAssertion();
|
|
38
38
|
},
|
|
39
39
|
compareWithSnapshot: options => test.compareWithSnapshot(options),
|
|
@@ -109,7 +109,7 @@ class ExecutionContext extends Assertions {
|
|
|
109
109
|
logs: [...logs], // Don't allow modification of logs.
|
|
110
110
|
passed,
|
|
111
111
|
title: attemptTitle,
|
|
112
|
-
commit
|
|
112
|
+
commit({retainLogs = true} = {}) {
|
|
113
113
|
if (committed) {
|
|
114
114
|
return;
|
|
115
115
|
}
|
|
@@ -132,7 +132,7 @@ class ExecutionContext extends Assertions {
|
|
|
132
132
|
startingSnapshotCount,
|
|
133
133
|
});
|
|
134
134
|
},
|
|
135
|
-
discard
|
|
135
|
+
discard({retainLogs = false} = {}) {
|
|
136
136
|
if (committed) {
|
|
137
137
|
test.saveFirstError(new Error('Can’t discard a result that was previously committed'));
|
|
138
138
|
return;
|
package/lib/worker/base.js
CHANGED
|
@@ -81,7 +81,7 @@ const run = async options => {
|
|
|
81
81
|
|
|
82
82
|
runner.on('finish', async () => {
|
|
83
83
|
try {
|
|
84
|
-
const {touchedFiles} = runner.saveSnapshotState();
|
|
84
|
+
const {touchedFiles} = await runner.saveSnapshotState();
|
|
85
85
|
if (touchedFiles) {
|
|
86
86
|
channel.send({type: 'touched-files', files: touchedFiles});
|
|
87
87
|
}
|
|
@@ -123,13 +123,13 @@ const run = async options => {
|
|
|
123
123
|
// Install before processing options.require, so if helpers are added to the
|
|
124
124
|
// require configuration the *compiled* helper will be loaded.
|
|
125
125
|
const {projectDir, providerStates = []} = options;
|
|
126
|
-
const providers =
|
|
126
|
+
const providers = [];
|
|
127
|
+
await Promise.all(providerStates.map(async ({type, state}) => {
|
|
127
128
|
if (type === 'typescript') {
|
|
128
|
-
|
|
129
|
+
const provider = await providerManager.typescript(projectDir);
|
|
130
|
+
providers.push(provider.worker({extensionsToLoadAsModules, state}));
|
|
129
131
|
}
|
|
130
|
-
|
|
131
|
-
return null;
|
|
132
|
-
}))).filter(provider => provider !== null);
|
|
132
|
+
}));
|
|
133
133
|
|
|
134
134
|
const require = createRequire(import.meta.url);
|
|
135
135
|
const load = async ref => {
|
package/lib/worker/channel.cjs
CHANGED
|
@@ -3,12 +3,44 @@ const events = require('events');
|
|
|
3
3
|
const process = require('process');
|
|
4
4
|
const {MessageChannel, threadId} = require('worker_threads');
|
|
5
5
|
|
|
6
|
-
const pEvent = require('p-event');
|
|
7
|
-
|
|
8
6
|
const timers = require('../now-and-timers.cjs');
|
|
9
7
|
|
|
10
8
|
const {isRunningInChildProcess, isRunningInThread} = require('./utils.cjs');
|
|
11
9
|
|
|
10
|
+
let pEvent = async (emitter, event, options) => {
|
|
11
|
+
// We need to import p-event, but import() is asynchronous. Buffer any events
|
|
12
|
+
// emitted in the meantime. Don't handle errors.
|
|
13
|
+
const buffer = [];
|
|
14
|
+
const addToBuffer = (...args) => buffer.push(args);
|
|
15
|
+
emitter.on(event, addToBuffer);
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
({pEvent} = await import('p-event')); // eslint-disable-line node/no-unsupported-features/es-syntax
|
|
19
|
+
} finally {
|
|
20
|
+
emitter.off(event, addToBuffer);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (buffer.length === 0) {
|
|
24
|
+
return pEvent(emitter, event, options);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Now replay buffered events.
|
|
28
|
+
const replayEmitter = new events.EventEmitter();
|
|
29
|
+
const promise = pEvent(replayEmitter, event, options);
|
|
30
|
+
for (const args of buffer) {
|
|
31
|
+
replayEmitter.emit(event, ...args);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const replay = (...args) => replayEmitter.emit(event, ...args);
|
|
35
|
+
emitter.on(event, replay);
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
return await promise;
|
|
39
|
+
} finally {
|
|
40
|
+
emitter.off(event, replay);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
12
44
|
const selectAvaMessage = type => message => message.ava && message.ava.type === type;
|
|
13
45
|
|
|
14
46
|
class RefCounter {
|
|
@@ -6,12 +6,11 @@ const {isRunningInThread, isRunningInChildProcess} = require('./utils.cjs');
|
|
|
6
6
|
|
|
7
7
|
// Check if the test is being run without AVA cli
|
|
8
8
|
if (!isRunningInChildProcess && !isRunningInThread) {
|
|
9
|
-
const chalk = require('chalk'); // Use default Chalk instance.
|
|
10
9
|
if (process.argv[1]) {
|
|
11
10
|
const fp = path.relative('.', process.argv[1]);
|
|
12
11
|
|
|
13
12
|
console.log();
|
|
14
|
-
console.error(`Test files must be run with the AVA CLI:\n\n $
|
|
13
|
+
console.error(`Test files must be run with the AVA CLI:\n\n $ ava ${fp}\n`);
|
|
15
14
|
|
|
16
15
|
process.exit(1); // eslint-disable-line unicorn/no-process-exit
|
|
17
16
|
} else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ava",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Node.js test runner that lets you develop with confidence.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": "avajs/ava",
|
|
@@ -70,72 +70,71 @@
|
|
|
70
70
|
"typescript"
|
|
71
71
|
],
|
|
72
72
|
"dependencies": {
|
|
73
|
-
"acorn": "^8.
|
|
73
|
+
"acorn": "^8.7.0",
|
|
74
74
|
"acorn-walk": "^8.2.0",
|
|
75
75
|
"ansi-styles": "^6.1.0",
|
|
76
76
|
"arrgv": "^1.0.2",
|
|
77
77
|
"arrify": "^3.0.0",
|
|
78
78
|
"callsites": "^4.0.0",
|
|
79
|
-
"cbor": "^8.0
|
|
80
|
-
"chalk": "^
|
|
81
|
-
"chokidar": "^3.5.
|
|
79
|
+
"cbor": "^8.1.0",
|
|
80
|
+
"chalk": "^5.0.0",
|
|
81
|
+
"chokidar": "^3.5.3",
|
|
82
82
|
"chunkd": "^2.0.1",
|
|
83
|
-
"ci-info": "^3.
|
|
83
|
+
"ci-info": "^3.3.0",
|
|
84
84
|
"ci-parallel-vars": "^1.0.1",
|
|
85
85
|
"clean-yaml-object": "^0.1.0",
|
|
86
86
|
"cli-truncate": "^3.1.0",
|
|
87
|
-
"code-excerpt": "^
|
|
87
|
+
"code-excerpt": "^4.0.0",
|
|
88
88
|
"common-path-prefix": "^3.0.0",
|
|
89
89
|
"concordance": "^5.0.4",
|
|
90
90
|
"currently-unhandled": "^0.4.1",
|
|
91
|
-
"debug": "^4.3.
|
|
91
|
+
"debug": "^4.3.3",
|
|
92
92
|
"del": "^6.0.0",
|
|
93
|
-
"emittery": "^0.10.
|
|
94
|
-
"equal-length": "^1.0.1",
|
|
93
|
+
"emittery": "^0.10.1",
|
|
95
94
|
"figures": "^4.0.0",
|
|
96
|
-
"globby": "^
|
|
95
|
+
"globby": "^13.1.1",
|
|
97
96
|
"ignore-by-default": "^2.0.0",
|
|
98
97
|
"indent-string": "^5.0.0",
|
|
99
98
|
"is-error": "^2.2.2",
|
|
100
99
|
"is-plain-object": "^5.0.0",
|
|
101
100
|
"is-promise": "^4.0.0",
|
|
102
101
|
"matcher": "^5.0.0",
|
|
103
|
-
"mem": "^9.0.
|
|
102
|
+
"mem": "^9.0.2",
|
|
104
103
|
"ms": "^2.1.3",
|
|
105
|
-
"p-event": "^
|
|
106
|
-
"p-map": "^5.
|
|
107
|
-
"picomatch": "^2.3.
|
|
104
|
+
"p-event": "^5.0.1",
|
|
105
|
+
"p-map": "^5.3.0",
|
|
106
|
+
"picomatch": "^2.3.1",
|
|
108
107
|
"pkg-conf": "^4.0.0",
|
|
109
|
-
"plur": "^
|
|
108
|
+
"plur": "^5.1.0",
|
|
110
109
|
"pretty-ms": "^7.0.1",
|
|
111
110
|
"resolve-cwd": "^3.0.0",
|
|
112
111
|
"slash": "^3.0.0",
|
|
113
|
-
"stack-utils": "^2.0.
|
|
112
|
+
"stack-utils": "^2.0.5",
|
|
114
113
|
"strip-ansi": "^7.0.1",
|
|
115
|
-
"supertap": "^
|
|
114
|
+
"supertap": "^3.0.1",
|
|
116
115
|
"temp-dir": "^2.0.0",
|
|
117
|
-
"write-file-atomic": "^
|
|
118
|
-
"yargs": "^17.
|
|
116
|
+
"write-file-atomic": "^4.0.1",
|
|
117
|
+
"yargs": "^17.3.1"
|
|
119
118
|
},
|
|
120
119
|
"devDependencies": {
|
|
121
120
|
"@ava/test": "github:avajs/test",
|
|
122
|
-
"@ava/typescript": "^
|
|
123
|
-
"@sinonjs/fake-timers": "^
|
|
121
|
+
"@ava/typescript": "^3.0.1",
|
|
122
|
+
"@sinonjs/fake-timers": "^9.1.1",
|
|
124
123
|
"ansi-escapes": "^5.0.0",
|
|
125
|
-
"c8": "^7.
|
|
124
|
+
"c8": "^7.11.0",
|
|
126
125
|
"delay": "^5.0.0",
|
|
127
|
-
"execa": "^
|
|
128
|
-
"fs-extra": "^10.0.
|
|
126
|
+
"execa": "^6.1.0",
|
|
127
|
+
"fs-extra": "^10.0.1",
|
|
129
128
|
"get-stream": "^6.0.1",
|
|
130
129
|
"replace-string": "^4.0.0",
|
|
131
|
-
"sinon": "^
|
|
132
|
-
"tap": "^
|
|
130
|
+
"sinon": "^13.0.1",
|
|
131
|
+
"tap": "^16.0.0",
|
|
133
132
|
"temp-write": "^5.0.0",
|
|
134
133
|
"tempy": "^2.0.0",
|
|
135
134
|
"touch": "^3.1.0",
|
|
136
|
-
"tsd": "^0.
|
|
137
|
-
"typescript": "^4.
|
|
138
|
-
"xo": "^0.
|
|
135
|
+
"tsd": "^0.19.1",
|
|
136
|
+
"typescript": "^4.6.2",
|
|
137
|
+
"xo": "^0.48.0",
|
|
139
138
|
"zen-observable": "^0.8.15"
|
|
140
139
|
},
|
|
141
140
|
"peerDependencies": {
|
package/readme.md
CHANGED
|
@@ -6,7 +6,7 @@ Follow the [AVA Twitter account](https://twitter.com/ava__js) for updates.
|
|
|
6
6
|
|
|
7
7
|
Read our [contributing guide](.github/CONTRIBUTING.md) if you're looking to contribute (issues / PRs / etc).
|
|
8
8
|
|
|
9
|
-

|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
Translations: [Español](https://github.com/avajs/ava-docs/blob/master/es_ES/readme.md), [Français](https://github.com/avajs/ava-docs/blob/master/fr_FR/readme.md), [Italiano](https://github.com/avajs/ava-docs/blob/master/it_IT/readme.md), [日本語](https://github.com/avajs/ava-docs/blob/master/ja_JP/readme.md), [한국어](https://github.com/avajs/ava-docs/blob/master/ko_KR/readme.md), [Português](https://github.com/avajs/ava-docs/blob/master/pt_BR/readme.md), [Русский](https://github.com/avajs/ava-docs/blob/master/ru_RU/readme.md), [简体中文](https://github.com/avajs/ava-docs/blob/master/zh_CN/readme.md)
|
|
@@ -71,7 +71,7 @@ Don't forget to configure the `test` script in your `package.json` as per above.
|
|
|
71
71
|
Create a file named `test.js` in the project root directory:
|
|
72
72
|
|
|
73
73
|
```js
|
|
74
|
-
|
|
74
|
+
import test from 'ava';
|
|
75
75
|
|
|
76
76
|
test('foo', t => {
|
|
77
77
|
t.pass();
|
|
@@ -139,17 +139,16 @@ We have a growing list of [common pitfalls](docs/08-common-pitfalls.md) you may
|
|
|
139
139
|
|
|
140
140
|
### Recipes
|
|
141
141
|
|
|
142
|
-
- [Shared workers](docs/recipes/shared-workers.md)
|
|
143
142
|
- [Test setup](docs/recipes/test-setup.md)
|
|
144
|
-
- [
|
|
143
|
+
- [TypeScript](docs/recipes/typescript.md)
|
|
144
|
+
- [Shared workers](docs/recipes/shared-workers.md)
|
|
145
145
|
- [Watch mode](docs/recipes/watch-mode.md)
|
|
146
|
-
- [Endpoint testing](docs/recipes/endpoint-testing.md)
|
|
147
146
|
- [When to use `t.plan()`](docs/recipes/when-to-use-plan.md)
|
|
148
|
-
- [Browser testing](docs/recipes/browser-testing.md)
|
|
149
|
-
- [TypeScript](docs/recipes/typescript.md)
|
|
150
|
-
- [Using ES modules](docs/recipes/es-modules.md)
|
|
151
147
|
- [Passing arguments to your test files](docs/recipes/passing-arguments-to-your-test-files.md)
|
|
152
|
-
- [
|
|
148
|
+
- [Splitting tests in CI](docs/recipes/splitting-tests-ci.md)
|
|
149
|
+
- [Code coverage](docs/recipes/code-coverage.md)
|
|
150
|
+
- [Endpoint testing](docs/recipes/endpoint-testing.md)
|
|
151
|
+
- [Browser testing](docs/recipes/browser-testing.md)
|
|
153
152
|
- [Testing Vue.js components](docs/recipes/vue.md)
|
|
154
153
|
- [Debugging tests with Chrome DevTools](docs/recipes/debugging-with-chrome-devtools.md)
|
|
155
154
|
- [Debugging tests with VSCode](docs/recipes/debugging-with-vscode.md)
|
|
@@ -188,7 +187,6 @@ It's the [Andromeda galaxy](https://simple.wikipedia.org/wiki/Andromeda_galaxy).
|
|
|
188
187
|
|
|
189
188
|
- [eslint-plugin-ava](https://github.com/avajs/eslint-plugin-ava) — Lint rules for AVA tests
|
|
190
189
|
- [@ava/typescript](https://github.com/avajs/typescript) — Test TypeScript projects
|
|
191
|
-
- [@ava/babel](https://github.com/avajs/babel) — Compile test files using Babel, for AVA 3
|
|
192
190
|
- [@ava/cooperate](https://github.com/avajs/cooperate) — Low-level primitives to enable cooperation between test files
|
|
193
191
|
- [@ava/get-port](https://github.com/avajs/get-port) — Reserve a port while testing
|
|
194
192
|
|
package/types/assertions.d.ts
CHANGED
|
@@ -138,6 +138,18 @@ export interface DeepEqualAssertion {
|
|
|
138
138
|
*/
|
|
139
139
|
<Actual, Expected extends Actual>(actual: Actual, expected: Expected, message?: string): actual is Expected;
|
|
140
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Assert that `actual` is [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to
|
|
143
|
+
* `expected`, returning a boolean indicating whether the assertion passed.
|
|
144
|
+
*/
|
|
145
|
+
<Actual extends Expected, Expected>(actual: Actual, expected: Expected, message?: string): expected is Actual;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Assert that `actual` is [deeply equal](https://github.com/concordancejs/concordance#comparison-details) to
|
|
149
|
+
* `expected`, returning a boolean indicating whether the assertion passed.
|
|
150
|
+
*/
|
|
151
|
+
<Actual, Expected>(actual: Actual, expected: Expected, message?: string): boolean;
|
|
152
|
+
|
|
141
153
|
/** Skip this assertion. */
|
|
142
154
|
skip(actual: any, expected: any, message?: string): void;
|
|
143
155
|
}
|