1 | 'use strict';
|
2 |
|
3 | const { getDocsUrl } = require('./util');
|
4 |
|
5 | const 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 |
|
15 | const 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 |
|
24 | const testDescription = node => {
|
25 | const [firstArgument] = node.arguments;
|
26 | const { type } = firstArgument;
|
27 |
|
28 | if (type === 'Literal') {
|
29 | return firstArgument.value;
|
30 | }
|
31 |
|
32 |
|
33 | return firstArgument.quasis[0].value.raw;
|
34 | };
|
35 |
|
36 | const 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 |
|
50 | module.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 | };
|