Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: avajs/ava
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v6.1.3
Choose a base ref
...
head repository: avajs/ava
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v6.2.0
Choose a head ref
  • 5 commits
  • 71 files changed
  • 4 contributors

Commits on Aug 20, 2024

  1. Update dependencies & other general maintenance

    * Upgrade XO
    * Update TypeScript-related dependencies — Don't upgrade @sindresorhus/tsconfig to v6 since it requires TS 5.5 and we test with 5.2.
    * Update dev dependencies
    * Update dependencies
    * Rebuild lockfile
    * Upgrade CodeCov action
    * Update reporter log instructions and remove deprecation warnings
    * Make Node.js 22.6 the recommended development version
    * Remove Node.js 21 from the engines list: It's no longer maintained by Node.js itself, see also <https://github.com/avajs/ava/blob/main/docs/support-statement.md>.
    novemberborn authored Aug 20, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    b15df53 View commit details
  2. Add filterNodeArgumentsForWorkerThreads option

    This filter function can be used to remove node arguments that may be applied to the main process, but cannot be used to start worker threads. It can only be configured in a JavaScript configuration file. It does not apply when using child processes.
    
    Co-authored-by: Tommy <[email protected]>
    Co-authored-by: Mark Wubben <[email protected]>
    3 people authored Aug 20, 2024

    Partially verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    We cannot verify signatures from co-authors, and some of the co-authors attributed to this commit require their commits to be signed.
    Copy the full SHA
    86185b4 View commit details

Commits on Oct 27, 2024

  1. Link to CLI options to clarify how to run tests in separate processes

    Co-authored-by: Mark Wubben <[email protected]>
    thoughtsunificator and novemberborn authored Oct 27, 2024

    Partially verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    We cannot verify signatures from co-authors, and some of the co-authors attributed to this commit require their commits to be signed.
    Copy the full SHA
    e5b355d View commit details
  2. Pre-release updates

    * Update dependencies
    * Add Node.js >= 23 to test matrix
    novemberborn authored Oct 27, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    563fa5f View commit details
  3. 6.2.0

    novemberborn committed Oct 27, 2024

    Verified

    This commit was signed with the committer’s verified signature.
    novemberborn Mark Wubben
    Copy the full SHA
    024de32 View commit details
