Skip to content

Commit a184661

Browse files
Add test-title-format rule (#235)
Co-authored-by: Sindre Sorhus <[email protected]>
1 parent b1f34a0 commit a184661

File tree

5 files changed

+177
-0
lines changed

5 files changed

+177
-0
lines changed

docs/rules/test-title-format.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Ensure test titles have a certain format
2+
3+
This rule is useful when you want to make sure all test titles match a common pattern to increase readability when tests fail.
4+
5+
For example, titles like `'Should throw when invalid.'`, `'Should fail when called.'` or `'Should pass when using any number.'` could be enforced with the following pattern `'^Should (pass|fail|throw) when [\\w ]+\\.$'` (Note the escaped `\`).
6+
7+
8+
## Fail
9+
10+
```js
11+
/* eslint ava/test-title: ["error", {format: "^Should"}] */
12+
import test from 'ava';
13+
14+
test('Not starting with `Should`', t => {
15+
t.pass();
16+
});
17+
```
18+
19+
```js
20+
/* eslint ava/test-title: ["error", {format: "\\.$"}] */
21+
import test from 'ava';
22+
23+
test('Doesn\'t end with a dot', t => {
24+
t.pass();
25+
});
26+
```
27+
28+
29+
## Pass
30+
31+
```js
32+
/* eslint ava/test-title: ["error", {format: "^Should"}] */
33+
import test from 'ava';
34+
35+
test('Should pass tests', t => {
36+
t.pass();
37+
});
38+
39+
test('Should behave as expected', t => {
40+
t.pass();
41+
});
42+
```
43+
44+
```js
45+
/* eslint ava/test-title: ["error", {format: "\\.$"}] */
46+
import test from 'ava';
47+
48+
test('End with a dot.', t => {
49+
t.pass();
50+
});
51+
```
52+
53+
54+
## Options
55+
56+
This rule supports the following options:
57+
58+
`format`: A regular expression string to match against the test titles.
59+
60+
You can set the options like this:
61+
62+
```json
63+
"ava/test-title-format": [
64+
"error",
65+
{
66+
"format": "^Should"
67+
}
68+
]
69+
```

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-power-assert': 'off',
4242
'ava/test-ended': 'error',
4343
'ava/test-title': 'error',
44+
'ava/test-title-format': 'off',
4445
'ava/use-t-well': 'error',
4546
'ava/use-t': 'error',
4647
'ava/use-test': 'error',

readme.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Configure it in `package.json`.
5959
"ava/prefer-power-assert": "off",
6060
"ava/test-ended": "error",
6161
"ava/test-title": "error",
62+
"ava/test-title-format": "off",
6263
"ava/use-t": "error",
6364
"ava/use-t-well": "error",
6465
"ava/use-test": "error",
@@ -94,6 +95,7 @@ The rules will only activate in test files.
9495
- [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.
9596
- [test-ended](docs/rules/test-ended.md) - Ensure callback tests are explicitly ended.
9697
- [test-title](docs/rules/test-title.md) - Ensure tests have a title.
98+
- [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.
98100
- [use-t-well](docs/rules/use-t-well.md) - Prevent the incorrect use of `t`. *(partly fixable)*
99101
- [use-test](docs/rules/use-test.md) - Ensure that AVA is imported with `test` as the variable name.

rules/test-title-format.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use strict';
2+
const {visitIf} = require('enhance-visitors');
3+
const createAvaRule = require('../create-ava-rule');
4+
const util = require('../util');
5+
6+
const create = context => {
7+
const ava = createAvaRule();
8+
9+
let titleRegExp;
10+
if (context.options[0] && context.options[0].format) {
11+
titleRegExp = new RegExp(context.options[0].format);
12+
} else {
13+
return {};
14+
}
15+
16+
return ava.merge({
17+
CallExpression: visitIf([
18+
ava.isInTestFile,
19+
ava.isTestNode,
20+
ava.hasNoHookModifier
21+
])(node => {
22+
const requiredLength = ava.hasTestModifier('todo') ? 1 : 2;
23+
const hasTitle = node.arguments.length >= requiredLength;
24+
25+
if (hasTitle) {
26+
const title = node.arguments[0];
27+
if (title.type === 'Literal' && !titleRegExp.test(title.value)) {
28+
context.report({
29+
node,
30+
message: `The test title doesn't match the required format: \`${titleRegExp}\``
31+
});
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+
}
46+
};

test/test-title-format.js

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import test from 'ava';
2+
import avaRuleTester from 'eslint-ava-rule-tester';
3+
import rule from '../rules/test-title-format';
4+
5+
const ruleTester = avaRuleTester(test, {
6+
env: {
7+
es6: true
8+
}
9+
});
10+
11+
const errors = [{ruleId: 'test-title-format'}];
12+
const header = 'const test = require(\'ava\');\n';
13+
14+
ruleTester.run('test-title-format', rule, {
15+
valid: [
16+
header + 'test("Foo", t => { t.pass(); });',
17+
{
18+
code: header + 'test("Foo", t => { t.pass(); });',
19+
options: [{format: '.'}]
20+
},
21+
{
22+
code: header + 'test("Should pass tests.", t => { t.pass(); });',
23+
options: [{format: '^Should .+\\.$'}]
24+
},
25+
{
26+
code: header + 'test.todo("Should pass tests.");',
27+
options: [{format: '^Should .+\\.$'}]
28+
},
29+
{
30+
code: header + 'test(t => { t.pass(); });',
31+
options: [{format: '^Should'}]
32+
},
33+
{
34+
code: header + 'notTest("Foo", t => { t.pass(); });',
35+
options: [{format: '^Should'}]
36+
},
37+
{
38+
code: header + 'test(macro, t => { t.pass(); });',
39+
options: [{format: '^Should'}]
40+
},
41+
// Shouldn't be triggered since it's not a test file
42+
{
43+
code: 'test("Test", t => { t.pass(); });',
44+
options: [{format: '^Should'}]
45+
}
46+
],
47+
invalid: [
48+
{
49+
code: header + 'test("Test something", t => { t.pass(); });',
50+
options: [{format: '^Should'}],
51+
errors
52+
},
53+
{
54+
code: header + 'test.todo("Test something");',
55+
options: [{format: '^Should'}],
56+
errors
57+
}
58+
]
59+
});

0 commit comments

Comments
 (0)