source: frontend/node_modules/eslint-plugin-jest/lib/rules/utils.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 16.6 KB
Line 
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.getAccessorValue = exports.followTypeAssertionChain = exports.createRule = exports.TestCaseProperty = exports.TestCaseName = exports.ModifierName = exports.HookName = exports.EqualityMatcher = exports.DescribeProperty = exports.DescribeAlias = void 0;
7exports.getNodeName = getNodeName;
8exports.scopeHasLocalReference = exports.parseExpectCall = exports.isTestCaseCall = exports.isSupportedAccessor = exports.isStringNode = exports.isParsedEqualityMatcherCall = exports.isIdentifier = exports.isHook = exports.isFunction = exports.isExpectMember = exports.isExpectCall = exports.isDescribeCall = exports.hasOnlyOneArgument = exports.getTestCallExpressionsFromDeclaredVariables = exports.getStringValue = void 0;
9
10var _path = require("path");
11
12var _experimentalUtils = require("@typescript-eslint/experimental-utils");
13
14var _package = require("../../package.json");
15
16const REPO_URL = 'https://github.com/jest-community/eslint-plugin-jest';
17
18const createRule = _experimentalUtils.ESLintUtils.RuleCreator(name => {
19 const ruleName = (0, _path.parse)(name).name;
20 return `${REPO_URL}/blob/v${_package.version}/docs/rules/${ruleName}.md`;
21});
22
23exports.createRule = createRule;
24
25const isTypeCastExpression = node => node.type === _experimentalUtils.AST_NODE_TYPES.TSAsExpression || node.type === _experimentalUtils.AST_NODE_TYPES.TSTypeAssertion;
26
27const followTypeAssertionChain = expression => isTypeCastExpression(expression) ? followTypeAssertionChain(expression.expression) : expression;
28/**
29 * A `Literal` with a `value` of type `string`.
30 */
31
32
33exports.followTypeAssertionChain = followTypeAssertionChain;
34
35/**
36 * Checks if the given `node` is a `StringLiteral`.
37 *
38 * If a `value` is provided & the `node` is a `StringLiteral`,
39 * the `value` will be compared to that of the `StringLiteral`.
40 *
41 * @param {Node} node
42 * @param {V} [value]
43 *
44 * @return {node is StringLiteral<V>}
45 *
46 * @template V
47 */
48const isStringLiteral = (node, value) => node.type === _experimentalUtils.AST_NODE_TYPES.Literal && typeof node.value === 'string' && (value === undefined || node.value === value);
49
50/**
51 * Checks if the given `node` is a `TemplateLiteral`.
52 *
53 * Complex `TemplateLiteral`s are not considered specific, and so will return `false`.
54 *
55 * If a `value` is provided & the `node` is a `TemplateLiteral`,
56 * the `value` will be compared to that of the `TemplateLiteral`.
57 *
58 * @param {Node} node
59 * @param {V} [value]
60 *
61 * @return {node is TemplateLiteral<V>}
62 *
63 * @template V
64 */
65const isTemplateLiteral = (node, value) => node.type === _experimentalUtils.AST_NODE_TYPES.TemplateLiteral && node.quasis.length === 1 && ( // bail out if not simple
66value === undefined || node.quasis[0].value.raw === value);
67
68/**
69 * Checks if the given `node` is a {@link StringNode}.
70 *
71 * @param {Node} node
72 * @param {V} [specifics]
73 *
74 * @return {node is StringNode}
75 *
76 * @template V
77 */
78const isStringNode = (node, specifics) => isStringLiteral(node, specifics) || isTemplateLiteral(node, specifics);
79/**
80 * Gets the value of the given `StringNode`.
81 *
82 * If the `node` is a `TemplateLiteral`, the `raw` value is used;
83 * otherwise, `value` is returned instead.
84 *
85 * @param {StringNode<S>} node
86 *
87 * @return {S}
88 *
89 * @template S
90 */
91
92
93exports.isStringNode = isStringNode;
94
95const getStringValue = node => isTemplateLiteral(node) ? node.quasis[0].value.raw : node.value;
96/**
97 * Represents a `MemberExpression` with a "known" `property`.
98 */
99
100
101exports.getStringValue = getStringValue;
102
103/**
104 * Guards that the given `call` has only one `argument`.
105 *
106 * @param {CallExpression} call
107 *
108 * @return {call is CallExpressionWithSingleArgument}
109 */
110const hasOnlyOneArgument = call => call.arguments.length === 1;
111/**
112 * An `Identifier` with a known `name` value - i.e `expect`.
113 */
114
115
116exports.hasOnlyOneArgument = hasOnlyOneArgument;
117
118/**
119 * Checks if the given `node` is an `Identifier`.
120 *
121 * If a `name` is provided, & the `node` is an `Identifier`,
122 * the `name` will be compared to that of the `identifier`.
123 *
124 * @param {Node} node
125 * @param {V} [name]
126 *
127 * @return {node is KnownIdentifier<Name>}
128 *
129 * @template V
130 */
131const isIdentifier = (node, name) => node.type === _experimentalUtils.AST_NODE_TYPES.Identifier && (name === undefined || node.name === name);
132/**
133 * Checks if the given `node` is a "supported accessor".
134 *
135 * This means that it's a node can be used to access properties,
136 * and who's "value" can be statically determined.
137 *
138 * `MemberExpression` nodes most commonly contain accessors,
139 * but it's possible for other nodes to contain them.
140 *
141 * If a `value` is provided & the `node` is an `AccessorNode`,
142 * the `value` will be compared to that of the `AccessorNode`.
143 *
144 * Note that `value` here refers to the normalised value.
145 * The property that holds the value is not always called `name`.
146 *
147 * @param {Node} node
148 * @param {V} [value]
149 *
150 * @return {node is AccessorNode<V>}
151 *
152 * @template V
153 */
154
155
156exports.isIdentifier = isIdentifier;
157
158const isSupportedAccessor = (node, value) => isIdentifier(node, value) || isStringNode(node, value);
159/**
160 * Gets the value of the given `AccessorNode`,
161 * account for the different node types.
162 *
163 * @param {AccessorNode<S>} accessor
164 *
165 * @return {S}
166 *
167 * @template S
168 */
169
170
171exports.isSupportedAccessor = isSupportedAccessor;
172
173const getAccessorValue = accessor => accessor.type === _experimentalUtils.AST_NODE_TYPES.Identifier ? accessor.name : getStringValue(accessor);
174
175exports.getAccessorValue = getAccessorValue;
176
177/**
178 * Checks if the given `node` is a valid `ExpectCall`.
179 *
180 * In order to be an `ExpectCall`, the `node` must:
181 * * be a `CallExpression`,
182 * * have an accessor named 'expect',
183 * * have a `parent`.
184 *
185 * @param {Node} node
186 *
187 * @return {node is ExpectCall}
188 */
189const isExpectCall = node => node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && isSupportedAccessor(node.callee, 'expect') && node.parent !== undefined;
190
191exports.isExpectCall = isExpectCall;
192
193const isExpectMember = (node, name) => node.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(node.property, name);
194/**
195 * Represents all the jest matchers.
196 */
197
198
199exports.isExpectMember = isExpectMember;
200let ModifierName;
201exports.ModifierName = ModifierName;
202
203(function (ModifierName) {
204 ModifierName["not"] = "not";
205 ModifierName["rejects"] = "rejects";
206 ModifierName["resolves"] = "resolves";
207})(ModifierName || (exports.ModifierName = ModifierName = {}));
208
209let EqualityMatcher;
210exports.EqualityMatcher = EqualityMatcher;
211
212(function (EqualityMatcher) {
213 EqualityMatcher["toBe"] = "toBe";
214 EqualityMatcher["toEqual"] = "toEqual";
215 EqualityMatcher["toStrictEqual"] = "toStrictEqual";
216})(EqualityMatcher || (exports.EqualityMatcher = EqualityMatcher = {}));
217
218const isParsedEqualityMatcherCall = (matcher, name) => (name ? matcher.name === name : EqualityMatcher.hasOwnProperty(matcher.name)) && matcher.arguments !== null && matcher.arguments.length === 1;
219/**
220 * Represents a parsed expect matcher, such as `toBe`, `toContain`, and so on.
221 */
222
223
224exports.isParsedEqualityMatcherCall = isParsedEqualityMatcherCall;
225
226const parseExpectMember = expectMember => ({
227 name: getAccessorValue(expectMember.property),
228 node: expectMember
229});
230
231const reparseAsMatcher = parsedMember => ({ ...parsedMember,
232
233 /**
234 * The arguments being passed to this `Matcher`, if any.
235 *
236 * If this matcher isn't called, this will be `null`.
237 */
238 arguments: parsedMember.node.parent.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? parsedMember.node.parent.arguments : null
239});
240/**
241 * Re-parses the given `parsedMember` as a `ParsedExpectModifier`.
242 *
243 * If the given `parsedMember` does not have a `name` of a valid `Modifier`,
244 * an exception will be thrown.
245 *
246 * @param {ParsedExpectMember<ModifierName>} parsedMember
247 *
248 * @return {ParsedExpectModifier}
249 */
250
251
252const reparseMemberAsModifier = parsedMember => {
253 if (isSpecificMember(parsedMember, ModifierName.not)) {
254 return parsedMember;
255 }
256 /* istanbul ignore if */
257
258
259 if (!isSpecificMember(parsedMember, ModifierName.resolves) && !isSpecificMember(parsedMember, ModifierName.rejects)) {
260 // ts doesn't think that the ModifierName.not check is the direct inverse as the above two checks
261 // todo: impossible at runtime, but can't be typed w/o negation support
262 throw new Error(`modifier name must be either "${ModifierName.resolves}" or "${ModifierName.rejects}" (got "${parsedMember.name}")`);
263 }
264
265 const negation = isExpectMember(parsedMember.node.parent, ModifierName.not) ? parsedMember.node.parent : undefined;
266 return { ...parsedMember,
267 negation
268 };
269};
270
271const isSpecificMember = (member, specific) => member.name === specific;
272/**
273 * Checks if the given `ParsedExpectMember` should be re-parsed as an `ParsedExpectModifier`.
274 *
275 * @param {ParsedExpectMember} member
276 *
277 * @return {member is ParsedExpectMember<ModifierName>}
278 */
279
280
281const shouldBeParsedExpectModifier = member => ModifierName.hasOwnProperty(member.name);
282
283const parseExpectCall = expect => {
284 const expectation = {
285 expect
286 };
287
288 if (!isExpectMember(expect.parent)) {
289 return expectation;
290 }
291
292 const parsedMember = parseExpectMember(expect.parent);
293
294 if (!shouldBeParsedExpectModifier(parsedMember)) {
295 expectation.matcher = reparseAsMatcher(parsedMember);
296 return expectation;
297 }
298
299 const modifier = expectation.modifier = reparseMemberAsModifier(parsedMember);
300 const memberNode = modifier.negation || modifier.node;
301
302 if (!isExpectMember(memberNode.parent)) {
303 return expectation;
304 }
305
306 expectation.matcher = reparseAsMatcher(parseExpectMember(memberNode.parent));
307 return expectation;
308};
309
310exports.parseExpectCall = parseExpectCall;
311let DescribeAlias;
312exports.DescribeAlias = DescribeAlias;
313
314(function (DescribeAlias) {
315 DescribeAlias["describe"] = "describe";
316 DescribeAlias["fdescribe"] = "fdescribe";
317 DescribeAlias["xdescribe"] = "xdescribe";
318})(DescribeAlias || (exports.DescribeAlias = DescribeAlias = {}));
319
320let TestCaseName;
321exports.TestCaseName = TestCaseName;
322
323(function (TestCaseName) {
324 TestCaseName["fit"] = "fit";
325 TestCaseName["it"] = "it";
326 TestCaseName["test"] = "test";
327 TestCaseName["xit"] = "xit";
328 TestCaseName["xtest"] = "xtest";
329})(TestCaseName || (exports.TestCaseName = TestCaseName = {}));
330
331let HookName;
332exports.HookName = HookName;
333
334(function (HookName) {
335 HookName["beforeAll"] = "beforeAll";
336 HookName["beforeEach"] = "beforeEach";
337 HookName["afterAll"] = "afterAll";
338 HookName["afterEach"] = "afterEach";
339})(HookName || (exports.HookName = HookName = {}));
340
341let DescribeProperty;
342exports.DescribeProperty = DescribeProperty;
343
344(function (DescribeProperty) {
345 DescribeProperty["each"] = "each";
346 DescribeProperty["only"] = "only";
347 DescribeProperty["skip"] = "skip";
348})(DescribeProperty || (exports.DescribeProperty = DescribeProperty = {}));
349
350let TestCaseProperty;
351exports.TestCaseProperty = TestCaseProperty;
352
353(function (TestCaseProperty) {
354 TestCaseProperty["each"] = "each";
355 TestCaseProperty["concurrent"] = "concurrent";
356 TestCaseProperty["only"] = "only";
357 TestCaseProperty["skip"] = "skip";
358 TestCaseProperty["todo"] = "todo";
359})(TestCaseProperty || (exports.TestCaseProperty = TestCaseProperty = {}));
360
361const joinNames = (a, b) => a && b ? `${a}.${b}` : null;
362
363function getNodeName(node) {
364 if (isSupportedAccessor(node)) {
365 return getAccessorValue(node);
366 }
367
368 switch (node.type) {
369 case _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression:
370 return getNodeName(node.tag);
371
372 case _experimentalUtils.AST_NODE_TYPES.MemberExpression:
373 return joinNames(getNodeName(node.object), getNodeName(node.property));
374
375 case _experimentalUtils.AST_NODE_TYPES.NewExpression:
376 case _experimentalUtils.AST_NODE_TYPES.CallExpression:
377 return getNodeName(node.callee);
378 }
379
380 return null;
381}
382
383const isFunction = node => node.type === _experimentalUtils.AST_NODE_TYPES.FunctionExpression || node.type === _experimentalUtils.AST_NODE_TYPES.ArrowFunctionExpression;
384
385exports.isFunction = isFunction;
386
387const isHook = node => node.callee.type === _experimentalUtils.AST_NODE_TYPES.Identifier && HookName.hasOwnProperty(node.callee.name);
388
389exports.isHook = isHook;
390
391const getTestCallExpressionsFromDeclaredVariables = declaredVariables => {
392 return declaredVariables.reduce((acc, {
393 references
394 }) => acc.concat(references.map(({
395 identifier
396 }) => identifier.parent).filter(node => !!node && node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && isTestCaseCall(node))), []);
397};
398
399exports.getTestCallExpressionsFromDeclaredVariables = getTestCallExpressionsFromDeclaredVariables;
400
401const isTestCaseName = node => node.type === _experimentalUtils.AST_NODE_TYPES.Identifier && TestCaseName.hasOwnProperty(node.name);
402
403const isTestCaseProperty = node => isSupportedAccessor(node) && TestCaseProperty.hasOwnProperty(getAccessorValue(node));
404/**
405 * Checks if the given `node` is a *call* to a test case function that would
406 * result in tests being run by `jest`.
407 *
408 * Note that `.each()` does not count as a call in this context, as it will not
409 * result in `jest` running any tests.
410 *
411 * @param {TSESTree.CallExpression} node
412 *
413 * @return {node is JestFunctionCallExpression<TestCaseName>}
414 */
415
416
417const isTestCaseCall = node => {
418 if (isTestCaseName(node.callee)) {
419 return true;
420 }
421
422 const callee = node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? node.callee.callee : node.callee;
423
424 if (callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && isTestCaseProperty(callee.property)) {
425 // if we're an `each()`, ensure we're the outer CallExpression (i.e `.each()()`)
426 if (getAccessorValue(callee.property) === 'each' && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.CallExpression) {
427 return false;
428 }
429
430 return callee.object.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression ? isTestCaseName(callee.object.object) : isTestCaseName(callee.object);
431 }
432
433 return false;
434};
435
436exports.isTestCaseCall = isTestCaseCall;
437
438const isDescribeAlias = node => node.type === _experimentalUtils.AST_NODE_TYPES.Identifier && DescribeAlias.hasOwnProperty(node.name);
439
440const isDescribeProperty = node => isSupportedAccessor(node) && DescribeProperty.hasOwnProperty(getAccessorValue(node));
441/**
442 * Checks if the given `node` is a *call* to a `describe` function that would
443 * result in a `describe` block being created by `jest`.
444 *
445 * Note that `.each()` does not count as a call in this context, as it will not
446 * result in `jest` creating any `describe` blocks.
447 *
448 * @param {TSESTree.CallExpression} node
449 *
450 * @return {node is JestFunctionCallExpression<TestCaseName>}
451 */
452
453
454const isDescribeCall = node => {
455 if (isDescribeAlias(node.callee)) {
456 return true;
457 }
458
459 const callee = node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? node.callee.callee : node.callee;
460
461 if (callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && isDescribeProperty(callee.property)) {
462 // if we're an `each()`, ensure we're the outer CallExpression (i.e `.each()()`)
463 if (getAccessorValue(callee.property) === 'each' && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.CallExpression) {
464 return false;
465 }
466
467 return callee.object.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression ? isDescribeAlias(callee.object.object) : isDescribeAlias(callee.object);
468 }
469
470 return false;
471};
472
473exports.isDescribeCall = isDescribeCall;
474
475const collectReferences = scope => {
476 const locals = new Set();
477 const unresolved = new Set();
478 let currentScope = scope;
479
480 while (currentScope !== null) {
481 for (const ref of currentScope.variables) {
482 const isReferenceDefined = ref.defs.some(def => {
483 return def.type !== 'ImplicitGlobalVariable';
484 });
485
486 if (isReferenceDefined) {
487 locals.add(ref.name);
488 }
489 }
490
491 for (const ref of currentScope.through) {
492 unresolved.add(ref.identifier.name);
493 }
494
495 currentScope = currentScope.upper;
496 }
497
498 return {
499 locals,
500 unresolved
501 };
502};
503
504const scopeHasLocalReference = (scope, referenceName) => {
505 const references = collectReferences(scope);
506 return (// referenceName was found as a local variable or function declaration.
507 references.locals.has(referenceName) || // referenceName was not found as an unresolved reference,
508 // meaning it is likely not an implicit global reference.
509 !references.unresolved.has(referenceName)
510 );
511};
512
513exports.scopeHasLocalReference = scopeHasLocalReference;
Note: See TracBrowser for help on using the repository browser.