Showing with 3,815 additions and 3,066 deletions.
  1. +4 −3 .github/workflows/ci.yml
  2. +2 −0 .xo-config.cjs
  3. +1 −1 docs/01-writing-tests.md
  4. +16 −0 docs/06-configuration.md
  5. +1 −1 docs/support-statement.md
  6. +3 −1 examples/endpoint-testing/app.js
  7. +15 −6 lib/assert.js
  8. +6 −0 lib/cli.js
  9. +3 −4 lib/eslint-plugin-helper-worker.js
  10. +7 −0 lib/load-config.js
  11. +9 −4 lib/plugin-support/shared-workers.js
  12. +2 −2 lib/runner.js
  13. +2 −2 lib/test.js
  14. +3 −3 lib/watcher.js
  15. +5 −5 lib/worker/base.js
  16. +3 −3 lib/worker/channel.cjs
  17. +3,539 −2,840 package-lock.json
  18. +27 −27 package.json
  19. +1 −1 scripts/ci.sh
  20. +2 −0 test-tap/api.js
  21. +3 −1 test-tap/fixture/load-config/cjs/ava.config.cjs
  22. +3 −1 test-tap/fixture/load-config/factory-no-plain-return/ava.config.js
  23. +3 −0 test-tap/fixture/load-config/non-function/ava.config.js
  24. +3 −0 test-tap/fixture/load-config/non-function/package.json
  25. +3 −1 test-tap/fixture/load-config/package-no-file-yes-factory/ava.config.js
  26. +2 −2 test-tap/fixture/report/regular/nested-objects.cjs
  27. +3 −1 test-tap/helper/fix-reporter-env.js
  28. +0 −18 test-tap/reporters/default.edgecases.v22.log
  29. 0 test-tap/reporters/{default.edgecases.v21.log → default.edgecases.v23.log}
  30. +0 −3 test-tap/reporters/default.failfast.v22.log
  31. 0 test-tap/reporters/{default.failfast.v21.log → default.failfast.v23.log}
  32. +0 −3 test-tap/reporters/default.failfast2.v22.log
  33. 0 test-tap/reporters/{default.failfast2.v21.log → default.failfast2.v23.log}
  34. +0 −6 test-tap/reporters/default.only.v22.log
  35. 0 test-tap/reporters/{default.only.v21.log → default.only.v23.log}
  36. +0 −21 test-tap/reporters/default.regular.v22.log
  37. 0 test-tap/reporters/{default.regular.v21.log → default.regular.v23.log}
  38. +0 −6 test-tap/reporters/default.timeoutcontextlogs.v22.log
  39. 0 test-tap/reporters/{default.timeoutcontextlogs.v21.log → default.timeoutcontextlogs.v23.log}
  40. +0 −6 test-tap/reporters/default.timeoutinmultiplefiles.v22.log
  41. 0 test-tap/reporters/{default.timeoutinmultiplefiles.v21.log → default.timeoutinmultiplefiles.v23.log}
  42. +0 −3 test-tap/reporters/default.timeoutinsinglefile.v22.log
  43. 0 test-tap/reporters/{default.timeoutinsinglefile.v21.log → default.timeoutinsinglefile.v23.log}
  44. +0 −3 test-tap/reporters/default.timeoutwithmatch.v22.log
  45. 0 test-tap/reporters/{default.timeoutwithmatch.v21.log → default.timeoutwithmatch.v23.log}
  46. +0 −9 test-tap/reporters/default.watch.v22.log
  47. 0 test-tap/reporters/{default.watch.v21.log → default.watch.v23.log}
  48. +14 −2 test-tap/reporters/readme.md
  49. +0 −18 test-tap/reporters/tap.edgecases.v22.log
  50. 0 test-tap/reporters/{tap.edgecases.v21.log → tap.edgecases.v23.log}
  51. +0 −3 test-tap/reporters/tap.failfast.v22.log
  52. 0 test-tap/reporters/{tap.failfast.v21.log → tap.failfast.v23.log}
  53. +0 −3 test-tap/reporters/tap.failfast2.v22.log
  54. 0 test-tap/reporters/{tap.failfast2.v21.log → tap.failfast2.v23.log}
  55. +0 −6 test-tap/reporters/tap.only.v22.log
  56. 0 test-tap/reporters/{tap.only.v21.log → tap.only.v23.log}
  57. +0 −21 test-tap/reporters/tap.regular.v22.log
  58. 0 test-tap/reporters/{tap.regular.v21.log → tap.regular.v23.log}
  59. +2 −0 test/config/loader.js
  60. +12 −0 test/config/snapshots/loader.js.md
  61. BIN test/config/snapshots/loader.js.snap
  62. +24 −24 test/helpers/exec.js
  63. +3 −0 test/node-arguments/fixtures/thread-arguments-filter/package.json
  64. +7 −0 test/node-arguments/fixtures/thread-arguments-filter/process.js
  65. +9 −0 test/node-arguments/fixtures/thread-arguments-filter/thread-arguments-filter.config.mjs
  66. +7 −0 test/node-arguments/fixtures/thread-arguments-filter/thread.js
  67. +44 −0 test/node-arguments/snapshots/test.js.md
  68. BIN test/node-arguments/snapshots/test.js.snap
  69. +20 −0 test/node-arguments/test.js
  70. +1 −1 test/shared-workers/lifecycle/test.js
  71. +1 −1 test/test-process-exit/test.js
7 changes: 4 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [^18.18, ^20.8, ^21, ^22]
node-version: [^18.18, ^20.8, ^22, ^23]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v4
@@ -31,17 +31,18 @@ jobs:
- run: npm install --no-audit
- run: ./scripts/ci.sh
shell: bash
- uses: codecov/codecov-action@v3
- uses: codecov/codecov-action@v4
with:
files: coverage/lcov.info
name: ${{ matrix.os }}/${{ matrix.node-version }}
token: ${{ secrets.CODECOV_TOKEN }}

