UNPKG

6.06 kBJavaScriptView Raw
1'use strict';
2
3const path = require('path');
4const { version } = require('../package.json');
5
6const REPO_URL = 'https://github.com/jest-community/eslint-plugin-jest';
7
8const expectCase = node =>
9 node.callee.name === 'expect' &&
10 node.arguments.length === 1 &&
11 node.parent &&
12 node.parent.type === 'MemberExpression' &&
13 node.parent.parent;
14
15const expectNotCase = node =>
16 expectCase(node) &&
17 node.parent.parent.type === 'MemberExpression' &&
18 methodName(node) === 'not';
19
20const expectResolveCase = node =>
21 expectCase(node) &&
22 node.parent.parent.type === 'MemberExpression' &&
23 methodName(node) === 'resolve';
24
25const expectRejectCase = node =>
26 expectCase(node) &&
27 node.parent.parent.type === 'MemberExpression' &&
28 methodName(node) === 'reject';
29
30const expectToBeCase = (node, arg) =>
31 !(expectNotCase(node) || expectResolveCase(node) || expectRejectCase(node)) &&
32 expectCase(node) &&
33 methodName(node) === 'toBe' &&
34 argument(node) &&
35 ((argument(node).type === 'Literal' &&
36 argument(node).value === null &&
37 arg === null) ||
38 (argument(node).name === 'undefined' && arg === undefined));
39
40const expectNotToBeCase = (node, arg) =>
41 expectNotCase(node) &&
42 methodName2(node) === 'toBe' &&
43 argument2(node) &&
44 ((argument2(node).type === 'Literal' &&
45 argument2(node).value === null &&
46 arg === null) ||
47 (argument2(node).name === 'undefined' && arg === undefined));
48
49const expectToEqualCase = (node, arg) =>
50 !(expectNotCase(node) || expectResolveCase(node) || expectRejectCase(node)) &&
51 expectCase(node) &&
52 methodName(node) === 'toEqual' &&
53 argument(node) &&
54 ((argument(node).type === 'Literal' &&
55 argument(node).value === null &&
56 arg === null) ||
57 (argument(node).name === 'undefined' && arg === undefined));
58
59const expectNotToEqualCase = (node, arg) =>
60 expectNotCase(node) &&
61 methodName2(node) === 'toEqual' &&
62 argument2(node) &&
63 ((argument2(node).type === 'Literal' &&
64 argument2(node).value === null &&
65 arg === null) ||
66 (argument2(node).name === 'undefined' && arg === undefined));
67
68const method = node => node.parent.property;
69
70const method2 = node => node.parent.parent.property;
71
72const methodName = node => method(node).name;
73
74const methodName2 = node => method2(node).name;
75
76const argument = node =>
77 node.parent.parent.arguments && node.parent.parent.arguments[0];
78
79const argument2 = node =>
80 node.parent.parent.parent.arguments && node.parent.parent.parent.arguments[0];
81
82const describeAliases = Object.assign(Object.create(null), {
83 describe: true,
84 'describe.only': true,
85 'describe.skip': true,
86 fdescribe: true,
87 xdescribe: true,
88});
89
90const testCaseNames = Object.assign(Object.create(null), {
91 fit: true,
92 it: true,
93 'it.only': true,
94 'it.skip': true,
95 test: true,
96 'test.only': true,
97 'test.skip': true,
98 xit: true,
99 xtest: true,
100});
101
102const getNodeName = node => {
103 function joinNames(a, b) {
104 return a && b ? `${a}.${b}` : null;
105 }
106
107 switch (node && node.type) {
108 case 'Identifier':
109 return node.name;
110 case 'Literal':
111 return node.value;
112 case 'TemplateLiteral':
113 if (node.expressions.length === 0) return node.quasis[0].value.cooked;
114 break;
115 case 'MemberExpression':
116 return joinNames(getNodeName(node.object), getNodeName(node.property));
117 }
118
119 return null;
120};
121
122const isTestCase = node =>
123 node &&
124 node.type === 'CallExpression' &&
125 testCaseNames[getNodeName(node.callee)];
126
127const isDescribe = node =>
128 node.type === 'CallExpression' && describeAliases[getNodeName(node.callee)];
129
130const isFunction = node =>
131 node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression';
132
133const isString = node =>
134 (node.type === 'Literal' && typeof node.value === 'string') ||
135 isTemplateLiteral(node);
136
137const isTemplateLiteral = node => node.type === 'TemplateLiteral';
138
139const hasExpressions = node => node.expressions && node.expressions.length > 0;
140
141const getStringValue = arg =>
142 isTemplateLiteral(arg) ? arg.quasis[0].value.raw : arg.value;
143
144/**
145 * Generates the URL to documentation for the given rule name. It uses the
146 * package version to build the link to a tagged version of the
147 * documentation file.
148 *
149 * @param {string} filename - Name of the eslint rule
150 * @returns {string} URL to the documentation for the given rule
151 */
152const getDocsUrl = filename => {
153 const ruleName = path.basename(filename, '.js');
154
155 return `${REPO_URL}/blob/v${version}/docs/rules/${ruleName}.md`;
156};
157
158const collectReferences = scope => {
159 const locals = new Set();
160 const unresolved = new Set();
161
162 let currentScope = scope;
163
164 while (currentScope !== null) {
165 for (const ref of currentScope.variables) {
166 const isReferenceDefined = ref.defs.some(def => {
167 return def.type !== 'ImplicitGlobalVariable';
168 });
169
170 if (isReferenceDefined) {
171 locals.add(ref.name);
172 }
173 }
174
175 for (const ref of currentScope.through) {
176 unresolved.add(ref.identifier.name);
177 }
178
179 currentScope = currentScope.upper;
180 }
181
182 return { locals, unresolved };
183};
184
185const scopeHasLocalReference = (scope, referenceName) => {
186 const references = collectReferences(scope);
187 return (
188 // referenceName was found as a local variable or function declaration.
189 references.locals.has(referenceName) ||
190 // referenceName was not found as an unresolved reference,
191 // meaning it is likely not an implicit global reference.
192 !references.unresolved.has(referenceName)
193 );
194};
195
196function composeFixers(node) {
197 return (...fixers) => {
198 return fixerApi => {
199 return fixers.reduce((all, fixer) => [...all, fixer(node, fixerApi)], []);
200 };
201 };
202}
203
204module.exports = {
205 method,
206 method2,
207 argument,
208 argument2,
209 expectCase,
210 expectNotCase,
211 expectResolveCase,
212 expectRejectCase,
213 expectToBeCase,
214 expectNotToBeCase,
215 expectToEqualCase,
216 expectNotToEqualCase,
217 getNodeName,
218 getStringValue,
219 isDescribe,
220 isFunction,
221 isTemplateLiteral,
222 isTestCase,
223 isString,
224 hasExpressions,
225 getDocsUrl,
226 scopeHasLocalReference,
227 composeFixers,
228};