Skip to content

Commit 8e64d58

Browse files
committed
Add prefer-t-throws rule
Fixes: avajs#156
1 parent 33dbbc7 commit 8e64d58

File tree

5 files changed

+137
-0
lines changed

5 files changed

+137
-0
lines changed

docs/rules/prefer-t-throws.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Prefer using `t.throws()` over try/catch
2+
3+
This rule will enforce the use of `t.throws()` when possible.
4+
5+
## Fail
6+
7+
```js
8+
const test = require('ava');
9+
10+
test('some test', async t => {
11+
try {
12+
await Promise.reject('error');
13+
t.fail();
14+
} catch (error) {
15+
t.is(error, 'error');
16+
}
17+
});
18+
```
19+
20+
## Pass
21+
22+
```js
23+
const test = require('ava');
24+
25+
test('some test', async t => {
26+
const error = await t.throws(Promise.reject('error'));
27+
t.is(error, 'error');
28+
});
29+
```
30+
31+
```js
32+
const test = require('ava');
33+
34+
test('some test', async t => {
35+
try {
36+
const result = await promiseThatMaybeFails();
37+
// This passes because using a try-catch can be intentional, for example, to test both the result and the error:
38+
// t.is(result, 'good result');
39+
} catch (error) {
40+
t.is(error, 'good error message');
41+
}
42+
});
43+
```
44+
45+
```js
46+
const test = require('ava');
47+
48+
test('some test', async t => {
49+
try {
50+
// This is also because handling multiple rejection promises with try/catch may be easier or may be a deliberate choice.
51+
await promiseThatMaybeFails();
52+
await anotherPromise;
53+
await timeout(100, 'good error message');
54+
t.fail();
55+
} catch (error) {
56+
t.is(error, 'good error message');
57+
}
58+
});
59+
```
60+

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ module.exports = {
4141
'ava/prefer-async-await': 'error',
4242
'ava/prefer-power-assert': 'off',
4343
'ava/prefer-t-regex': 'error',
44+
'ava/prefer-t-throws': 'error',
4445
'ava/test-title': 'error',
4546
'ava/test-title-format': 'off',
4647
'ava/use-t-well': 'error',

readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Configure it in `package.json`.
5656
"ava/prefer-async-await": "error",
5757
"ava/prefer-power-assert": "off",
5858
"ava/prefer-t-regex": "error",
59+
"ava/prefer-t-throws": "error",
5960
"ava/test-title": "error",
6061
"ava/test-title-format": "off",
6162
"ava/use-t": "error",
@@ -92,6 +93,7 @@ The rules will only activate in test files.
9293
- [prefer-async-await](docs/rules/prefer-async-await.md) - Prefer using async/await instead of returning a Promise.
9394
- [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.
9495
- [prefer-t-regex](docs/rules/prefer-t-regex.md) - Prefer using `t.regex()` to test regular expressions. *(fixable)*
96+
- [prefer-t-throws](docs/rules/prefer-t-throws.md) - Prefer using `t.throws()` over try/catch.
9597
- [test-title](docs/rules/test-title.md) - Ensure tests have a title.
9698
- [test-title-format](docs/rules/test-title-format.md) - Ensure test titles have a certain format.
9799
- [use-t](docs/rules/use-t.md) - Ensure test functions use `t` as their parameter.

rules/prefer-t-throws.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
const {visitIf} = require('enhance-visitors');
4+
const createAvaRule = require('../create-ava-rule');
5+
const util = require('../util');
6+
7+
const create = context => {
8+
const ava = createAvaRule();
9+
10+
return ava.merge({
11+
TryStatement: visitIf([
12+
ava.isInTestFile,
13+
ava.isInTestNode,
14+
])(node => {
15+
const expressions = node.block.body.filter(node => node.type === 'ExpressionStatement');
16+
if (expressions.length < 2) {
17+
return;
18+
}
19+
20+
const lastExpression = expressions[expressions.length - 1].expression;
21+
if (lastExpression.type !== 'CallExpression'
22+
|| lastExpression.callee.object.name !== 't'
23+
|| lastExpression.callee.property.name !== 'fail'
24+
) {
25+
return;
26+
}
27+
28+
if (expressions.filter(node => node.expression.type === 'AwaitExpression').length === 1) {
29+
context.report({
30+
node,
31+
message: 'Prefer using the `t.throws()` assertion.',
32+
});
33+
}
34+
}),
35+
});
36+
};
37+
38+
module.exports = {
39+
create,
40+
meta: {
41+
type: 'suggestion',
42+
docs: {
43+
url: util.getDocsUrl(__filename),
44+
},
45+
schema: [],
46+
},
47+
};

test/prefer-t-throws.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
'use strict';
2+
3+
const test = require('ava');
4+
const avaRuleTester = require('eslint-ava-rule-tester');
5+
const rule = require('../rules/prefer-t-throws');
6+
7+
const ruleTester = avaRuleTester(test, {
8+
parserOptions: {
9+
ecmaVersion: 'latest',
10+
},
11+
});
12+
13+
const header = 'const test = require(\'ava\');\n';
14+
15+
ruleTester.run('prefer-t-throws', rule, {
16+
valid: [
17+
`${header}test(async t => { const error = await t.throws(promise); t.is(error, 'error'); });`,
18+
`${header}test(async t => { try { await promise; } catch (error) { t.is(error, 'error'); } });`,
19+
`${header}test(async t => { try { await promise; await anotherPromise; t.fail(); } catch (error) { t.is(error, 'error'); } });`,
20+
],
21+
invalid: [
22+
{
23+
code: `${header}test(async t => { try { await Promise.reject('error'); t.fail(); } catch (error) { t.is(error, 'error'); } });`,
24+
errors: [{message: 'Prefer using the `t.throws()` assertion.'}],
25+
},
26+
],
27+
});

0 commit comments

Comments
 (0)