Skip to content

Commit c793b7a

Browse files
himynameisdaveSimenB
authored andcommitted
feat(rules): Add no-empty-title rule (#238)
Fixes #226
1 parent f6f6d84 commit c793b7a

File tree

7 files changed

+216
-10
lines changed

7 files changed

+216
-10
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ for more information about extending configuration files.
9696
| [lowercase-name][] | Disallow capitalized test names | | ![fixable-green][] |
9797
| [no-alias-methods][] | Disallow alias methods | ![recommended][] | ![fixable-green][] |
9898
| [no-disabled-tests][] | Disallow disabled tests | ![recommended][] | |
99+
| [no-empty-title][] | Disallow empty titles | | |
99100
| [no-focused-tests][] | Disallow focused tests | ![recommended][] | |
100101
| [no-hooks][] | Disallow setup and teardown hooks | | |
101102
| [no-identical-title][] | Disallow identical titles | ![recommended][] | |
@@ -131,6 +132,7 @@ for more information about extending configuration files.
131132
[lowercase-name]: docs/rules/lowercase-name.md
132133
[no-alias-methods]: docs/rules/no-alias-methods.md
133134
[no-disabled-tests]: docs/rules/no-disabled-tests.md
135+
[no-empty-title]: docs/rules/no-empty-title.md
134136
[no-focused-tests]: docs/rules/no-focused-tests.md
135137
[no-hooks]: docs/rules/no-hooks.md
136138
[no-identical-title]: docs/rules/no-identical-title.md

docs/rules/no-empty-title.md

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Disallow empty titles
2+
3+
Having an empty string as your test title is pretty useless. This rule reports
4+
an error if it finds an empty string as s test title.
5+
6+
This rule is not auto-fixable.
7+
8+
## Rule Details
9+
10+
The following patterns are considered warnings:
11+
12+
```js
13+
describe('', () => {});
14+
describe('foo', () => {
15+
it('', () => {});
16+
});
17+
it('', () => {});
18+
test('', () => {});
19+
xdescribe('', () => {});
20+
xit('', () => {});
21+
xtest('', () => {});
22+
```
23+
24+
These patterns would not be considered warnings:
25+
26+
```js
27+
describe('foo', () => {});
28+
describe('foo', () => {
29+
it('bar', () => {});
30+
});
31+
test('foo', () => {});
32+
it('foo', () => {});
33+
xdescribe('foo', () => {});
34+
xit('foo', () => {});
35+
xtest('foo', () => {});
36+
```

rules/__tests__/no-empty-title.js

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
'use strict';
2+
3+
const { RuleTester } = require('eslint');
4+
const rule = require('../no-empty-title');
5+
6+
const ruleTester = new RuleTester({
7+
parserOptions: {
8+
sourceType: 'module',
9+
},
10+
});
11+
12+
ruleTester.run('no-empty-title', rule, {
13+
valid: [
14+
'someFn("", function () {})',
15+
'describe(1, function () {})',
16+
'describe("foo", function () {})',
17+
'describe("foo", function () { it("bar", function () {}) })',
18+
'test("foo", function () {})',
19+
'test(`foo`, function () {})',
20+
'test(`${foo}`, function () {})',
21+
"it('foo', function () {})",
22+
"xdescribe('foo', function () {})",
23+
"xit('foo', function () {})",
24+
"xtest('foo', function () {})",
25+
],
26+
invalid: [
27+
{
28+
code: 'describe("", function () {})',
29+
errors: [
30+
{
31+
message: rule.errorMessages.describe,
32+
column: 1,
33+
line: 1,
34+
},
35+
],
36+
},
37+
{
38+
code: ["describe('foo', () => {", "it('', () => {})", '})'].join('\n'),
39+
errors: [
40+
{
41+
message: rule.errorMessages.test,
42+
column: 1,
43+
line: 2,
44+
},
45+
],
46+
},
47+
{
48+
code: 'it("", function () {})',
49+
errors: [
50+
{
51+
message: rule.errorMessages.test,
52+
column: 1,
53+
line: 1,
54+
},
55+
],
56+
},
57+
{
58+
code: 'test("", function () {})',
59+
errors: [
60+
{
61+
message: rule.errorMessages.test,
62+
column: 1,
63+
line: 1,
64+
},
65+
],
66+
},
67+
{
68+
code: 'test(``, function () {})',
69+
errors: [
70+
{
71+
message: rule.errorMessages.test,
72+
column: 1,
73+
line: 1,
74+
},
75+
],
76+
},
77+
{
78+
code: "xdescribe('', () => {})",
79+
errors: [
80+
{
81+
message: rule.errorMessages.describe,
82+
column: 1,
83+
line: 1,
84+
},
85+
],
86+
},
87+
{
88+
code: "xit('', () => {})",
89+
errors: [
90+
{
91+
message: rule.errorMessages.test,
92+
column: 1,
93+
line: 1,
94+
},
95+
],
96+
},
97+
{
98+
code: "xtest('', () => {})",
99+
errors: [
100+
{
101+
message: rule.errorMessages.test,
102+
column: 1,
103+
line: 1,
104+
},
105+
],
106+
},
107+
],
108+
});

rules/__tests__/no-identical-title.test.js

+6
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ ruleTester.run('no-identical-title', rule, {
6969
es6: true,
7070
},
7171
},
72+
{
73+
code: 'it(`${n}`, function() {});',
74+
env: {
75+
es6: true,
76+
},
77+
},
7278
[
7379
'describe("title " + foo, function() {',
7480
' describe("describe1", function() {});',

rules/no-empty-title.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
'use strict';
2+
3+
const {
4+
getDocsUrl,
5+
hasExpressions,
6+
isDescribe,
7+
isTestCase,
8+
isTemplateLiteral,
9+
isString,
10+
getStringValue,
11+
} = require('./util');
12+
13+
const errorMessages = {
14+
describe: 'describe should not have an empty title',
15+
test: 'test should not have an empty title',
16+
};
17+
18+
module.exports = {
19+
meta: {
20+
docs: {
21+
url: getDocsUrl(__filename),
22+
},
23+
},
24+
create(context) {
25+
return {
26+
CallExpression(node) {
27+
const is = {
28+
describe: isDescribe(node),
29+
testCase: isTestCase(node),
30+
};
31+
if (!is.describe && !is.testCase) {
32+
return;
33+
}
34+
const [firstArgument] = node.arguments;
35+
if (!isString(firstArgument)) {
36+
return;
37+
}
38+
if (isTemplateLiteral(firstArgument) && hasExpressions(firstArgument)) {
39+
return;
40+
}
41+
if (getStringValue(firstArgument) === '') {
42+
const message = is.describe
43+
? errorMessages.describe
44+
: errorMessages.test;
45+
context.report({
46+
message,
47+
node,
48+
});
49+
}
50+
},
51+
};
52+
},
53+
errorMessages,
54+
};

rules/no-identical-title.js

+2-9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const {
66
isTestCase,
77
isString,
88
hasExpressions,
9+
getStringValue,
910
} = require('./util');
1011

1112
const newDescribeContext = () => ({
@@ -50,14 +51,6 @@ const isFirstArgValid = arg => {
5051
return true;
5152
};
5253

53-
const getArgValue = arg => {
54-
if (arg.type === 'TemplateLiteral') {
55-
return arg.quasis[0].value.raw;
56-
}
57-
58-
return arg.value;
59-
};
60-
6154
module.exports = {
6255
meta: {
6356
docs: {
@@ -76,7 +69,7 @@ module.exports = {
7669
if (!isFirstArgValid(firstArgument)) {
7770
return;
7871
}
79-
const title = getArgValue(firstArgument);
72+
const title = getStringValue(firstArgument);
8073
handleTestCaseTitles(context, currentLayer.testTitles, node, title);
8174
handleDescribeBlockTitles(
8275
context,

rules/util.js

+8-1
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,15 @@ const isFunction = node =>
132132

133133
const isString = node =>
134134
(node.type === 'Literal' && typeof node.value === 'string') ||
135-
node.type === 'TemplateLiteral';
135+
isTemplateLiteral(node);
136+
137+
const isTemplateLiteral = node => node.type === 'TemplateLiteral';
136138

137139
const hasExpressions = node => node.expressions && node.expressions.length > 0;
138140

141+
const getStringValue = arg =>
142+
isTemplateLiteral(arg) ? arg.quasis[0].value.raw : arg.value;
143+
139144
/**
140145
* Generates the URL to documentation for the given rule name. It uses the
141146
* package version to build the link to a tagged version of the
@@ -210,8 +215,10 @@ module.exports = {
210215
expectToEqualCase,
211216
expectNotToEqualCase,
212217
getNodeName,
218+
getStringValue,
213219
isDescribe,
214220
isFunction,
221+
isTemplateLiteral,
215222
isTestCase,
216223
isString,
217224
hasExpressions,

0 commit comments

Comments
 (0)