1 | 'use strict';
|
2 |
|
3 | const { getDocsUrl, getNodeName, isTestCase, isDescribe } = require('./util');
|
4 |
|
5 | module.exports = {
|
6 | meta: {
|
7 | docs: {
|
8 | url: getDocsUrl(__filename),
|
9 | },
|
10 | fixable: 'code',
|
11 | schema: [
|
12 | {
|
13 | type: 'object',
|
14 | properties: {
|
15 | fn: {
|
16 | enum: ['it', 'test'],
|
17 | },
|
18 | withinDescribe: {
|
19 | enum: ['it', 'test'],
|
20 | },
|
21 | },
|
22 | additionalProperties: false,
|
23 | },
|
24 | ],
|
25 | },
|
26 | create(context) {
|
27 | const configObj = context.options[0] || {};
|
28 | const testKeyword = configObj.fn || 'test';
|
29 | const testKeywordWithinDescribe =
|
30 | configObj.withinDescribe || configObj.fn || 'it';
|
31 |
|
32 | let describeNestingLevel = 0;
|
33 |
|
34 | return {
|
35 | CallExpression(node) {
|
36 | const nodeName = getNodeName(node.callee);
|
37 |
|
38 | if (isDescribe(node)) {
|
39 | describeNestingLevel++;
|
40 | }
|
41 |
|
42 | if (
|
43 | isTestCase(node) &&
|
44 | describeNestingLevel === 0 &&
|
45 | nodeName.indexOf(testKeyword) === -1
|
46 | ) {
|
47 | const oppositeTestKeyword = getOppositeTestKeyword(testKeyword);
|
48 |
|
49 | context.report({
|
50 | message:
|
51 | "Prefer using '{{ testKeyword }}' instead of '{{ oppositeTestKeyword }}'",
|
52 | node: node.callee,
|
53 | data: { testKeyword, oppositeTestKeyword },
|
54 | fix(fixer) {
|
55 | const nodeToReplace =
|
56 | node.callee.type === 'MemberExpression'
|
57 | ? node.callee.object
|
58 | : node.callee;
|
59 |
|
60 | const fixedNodeName = getPreferredNodeName(nodeName, testKeyword);
|
61 | return [fixer.replaceText(nodeToReplace, fixedNodeName)];
|
62 | },
|
63 | });
|
64 | }
|
65 |
|
66 | if (
|
67 | isTestCase(node) &&
|
68 | describeNestingLevel > 0 &&
|
69 | nodeName.indexOf(testKeywordWithinDescribe) === -1
|
70 | ) {
|
71 | const oppositeTestKeyword = getOppositeTestKeyword(
|
72 | testKeywordWithinDescribe
|
73 | );
|
74 |
|
75 | context.report({
|
76 | message:
|
77 | "Prefer using '{{ testKeywordWithinDescribe }}' instead of '{{ oppositeTestKeyword }}' within describe",
|
78 | node: node.callee,
|
79 | data: { testKeywordWithinDescribe, oppositeTestKeyword },
|
80 | fix(fixer) {
|
81 | const nodeToReplace =
|
82 | node.callee.type === 'MemberExpression'
|
83 | ? node.callee.object
|
84 | : node.callee;
|
85 |
|
86 | const fixedNodeName = getPreferredNodeName(
|
87 | nodeName,
|
88 | testKeywordWithinDescribe
|
89 | );
|
90 | return [fixer.replaceText(nodeToReplace, fixedNodeName)];
|
91 | },
|
92 | });
|
93 | }
|
94 | },
|
95 | 'CallExpression:exit'(node) {
|
96 | if (isDescribe(node)) {
|
97 | describeNestingLevel--;
|
98 | }
|
99 | },
|
100 | };
|
101 | },
|
102 | };
|
103 |
|
104 | function getPreferredNodeName(nodeName, preferredTestKeyword) {
|
105 | switch (nodeName) {
|
106 | case 'fit':
|
107 | return 'test.only';
|
108 | default:
|
109 | return nodeName.startsWith('f') || nodeName.startsWith('x')
|
110 | ? nodeName.charAt(0) + preferredTestKeyword
|
111 | : preferredTestKeyword;
|
112 | }
|
113 | }
|
114 |
|
115 | function getOppositeTestKeyword(test) {
|
116 | if (test === 'test') {
|
117 | return 'it';
|
118 | }
|
119 |
|
120 | return 'test';
|
121 | }
|