Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AVA 6 watch mode #3218

Merged
merged 5 commits into from
Jul 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
node-version: [^16.18, ^18.16, ^20.3]
os: [ubuntu-latest, windows-latest]
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- uses: actions/checkout@v3
- name: Enable symlinks
Expand All @@ -29,7 +29,8 @@ jobs:
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm install --no-audit
- run: npm run cover
- run: ./scripts/ci.sh
shell: bash
- uses: codecov/codecov-action@v3
with:
files: coverage/lcov.info
Expand Down Expand Up @@ -83,7 +84,8 @@ jobs:
with:
node-version-file: package.json
- run: npm install --no-package-lock --no-audit
- run: npm run cover
- run: ./scripts/ci.sh
shell: bash

xo:
name: Lint source files
Expand Down
6 changes: 5 additions & 1 deletion ava.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import process from 'node:process';

const skipWatchMode = process.env.TEST_AVA_SKIP_WATCH_MODE ? ['!test/watch-mode/**'] : [];

export default { // eslint-disable-line import/no-anonymous-default-export
files: ['test/**', '!test/**/{fixtures,helpers}/**'],
files: ['test/**', '!test/**/{fixtures,helpers}/**', ...skipWatchMode],
ignoredByWatcher: ['{coverage,docs,media,test-types,test-tap}/**'],
environmentVariables: {
AVA_FAKE_SCM_ROOT: '.fake-root', // This is an internal test flag.
Expand Down
2 changes: 1 addition & 1 deletion docs/06-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Arguments passed to the CLI will always take precedence over the CLI options con
## Options

- `files`: an array of glob patterns to select test files. Files with an underscore prefix are ignored. By default only selects files with `cjs`, `mjs` & `js` extensions, even if the pattern matches other files. Specify `extensions` to allow other file extensions
- `ignoredByWatcher`: an array of glob patterns to match files that, even if changed, are ignored by the watcher. See the [watch mode recipe for details](https://github.com/avajs/ava/blob/main/docs/recipes/watch-mode.md)
- `watchMode`: See the [watch mode recipe for details](https://github.com/avajs/ava/blob/main/docs/recipes/watch-mode.md)
- `match`: not typically useful in the `package.json` configuration, but equivalent to [specifying `--match` on the CLI](./05-command-line.md#running-tests-with-matching-titles)
- `cache`: defaults to `true` to cache compiled files under `node_modules/.cache/ava`. If `false`, files are cached in a temporary directory instead
- `concurrency`: max number of test files running at the same time (default: CPU cores)
Expand Down
39 changes: 32 additions & 7 deletions docs/recipes/watch-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ Translations: [Français](https://github.com/avajs/ava-docs/blob/main/fr_FR/docs

AVA comes with an intelligent watch mode. It watches for files to change and runs just those tests that are affected.

AVA 6 is introducing a new watch mode that relies on recurse file watching in Node.js. To use the old watch mode, set the implementation to `ava5+chokidar` and install [`chokidar`] alongside AVA:

`ava.config.mjs`:
```js
export default {
watchMode: {
implementation: 'ava5+chokidar',
},
}
```

## Running tests with watch mode enabled

You can enable watch mode using the `--watch` or `-w` flags:
Expand All @@ -16,21 +27,39 @@ Please note that integrated debugging and the TAP reporter are unavailable when

## Requirements

AVA uses [`chokidar`] as the file watcher. Note that even if you see warnings about optional dependencies failing during install, it will still work fine. Please refer to the *[Install Troubleshooting]* section of `chokidar` documentation for how to resolve the installation problems with chokidar.
AVA 5 uses [`chokidar`] as the file watcher. Note that even if you see warnings about optional dependencies failing during install, it will still work fine. Please refer to the *[Install Troubleshooting]* section of `chokidar` documentation for how to resolve the installation problems with chokidar.

The same applies with AVA 6 when using the `ava5+chokidar` watcher. However you'll need to install `chokidar` separately.

Otherwise, AVA 6 uses `fs.watch()`. Support for `recursive` mode is required. Note that this has only become available on Linux since Node.js 20. [Other caveats apply](https://nodejs.org/api/fs.html#caveats), for example this won't work well on network filesystems and Docker host mounts.

## Ignoring changes

By default AVA watches for changes to all files, except for those with a `.snap.md` extension, `ava.config.*` and files in [certain directories](https://github.com/novemberborn/ignore-by-default/blob/master/index.js) as provided by the [`ignore-by-default`] package.

You can configure additional patterns for files to ignore in the [`ava` section of your `package.json`, or `ava.config.*` file][config], using the `ignoredByWatcher` key.
With AVA 5, you can configure additional patterns for files to ignore in the [`ava` section of your `package.json`, or `ava.config.*` file][config], using the `ignoredByWatcher` key.

With AVA 6, place these patterns within the `watchMode` object:

```js
export default {
watchMode: {
ignoreChanges: ['coverage'],
},
};
```

If your tests write to disk they may trigger the watcher to rerun your tests. Configuring additional ignore patterns helps avoid this.

## Dependency tracking

AVA tracks which source files your test files depend on. If you change such a dependency only the test file that depends on it will be rerun. AVA will rerun all tests if it cannot determine which test file depends on the changed source file.

Dependency tracking works for required modules. Custom extensions and transpilers are supported, provided you [added them in your `package.json` or `ava.config.*` file][config], and not from inside your test file. Files accessed using the `fs` module are not tracked.
AVA 5 (and the `ava5+chokidar` watcher in AVA 6) spies on `require()` calls to track dependencies. Custom extensions and transpilers are supported, provided you [added them in your `package.json` or `ava.config.*` file][config], and not from inside your test file.

With AVA 6, dependency tracking works for `require()` and `import` syntax, as supported by [@vercel/nft](https://github.com/vercel/nft). `import()` is supported but dynamic paths such as `import(myVariable)` are not.

Files accessed using the `fs` module are not tracked.

## Watch mode and the `.only` modifier

Expand All @@ -56,10 +85,6 @@ Sometimes watch mode does something surprising like rerunning all tests when you
$ DEBUG=ava:watcher npx ava --watch
```

## Help us make watch mode better

Watch mode is relatively new and there might be some rough edges. Please [report](https://github.com/avajs/ava/issues) any issues you encounter. Thanks!

[`chokidar`]: https://github.com/paulmillr/chokidar
[Install Troubleshooting]: https://github.com/paulmillr/chokidar#install-troubleshooting
[`ignore-by-default`]: https://github.com/novemberborn/ignore-by-default
Expand Down
8 changes: 4 additions & 4 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,15 +197,14 @@ export default class Api extends Emittery {

await this.emit('run', {
bailWithoutReporting: debugWithoutSpecificFile,
clearLogOnNextRun: runtimeOptions.clearLogOnNextRun === true,
debug: Boolean(this.options.debug),
failFastEnabled: failFast,
filePathPrefix: getFilePathPrefix(selectedFiles),
files: selectedFiles,
matching: apiOptions.match.length > 0,
previousFailures: runtimeOptions.previousFailures || 0,
runOnlyExclusive: runtimeOptions.runOnlyExclusive === true,
runVector: runtimeOptions.runVector || 0,
firstRun: runtimeOptions.firstRun ?? true,
status: runStatus,
});

Expand Down Expand Up @@ -303,7 +302,8 @@ export default class Api extends Emittery {

// Allow shared workers to clean up before the run ends.
await Promise.all(deregisteredSharedWorkers);
scheduler.storeFailedTestFiles(runStatus, this.options.cacheEnabled === false ? null : this._createCacheDir());
const files = scheduler.storeFailedTestFiles(runStatus, this.options.cacheEnabled === false ? null : this._createCacheDir());
runStatus.emitStateChange({type: 'touched-files', files});
} catch (error) {
if (error && error.name === 'AggregateError') {
for (const error_ of error.errors) {
Expand All @@ -315,7 +315,7 @@ export default class Api extends Emittery {
}

timeoutTrigger.discard();
return runStatus;
return runStatus.end();
}

_getLocalCacheDir() {
Expand Down