123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513 |
- "use strict";
- Object.defineProperty(exports, "__esModule", {
- value: true
- });
- exports.getAccessorValue = exports.followTypeAssertionChain = exports.createRule = exports.TestCaseProperty = exports.TestCaseName = exports.ModifierName = exports.HookName = exports.EqualityMatcher = exports.DescribeProperty = exports.DescribeAlias = void 0;
- exports.getNodeName = getNodeName;
- exports.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;
- var _path = require("path");
- var _experimentalUtils = require("@typescript-eslint/experimental-utils");
- var _package = require("../../package.json");
- const REPO_URL = 'https://github.com/jest-community/eslint-plugin-jest';
- const createRule = _experimentalUtils.ESLintUtils.RuleCreator(name => {
- const ruleName = (0, _path.parse)(name).name;
- return `${REPO_URL}/blob/v${_package.version}/docs/rules/${ruleName}.md`;
- });
- exports.createRule = createRule;
- const isTypeCastExpression = node => node.type === _experimentalUtils.AST_NODE_TYPES.TSAsExpression || node.type === _experimentalUtils.AST_NODE_TYPES.TSTypeAssertion;
- const followTypeAssertionChain = expression => isTypeCastExpression(expression) ? followTypeAssertionChain(expression.expression) : expression;
- /**
- * A `Literal` with a `value` of type `string`.
- */
- exports.followTypeAssertionChain = followTypeAssertionChain;
- /**
- * Checks if the given `node` is a `StringLiteral`.
- *
- * If a `value` is provided & the `node` is a `StringLiteral`,
- * the `value` will be compared to that of the `StringLiteral`.
- *
- * @param {Node} node
- * @param {V} [value]
- *
- * @return {node is StringLiteral<V>}
- *
- * @template V
- */
- const isStringLiteral = (node, value) => node.type === _experimentalUtils.AST_NODE_TYPES.Literal && typeof node.value === 'string' && (value === undefined || node.value === value);
- /**
- * Checks if the given `node` is a `TemplateLiteral`.
- *
- * Complex `TemplateLiteral`s are not considered specific, and so will return `false`.
- *
- * If a `value` is provided & the `node` is a `TemplateLiteral`,
- * the `value` will be compared to that of the `TemplateLiteral`.
- *
- * @param {Node} node
- * @param {V} [value]
- *
- * @return {node is TemplateLiteral<V>}
- *
- * @template V
- */
- const isTemplateLiteral = (node, value) => node.type === _experimentalUtils.AST_NODE_TYPES.TemplateLiteral && node.quasis.length === 1 && ( // bail out if not simple
- value === undefined || node.quasis[0].value.raw === value);
- /**
- * Checks if the given `node` is a {@link StringNode}.
- *
- * @param {Node} node
- * @param {V} [specifics]
- *
- * @return {node is StringNode}
- *
- * @template V
- */
- const isStringNode = (node, specifics) => isStringLiteral(node, specifics) || isTemplateLiteral(node, specifics);
- /**
- * Gets the value of the given `StringNode`.
- *
- * If the `node` is a `TemplateLiteral`, the `raw` value is used;
- * otherwise, `value` is returned instead.
- *
- * @param {StringNode<S>} node
- *
- * @return {S}
- *
- * @template S
- */
- exports.isStringNode = isStringNode;
- const getStringValue = node => isTemplateLiteral(node) ? node.quasis[0].value.raw : node.value;
- /**
- * Represents a `MemberExpression` with a "known" `property`.
- */
- exports.getStringValue = getStringValue;
- /**
- * Guards that the given `call` has only one `argument`.
- *
- * @param {CallExpression} call
- *
- * @return {call is CallExpressionWithSingleArgument}
- */
- const hasOnlyOneArgument = call => call.arguments.length === 1;
- /**
- * An `Identifier` with a known `name` value - i.e `expect`.
- */
- exports.hasOnlyOneArgument = hasOnlyOneArgument;
- /**
- * Checks if the given `node` is an `Identifier`.
- *
- * If a `name` is provided, & the `node` is an `Identifier`,
- * the `name` will be compared to that of the `identifier`.
- *
- * @param {Node} node
- * @param {V} [name]
- *
- * @return {node is KnownIdentifier<Name>}
- *
- * @template V
- */
- const isIdentifier = (node, name) => node.type === _experimentalUtils.AST_NODE_TYPES.Identifier && (name === undefined || node.name === name);
- /**
- * Checks if the given `node` is a "supported accessor".
- *
- * This means that it's a node can be used to access properties,
- * and who's "value" can be statically determined.
- *
- * `MemberExpression` nodes most commonly contain accessors,
- * but it's possible for other nodes to contain them.
- *
- * If a `value` is provided & the `node` is an `AccessorNode`,
- * the `value` will be compared to that of the `AccessorNode`.
- *
- * Note that `value` here refers to the normalised value.
- * The property that holds the value is not always called `name`.
- *
- * @param {Node} node
- * @param {V} [value]
- *
- * @return {node is AccessorNode<V>}
- *
- * @template V
- */
- exports.isIdentifier = isIdentifier;
- const isSupportedAccessor = (node, value) => isIdentifier(node, value) || isStringNode(node, value);
- /**
- * Gets the value of the given `AccessorNode`,
- * account for the different node types.
- *
- * @param {AccessorNode<S>} accessor
- *
- * @return {S}
- *
- * @template S
- */
- exports.isSupportedAccessor = isSupportedAccessor;
- const getAccessorValue = accessor => accessor.type === _experimentalUtils.AST_NODE_TYPES.Identifier ? accessor.name : getStringValue(accessor);
- exports.getAccessorValue = getAccessorValue;
- /**
- * Checks if the given `node` is a valid `ExpectCall`.
- *
- * In order to be an `ExpectCall`, the `node` must:
- * * be a `CallExpression`,
- * * have an accessor named 'expect',
- * * have a `parent`.
- *
- * @param {Node} node
- *
- * @return {node is ExpectCall}
- */
- const isExpectCall = node => node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && isSupportedAccessor(node.callee, 'expect') && node.parent !== undefined;
- exports.isExpectCall = isExpectCall;
- const isExpectMember = (node, name) => node.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(node.property, name);
- /**
- * Represents all the jest matchers.
- */
- exports.isExpectMember = isExpectMember;
- let ModifierName;
- exports.ModifierName = ModifierName;
- (function (ModifierName) {
- ModifierName["not"] = "not";
- ModifierName["rejects"] = "rejects";
- ModifierName["resolves"] = "resolves";
- })(ModifierName || (exports.ModifierName = ModifierName = {}));
- let EqualityMatcher;
- exports.EqualityMatcher = EqualityMatcher;
- (function (EqualityMatcher) {
- EqualityMatcher["toBe"] = "toBe";
- EqualityMatcher["toEqual"] = "toEqual";
- EqualityMatcher["toStrictEqual"] = "toStrictEqual";
- })(EqualityMatcher || (exports.EqualityMatcher = EqualityMatcher = {}));
- const isParsedEqualityMatcherCall = (matcher, name) => (name ? matcher.name === name : EqualityMatcher.hasOwnProperty(matcher.name)) && matcher.arguments !== null && matcher.arguments.length === 1;
- /**
- * Represents a parsed expect matcher, such as `toBe`, `toContain`, and so on.
- */
- exports.isParsedEqualityMatcherCall = isParsedEqualityMatcherCall;
- const parseExpectMember = expectMember => ({
- name: getAccessorValue(expectMember.property),
- node: expectMember
- });
- const reparseAsMatcher = parsedMember => ({ ...parsedMember,
- /**
- * The arguments being passed to this `Matcher`, if any.
- *
- * If this matcher isn't called, this will be `null`.
- */
- arguments: parsedMember.node.parent.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? parsedMember.node.parent.arguments : null
- });
- /**
- * Re-parses the given `parsedMember` as a `ParsedExpectModifier`.
- *
- * If the given `parsedMember` does not have a `name` of a valid `Modifier`,
- * an exception will be thrown.
- *
- * @param {ParsedExpectMember<ModifierName>} parsedMember
- *
- * @return {ParsedExpectModifier}
- */
- const reparseMemberAsModifier = parsedMember => {
- if (isSpecificMember(parsedMember, ModifierName.not)) {
- return parsedMember;
- }
- /* istanbul ignore if */
- if (!isSpecificMember(parsedMember, ModifierName.resolves) && !isSpecificMember(parsedMember, ModifierName.rejects)) {
- // ts doesn't think that the ModifierName.not check is the direct inverse as the above two checks
- // todo: impossible at runtime, but can't be typed w/o negation support
- throw new Error(`modifier name must be either "${ModifierName.resolves}" or "${ModifierName.rejects}" (got "${parsedMember.name}")`);
- }
- const negation = isExpectMember(parsedMember.node.parent, ModifierName.not) ? parsedMember.node.parent : undefined;
- return { ...parsedMember,
- negation
- };
- };
- const isSpecificMember = (member, specific) => member.name === specific;
- /**
- * Checks if the given `ParsedExpectMember` should be re-parsed as an `ParsedExpectModifier`.
- *
- * @param {ParsedExpectMember} member
- *
- * @return {member is ParsedExpectMember<ModifierName>}
- */
- const shouldBeParsedExpectModifier = member => ModifierName.hasOwnProperty(member.name);
- const parseExpectCall = expect => {
- const expectation = {
- expect
- };
- if (!isExpectMember(expect.parent)) {
- return expectation;
- }
- const parsedMember = parseExpectMember(expect.parent);
- if (!shouldBeParsedExpectModifier(parsedMember)) {
- expectation.matcher = reparseAsMatcher(parsedMember);
- return expectation;
- }
- const modifier = expectation.modifier = reparseMemberAsModifier(parsedMember);
- const memberNode = modifier.negation || modifier.node;
- if (!isExpectMember(memberNode.parent)) {
- return expectation;
- }
- expectation.matcher = reparseAsMatcher(parseExpectMember(memberNode.parent));
- return expectation;
- };
- exports.parseExpectCall = parseExpectCall;
- let DescribeAlias;
- exports.DescribeAlias = DescribeAlias;
- (function (DescribeAlias) {
- DescribeAlias["describe"] = "describe";
- DescribeAlias["fdescribe"] = "fdescribe";
- DescribeAlias["xdescribe"] = "xdescribe";
- })(DescribeAlias || (exports.DescribeAlias = DescribeAlias = {}));
- let TestCaseName;
- exports.TestCaseName = TestCaseName;
- (function (TestCaseName) {
- TestCaseName["fit"] = "fit";
- TestCaseName["it"] = "it";
- TestCaseName["test"] = "test";
- TestCaseName["xit"] = "xit";
- TestCaseName["xtest"] = "xtest";
- })(TestCaseName || (exports.TestCaseName = TestCaseName = {}));
- let HookName;
- exports.HookName = HookName;
- (function (HookName) {
- HookName["beforeAll"] = "beforeAll";
- HookName["beforeEach"] = "beforeEach";
- HookName["afterAll"] = "afterAll";
- HookName["afterEach"] = "afterEach";
- })(HookName || (exports.HookName = HookName = {}));
- let DescribeProperty;
- exports.DescribeProperty = DescribeProperty;
- (function (DescribeProperty) {
- DescribeProperty["each"] = "each";
- DescribeProperty["only"] = "only";
- DescribeProperty["skip"] = "skip";
- })(DescribeProperty || (exports.DescribeProperty = DescribeProperty = {}));
- let TestCaseProperty;
- exports.TestCaseProperty = TestCaseProperty;
- (function (TestCaseProperty) {
- TestCaseProperty["each"] = "each";
- TestCaseProperty["concurrent"] = "concurrent";
- TestCaseProperty["only"] = "only";
- TestCaseProperty["skip"] = "skip";
- TestCaseProperty["todo"] = "todo";
- })(TestCaseProperty || (exports.TestCaseProperty = TestCaseProperty = {}));
- const joinNames = (a, b) => a && b ? `${a}.${b}` : null;
- function getNodeName(node) {
- if (isSupportedAccessor(node)) {
- return getAccessorValue(node);
- }
- switch (node.type) {
- case _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression:
- return getNodeName(node.tag);
- case _experimentalUtils.AST_NODE_TYPES.MemberExpression:
- return joinNames(getNodeName(node.object), getNodeName(node.property));
- case _experimentalUtils.AST_NODE_TYPES.NewExpression:
- case _experimentalUtils.AST_NODE_TYPES.CallExpression:
- return getNodeName(node.callee);
- }
- return null;
- }
- const isFunction = node => node.type === _experimentalUtils.AST_NODE_TYPES.FunctionExpression || node.type === _experimentalUtils.AST_NODE_TYPES.ArrowFunctionExpression;
- exports.isFunction = isFunction;
- const isHook = node => node.callee.type === _experimentalUtils.AST_NODE_TYPES.Identifier && HookName.hasOwnProperty(node.callee.name);
- exports.isHook = isHook;
- const getTestCallExpressionsFromDeclaredVariables = declaredVariables => {
- return declaredVariables.reduce((acc, {
- references
- }) => acc.concat(references.map(({
- identifier
- }) => identifier.parent).filter(node => !!node && node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && isTestCaseCall(node))), []);
- };
- exports.getTestCallExpressionsFromDeclaredVariables = getTestCallExpressionsFromDeclaredVariables;
- const isTestCaseName = node => node.type === _experimentalUtils.AST_NODE_TYPES.Identifier && TestCaseName.hasOwnProperty(node.name);
- const isTestCaseProperty = node => isSupportedAccessor(node) && TestCaseProperty.hasOwnProperty(getAccessorValue(node));
- /**
- * Checks if the given `node` is a *call* to a test case function that would
- * result in tests being run by `jest`.
- *
- * Note that `.each()` does not count as a call in this context, as it will not
- * result in `jest` running any tests.
- *
- * @param {TSESTree.CallExpression} node
- *
- * @return {node is JestFunctionCallExpression<TestCaseName>}
- */
- const isTestCaseCall = node => {
- if (isTestCaseName(node.callee)) {
- return true;
- }
- 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;
- if (callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && isTestCaseProperty(callee.property)) {
- // if we're an `each()`, ensure we're the outer CallExpression (i.e `.each()()`)
- if (getAccessorValue(callee.property) === 'each' && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.CallExpression) {
- return false;
- }
- return callee.object.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression ? isTestCaseName(callee.object.object) : isTestCaseName(callee.object);
- }
- return false;
- };
- exports.isTestCaseCall = isTestCaseCall;
- const isDescribeAlias = node => node.type === _experimentalUtils.AST_NODE_TYPES.Identifier && DescribeAlias.hasOwnProperty(node.name);
- const isDescribeProperty = node => isSupportedAccessor(node) && DescribeProperty.hasOwnProperty(getAccessorValue(node));
- /**
- * Checks if the given `node` is a *call* to a `describe` function that would
- * result in a `describe` block being created by `jest`.
- *
- * Note that `.each()` does not count as a call in this context, as it will not
- * result in `jest` creating any `describe` blocks.
- *
- * @param {TSESTree.CallExpression} node
- *
- * @return {node is JestFunctionCallExpression<TestCaseName>}
- */
- const isDescribeCall = node => {
- if (isDescribeAlias(node.callee)) {
- return true;
- }
- 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;
- if (callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && isDescribeProperty(callee.property)) {
- // if we're an `each()`, ensure we're the outer CallExpression (i.e `.each()()`)
- if (getAccessorValue(callee.property) === 'each' && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.CallExpression) {
- return false;
- }
- return callee.object.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression ? isDescribeAlias(callee.object.object) : isDescribeAlias(callee.object);
- }
- return false;
- };
- exports.isDescribeCall = isDescribeCall;
- const collectReferences = scope => {
- const locals = new Set();
- const unresolved = new Set();
- let currentScope = scope;
- while (currentScope !== null) {
- for (const ref of currentScope.variables) {
- const isReferenceDefined = ref.defs.some(def => {
- return def.type !== 'ImplicitGlobalVariable';
- });
- if (isReferenceDefined) {
- locals.add(ref.name);
- }
- }
- for (const ref of currentScope.through) {
- unresolved.add(ref.identifier.name);
- }
- currentScope = currentScope.upper;
- }
- return {
- locals,
- unresolved
- };
- };
- const scopeHasLocalReference = (scope, referenceName) => {
- const references = collectReferences(scope);
- return (// referenceName was found as a local variable or function declaration.
- references.locals.has(referenceName) || // referenceName was not found as an unresolved reference,
- // meaning it is likely not an implicit global reference.
- !references.unresolved.has(referenceName)
- );
- };
- exports.scopeHasLocalReference = scopeHasLocalReference;
|