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

Add no-error-ctor-with-notthrows rule #326

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 35 additions & 0 deletions docs/rules/no-error-ctor-with-notthrows.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# No specifying error type in `t.notThrows()`

AVA will fail if error constructor is specified in the second argument of `t.notThrows()`.


## Fail

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

test('some test', t => {
t.notThrows(() => {
t.pass();
}, TypeError);
});
```


## Pass

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

test('some test', t => {
t.notThrows(() => {
t.pass();
});
});

test('some test', t => {
t.throws(() => {
t.pass();
}, TypeError);
});
```
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ module.exports = {
'ava/no-async-fn-without-await': 'error',
'ava/no-cb-test': 'off',
'ava/no-duplicate-modifiers': 'error',
'ava/no-error-ctor-with-notthrows': 'error',
'ava/no-identical-title': 'error',
'ava/no-ignored-test-files': 'error',
'ava/no-import-test-files': 'error',
Expand Down
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Configure it in `package.json`.
"ava/no-async-fn-without-await": "error",
"ava/no-cb-test": "off",
"ava/no-duplicate-modifiers": "error",
"ava/no-error-ctor-with-notthrows": "error",
"ava/no-identical-title": "error",
"ava/no-ignored-test-files": "error",
"ava/no-import-test-files": "error",
Expand Down Expand Up @@ -82,6 +83,7 @@ The rules will only activate in test files.
- [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-error-ctor-with-notthrows](docs/rules/no-error-ctor-with-notthrows.md) - Ensure no error constructor is specified in `t.notThrows()`.
- [no-identical-title](docs/rules/no-identical-title.md) - Ensure no tests have the same title.
- [no-ignored-test-files](docs/rules/no-ignored-test-files.md) - Ensure no tests are written in ignored files.
- [no-import-test-files](docs/rules/no-import-test-files.md) - Ensure no test files are imported anywhere.
Expand Down
40 changes: 40 additions & 0 deletions rules/no-error-ctor-with-notthrows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';
const {visitIf} = require('enhance-visitors');
const util = require('../util');
const createAvaRule = require('../create-ava-rule');

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

return ava.merge({
CallExpression: visitIf([
ava.isInTestFile,
ava.isInTestNode
])(node => {
const functionArgIndex = node.arguments.length - 1;

if (typeof node.callee.property === 'undefined' || functionArgIndex !== 1 || node.callee.type !== 'MemberExpression' || node.arguments[1].type !== 'Identifier' || util.getNameOfRootNodeObject(node.callee) !== 't') {
return;
}

const calleeProperty = node.callee.property.name;
const functionArgName = node.arguments[1].name;
if ((calleeProperty === 'notThrows' || calleeProperty === 'notThrowsAsync') && functionArgName.endsWith('Error')) {
context.report({
node,
message: `Do not specify an error constructor in the second argument of \`t.${calleeProperty}()\``
});
}
})
});
};

module.exports = {
create,
meta: {
docs: {
url: util.getDocsUrl(__filename)
},
type: 'problem'
}
};
209 changes: 209 additions & 0 deletions test/no-error-ctor-with-notthrows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
const test = require('ava');
const avaRuleTester = require('eslint-ava-rule-tester');
const rule = require('../rules/no-error-ctor-with-notthrows');

const ruleTester = avaRuleTester(test, {
env: {
es6: true
},
parserOptions: {
ecmaVersion: 2019
}
});

const errors = [{}];

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

ruleTester.run('no-error-ctor-with-notthrows', rule, {
valid: [
`${header}
test('some test', t => {
t.notThrows(() => {
t.pass();
});
});`,

`${header}
test(t => {
t.notThrows(() => {
t.pass();
});
});`,

`${header}
test(t => {
t.throws(() => {
t.pass();
}, TypeError);
});`,

`${header}
test(t => { t.end(); })`,

`${header}
test('some test', t => {
t.notThrows(() => {
t.pass();
}, true);
});`,

`${header}
test('some test', t => {
t.notThrows(() => {
t.pass();
}, 'some string');
});`,

`${header}
test('some test', t => {
t.notThrows(() => {
t.pass();
}, {firstName:'some', lastName: 'object'});
});`,

`${header}
test('some test', t => {
t.notThrowsAsync(() => {
t.pass();
});
});`,

`${header}
test(t => {
t.notThrowsAsync(() => {
t.pass();
});
});`,

`${header}
test('some test', t => {
t.notThrowsAsync(() => {
t.pass();
}, {firstName:'some', lastName: 'object'});
});`,

`${header}
test('some test', t => {
notThrows(foo);
});`,

`${header}
test('some test', t => {
myCustomNotThrows.notThrows(foo);
});`,

`${header}
t.notThrows(() => {
t.pass();
}, void 0);`,

// Shouldn't be triggered since it's not a test file
`test('some test', t => {
t.notThrowsAsync(() => {
t.pass();
}, TypeError);
});`
],
invalid: [
{
code: `${header}
test(t => {
t.notThrows(() => {
t.pass();
}, TypeError);
});`,
errors
},
{
code: `${header}
test('some test', t => {
t.notThrows(() => {
t.pass();
}, TypeError);
});`,
errors
},
{
code: `${header}
test(t => {
t.notThrowsAsync(() => {
t.pass();
}, TypeError);
});`,
errors
},
{
code: `${header}
test('some test', t => {
t.notThrowsAsync(() => {
t.pass();
}, TypeError);
});`,
errors
},
{
code: `${header}
test('some test', t => {
t.notThrowsAsync(() => {
t.pass();
}, Error);
});`,
errors
},
{
code: `${header}
test('some test', t => {
t.notThrowsAsync(() => {
t.pass();
}, SyntaxError);
});`,
errors
},
{
code: `${header}
test('some test', t => {
t.notThrowsAsync(() => {
t.pass();
}, AssertionError);
});`,
errors
},
{
code: `${header}
test('some test', t => {
t.notThrowsAsync(() => {
t.pass();
}, ReferenceError);
});`,
errors
},
{
code: `${header}
test('some test', t => {
t.notThrowsAsync(() => {
t.pass();
}, RangeError);
});`,
errors
},
{
code: `${header}
test('some test', t => {
t.notThrowsAsync(() => {
t.pass();
}, SystemError);
});`,
errors
},
{
code: `${header}
test('some test', t => {
t.notThrowsAsync(() => {
t.pass();
}, $DOMError);
});`,
errors
}
]
});