UNPKG

2.5 kBJavaScriptView Raw
1'use strict';
2
3const { getDocsUrl } = require('./util');
4
5const isItTestOrDescribeFunction = node => {
6 return (
7 node.type === 'CallExpression' &&
8 node.callee &&
9 (node.callee.name === 'it' ||
10 node.callee.name === 'test' ||
11 node.callee.name === 'describe')
12 );
13};
14
15const isItDescription = node => {
16 return (
17 node.arguments &&
18 node.arguments[0] &&
19 (node.arguments[0].type === 'Literal' ||
20 node.arguments[0].type === 'TemplateLiteral')
21 );
22};
23
24const testDescription = node => {
25 const [firstArgument] = node.arguments;
26 const { type } = firstArgument;
27
28 if (type === 'Literal') {
29 return firstArgument.value;
30 }
31
32 // `isItDescription` guarantees this is `type === 'TemplateLiteral'`
33 return firstArgument.quasis[0].value.raw;
34};
35
36const descriptionBeginsWithLowerCase = node => {
37 if (isItTestOrDescribeFunction(node) && isItDescription(node)) {
38 const description = testDescription(node);
39 if (!description[0]) {
40 return false;
41 }
42
43 if (description[0] !== description[0].toLowerCase()) {
44 return node.callee.name;
45 }
46 }
47 return false;
48};
49
50module.exports = {
51 meta: {
52 docs: {
53 url: getDocsUrl(__filename),
54 },
55 fixable: 'code',
56 },
57 create(context) {
58 const ignore = (context.options[0] && context.options[0].ignore) || [];
59 const ignoredFunctionNames = ignore.reduce((accumulator, value) => {
60 accumulator[value] = true;
61 return accumulator;
62 }, Object.create(null));
63
64 const isIgnoredFunctionName = node =>
65 ignoredFunctionNames[node.callee.name];
66
67 return {
68 CallExpression(node) {
69 const erroneousMethod = descriptionBeginsWithLowerCase(node);
70
71 if (erroneousMethod && !isIgnoredFunctionName(node)) {
72 context.report({
73 message: '`{{ method }}`s should begin with lowercase',
74 data: { method: erroneousMethod },
75 node,
76 fix(fixer) {
77 const [firstArg] = node.arguments;
78 const description = testDescription(node);
79
80 const rangeIgnoringQuotes = [
81 firstArg.range[0] + 1,
82 firstArg.range[1] - 1,
83 ];
84 const newDescription =
85 description.substring(0, 1).toLowerCase() +
86 description.substring(1);
87
88 return [
89 fixer.replaceTextRange(rangeIgnoringQuotes, newDescription),
90 ];
91 },
92 });
93 }
94 },
95 };
96 },
97};