Skip to content

Commit 03f6195

Browse files
committed
chore: second go at implementing with TS
1 parent f959c27 commit 03f6195

File tree

2 files changed

+82
-46
lines changed

2 files changed

+82
-46
lines changed

src/rules/__tests__/lowercase-name.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const ruleTester = new RuleTester({
1313

1414
ruleTester.run('lowercase-name', rule, {
1515
valid: [
16+
'randomFunction()',
1617
'it()',
1718
"it(' ', function () {})",
1819
'it(" ", function () {})',
@@ -24,6 +25,8 @@ ruleTester.run('lowercase-name', rule, {
2425
'it("123 foo", function () {})',
2526
'it(42, function () {})',
2627
'it(``)',
28+
'it("")',
29+
'it(42)',
2730
'test()',
2831
"test('foo', function () {})",
2932
'test("foo", function () {})',
@@ -32,6 +35,8 @@ ruleTester.run('lowercase-name', rule, {
3235
'test("123 foo", function () {})',
3336
'test("42", function () {})',
3437
'test(``)',
38+
'test("")',
39+
'test(42)',
3540
'describe()',
3641
"describe('foo', function () {})",
3742
'describe("foo", function () {})',
@@ -41,6 +46,8 @@ ruleTester.run('lowercase-name', rule, {
4146
'describe("42", function () {})',
4247
'describe(function () {})',
4348
'describe(``)',
49+
'describe("")',
50+
'describe(42)',
4451
],
4552

4653
invalid: [

src/rules/lowercase-name.ts

Lines changed: 75 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,85 @@
11
import { createRule } from './tsUtils';
22
import {
3-
TSESTree,
43
AST_NODE_TYPES,
4+
TSESTree,
55
} from '@typescript-eslint/experimental-utils';
66

7-
const isIdentifier = (
8-
callee: TSESTree.LeftHandSideExpression,
9-
): callee is TSESTree.Identifier => !!callee;
7+
interface JestFunctionIdentifier extends TSESTree.Identifier {
8+
name: 'it' | 'test' | 'describe';
9+
}
10+
11+
interface TestFunctionCallExpression extends TSESTree.CallExpression {
12+
callee: JestFunctionIdentifier;
13+
}
14+
15+
type ArgumentLiteral = TSESTree.Literal | TSESTree.TemplateLiteral;
16+
17+
interface FirstArgumentStringCallExpression extends TSESTree.CallExpression {
18+
arguments: [ArgumentLiteral];
19+
}
20+
21+
type CallExpressionWithCorrectCalleeAndArguments = TestFunctionCallExpression &
22+
FirstArgumentStringCallExpression;
1023

1124
const isItTestOrDescribeFunction = (
12-
callee: TSESTree.LeftHandSideExpression,
13-
): callee is TSESTree.Identifier => {
14-
if (!isIdentifier(callee)) {
25+
node: TSESTree.CallExpression,
26+
): node is TestFunctionCallExpression => {
27+
const { callee } = node;
28+
29+
if (callee.type !== AST_NODE_TYPES.Identifier) {
1530
return false;
1631
}
1732

18-
return (
19-
callee.name === 'it' || callee.name === 'test' || callee.name === 'describe'
20-
);
33+
const { name } = callee;
34+
35+
return name === 'it' || name === 'test' || name === 'describe';
2136
};
2237

23-
const isItDescription = (
24-
expression?: TSESTree.Expression,
25-
): expression is TSESTree.Literal | TSESTree.TemplateLiteral =>
26-
!!expression &&
27-
(expression.type === AST_NODE_TYPES.Literal ||
28-
expression.type === AST_NODE_TYPES.TemplateLiteral);
38+
const hasStringAsFirstArgument = (
39+
node: TSESTree.CallExpression,
40+
): node is FirstArgumentStringCallExpression =>
41+
node.arguments &&
42+
node.arguments[0] &&
43+
(node.arguments[0].type === AST_NODE_TYPES.Literal ||
44+
node.arguments[0].type === AST_NODE_TYPES.TemplateLiteral);
45+
46+
const isJestFunctionWithLiteralArg = (
47+
node: TSESTree.CallExpression,
48+
): node is CallExpressionWithCorrectCalleeAndArguments =>
49+
isItTestOrDescribeFunction(node) && hasStringAsFirstArgument(node);
2950

30-
const testDescription = (
31-
firstArgument: TSESTree.Literal | TSESTree.TemplateLiteral,
32-
) => {
33-
if (firstArgument.type === AST_NODE_TYPES.Literal) {
34-
return firstArgument.value as string;
51+
const testDescription = (argument: ArgumentLiteral): string | null => {
52+
if (argument.type === AST_NODE_TYPES.Literal) {
53+
const { value } = argument;
54+
55+
if (typeof value === 'string') {
56+
return value;
57+
}
58+
return null;
3559
}
3660

37-
return firstArgument.quasis[0].value.raw;
61+
return argument.quasis[0].value.raw;
3862
};
3963

40-
const descriptionBeginsWithLowerCase = (
41-
node: TSESTree.CallExpression,
42-
): string | false => {
43-
const {
44-
arguments: [firstArgument],
45-
callee,
46-
} = node;
47-
if (isItTestOrDescribeFunction(callee) && isItDescription(firstArgument)) {
48-
const description = testDescription(firstArgument);
49-
if (!description[0]) {
50-
return false;
51-
}
64+
const jestFunctionName = (
65+
node: CallExpressionWithCorrectCalleeAndArguments,
66+
) => {
67+
const description = testDescription(node.arguments[0]);
68+
if (description === null) {
69+
return null;
70+
}
5271

53-
if (description[0] !== description[0].toLowerCase()) {
54-
return callee.name;
55-
}
72+
const firstCharacter = description.charAt(0);
73+
74+
if (!firstCharacter) {
75+
return null;
76+
}
77+
78+
if (firstCharacter !== firstCharacter.toLowerCase()) {
79+
return node.callee.name;
5680
}
57-
return false;
81+
82+
return null;
5883
};
5984

6085
export default createRule({
@@ -81,7 +106,9 @@ export default createRule({
81106
},
82107
],
83108
} as const,
84-
defaultOptions: [{ ignore: [] } as { ignore: readonly string[] }],
109+
defaultOptions: [
110+
{ ignore: [] } as { ignore: readonly (JestFunctionIdentifier['name'])[] },
111+
],
85112
create(context, [{ ignore }]) {
86113
const ignoredFunctionNames = ignore.reduce<
87114
Record<string, true | undefined>
@@ -90,12 +117,16 @@ export default createRule({
90117
return accumulator;
91118
}, Object.create(null));
92119

93-
const isIgnoredFunctionName = (node: TSESTree.CallExpression) =>
94-
ignoredFunctionNames[(node.callee as TSESTree.Identifier).name];
120+
const isIgnoredFunctionName = (
121+
node: CallExpressionWithCorrectCalleeAndArguments,
122+
) => ignoredFunctionNames[node.callee.name];
95123

96124
return {
97125
CallExpression(node) {
98-
const erroneousMethod = descriptionBeginsWithLowerCase(node);
126+
if (!isJestFunctionWithLiteralArg(node)) {
127+
return;
128+
}
129+
const erroneousMethod = jestFunctionName(node);
99130

100131
if (erroneousMethod && !isIgnoredFunctionName(node)) {
101132
context.report({
@@ -104,10 +135,8 @@ export default createRule({
104135
node,
105136
fix(fixer) {
106137
const [firstArg] = node.arguments;
107-
// guaranteed by descriptionBeginsWithLowerCase
108-
const description = testDescription(firstArg as
109-
| TSESTree.Literal
110-
| TSESTree.TemplateLiteral);
138+
// guaranteed by jestFunctionName
139+
const description = testDescription(firstArg)!;
111140

112141
const rangeIgnoringQuotes: [number, number] = [
113142
firstArg.range[0] + 1,

0 commit comments

Comments
 (0)