Skip to content

Commit a4e4ffb

Browse files
jfmengelssindresorhus
authored andcommitted
Add prefer-async-await rule (fixes #123) (#134)
1 parent 8e06ac8 commit a4e4ffb

File tree

5 files changed

+139
-0
lines changed

5 files changed

+139
-0
lines changed

docs/rules/prefer-async-await.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Prefer using async/await instead of returning a Promise
2+
3+
Translations: [Français](https://github.com/avajs/ava-docs/blob/master/fr_FR/related/eslint-plugin-ava/docs/rules/prefer-async-await.md)
4+
5+
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.
6+
7+
This rule will report an error when it finds a test that returns an expression that looks like a Promise (containing a `.then()` call), which could be simplified by using the async/await syntax.
8+
9+
10+
## Fail
11+
12+
```js
13+
import test from 'ava';
14+
15+
test(t => {
16+
return foo().then(res => {
17+
t.is(res, 1);
18+
});
19+
});
20+
```
21+
22+
23+
## Pass
24+
25+
```js
26+
import test from 'ava';
27+
28+
test(async t => {
29+
t.is(await foo(), 1);
30+
});
31+
```

index.js

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ module.exports = {
2727
'ava/no-todo-implementation': 'error',
2828
'ava/no-todo-test': 'warn',
2929
'ava/no-unknown-modifiers': 'error',
30+
'ava/prefer-async-await': 'error',
3031
'ava/prefer-power-assert': 'off',
3132
'ava/test-ended': 'error',
3233
'ava/test-title': ['error', 'if-multiple'],

readme.md

+2
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Configure it in `package.json`.
4545
"ava/no-todo-implementation": "error",
4646
"ava/no-todo-test": "warn",
4747
"ava/no-unknown-modifiers": "error",
48+
"ava/prefer-async-await": "error",
4849
"ava/prefer-power-assert": "off",
4950
"ava/test-ended": "error",
5051
"ava/test-title": ["error", "if-multiple"],
@@ -76,6 +77,7 @@ The rules will only activate in test files.
7677
- [no-todo-implementation](docs/rules/no-todo-implementation.md) - Ensure `test.todo()` is not given an implementation function.
7778
- [no-todo-test](docs/rules/no-todo-test.md) - Ensure no `test.todo()` is used.
7879
- [no-unknown-modifiers](docs/rules/no-unknown-modifiers.md) - Prevent the use of unknown test modifiers.
80+
- [prefer-async-await](docs/rules/prefer-async-await.md) - Prefer using async/await instead of returning a Promise.
7981
- [prefer-power-assert](docs/rules/prefer-power-assert.md) - Allow only use of the asserts that have no [power-assert](https://github.com/power-assert-js/power-assert) alternative.
8082
- [test-ended](docs/rules/test-ended.md) - Ensure callback tests are explicitly ended.
8183
- [test-title](docs/rules/test-title.md) - Ensure tests have a title.

rules/prefer-async-await.js

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
'use strict';
2+
const visitIf = require('enhance-visitors').visitIf;
3+
const createAvaRule = require('../create-ava-rule');
4+
5+
function containsThen(node) {
6+
if (!node ||
7+
node.type !== 'CallExpression' ||
8+
node.callee.type !== 'MemberExpression'
9+
) {
10+
return false;
11+
}
12+
13+
const callee = node.callee;
14+
if (callee.property.type === 'Identifier' &&
15+
callee.property.name === 'then'
16+
) {
17+
return true;
18+
}
19+
20+
return containsThen(callee.object);
21+
}
22+
23+
const create = context => {
24+
const ava = createAvaRule();
25+
26+
const check = visitIf([
27+
ava.isInTestFile,
28+
ava.isInTestNode
29+
])(node => {
30+
if (node.body.type !== 'BlockStatement') {
31+
return;
32+
}
33+
34+
const statements = node.body.body;
35+
const returnStatement = statements.find(statement => statement.type === 'ReturnStatement');
36+
if (returnStatement && containsThen(returnStatement.argument)) {
37+
context.report({
38+
node,
39+
message: 'Prefer using async/await instead of returning a Promise.'
40+
});
41+
}
42+
});
43+
44+
return ava.merge({
45+
ArrowFunctionExpression: check,
46+
FunctionExpression: check
47+
});
48+
};
49+
50+
module.exports = {
51+
create,
52+
meta: {}
53+
};

test/prefer-async-await.js

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import test from 'ava';
2+
import avaRuleTester from 'eslint-ava-rule-tester';
3+
import rule from '../rules/prefer-async-await';
4+
5+
const ruleTester = avaRuleTester(test, {
6+
env: {
7+
es6: true
8+
}
9+
});
10+
11+
const header = `const test = require('ava');\n`;
12+
const errors = [{
13+
ruleId: 'prefer-async-await',
14+
message: 'Prefer using async/await instead of returning a Promise.'
15+
}];
16+
17+
ruleTester.run('prefer-async-await', rule, {
18+
valid: [
19+
header + 'test(t => { t.is(1, 1); });',
20+
header + 'test(t => { foo(); });',
21+
header + 'test(t => { return foo(); });',
22+
header + 'test(t => { foo().then(fn); });',
23+
header + 'test(t => { function foo() { return foo().then(fn); } });',
24+
header + 'test(t => foo().then(fn));',
25+
// TODO: this should be an error, needs improvement
26+
header + 'test(t => { const bar = foo().then(fn); return bar; });',
27+
// shouldn't be triggered since it's not a test file
28+
'test(t => { return foo().then(fn); });'
29+
],
30+
invalid: [
31+
{
32+
code: header + 'test(t => { return foo().then(fn); });',
33+
errors
34+
},
35+
{
36+
code: header + 'test(function(t) { return foo().then(fn); });',
37+
errors
38+
},
39+
{
40+
code: header + 'test(t => { return foo().then(fn).catch(fn2); });',
41+
errors
42+
},
43+
{
44+
code: header + 'test(t => { return foo().catch(fn2).then(fn); });',
45+
errors
46+
},
47+
{
48+
code: header + 'test(t => { const bar = foo(); return bar.then(fn); });',
49+
errors
50+
}
51+
]
52+
});

0 commit comments

Comments
 (0)