typescript:
name: TypeScript compatibility
runs-on: ubuntu-latest
strategy:
matrix:
ts-version: [~5.2, ~5.3]
ts-version: [~5.2, ~5.3, ~5.4, ~5.5, ~5.6]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
2 changes: 2 additions & 0 deletions .xo-config.cjs
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@ module.exports = {
files: '**/*.d.*(c|m)ts',
rules: {
'import/extensions': 'off',
'n/file-extension-in-import': 'off',
},
},
{
@@ -91,6 +92,7 @@ module.exports = {
rules: {
'import/no-anonymous-default-export': 'off',
'n/prefer-global/process': 'off',
'promise/prefer-await-to-then': 'off',
'unicorn/error-message': 'off',
},
},
2 changes: 1 addition & 1 deletion docs/01-writing-tests.md
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ AVA tries to run test files with their current working directory set to the dire

## Test isolation

By default each test file is run in a new worker thread. You can fall back running in separate processes.
By default each test file is run in a new worker thread. You can fall back running in separate processes (see `workerThreads` [CLI option](./06-configuration.md#options)).

AVA will set `process.env.NODE_ENV` to `test`, unless the `NODE_ENV` environment variable has been set. This is useful if the code you're testing has test defaults (for example when picking what database to connect to). It may cause your code or its dependencies to behave differently though. Note that `'NODE_ENV' in process.env` will always be `true`.

16 changes: 16 additions & 0 deletions docs/06-configuration.md
Original file line number Diff line number Diff line change
@@ -337,3 +337,19 @@ These may also export a function which is then invoked, and can receive argument
The `nodeArguments` configuration may be used to specify additional arguments for launching worker processes. These are combined with `--node-arguments` passed on the CLI and any arguments passed to the `node` binary when starting AVA.

[CLI]: ./05-command-line.md

## Node arguments filter for worker threads

In a config file only, `filterNodeArgumentsForWorkerThreads` may provide a function used for filtering `nodeArguments` sent to worker threads. This enables excluding arguments that throw if sent to a thread. The filter is ignored by worker processes.

`ava.config.js`:
```js
const processOnly = new Set([
'--allow-natives-syntax',
'--expose-gc'
]);

export default {
filterNodeArgumentsForWorkerThreads: argument => !processOnly.has(argument)
}
```
2 changes: 1 addition & 1 deletion docs/support-statement.md
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ AVA supports the latest release of any major version that [is supported by Node.

When we drop support for an LTS-covered major version we will bump AVA's major version number.

We will drop support for odd-numbered Node.js versions (e.g. `11` or `13`) *without* bumping AVA's major version number.
We will drop support for odd-numbered Node.js versions (e.g. `21` or `23`) *without* bumping AVA's major version number.

We try to avoid *accidentally* dropping support for non-latest Node.js releases. If such breakage does occur we'll accept pull requests to restore functionality. We might decide to deprecate the offending AVA release and bump AVA's major version number instead.

4 changes: 3 additions & 1 deletion examples/endpoint-testing/app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

module.exports = (request, response) => {
const app = (request, response) => {
if (request.url === '/user') {
response.setHeader('Content-Type', 'application/json');
response.end(JSON.stringify({email: 'ava@rocks.com'}));
@@ -9,3 +9,5 @@ module.exports = (request, response) => {
response.end();
}
};

module.exports = app;
21 changes: 15 additions & 6 deletions lib/assert.js
Original file line number Diff line number Diff line change
@@ -423,7 +423,7 @@ export class Assertions {
retval = fn();
if (isPromise(retval)) {
// Here isPromise() checks if something is "promise like". Cast to an actual promise.
Promise.resolve(retval).catch(noop);
Promise.resolve(retval).catch(noop); // eslint-disable-line promise/prefer-await-to-then
throw fail(new AssertionError(message, {
assertion: 't.throws()',
formattedDetails: [formatWithLabel('Function returned a promise. Use `t.throwsAsync()` instead:', retval)],
@@ -462,7 +462,10 @@ export class Assertions {
try {
assertMessage(message, 't.throwsAsync()');
} catch (error) {
Promise.resolve(thrower).catch(noop);
try {
await thrower;
} catch {}

throw error;
}

@@ -476,15 +479,18 @@ export class Assertions {
try {
expectations = validateExpectations('t.throwsAsync()', expectations, args.length, experiments);
} catch (error) {
Promise.resolve(thrower).catch(noop);
try {
await thrower;
} catch {}

throw fail(error);
}

const handlePromise = async (promise, wasReturned) => {
// Record the stack before it gets lost in the promise chain.
const assertionStack = getAssertionStack();
// Handle "promise like" objects by casting to a real Promise.
const intermediate = Promise.resolve(promise).then(value => {
const intermediate = Promise.resolve(promise).then(value => { // eslint-disable-line promise/prefer-await-to-then
throw failPending(new AssertionError(message, {
assertion: 't.throwsAsync()',
assertionStack,
@@ -568,7 +574,10 @@ export class Assertions {
try {
assertMessage(message, 't.notThrowsAsync()');
} catch (error) {
Promise.resolve(nonThrower).catch(noop);
try {
await nonThrower;
} catch {}

throw error;
}

@@ -583,7 +592,7 @@ export class Assertions {
// Create an error object to record the stack before it gets lost in the promise chain.
const assertionStack = getAssertionStack();
// Handle "promise like" objects by casting to a real Promise.
const intermediate = Promise.resolve(promise).then(noop, error => {
const intermediate = Promise.resolve(promise).then(noop, error => { // eslint-disable-line promise/prefer-await-to-then
throw failPending(new AssertionError(message, {
assertion: 't.notThrowsAsync()',
assertionStack,
6 changes: 6 additions & 0 deletions lib/cli.js
Original file line number Diff line number Diff line change
@@ -388,9 +388,15 @@ export default async function loadCli() { // eslint-disable-line complexity
exit(error.message);
}

const workerThreads = combined.workerThreads !== false;

let nodeArguments;
try {
nodeArguments = normalizeNodeArguments(conf.nodeArguments, argv['node-arguments']);
if (workerThreads && 'filterNodeArgumentsForWorkerThreads' in conf) {
const {filterNodeArgumentsForWorkerThreads: filter} = conf;
nodeArguments = nodeArguments.filter(argument => filter(argument));
}
} catch (error) {
exit(error.message);
}
7 changes: 3 additions & 4 deletions lib/eslint-plugin-helper-worker.js
Original file line number Diff line number Diff line change
@@ -41,10 +41,9 @@ const buildGlobs = ({conf, providers, projectDir, overrideExtensions, overrideFi

const resolveGlobs = async (projectDir, overrideExtensions, overrideFiles) => {
if (!configCache.has(projectDir)) {
configCache.set(projectDir, loadConfig({resolveFrom: projectDir}).then(async ({config: conf}) => {
const providers = await collectProviders({conf, projectDir});
return {conf, providers};
}));
const {config: conf} = await loadConfig({resolveFrom: projectDir});
const providers = await collectProviders({conf, projectDir});
configCache.set(projectDir, {conf, providers});
}

const {conf, providers} = await configCache.get(projectDir);
7 changes: 7 additions & 0 deletions lib/load-config.js
Original file line number Diff line number Diff line change
@@ -157,6 +157,13 @@ export async function loadConfig({configFile, resolveFrom = process.cwd(), defau
...defaults, nonSemVerExperiments: {}, ...fileConf, ...packageConf, projectDir, configFile,
};

if (
'filterNodeArgumentsForWorkerThreads' in config
&& typeof config.filterNodeArgumentsForWorkerThreads !== 'function'
) {
throw new Error(`filterNodeArgumentsForWorkerThreads from ${fileForErrorMessage} must be a function`);
}

const {nonSemVerExperiments: experiments} = config;
if (!isPlainObject(experiments)) {
throw new Error(`nonSemVerExperiments from ${fileForErrorMessage} must be an object`);
13 changes: 9 additions & 4 deletions lib/plugin-support/shared-workers.js
Original file line number Diff line number Diff line change
@@ -16,6 +16,11 @@ const waitForAvailable = async worker => {
}
};

const waitForError = async worker => {
const [error] = await events.once(worker, 'error');
return tagWorkerError(error);
};

function launchWorker(filename, initialData) {
if (launchedWorkers.has(filename)) {
return launchedWorkers.get(filename);
@@ -34,7 +39,7 @@ function launchWorker(filename, initialData) {
const launched = {
statePromises: {
available: waitForAvailable(worker),
error: events.once(worker, 'error').then(([error]) => tagWorkerError(error)),
error: waitForError(worker),
},
exited: false,
worker,
@@ -79,7 +84,7 @@ export async function observeWorkerProcess(fork, runStatus) {
}
};

fork.promise.finally(() => {
fork.promise.finally(() => { // eslint-disable-line promise/prefer-await-to-then
removeAllInstances();
});

@@ -94,7 +99,7 @@ export async function observeWorkerProcess(fork, runStatus) {
}
};

launched.statePromises.error.then(error => {
launched.statePromises.error.then(error => { // eslint-disable-line promise/prefer-await-to-then
launched.worker.off('message', handleWorkerMessage);
removeAllInstances();
runStatus.emitStateChange({type: 'shared-worker-error', err: serializeError(error)});
@@ -113,7 +118,7 @@ export async function observeWorkerProcess(fork, runStatus) {
port,
}, [port]);

fork.promise.finally(() => {
fork.promise.finally(() => { // eslint-disable-line promise/prefer-await-to-then
launched.worker.postMessage({
type: 'deregister-test-worker',
id: fork.threadId,
4 changes: 2 additions & 2 deletions lib/runner.js
Original file line number Diff line number Diff line change
@@ -489,7 +489,7 @@ export default class Runner extends Emittery {

// Note that the hooks and tests always begin running asynchronously.
const beforePromise = this.runHooks(this.tasks.before, contextRef);
const serialPromise = beforePromise.then(beforeHooksOk => {
const serialPromise = beforePromise.then(beforeHooksOk => { // eslint-disable-line promise/prefer-await-to-then
// Don't run tests if a `before` hook failed.
if (!beforeHooksOk) {
return false;
@@ -511,7 +511,7 @@ export default class Runner extends Emittery {
return this.runTest(task, contextRef.copy());
}, true);
});
const concurrentPromise = Promise.all([beforePromise, serialPromise]).then(async ([beforeHooksOk, serialOk]) => {
const concurrentPromise = Promise.all([beforePromise, serialPromise]).then(async ([beforeHooksOk, serialOk]) => { // eslint-disable-line promise/prefer-await-to-then
// Don't run tests if a `before` hook failed, or if `failFast` is enabled
// and a previous serial test failed.
if (!beforeHooksOk || (!serialOk && this.failFast)) {
4 changes: 2 additions & 2 deletions lib/test.js
Original file line number Diff line number Diff line change
@@ -590,7 +590,7 @@ export default class Test {
};

promise
.catch(error => {
.catch(error => { // eslint-disable-line promise/prefer-await-to-then
if (this.testFailure !== null && error === this.testFailure) {
return;
}
@@ -607,7 +607,7 @@ export default class Test {
}));
}
})
.then(() => resolve(this.finish()));
.then(() => resolve(this.finish())); // eslint-disable-line promise/prefer-await-to-then
});
}

6 changes: 3 additions & 3 deletions lib/watcher.js
Original file line number Diff line number Diff line change
@@ -72,7 +72,7 @@ async function * plan({api, filter, globs, projectDir, providers, stdin, abortSi
};

// Begin a file trace in the background.
fileTracer.update(findTests(cwdAndGlobs).then(testFiles => testFiles.map(path => ({
fileTracer.update(findTests(cwdAndGlobs).then(testFiles => testFiles.map(path => ({ // eslint-disable-line promise/prefer-await-to-then
path: nodePath.relative(projectDir, path),
isTest: true,
exists: true,
@@ -187,7 +187,7 @@ async function * plan({api, filter, globs, projectDir, providers, stdin, abortSi
// If the file tracer is still analyzing dependencies, wait for that to
// complete.
if (fileTracer.busy !== null) {
fileTracer.busy.then(() => debounce.refresh());
fileTracer.busy.then(() => debounce.refresh()); // eslint-disable-line promise/prefer-await-to-then
takeCoverageForSelfTests?.();
return;
}
@@ -526,7 +526,7 @@ class FileTracer {
}

update(changes) {
const current = this.#update(changes).finally(() => {
const current = this.#update(changes).finally(() => { // eslint-disable-line promise/prefer-await-to-then
if (this.#pendingTrace === current) {
this.#pendingTrace = null;
this.#updateRunning = new Promise(resolve => {
10 changes: 5 additions & 5 deletions lib/worker/base.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {mkdir} from 'node:fs/promises';
import {createRequire} from 'node:module';
import {join as joinPath, resolve as resolvePath} from 'node:path';
import path from 'node:path';
import process from 'node:process';
import {pathToFileURL} from 'node:url';
import {workerData} from 'node:worker_threads';
@@ -89,7 +89,7 @@ const run = async options => {

refs.runnerChain = runner.chain;

channel.peerFailed.then(() => {
channel.peerFailed.then(() => { // eslint-disable-line promise/prefer-await-to-then
runner.interrupt();
});

@@ -187,7 +187,7 @@ const run = async options => {

// Try to load the module as a file, relative to the project directory.
// Match load() behavior.
const fullPath = resolvePath(projectDir, ref);
const fullPath = path.resolve(projectDir, ref);
try {
for (const extension of extensionsToLoadAsModules) {
if (fullPath.endsWith(`.${extension}`)) {
@@ -208,9 +208,9 @@ const run = async options => {

let importFromProject = async ref => {
// Do not use the cacheDir since it's not guaranteed to be inside node_modules.
const avaCacheDir = joinPath(projectDir, 'node_modules', '.cache', 'ava');
const avaCacheDir = path.join(projectDir, 'node_modules', '.cache', 'ava');
await mkdir(avaCacheDir, {recursive: true});
const stubPath = joinPath(avaCacheDir, 'import-from-project.mjs');
const stubPath = path.join(avaCacheDir, 'import-from-project.mjs');
await writeFileAtomic(stubPath, 'export const importFromProject = ref => import(ref);\n');
({importFromProject} = await import(pathToFileURL(stubPath)));
return importFromProject(ref);
6 changes: 3 additions & 3 deletions lib/worker/channel.cjs
Original file line number Diff line number Diff line change
@@ -158,9 +158,9 @@ function registerSharedWorker(filename, initialData) {
// The attaching of message listeners will cause the port to be referenced by
// Node.js. In order to keep track, explicitly reference before attaching.
sharedWorkerHandle.ref();
const ready = selectAvaMessage(ourPort, 'ready').then(() => {
const ready = selectAvaMessage(ourPort, 'ready').then(() => { // eslint-disable-line promise/prefer-await-to-then
currentlyAvailable = error === null;
}).finally(() => {
}).finally(() => { // eslint-disable-line promise/prefer-await-to-then
// Once ready, it's up to user code to subscribe to messages, which (see
// below) causes us to reference the port.
sharedWorkerHandle.unref();
@@ -170,7 +170,7 @@ function registerSharedWorker(filename, initialData) {

// Errors are received over the test worker channel, not the message port
// dedicated to the shared worker.
events.once(channelEmitter, 'shared-worker-error').then(() => {
events.once(channelEmitter, 'shared-worker-error').then(() => { // eslint-disable-line promise/prefer-await-to-then
unsubscribe();
sharedWorkerHandle.forceUnref();
error = new Error('The shared worker is no longer available');
Loading