Skip to content

Commit 7cfdf34

Browse files
jfmengelssindresorhus
authored andcommitted
Add no-duplicate-modifiers rule (fixes #137) (#138)
1 parent d4c419c commit 7cfdf34

File tree

9 files changed

+146
-19
lines changed

9 files changed

+146
-19
lines changed

docs/rules/max-asserts.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Skipped assertions are counted.
1111

1212
```js
1313
/*eslint ava/max-asserts: ["error", 5]*/
14-
import test = from 'ava';
14+
import test from 'ava';
1515

1616
test('getSomeObject should define the players\' names', t => {
1717
const object = lib.getSomeObject();
@@ -30,7 +30,7 @@ test('getSomeObject should define the players\' names', t => {
3030
## Pass
3131

3232
```js
33-
import test = from 'ava';
33+
import test from 'ava';
3434

3535
test('getSomeObject should define the player\'s name', t => {
3636
const object = lib.getSomeObject();

docs/rules/no-duplicate-modifiers.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Ensure tests do not have duplicate modifiers
2+
3+
Translations: [Français](https://github.com/avajs/ava-docs/blob/master/fr_FR/related/eslint-plugin-ava/docs/rules/no-duplicate-modifiers.md)
4+
5+
Prevent the use of duplicate [test modifiers](https://github.com/avajs/ava#api).
6+
7+
8+
## Fail
9+
10+
```js
11+
import test from 'ava';
12+
13+
test.only.only(t => {});
14+
test.serial.serial(t => {});
15+
test.cb.cb(t => {});
16+
test.beforeEach.beforeEach(t => {});
17+
test.only.only.cb(t => {});
18+
```
19+
20+
21+
## Pass
22+
23+
```js
24+
import test from 'ava';
25+
26+
test.only(t => {});
27+
test.serial(t => {});
28+
test.cb.only(t => {});
29+
test.beforeEach(t => {});
30+
```

docs/rules/no-unknown-modifiers.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@
22

33
Translations: [Français](https://github.com/avajs/ava-docs/blob/master/fr_FR/related/eslint-plugin-ava/docs/rules/no-unknown-modifiers.md)
44

5-
Prevent the use of unknown test modifiers.
5+
Prevent the use of unknown [test modifiers](https://github.com/avajs/ava#api).
66

77

88
## Fail
99

1010
```js
11-
import test = from 'ava';
11+
import test from 'ava';
1212

1313
test.onlu(t => {});
1414
test.seril(t => {});
@@ -21,7 +21,7 @@ test.unknown(t => {});
2121
## Pass
2222

2323
```js
24-
import test = from 'ava';
24+
import test from 'ava';
2525

2626
test.only(t => {});
2727
test.serial(t => {});

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module.exports = {
1717
'ava/assertion-message': ['off', 'always'],
1818
'ava/max-asserts': ['off', 5],
1919
'ava/no-cb-test': 'off',
20+
'ava/no-duplicate-modifiers': 'error',
2021
'ava/no-identical-title': 'error',
2122
'ava/no-ignored-test-files': 'error',
2223
'ava/no-invalid-end': 'error',

readme.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ Configure it in `package.json`.
3535
"ava/assertion-message": ["off", "always"],
3636
"ava/max-asserts": ["off", 5],
3737
"ava/no-cb-test": "off",
38+
"ava/no-duplicate-modifiers": "error",
3839
"ava/no-identical-title": "error",
3940
"ava/no-ignored-test-files": "error",
4041
"ava/no-invalid-end": "error",
@@ -68,11 +69,12 @@ The rules will only activate in test files.
6869
- [assertion-message](docs/rules/assertion-message.md) - Enforce or disallow assertion messages.
6970
- [max-asserts](docs/rules/max-asserts.md) - Limit the number of assertions in a test.
7071
- [no-cb-test](docs/rules/no-cb-test.md) - Ensure no `test.cb()` is used.
72+
- [no-duplicate-modifiers](docs/rules/no-duplicate-modifiers.md) - Ensure tests do not have duplicate modifiers.
7173
- [no-identical-title](docs/rules/no-identical-title.md) - Ensure no tests have the same title.
7274
- [no-ignored-test-files](docs/rules/no-ignored-test-files.md) - Ensure no tests are written in ignored files.
7375
- [no-invalid-end](docs/rules/no-invalid-end.md) - Ensure `t.end()` is only called inside `test.cb()`.
74-
- [no-only-test](docs/rules/no-only-test.md) - Ensure no `test.only()` are present.
7576
- [no-nested-tests](docs/rules/no-nested-tests.md) - Ensure no tests are nested.
77+
- [no-only-test](docs/rules/no-only-test.md) - Ensure no `test.only()` are present.
7678
- [no-skip-assert](docs/rules/no-skip-assert.md) - Ensure no assertions are skipped.
7779
- [no-skip-test](docs/rules/no-skip-test.md) - Ensure no tests are skipped.
7880
- [no-statement-after-end](docs/rules/no-statement-after-end.md) - Ensure `t.end()` is the last statement executed.

rules/no-duplicate-modifiers.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict';
2+
const visitIf = require('enhance-visitors').visitIf;
3+
const util = require('../util');
4+
const createAvaRule = require('../create-ava-rule');
5+
6+
const create = context => {
7+
const ava = createAvaRule();
8+
9+
return ava.merge({
10+
CallExpression: visitIf([
11+
ava.isInTestFile,
12+
ava.isTestNode
13+
])(node => {
14+
const testModifiers = util.getTestModifiers(node).sort();
15+
if (testModifiers.length === 0) {
16+
return;
17+
}
18+
19+
testModifiers.reduce((prev, current) => {
20+
if (prev === current) {
21+
context.report({
22+
node,
23+
message: `Duplicate test modifier \`${current}\`.`
24+
});
25+
}
26+
return current;
27+
});
28+
})
29+
});
30+
};
31+
32+
module.exports = {
33+
create,
34+
meta: {}
35+
};

rules/no-unknown-modifiers.js

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict';
22
const visitIf = require('enhance-visitors').visitIf;
3+
const util = require('../util');
34
const createAvaRule = require('../create-ava-rule');
45

56
const modifiers = [
@@ -16,19 +17,7 @@ const modifiers = [
1617
'failing'
1718
];
1819

19-
function getTestModifiers(node) {
20-
if (node.type === 'CallExpression') {
21-
return getTestModifiers(node.callee);
22-
}
23-
24-
if (node.type === 'MemberExpression') {
25-
return getTestModifiers(node.object).concat(node.property.name);
26-
}
27-
28-
return [];
29-
}
30-
31-
const unknownModifiers = node => getTestModifiers(node)
20+
const unknownModifiers = node => util.getTestModifiers(node)
3221
.filter(modifier => modifiers.indexOf(modifier) === -1);
3322

3423
const create = context => {

test/no-duplicate-modifiers.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import test from 'ava';
2+
import avaRuleTester from 'eslint-ava-rule-tester';
3+
import rule from '../rules/no-duplicate-modifiers';
4+
5+
const ruleTester = avaRuleTester(test, {
6+
env: {
7+
es6: true
8+
}
9+
});
10+
11+
const ruleError = {ruleId: 'no-duplicate-modifiers'};
12+
const header = `const test = require('ava');\n`;
13+
14+
const modifiers = [
15+
'after',
16+
'afterEach',
17+
'always',
18+
'before',
19+
'beforeEach',
20+
'cb',
21+
'failing',
22+
'only',
23+
'serial',
24+
'skip',
25+
'todo'
26+
];
27+
28+
const valid = modifiers.map(modifier => `${header} test.${modifier}(t => {});`);
29+
const invalid = modifiers.map(modifier => ({
30+
code: `${header} test.${modifier}.${modifier}(t => {});`,
31+
errors: [{
32+
...ruleError,
33+
message: `Duplicate test modifier \`${modifier}\`.`
34+
}]
35+
}));
36+
37+
ruleTester.run('no-duplicate-modifiers', rule, {
38+
valid: valid.concat([
39+
`${header} test(t => {});`,
40+
`${header} test.cb.only(t => {});`,
41+
`${header} test.after.always(t => {});`,
42+
`${header} test.afterEach.always(t => {});`,
43+
`${header} test.failing.cb(t => {});`,
44+
// shouldn't be triggered since it's not a test file
45+
`test.serial.serial(t => {});`
46+
]),
47+
invalid: invalid.concat([
48+
{
49+
code: `${header} test.serial.cb.only.serial(t => {});`,
50+
errors: [
51+
{
52+
...ruleError,
53+
message: 'Duplicate test modifier `serial`.'
54+
}
55+
]
56+
}
57+
])
58+
});

util.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,15 @@ exports.getAvaConfig = filepath => {
4040
exports.isFunctionExpression = node => {
4141
return node && functionExpressions.indexOf(node.type) !== -1;
4242
};
43+
44+
exports.getTestModifiers = function getTestModifiers(node) {
45+
if (node.type === 'CallExpression') {
46+
return getTestModifiers(node.callee);
47+
}
48+
49+
if (node.type === 'MemberExpression') {
50+
return getTestModifiers(node.object).concat(node.property.name);
51+
}
52+
53+
return [];
54+
};

0 commit comments

Comments
 (0)