Skip to content

Commit

Permalink
Add support for .cjs files; select but do not yet handle .mjs files
Browse files Browse the repository at this point in the history
As per #2293, this begins the work to support `.cjs` and `.mjs` files.

* AVA will now select and load `.cjs` test files
* AVA will now select, but refuse to load, `.mjs` test files
* AVA will detect the module `"type"` in `package.json`, and refuse to load `.js` test files if the type is `"module"`
* `.js` config files can now only contain an `export default` statement
* `.cjs` config files are now supported
* `.mjs` config files are now recognized, but AVA refuses to load them
* `--config` files must have a known extension
  • Loading branch information
novemberborn authored Jan 4, 2020
1 parent 98c47b4 commit 8fa2825
Show file tree
Hide file tree
Showing 35 changed files with 454 additions and 142 deletions.
5 changes: 3 additions & 2 deletions docs/05-command-line.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ Positionals:
Options:
--version Show version number [boolean]
--color Force color output [boolean]
--config JavaScript file for AVA to read its config from,
instead of using package.json or ava.config.js files
--config Specific JavaScript file for AVA to read its config
from, instead of using package.json or ava.config.*
files
--help Show help [boolean]
--concurrency, -c Max number of test files running at the same time
(Default: CPU cores) [number]
Expand Down
82 changes: 65 additions & 17 deletions docs/06-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Translations: [Français](https://github.com/avajs/ava-docs/blob/master/fr_FR/do

**This documents the upcoming AVA 3 release. See the [AVA 2](https://github.com/avajs/ava/blob/v2.4.0/docs/06-configuration.md) documentation instead.**

All of the [CLI options][CLI] can be configured in the `ava` section of either your `package.json` file, or an `ava.config.js` file. This allows you to modify the default behavior of the `ava` command, so you don't have to repeatedly type the same options on the command prompt.
All of the [CLI options][CLI] can be configured in the `ava` section of either your `package.json` file, or an `ava.config.*` file. This allows you to modify the default behavior of the `ava` command, so you don't have to repeatedly type the same options on the command prompt.

To ignore files, prefix the pattern with an `!` (exclamation mark).

Expand Down Expand Up @@ -40,7 +40,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 `js` extensions, even if the pattern matches other files. Specify `extensions` to allow other file extensions
- `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/master/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`: cache compiled files under `node_modules/.cache/ava`. If `false`, files are cached in a temporary directory instead
Expand All @@ -50,34 +50,42 @@ Arguments passed to the CLI will always take precedence over the CLI options con
- `tap`: if `true`, enables the [TAP reporter](./05-command-line.md#tap-reporter)
- `verbose`: if `true`, enables verbose output
- `snapshotDir`: specifies a fixed location for storing snapshot files. Use this if your snapshots are ending up in the wrong location
- `extensions`: extensions of test files. Setting this overrides the default `"js"` value, so make sure to include that extension in the list
- `extensions`: extensions of test files. Setting this overrides the default `["cjs", "mjs", "js"]` value, so make sure to include those extensions in the list
- `require`: extra modules to require before tests are run. Modules are required in the [worker processes](./01-writing-tests.md#process-isolation)
- `timeout`: Timeouts in AVA behave differently than in other test frameworks. AVA resets a timer after each test, forcing tests to quit if no new test results were received within the specified timeout. This can be used to handle stalled tests. See our [timeout documentation](./07-test-timeouts.md) for more options.

Note that providing files on the CLI overrides the `files` option.

Provide the `babel` option (and install [`@ava/babel`](https://github.com/avajs/babel) as an additional dependency) to enable Babel compilation.

## Using `ava.config.js`
## Using `ava.config.*` files

To use an `ava.config.js` file:
Rather than specifying the configuration in the `package.json` file you can use `ava.config.js` or `ava.config.cjs` files.

1. It must be in the same directory as your `package.json`
To use these files:

1. They must be in the same directory as your `package.json`
2. Your `package.json` must not contain an `ava` property (or, if it does, it must be an empty object)
3. You must use `export default`, though [`require()`](https://nodejs.org/api/modules.html#modules_require_id) is available to load non-ES modules
3. You must not both have an `ava.config.js` *and* an `ava.config.cjs` file

AVA recognizes `ava.config.mjs` files but refuses to load them.

### `ava.config.js`

The config file must have a default export, using ES modules. It can either be a plain object or a factory function which returns a plain object:
For `ava.config.js` files you must use `export default`. You cannot use ["module scope"](https://nodejs.org/docs/latest-v12.x/api/modules.html#modules_the_module_scope). You cannot import dependencies.

The default export can either be a plain object or a factory function which returns a plain object:

```js
export default {
require: ['esm']
require: ['./_my-test-helper']
};
```

```js
export default function factory() {
return {
require: ['esm']
require: ['./_my-test-helper']
};
};
```
Expand All @@ -100,34 +108,74 @@ export default ({projectDir}) => {

Note that the final configuration must not be a promise.

### `ava.config.cjs`

For `ava.config.cjs` files you must assign `module.exports`. ["Module scope"](https://nodejs.org/docs/latest-v12.x/api/modules.html#modules_the_module_scope) is available. You can `require()` dependencies.

The module export can either be a plain object or a factory function which returns a plain object:

```js
module.exports = {
require: ['./_my-test-helper']
};
```

```js
module.exports = () => {
return {
require: ['./_my-test-helper']
};
};
```

The factory function is called with an object containing a `projectDir` property, which you could use to change the returned configuration:

```js
module.exports = ({projectDir}) => {
if (projectDir === '/Users/username/projects/my-project') {
return {
// Config A
};
}

return {
// Config B
};
};
```

Note that the final configuration must not be a promise.

## Alternative configuration files

The [CLI] lets you specify a specific configuration file, using the `--config` flag. This file is processed just like an `ava.config.js` file would be. When the `--config` flag is set, the provided file will override all configuration from the `package.json` and `ava.config.js` files. The configuration is not merged.
The [CLI] lets you specify a specific configuration file, using the `--config` flag. This file must have either a `.js` or `.cjs` extension and is processed like an `ava.config.js` or `ava.config.cjs` file would be.

When the `--config` flag is set, the provided file will override all configuration from the `package.json` and `ava.config.js` or `ava.config.cjs` files. The configuration is not merged.

The configuration file *must* be in the same directory as the `package.json` file.

You can use this to customize configuration for a specific test run. For instance, you may want to run unit tests separately from integration tests:

`ava.config.js`:
`ava.config.cjs`:

```js
export default {
module.exports = {
files: ['unit-tests/**/*']
};
```

`integration-tests.config.js`:
`integration-tests.config.cjs`:

```js
import baseConfig from './ava.config.js';
const baseConfig = require('./ava.config.cjs');

export default {
module.exports = {
...baseConfig,
files: ['integration-tests/**/*']
};
```

You can now run your unit tests through `npx ava` and the integration tests through `npx ava --config integration-tests.config.js`.
You can now run your unit tests through `npx ava` and the integration tests through `npx ava --config integration-tests.config.cjs`.

## Object printing depth

Expand Down
29 changes: 9 additions & 20 deletions docs/recipes/es-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@

Translations: [Français](https://github.com/avajs/ava-docs/blob/master/fr_FR/docs/recipes/es-modules.md)

As of Node.js 8.5.0, [ES modules](http://2ality.com/2017/09/native-esm-node.html) are natively supported, but behind the `--experimental-modules` command line flag. It works using the `.mjs` file extension. AVA does not currently support the command line option or the new file extension, but you *can* use the [`esm`](https://github.com/standard-things/esm) module to use the new syntax.
As of Node.js 13, [ECMAScript modules](https://nodejs.org/docs/latest/api/esm.html#esm_introduction) are natively supported in Node.js itself. AVA does not quite support them *yet*, but we're close.

This comment has been minimized.

Copy link
@dimaqq

dimaqq Jan 7, 2020

🎆 🍾 🎉


For the time being, AVA *does* select test files with the `.mjs` extension, however it refuses to load them. Similarly the `package.json` `"type": "module"` field is recognized, but if set AVA will refuse to load test files with the `.js` extension.

For now, your best bet is to use the [`esm`](https://github.com/standard-things/esm) package. Make sure to use the `.js` extension and *do not* set `"type": "module"` in `package.json`.

Here's how you get it working with AVA.

First, install [`esm`](https://github.com/standard-things/esm):
First, install `esm`:

```
$ npm install esm
```

Configure it in your `package.json` or `ava.config.js` file, and add it to AVA's `"require"` option as well. Make sure to add it as the first item.
Configure it in your `package.json` or `ava.config.*` file, and add it to AVA's `"require"` option as well. Make sure to add it as the first item.

**`package.json`:**

Expand All @@ -29,7 +33,7 @@ Configure it in your `package.json` or `ava.config.js` file, and add it to AVA's
You can now use native ES modules with AVA:

```js
// sum.mjs
// sum.js
export default function sum(a, b) {
return a + b;
};
Expand All @@ -38,24 +42,9 @@ export default function sum(a, b) {
```js
// test.js
const test = require('ava');
const sum = require('./sum.mjs');
const sum = require('./sum');

test('2 + 2 = 4', t => {
t.is(sum(2, 2), 4);
});
```

You need to configure AVA to recognize `.mjs` extensions;

**`package.json`:**

```json
{
"ava": {
"extensions": [
"js",
"mjs"
]
}
}
```
2 changes: 1 addition & 1 deletion docs/recipes/flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Until [1.4.1](https://github.com/avajs/ava/releases/tag/v1.4.1) AVA came bundled

This guide assumes you've already set up Flow for your project. Note that AVA's definition as been tested with version 0.95.1.

We recommend you use AVA's [Babel support](https://github.com/avajs/babel) to strip Flow type annotations and declarations. AVA automatically applies your project's Babel configuration, so everything may just work without changes. Alternatively install [`@babel/plugin-transform-flow-strip-types`](https://www.npmjs.com/package/@babel/plugin-transform-flow-strip-types) and customize AVA's configuration in the `package.json` file (or the `ava.config.js` file) as follows.
We recommend you use AVA's [Babel support](https://github.com/avajs/babel) to strip Flow type annotations and declarations. AVA automatically applies your project's Babel configuration, so everything may just work without changes. Alternatively install [`@babel/plugin-transform-flow-strip-types`](https://www.npmjs.com/package/@babel/plugin-transform-flow-strip-types) and customize AVA's configuration in the `package.json` file (or the `ava.config.*` file) as follows.

**`package.json`:**

Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/react.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Translations: [Español](https://github.com/avajs/ava-docs/blob/master/es_ES/doc

When you [enable Babel](https://github.com/avajs/babel), AVA will automatically extend your regular (project-level) Babel configuration. You should be able to use React in your test files without any additional configuration.

However if you want to set it up explicitly, add the preset to the test options in AVA's Babel pipeline by modifying your `package.json` or `ava.config.js` file.
However if you want to set it up explicitly, add the preset to the test options in AVA's Babel pipeline by modifying your `package.json` or `ava.config.*` file.

**`package.json`:**

Expand Down
8 changes: 4 additions & 4 deletions docs/recipes/watch-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ And then use:
$ npm run watch:test
```

Finally you could configure AVA to *always* run in watch mode by setting the `watch` key in the [`ava` section of your `package.json`, or `ava.config.js` file][config].
Finally you could configure AVA to *always* run in watch mode by setting the `watch` key in the [`ava` section of your `package.json`, or `ava.config.*` file][config].

**`package.json`:**

Expand All @@ -67,17 +67,17 @@ AVA uses [`chokidar`] as the file watcher. Note that even if you see warnings ab

## Ignoring changes

By default AVA watches for changes to all files, except for those with a `.snap.md` extension, `ava.config.js` and files in [certain directories](https://github.com/novemberborn/ignore-by-default/blob/master/index.js) as provided by the [`ignore-by-default`] package.
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.js` file][config], using the `ignoredByWatcher` key.
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.

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.js` file][config], and not from inside your test file. Files accessed using the `fs` module are not tracked.
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.

## Watch mode and the `.only` modifier

Expand Down
2 changes: 1 addition & 1 deletion lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class Api extends Emittery {
constructor(options) {
super();

this.options = {match: [], ...options};
this.options = {match: [], moduleTypes: {}, ...options};
this.options.require = resolveModules(this.options.require);

this._cacheDir = null;
Expand Down
21 changes: 20 additions & 1 deletion lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const arrify = require('arrify');
const yargs = require('yargs');
const Promise = require('bluebird');
const isCi = require('is-ci');
const readPkg = require('read-pkg');
const loadConfig = require('./load-config');

// Bluebird specific
Expand Down Expand Up @@ -113,7 +114,7 @@ exports.run = async () => { // eslint-disable-line complexity
type: 'boolean'
},
config: {
description: 'JavaScript file for AVA to read its config from, instead of using package.json or ava.config.js files'
description: 'Specific JavaScript file for AVA to read its config from, instead of using package.json or ava.config.* files'
}
})
.command('* [<pattern>...]', 'Run tests', yargs => yargs.options(FLAGS).positional('pattern', {
Expand Down Expand Up @@ -254,6 +255,23 @@ exports.run = async () => { // eslint-disable-line complexity
const {normalizeGlobs, normalizePatterns} = require('./globs');
const validateEnvironmentVariables = require('./environment-variables');

let pkg;
try {
pkg = readPkg.sync({cwd: projectDir});
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}

const {type: defaultModuleType = 'commonjs'} = pkg || {};

const moduleTypes = {
cjs: 'commonjs',
mjs: 'module',
js: defaultModuleType
};

let babelProvider;
if (Reflect.has(conf, 'babel')) {
try {
Expand Down Expand Up @@ -306,6 +324,7 @@ exports.run = async () => { // eslint-disable-line complexity
failFast: combined.failFast,
failWithoutAssertions: combined.failWithoutAssertions !== false,
globs,
moduleTypes,
environmentVariables,
match,
parallelRuns,
Expand Down
17 changes: 13 additions & 4 deletions lib/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,19 @@ module.exports = (configuredExtensions, babelProvider) => {
throw new Error(`Unexpected duplicate extensions in options: '${[...duplicates].join('\', \'')}'.`);
}

// Unless the default was used by `babelProvider`, as long as the extensions aren't explicitly set to be the empty
// array, set the default.
if (!seen.has('js') && configuredExtensions === undefined) {
seen.add('js');
// Unless the default was used by `babelProvider`, as long as the extensions aren't explicitly set, set the default.
if (configuredExtensions === undefined) {
if (!seen.has('cjs')) {
seen.add('cjs');
}

if (!seen.has('mjs')) {
seen.add('mjs');
}

if (!seen.has('js')) {
seen.add('js');
}
}

return [...seen];
Expand Down
3 changes: 2 additions & 1 deletion lib/globs.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ const defaultMicromatchIgnorePatterns = [

const defaultIgnoredByWatcherPatterns = [
'**/*.snap.md', // No need to rerun tests when the Markdown files change.
'ava.config.js' // Config is not reloaded so avoid rerunning tests when it changes.
'ava.config.js', // Config is not reloaded so avoid rerunning tests when it changes.
'ava.config.cjs' // Config is not reloaded so avoid rerunning tests when it changes.
];

const buildExtensionPattern = extensions => extensions.length === 1 ? extensions[0] : `{${extensions.join(',')}}`;
Expand Down
Loading

0 comments on commit 8fa2825

Please sign in to comment.