Skip to content

Commit

Permalink
Add no-async-fn-without-await rule (fixes #132) (#140)
Browse files Browse the repository at this point in the history
  • Loading branch information
jfmengels authored and sindresorhus committed Aug 31, 2016
1 parent 7cfdf34 commit a443d7a
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 0 deletions.
33 changes: 33 additions & 0 deletions docs/rules/no-async-fn-without-await.md
@@ -0,0 +1,33 @@
# Ensure that async tests use `await`

Translations: [Français](https://github.com/avajs/ava-docs/blob/master/fr_FR/related/eslint-plugin-ava/docs/rules/no-async-fn-without-await.md)

AVA comes with built-in support for [async functions](http://www.2ality.com/2016/02/async-functions.html) (async/await). This allows you to write shorter and clearer tests.

Declaring an async test without using the `await` keyword means that either a Promise is not awaited on as intended, or that the function could have been declared as a regular function, which is confusing and slower.

This rule will report an error when it finds an async test which does not use the `await` keyword.


## Fail

```js
import test from 'ava';

test(async t => {
return foo().then(res => {
t.is(res, 1);
});
});
```


## Pass

```js
import test from 'ava';

test(async t => {
t.is(await foo(), 1);
});
```
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -16,6 +16,7 @@ module.exports = {
'ava/assertion-arguments': 'error',
'ava/assertion-message': ['off', 'always'],
'ava/max-asserts': ['off', 5],
'ava/no-async-fn-without-await': 'error',
'ava/no-cb-test': 'off',
'ava/no-duplicate-modifiers': 'error',
'ava/no-identical-title': 'error',
Expand Down
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -73,6 +73,7 @@
},
"devDependencies": {
"ava": "*",
"babel-eslint": "^6.1.2",
"coveralls": "^2.11.9",
"eslint": "^3.0.1",
"eslint-ava-rule-tester": "^2.0.0",
Expand Down
2 changes: 2 additions & 0 deletions readme.md
Expand Up @@ -34,6 +34,7 @@ Configure it in `package.json`.
"ava/assertion-arguments": "error",
"ava/assertion-message": ["off", "always"],
"ava/max-asserts": ["off", 5],
"ava/no-async-fn-without-await": "error",
"ava/no-cb-test": "off",
"ava/no-duplicate-modifiers": "error",
"ava/no-identical-title": "error",
Expand Down Expand Up @@ -68,6 +69,7 @@ The rules will only activate in test files.
- [assertion-arguments](docs/rules/assertion-arguments.md) - Enforce passing correct arguments to assertions.
- [assertion-message](docs/rules/assertion-message.md) - Enforce or disallow assertion messages.
- [max-asserts](docs/rules/max-asserts.md) - Limit the number of assertions in a test.
- [no-async-fn-without-await](docs/rules/no-async-fn-without-await.md) - Ensure that async tests use `await`.
- [no-cb-test](docs/rules/no-cb-test.md) - Ensure no `test.cb()` is used.
- [no-duplicate-modifiers](docs/rules/no-duplicate-modifiers.md) - Ensure tests do not have duplicate modifiers.
- [no-identical-title](docs/rules/no-identical-title.md) - Ensure no tests have the same title.
Expand Down
42 changes: 42 additions & 0 deletions rules/no-async-fn-without-await.js
@@ -0,0 +1,42 @@
'use strict';
const visitIf = require('enhance-visitors').visitIf;
const createAvaRule = require('../create-ava-rule');

const create = context => {
const ava = createAvaRule();
let testIsAsync = false;
let testUsed = false;

return ava.merge({
'CallExpression': visitIf([
ava.isInTestFile,
ava.isTestNode
])(node => {
const implementationFn = node.arguments[0];
testIsAsync = implementationFn && implementationFn.async;
}),
'YieldExpression': () => {
if (testIsAsync) {
testUsed = true;
}
},
'CallExpression:exit': visitIf([
ava.isInTestFile,
ava.isTestNode
])(node => {
if (testIsAsync && !testUsed) {
context.report({
node,
message: 'Function was declared as `async` but doesn\'t use `await`'
});
}
testIsAsync = false;
testUsed = false;
})
});
};

module.exports = {
create,
meta: {}
};
53 changes: 53 additions & 0 deletions test/no-async-fn-without-await.js
@@ -0,0 +1,53 @@
import test from 'ava';
import avaRuleTester from 'eslint-ava-rule-tester';
import rule from '../rules/no-async-fn-without-await';

const ruleTester = avaRuleTester(test, {
parser: 'babel-eslint',
env: {
es6: true
}
});

const error = {
ruleId: 'no-async-fn-without-await',
message: 'Function was declared as `async` but doesn\'t use `await`'
};
const header = `const test = require('ava');\n`;

ruleTester.run('no-async-fn-without-await', rule, {
valid: [
`${header} test(fn);`,
`${header} test(t => {});`,
`${header} test(function(t) {});`,
`${header} test(async t => { await foo(); });`,
`${header} test(async t => { t.is(await foo(), 1); });`,
`${header} test(async function(t) { await foo(); });`,
`${header} test(async t => { if (bar) { await foo(); } });`,
`${header} test(async t => { if (bar) {} else { await foo(); } });`,
// shouldn't be triggered since it's not a test file
'test(async t => {});'
],
invalid: [
{
code: `${header} test(async t => {});`,
errors: [error]
},
{
code: `${header} test(async function(t) {});`,
errors: [error]
},
{
code: `${header} test(async t => {}); test(async t => {});`,
errors: [error, error]
},
{
code: `${header} test(async t => {}); test(async t => { await foo(); });`,
errors: [error]
},
{
code: `${header} test(async t => { await foo(); }); test(async t => {});`,
errors: [error]
}
]
});

0 comments on commit a443d7a

Please sign in to comment.