Skip to content

Commit

Permalink
Add prefer-t-throws rule
Browse files Browse the repository at this point in the history
Fixes: avajs#156
  • Loading branch information
Mesteery committed Feb 6, 2022
1 parent 33dbbc7 commit 8e64d58
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 0 deletions.
60 changes: 60 additions & 0 deletions docs/rules/prefer-t-throws.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Prefer using `t.throws()` over try/catch

This rule will enforce the use of `t.throws()` when possible.

## Fail

```js
const test = require('ava');

test('some test', async t => {
try {
await Promise.reject('error');
t.fail();
} catch (error) {
t.is(error, 'error');
}
});
```

## Pass

```js
const test = require('ava');

test('some test', async t => {
const error = await t.throws(Promise.reject('error'));
t.is(error, 'error');
});
```

```js
const test = require('ava');

test('some test', async t => {
try {
const result = await promiseThatMaybeFails();
// This passes because using a try-catch can be intentional, for example, to test both the result and the error:
// t.is(result, 'good result');
} catch (error) {
t.is(error, 'good error message');
}
});
```

```js
const test = require('ava');

test('some test', async t => {
try {
// This is also because handling multiple rejection promises with try/catch may be easier or may be a deliberate choice.
await promiseThatMaybeFails();
await anotherPromise;
await timeout(100, 'good error message');
t.fail();
} catch (error) {
t.is(error, 'good error message');
}
});
```

1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ module.exports = {
'ava/prefer-async-await': 'error',
'ava/prefer-power-assert': 'off',
'ava/prefer-t-regex': 'error',
'ava/prefer-t-throws': 'error',
'ava/test-title': 'error',
'ava/test-title-format': 'off',
'ava/use-t-well': 'error',
Expand Down
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ Configure it in `package.json`.
"ava/prefer-async-await": "error",
"ava/prefer-power-assert": "off",
"ava/prefer-t-regex": "error",
"ava/prefer-t-throws": "error",
"ava/test-title": "error",
"ava/test-title-format": "off",
"ava/use-t": "error",
Expand Down Expand Up @@ -92,6 +93,7 @@ The rules will only activate in test files.
- [prefer-async-await](docs/rules/prefer-async-await.md) - Prefer using async/await instead of returning a Promise.
- [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.
- [prefer-t-regex](docs/rules/prefer-t-regex.md) - Prefer using `t.regex()` to test regular expressions. *(fixable)*
- [prefer-t-throws](docs/rules/prefer-t-throws.md) - Prefer using `t.throws()` over try/catch.
- [test-title](docs/rules/test-title.md) - Ensure tests have a title.
- [test-title-format](docs/rules/test-title-format.md) - Ensure test titles have a certain format.
- [use-t](docs/rules/use-t.md) - Ensure test functions use `t` as their parameter.
Expand Down
47 changes: 47 additions & 0 deletions rules/prefer-t-throws.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
'use strict';

const {visitIf} = require('enhance-visitors');
const createAvaRule = require('../create-ava-rule');
const util = require('../util');

const create = context => {
const ava = createAvaRule();

return ava.merge({
TryStatement: visitIf([
ava.isInTestFile,
ava.isInTestNode,
])(node => {
const expressions = node.block.body.filter(node => node.type === 'ExpressionStatement');
if (expressions.length < 2) {
return;
}

const lastExpression = expressions[expressions.length - 1].expression;
if (lastExpression.type !== 'CallExpression'
|| lastExpression.callee.object.name !== 't'
|| lastExpression.callee.property.name !== 'fail'
) {
return;
}

if (expressions.filter(node => node.expression.type === 'AwaitExpression').length === 1) {
context.report({
node,
message: 'Prefer using the `t.throws()` assertion.',
});
}
}),
});
};

module.exports = {
create,
meta: {
type: 'suggestion',
docs: {
url: util.getDocsUrl(__filename),
},
schema: [],
},
};
27 changes: 27 additions & 0 deletions test/prefer-t-throws.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
'use strict';

const test = require('ava');
const avaRuleTester = require('eslint-ava-rule-tester');
const rule = require('../rules/prefer-t-throws');

const ruleTester = avaRuleTester(test, {
parserOptions: {
ecmaVersion: 'latest',
},
});

const header = 'const test = require(\'ava\');\n';

ruleTester.run('prefer-t-throws', rule, {
valid: [
`${header}test(async t => { const error = await t.throws(promise); t.is(error, 'error'); });`,
`${header}test(async t => { try { await promise; } catch (error) { t.is(error, 'error'); } });`,
`${header}test(async t => { try { await promise; await anotherPromise; t.fail(); } catch (error) { t.is(error, 'error'); } });`,
],
invalid: [
{
code: `${header}test(async t => { try { await Promise.reject('error'); t.fail(); } catch (error) { t.is(error, 'error'); } });`,
errors: [{message: 'Prefer using the `t.throws()` assertion.'}],
},
],
});

0 comments on commit 8e64d58

Please sign in to comment.