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

Adds support for ava.config.js files #1761

Merged
merged 48 commits into from
May 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
e9e688f
Replaces pkg-conf with lib/load-config [WIP]
good-idea Apr 2, 2018
6343a1d
Changes handling of defaults, implements in profile.js
good-idea Apr 2, 2018
e98b150
Replaces pkg-conf with lib/load-config [WIP]
good-idea Apr 2, 2018
c443e54
Changes handling of defaults, implements in profile.js
good-idea Apr 2, 2018
0755a1b
Un-pretties test/cli.js
good-idea Apr 8, 2018
27038fc
Simplifies config loading
good-idea Apr 8, 2018
64ef0da
Adds support for ESM import
good-idea Apr 9, 2018
fab1349
Fixes lint errors
good-idea Apr 9, 2018
27444e6
Merge from remote, clear up conflicts
good-idea Apr 9, 2018
5e90143
Lints test fixtures
good-idea Apr 9, 2018
ceb8d85
Changes implementation of getting the project dir from the config
good-idea Apr 9, 2018
2f2af6e
Fixes prettied lines in test/cli.js
good-idea Apr 9, 2018
c5d9039
Removes object spread to make node 6-safe
good-idea Apr 10, 2018
e5be1ae
Merge branch 'master' into config-file
good-idea Apr 17, 2018
2ef9a6a
Removes '_meta' from config in favor of '{projectDir}'
good-idea Apr 17, 2018
2cbfd87
Changes factory function argument to {projectDir}
good-idea Apr 22, 2018
e3ae8c4
Updates esm and moves to dependencies
good-idea Apr 22, 2018
4079be1
Merges in changes to master, fixes conflicts
good-idea Apr 22, 2018
1bdcae3
Removes esm problem comment
good-idea Apr 22, 2018
5b88b64
Re-merges package-lock.json
good-idea Apr 22, 2018
04b8fbd
Changes esm implementation to const requireEsm = ...
good-idea Apr 22, 2018
ca5eb23
Adds ava.config.js info to documentation
good-idea Apr 22, 2018
4cb966b
Update package.json
sindresorhus Apr 23, 2018
22097fa
Update package.json
sindresorhus Apr 23, 2018
86e06f8
Update package.json
sindresorhus Apr 23, 2018
863d8b7
Update ava.config.js
sindresorhus Apr 23, 2018
867f3de
Update ava.config.js
sindresorhus Apr 23, 2018
0f9b259
Update package.json
sindresorhus Apr 23, 2018
b6dfeb3
Update package.json
sindresorhus Apr 23, 2018
b51aff3
Update package.json
sindresorhus Apr 23, 2018
7f24c2b
Update ava.config.js
sindresorhus Apr 23, 2018
6ad5a42
Update package.json
sindresorhus Apr 23, 2018
fed12af
Update readme.md
sindresorhus Apr 23, 2018
6b8ba43
Update load-config.js
sindresorhus Apr 23, 2018
46f724f
Removes eslint-disable comment
good-idea Apr 23, 2018
7886b2b
Update readme.md
good-idea Apr 30, 2018
13a333a
Disallows config to be or return promises
good-idea Apr 30, 2018
0f87d09
Merge in latest upstream master
good-idea Apr 30, 2018
d9b091d
Cleans up package-lock.json diff
good-idea Apr 30, 2018
4215a47
Merge branch 'master' into pr/1761
novemberborn May 28, 2018
1cff8f1
Fix package-lock, keep esm version the same as master for now
novemberborn May 28, 2018
02ca5c1
Mention ava.config.js in recipes
novemberborn May 28, 2018
cece1e7
Update README
novemberborn May 28, 2018
cfc9d70
Refactor loading, only use ESM
novemberborn May 28, 2018
79523ff
Merge branch 'master' into pr/1761
novemberborn May 30, 2018
3b5536c
Clean up fixtures
novemberborn May 30, 2018
2bb057b
Test more edge cases
novemberborn May 30, 2018
43e06dd
Exit CLI with nicely formatted error when loading ava.config.js fails
novemberborn May 30, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/recipes/babel.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ If you are using Babel for your source files then you must also [configure sourc

## Customize how AVA compiles your test files

You can override the default Babel configuration AVA uses for test file compilation in `package.json`. For example, the configuration below adds support for JSX syntax and stage 3 features:
You can override the default Babel configuration AVA uses for test file compilation in `package.json` or `ava.config.js`. For example, the configuration below adds support for JSX syntax and stage 3 features:

```json
{
Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/es-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ First, install [`esm`](https://github.com/standard-things/esm):
$ npm install esm
```

Configure it in your `package.json` 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.js` file, and add it to AVA's `"require"` option as well. Make sure to add it as the first item:

```json
{
Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ AVA comes bundled with a Flow definition file. This allows developers to leverag

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

We recommend you use AVA's built-in Babel pipeline 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 as follows:
We recommend you use AVA's built-in Babel pipeline 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:

```json
{
Expand Down
2 changes: 1 addition & 1 deletion docs/recipes/jspm-systemjs.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ You will need to install the [AVA JSPM loader](https://github.com/skorlir/ava-js
```
$ npm install --save-dev ava-jspm-loader @babel/register
```
You will also need to update your AVA config in package.json to use the JSPM loader.
You will also need to update your AVA config in the `package.json` or `ava.config.js` to use the JSPM loader.

```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

AVA automatically extends 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`:
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:

```json
{
Expand Down
9 changes: 4 additions & 5 deletions docs/recipes/watch-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,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`]:
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]:

```json
{
Expand All @@ -67,7 +67,7 @@ In AVA there's a distinction between *source files* and *test files*. As you can

By default AVA watches for changes to the test files, snapshot files, `package.json`, and any other `.js` files. It'll ignore 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 patterns for the source files in the [`ava` section of your `package.json`] file, using the `source` key.
You can configure patterns for the source files in the [`ava` section of your `package.json`, or `ava.config.js` file][config], using the `source` key.

You can specify patterns to match files in the folders that would otherwise be ignored, e.g. use `node_modules/some-dependency/*.js` to specify all `.js` files in `node_modules/some-dependency` as a source, even though normally all files in `node_modules` are ignored. Note that you need to specify an exact directory; `{bower_components,node_modules}/**/*.js` won't work.

Expand All @@ -77,7 +77,7 @@ If your tests write to disk they may trigger the watcher to rerun your tests. Co

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`], 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.js` 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 Expand Up @@ -118,5 +118,4 @@ Watch mode is relatively new and there might be some rough edges. Please [report
[Install Troubleshooting]: https://github.com/paulmillr/chokidar#install-troubleshooting
[`ignore-by-default`]: https://github.com/novemberborn/ignore-by-default
[`.only` modifier]: https://github.com/avajs/ava#running-specific-tests
[`ava` section of your `package.json`]: https://github.com/avajs/ava#configuration
[added them in your `package.json`]: https://github.com/avajs/ava#configuration
[config]: https://github.com/avajs/ava#configuration
22 changes: 17 additions & 5 deletions lib/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ const figures = require('figures');
const arrify = require('arrify');
const meow = require('meow');
const Promise = require('bluebird');
const pkgConf = require('pkg-conf');
const isCi = require('is-ci');
const loadConf = require('./load-config');

// Bluebird specific
Promise.longStackTraces();
Expand All @@ -18,10 +18,13 @@ function exit(message) {
}

exports.run = () => { // eslint-disable-line complexity
const conf = pkgConf.sync('ava');

const filepath = pkgConf.filepath(conf);
const projectDir = filepath === null ? process.cwd() : path.dirname(filepath);
let conf = {};
let confError = null;
try {
conf = loadConf();
} catch (err) {
confError = err;
}

const cli = meow(`
Usage
Expand Down Expand Up @@ -110,6 +113,15 @@ exports.run = () => { // eslint-disable-line complexity
updateNotifier({pkg: cli.pkg}).notify();
const chalk = require('./chalk').set({enabled: cli.flags.color});

if (confError) {
if (confError.parent) {
exit(`${confError.message}\n\n${chalk.gray((confError.parent && confError.parent.stack) || confError.parent)}`);
} else {
exit(confError.message);
}
}

const {projectDir} = conf;
if (cli.flags.resetCache) {
const cacheDir = path.join(projectDir, 'node_modules', '.cache', 'ava');
del('*', {
Expand Down
58 changes: 58 additions & 0 deletions lib/load-config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use strict';
const path = require('path');
const esm = require('esm');
const isPlainObject = require('is-plain-object');
const pkgConf = require('pkg-conf');

const NO_SUCH_FILE = Symbol('no ava.config.js file');
const MISSING_DEFAULT_EXPORT = Symbol('missing default export');

function loadConfig(defaults = {}) {
const packageConf = pkgConf.sync('ava');
const filepath = pkgConf.filepath(packageConf);
const projectDir = filepath === null ? process.cwd() : path.dirname(filepath);

let fileConf;
try {
({default: fileConf = MISSING_DEFAULT_EXPORT} = esm(module, {
cjs: false,
mode: 'all'
})(path.join(projectDir, 'ava.config.js')));
} catch (err) {
if (err && err.code === 'MODULE_NOT_FOUND') {
fileConf = NO_SUCH_FILE;
} else {
throw Object.assign(new Error('Error loading ava.config.js'), {parent: err});
}
}

if (fileConf === MISSING_DEFAULT_EXPORT) {
throw new Error('ava.config.js must have a default export, using ES module syntax');
}

if (fileConf !== NO_SUCH_FILE) {
if (Object.keys(packageConf).length > 0) {
throw new Error('Conflicting configuration in ava.config.js and package.json');
}

if (fileConf && typeof fileConf.then === 'function') {
throw new TypeError('ava.config.js must not export a promise');
}
if (!isPlainObject(fileConf) && typeof fileConf !== 'function') {
throw new TypeError('ava.config.js must export a plain object or factory function');
}

if (typeof fileConf === 'function') {
fileConf = fileConf({projectDir});
if (fileConf && typeof fileConf.then === 'function') {
throw new TypeError('Factory method exported by ava.config.js must not return a promise');
}
if (!isPlainObject(fileConf)) {
throw new TypeError('Factory method exported by ava.config.js must return a plain object');
}
}
}

return Object.assign({}, defaults, fileConf, packageConf, {projectDir});
}
module.exports = loadConfig;
3 changes: 1 addition & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
"emittery": "^0.3.0",
"empower-core": "^0.6.1",
"equal-length": "^1.0.0",
"esm": "^3.0.15",
"escape-string-regexp": "^1.0.5",
"figures": "^2.0.0",
"get-port": "^3.2.0",
Expand Down Expand Up @@ -143,7 +144,6 @@
"codecov": "^3.0.0",
"del": "^3.0.0",
"delay": "^2.0.0",
"esm": "^3.0.15",
"execa": "^0.10.0",
"flow-bin": "^0.69.0",
"get-stream": "^3.0.0",
Expand Down
18 changes: 7 additions & 11 deletions profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ require('./lib/worker/load-chalk'); // eslint-disable-line import/no-unassigned-
const path = require('path');
const meow = require('meow');
const Promise = require('bluebird');
const pkgConf = require('pkg-conf');
const uniqueTempDir = require('unique-temp-dir');
const arrify = require('arrify');
const resolveCwd = require('resolve-cwd');
const babelPipeline = require('./lib/babel-pipeline');
const RunStatus = require('./lib/run-status');
const VerboseReporter = require('./lib/reporters/verbose');
const loadConfig = require('./lib/load-config');

function resolveModules(modules) {
return arrify(modules).map(name => {
Expand All @@ -30,18 +30,14 @@ function resolveModules(modules) {

Promise.longStackTraces();

const conf = pkgConf.sync('ava', {
defaults: {
babel: {
testOptions: {}
},
compileEnhancements: true
}
const conf = loadConfig({
babel: {
testOptions: {}
},
compileEnhancements: true
});

const filepath = pkgConf.filepath(conf);
const projectDir = filepath === null ? process.cwd() : path.dirname(filepath);

const {projectDir} = conf;
// Define a minimal set of options from the main CLI
const cli = meow(`
Usage
Expand Down
43 changes: 42 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ AVA automatically removes unrelated lines in stack traces, allowing you to find

## Configuration

All of the CLI options can be configured in the `ava` section of your `package.json`. 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 can be configured in the `ava` section of either your `package.json` 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.

To ignore a file or directory, prefix the pattern with an `!` (exclamation mark).

Expand Down Expand Up @@ -291,6 +291,47 @@ Arguments passed to the CLI will always take precedence over the configuration i

Note that providing files on the CLI overrides the `files` option. If you've configured a glob pattern, for instance `test/**/*.test.js`, you may want to repeat it when using the CLI: `ava 'test/integration/*.test.js'`.

### Using `ava.config.js`

To use an `ava.config.js` file:

1. It 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)

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:

```js
export default {
require: ['esm']
};
```

```js
export default function factory() {
return {
require: ['esm']
};
};
```

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

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

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

Note that the final configuration must not be a promise.

## Documentation

Tests are run concurrently. You can specify synchronous and asynchronous tests. Tests are considered synchronous unless you return a promise or [observable](https://github.com/zenparsing/zen-observable).
Expand Down
14 changes: 14 additions & 0 deletions test/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,20 @@ for (const [where, which, msg = '\'js\', \'jsx\''] of [
});
}

test('formats errors from ava.config.js', t => {
execCli(['es2015.js'], {dirname: 'fixture/load-config/throws'}, (err, stdout, stderr) => {
t.ok(err);

const lines = stderr.split('\n');
t.is(lines[0], '');
t.is(lines[1], figures.cross + ' Error loading ava.config.js');
t.is(lines[2], '');
t.match(lines[3], /ava\.config\.js/);
t.match(lines[4], /foo/);
t.end();
});
});

test('enabling long stack traces will provide detailed debug information', t => {
execCli('fixture/long-stack-trace', (err, stdout, stderr) => {
t.ok(err);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Config {}

export default () => new Config();
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default () => new Promise(resolve => {
resolve({
files: 'this-should-not-work'
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions test/fixture/load-config/no-default-export/ava.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const config = {}; // eslint-disable-line import/prefer-default-export
1 change: 1 addition & 0 deletions test/fixture/load-config/no-default-export/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions test/fixture/load-config/no-plain-config/ava.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'should not work!';
1 change: 1 addition & 0 deletions test/fixture/load-config/no-plain-config/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
1 change: 1 addition & 0 deletions test/fixture/load-config/no-promise-config/ava.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default Promise.resolve('should not work!');
1 change: 1 addition & 0 deletions test/fixture/load-config/no-promise-config/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default ({projectDir}) => {
return {
files: projectDir
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
5 changes: 5 additions & 0 deletions test/fixture/load-config/package-no-file-yes/ava.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const config = {
files: 'config-file-esm-test-value'
};

export default config;
1 change: 1 addition & 0 deletions test/fixture/load-config/package-no-file-yes/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
6 changes: 6 additions & 0 deletions test/fixture/load-config/package-only/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"ava": {
"failFast": true,
"concurrency": 10
}
}
3 changes: 3 additions & 0 deletions test/fixture/load-config/package-yes-file-yes/ava.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default {
files: 'package-yes-files-yes-test-value'
};
8 changes: 8 additions & 0 deletions test/fixture/load-config/package-yes-file-yes/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"ava": {
"files": [
"abc",
"!xyz"
]
}
}
1 change: 1 addition & 0 deletions test/fixture/load-config/throws/ava.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
throw new Error('foo');
1 change: 1 addition & 0 deletions test/fixture/load-config/throws/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
Loading