"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.detectTestingLibraryUtils = void 0; const utils_1 = require("@typescript-eslint/utils"); const node_utils_1 = require("../node-utils"); const utils_2 = require("../utils"); const SETTING_OPTION_OFF = 'off'; const USER_EVENT_PACKAGE = '@testing-library/user-event'; const REACT_DOM_TEST_UTILS_PACKAGE = 'react-dom/test-utils'; const FIRE_EVENT_NAME = 'fireEvent'; const CREATE_EVENT_NAME = 'createEvent'; const USER_EVENT_NAME = 'userEvent'; const RENDER_NAME = 'render'; function detectTestingLibraryUtils(ruleCreate, { skipRuleReportingCheck = false } = {}) { return (context, optionsWithDefault) => { const importedTestingLibraryNodes = []; let importedCustomModuleNode = null; let importedUserEventLibraryNode = null; let importedReactDomTestUtilsNode = null; const customModuleSetting = context.settings['testing-library/utils-module']; const customRendersSetting = context.settings['testing-library/custom-renders']; const customQueriesSetting = context.settings['testing-library/custom-queries']; function isPotentialTestingLibraryFunction(node, isPotentialFunctionCallback) { if (!node) { return false; } const referenceNode = (0, node_utils_1.getReferenceNode)(node); const referenceNodeIdentifier = (0, node_utils_1.getPropertyIdentifierNode)(referenceNode); if (!referenceNodeIdentifier) { return false; } const importedUtilSpecifier = getTestingLibraryImportedUtilSpecifier(referenceNodeIdentifier); const originalNodeName = (0, node_utils_1.isImportSpecifier)(importedUtilSpecifier) && importedUtilSpecifier.local.name !== importedUtilSpecifier.imported.name ? importedUtilSpecifier.imported.name : undefined; if (!isPotentialFunctionCallback(node.name, originalNodeName)) { return false; } if (isAggressiveModuleReportingEnabled()) { return true; } return isNodeComingFromTestingLibrary(referenceNodeIdentifier); } const isAggressiveModuleReportingEnabled = () => !customModuleSetting; const isAggressiveRenderReportingEnabled = () => { const isSwitchedOff = customRendersSetting === SETTING_OPTION_OFF; const hasCustomOptions = Array.isArray(customRendersSetting) && customRendersSetting.length > 0; return !isSwitchedOff && !hasCustomOptions; }; const isAggressiveQueryReportingEnabled = () => { const isSwitchedOff = customQueriesSetting === SETTING_OPTION_OFF; const hasCustomOptions = Array.isArray(customQueriesSetting) && customQueriesSetting.length > 0; return !isSwitchedOff && !hasCustomOptions; }; const getCustomModule = () => { if (!isAggressiveModuleReportingEnabled() && customModuleSetting !== SETTING_OPTION_OFF) { return customModuleSetting; } return undefined; }; const getCustomRenders = () => { if (!isAggressiveRenderReportingEnabled() && customRendersSetting !== SETTING_OPTION_OFF) { return customRendersSetting; } return []; }; const getCustomQueries = () => { if (!isAggressiveQueryReportingEnabled() && customQueriesSetting !== SETTING_OPTION_OFF) { return customQueriesSetting; } return []; }; const getTestingLibraryImportNode = () => { return importedTestingLibraryNodes[0]; }; const getAllTestingLibraryImportNodes = () => { return importedTestingLibraryNodes; }; const getCustomModuleImportNode = () => { return importedCustomModuleNode; }; const getTestingLibraryImportName = () => { return (0, node_utils_1.getImportModuleName)(importedTestingLibraryNodes[0]); }; const getCustomModuleImportName = () => { return (0, node_utils_1.getImportModuleName)(importedCustomModuleNode); }; const isTestingLibraryImported = (isStrict = false) => { const isSomeModuleImported = importedTestingLibraryNodes.length !== 0 || !!importedCustomModuleNode; return ((!isStrict && isAggressiveModuleReportingEnabled()) || isSomeModuleImported); }; const isQuery = (node) => { const hasQueryPattern = /^(get|query|find)(All)?By.+$/.test(node.name); if (!hasQueryPattern) { return false; } if (isAggressiveQueryReportingEnabled()) { return true; } const customQueries = getCustomQueries(); const isBuiltInQuery = utils_2.ALL_QUERIES_COMBINATIONS.includes(node.name); const isReportableCustomQuery = customQueries.some((pattern) => new RegExp(pattern).test(node.name)); return isBuiltInQuery || isReportableCustomQuery; }; const isGetQueryVariant = (node) => { return isQuery(node) && node.name.startsWith('get'); }; const isQueryQueryVariant = (node) => { return isQuery(node) && node.name.startsWith('query'); }; const isFindQueryVariant = (node) => { return isQuery(node) && node.name.startsWith('find'); }; const isSyncQuery = (node) => { return isGetQueryVariant(node) || isQueryQueryVariant(node); }; const isAsyncQuery = (node) => { return isFindQueryVariant(node); }; const isCustomQuery = (node) => { return isQuery(node) && !utils_2.ALL_QUERIES_COMBINATIONS.includes(node.name); }; const isBuiltInQuery = (node) => { return isQuery(node) && utils_2.ALL_QUERIES_COMBINATIONS.includes(node.name); }; const isAsyncUtil = (node, validNames = utils_2.ASYNC_UTILS) => { return isPotentialTestingLibraryFunction(node, (identifierNodeName, originalNodeName) => { return (validNames.includes(identifierNodeName) || (!!originalNodeName && validNames.includes(originalNodeName))); }); }; const isFireEventUtil = (node) => { return isPotentialTestingLibraryFunction(node, (identifierNodeName, originalNodeName) => { return [identifierNodeName, originalNodeName].includes('fireEvent'); }); }; const isUserEventUtil = (node) => { const userEvent = findImportedUserEventSpecifier(); let userEventName; if (userEvent) { userEventName = userEvent.name; } else if (isAggressiveModuleReportingEnabled()) { userEventName = USER_EVENT_NAME; } if (!userEventName) { return false; } return node.name === userEventName; }; const isFireEventMethod = (node) => { const fireEventUtil = findImportedTestingLibraryUtilSpecifier(FIRE_EVENT_NAME); let fireEventUtilName; if (fireEventUtil) { fireEventUtilName = utils_1.ASTUtils.isIdentifier(fireEventUtil) ? fireEventUtil.name : fireEventUtil.local.name; } else if (isAggressiveModuleReportingEnabled()) { fireEventUtilName = FIRE_EVENT_NAME; } if (!fireEventUtilName) { return false; } const parentMemberExpression = node.parent && (0, node_utils_1.isMemberExpression)(node.parent) ? node.parent : undefined; const parentCallExpression = node.parent && (0, node_utils_1.isCallExpression)(node.parent) ? node.parent : undefined; if (!parentMemberExpression && !parentCallExpression) { return false; } if (parentCallExpression) { return [fireEventUtilName, FIRE_EVENT_NAME].includes(node.name); } const definedParentMemberExpression = parentMemberExpression; const regularCall = utils_1.ASTUtils.isIdentifier(definedParentMemberExpression.object) && (0, node_utils_1.isCallExpression)(definedParentMemberExpression.parent) && definedParentMemberExpression.object.name === fireEventUtilName && node.name !== FIRE_EVENT_NAME && node.name !== fireEventUtilName; const wildcardCall = (0, node_utils_1.isMemberExpression)(definedParentMemberExpression.object) && utils_1.ASTUtils.isIdentifier(definedParentMemberExpression.object.object) && definedParentMemberExpression.object.object.name === fireEventUtilName && utils_1.ASTUtils.isIdentifier(definedParentMemberExpression.object.property) && definedParentMemberExpression.object.property.name === FIRE_EVENT_NAME && node.name !== FIRE_EVENT_NAME && node.name !== fireEventUtilName; const wildcardCallWithCallExpression = utils_1.ASTUtils.isIdentifier(definedParentMemberExpression.object) && definedParentMemberExpression.object.name === fireEventUtilName && utils_1.ASTUtils.isIdentifier(definedParentMemberExpression.property) && definedParentMemberExpression.property.name === FIRE_EVENT_NAME && !(0, node_utils_1.isMemberExpression)(definedParentMemberExpression.parent) && node.name === FIRE_EVENT_NAME && node.name !== fireEventUtilName; return regularCall || wildcardCall || wildcardCallWithCallExpression; }; const isUserEventMethod = (node) => { const userEvent = findImportedUserEventSpecifier(); let userEventName; if (userEvent) { userEventName = userEvent.name; } else if (isAggressiveModuleReportingEnabled()) { userEventName = USER_EVENT_NAME; } if (!userEventName) { return false; } const parentMemberExpression = node.parent && (0, node_utils_1.isMemberExpression)(node.parent) ? node.parent : undefined; if (!parentMemberExpression) { return false; } if ([userEventName, USER_EVENT_NAME].includes(node.name) || (utils_1.ASTUtils.isIdentifier(parentMemberExpression.object) && parentMemberExpression.object.name === node.name)) { return false; } return (utils_1.ASTUtils.isIdentifier(parentMemberExpression.object) && parentMemberExpression.object.name === userEventName); }; const isRenderUtil = (node) => isPotentialTestingLibraryFunction(node, (identifierNodeName, originalNodeName) => { if (isAggressiveRenderReportingEnabled()) { return identifierNodeName.toLowerCase().includes(RENDER_NAME); } return [RENDER_NAME, ...getCustomRenders()].some((validRenderName) => validRenderName === identifierNodeName || (Boolean(originalNodeName) && validRenderName === originalNodeName)); }); const isCreateEventUtil = (node) => { const isCreateEventCallback = (identifierNodeName, originalNodeName) => [identifierNodeName, originalNodeName].includes(CREATE_EVENT_NAME); if ((0, node_utils_1.isCallExpression)(node) && (0, node_utils_1.isMemberExpression)(node.callee) && utils_1.ASTUtils.isIdentifier(node.callee.object)) { return isPotentialTestingLibraryFunction(node.callee.object, isCreateEventCallback); } if ((0, node_utils_1.isCallExpression)(node) && (0, node_utils_1.isMemberExpression)(node.callee) && (0, node_utils_1.isMemberExpression)(node.callee.object) && utils_1.ASTUtils.isIdentifier(node.callee.object.property)) { return isPotentialTestingLibraryFunction(node.callee.object.property, isCreateEventCallback); } const identifier = (0, node_utils_1.getDeepestIdentifierNode)(node); return isPotentialTestingLibraryFunction(identifier, isCreateEventCallback); }; const isRenderVariableDeclarator = (node) => { if (!node.init) { return false; } const initIdentifierNode = (0, node_utils_1.getDeepestIdentifierNode)(node.init); if (!initIdentifierNode) { return false; } return isRenderUtil(initIdentifierNode); }; const isDebugUtil = (identifierNode, validNames = utils_2.DEBUG_UTILS) => { const isBuiltInConsole = (0, node_utils_1.isMemberExpression)(identifierNode.parent) && utils_1.ASTUtils.isIdentifier(identifierNode.parent.object) && identifierNode.parent.object.name === 'console'; return (!isBuiltInConsole && isPotentialTestingLibraryFunction(identifierNode, (identifierNodeName, originalNodeName) => { return (validNames.includes(identifierNodeName) || (!!originalNodeName && validNames.includes(originalNodeName))); })); }; const isActUtil = (node) => { const isTestingLibraryAct = isPotentialTestingLibraryFunction(node, (identifierNodeName, originalNodeName) => { return [identifierNodeName, originalNodeName] .filter(Boolean) .includes('act'); }); const isReactDomTestUtilsAct = (() => { if (!importedReactDomTestUtilsNode) { return false; } const referenceNode = (0, node_utils_1.getReferenceNode)(node); const referenceNodeIdentifier = (0, node_utils_1.getPropertyIdentifierNode)(referenceNode); if (!referenceNodeIdentifier) { return false; } const importedUtilSpecifier = (0, node_utils_1.findImportSpecifier)(node.name, importedReactDomTestUtilsNode); if (!importedUtilSpecifier) { return false; } const importDeclaration = (() => { if ((0, node_utils_1.isImportDeclaration)(importedUtilSpecifier.parent)) { return importedUtilSpecifier.parent; } const variableDeclarator = (0, node_utils_1.findClosestVariableDeclaratorNode)(importedUtilSpecifier); if ((0, node_utils_1.isCallExpression)(variableDeclarator === null || variableDeclarator === void 0 ? void 0 : variableDeclarator.init)) { return variableDeclarator === null || variableDeclarator === void 0 ? void 0 : variableDeclarator.init; } return undefined; })(); if (!importDeclaration) { return false; } const importDeclarationName = (0, node_utils_1.getImportModuleName)(importDeclaration); if (!importDeclarationName) { return false; } if (importDeclarationName !== REACT_DOM_TEST_UTILS_PACKAGE) { return false; } return (0, node_utils_1.hasImportMatch)(importedUtilSpecifier, referenceNodeIdentifier.name); })(); return isTestingLibraryAct || isReactDomTestUtilsAct; }; const isTestingLibraryUtil = (node) => { return (isAsyncUtil(node) || isQuery(node) || isRenderUtil(node) || isFireEventMethod(node) || isUserEventMethod(node) || isActUtil(node) || isCreateEventUtil(node)); }; const isPresenceAssert = (node) => { const { matcher, isNegated } = (0, node_utils_1.getAssertNodeInfo)(node); if (!matcher) { return false; } return isNegated ? utils_2.ABSENCE_MATCHERS.includes(matcher) : utils_2.PRESENCE_MATCHERS.includes(matcher); }; const isAbsenceAssert = (node) => { const { matcher, isNegated } = (0, node_utils_1.getAssertNodeInfo)(node); if (!matcher) { return false; } return isNegated ? utils_2.PRESENCE_MATCHERS.includes(matcher) : utils_2.ABSENCE_MATCHERS.includes(matcher); }; const isMatchingAssert = (node, matcherName) => { const { matcher } = (0, node_utils_1.getAssertNodeInfo)(node); if (!matcher) { return false; } return matcher === matcherName; }; const findImportedTestingLibraryUtilSpecifier = (specifierName) => { var _a; const node = (_a = getCustomModuleImportNode()) !== null && _a !== void 0 ? _a : getTestingLibraryImportNode(); if (!node) { return undefined; } return (0, node_utils_1.findImportSpecifier)(specifierName, node); }; const findImportedUserEventSpecifier = () => { if (!importedUserEventLibraryNode) { return null; } if ((0, node_utils_1.isImportDeclaration)(importedUserEventLibraryNode)) { const userEventIdentifier = importedUserEventLibraryNode.specifiers.find((specifier) => (0, node_utils_1.isImportDefaultSpecifier)(specifier)); if (userEventIdentifier) { return userEventIdentifier.local; } } else { if (!utils_1.ASTUtils.isVariableDeclarator(importedUserEventLibraryNode.parent)) { return null; } const requireNode = importedUserEventLibraryNode.parent; if (!utils_1.ASTUtils.isIdentifier(requireNode.id)) { return null; } return requireNode.id; } return null; }; const getTestingLibraryImportedUtilSpecifier = (node) => { var _a; const identifierName = (_a = (0, node_utils_1.getPropertyIdentifierNode)(node)) === null || _a === void 0 ? void 0 : _a.name; if (!identifierName) { return undefined; } return findImportedTestingLibraryUtilSpecifier(identifierName); }; const canReportErrors = () => { return skipRuleReportingCheck || isTestingLibraryImported(); }; const isNodeComingFromTestingLibrary = (node) => { var _a; const importNode = getTestingLibraryImportedUtilSpecifier(node); if (!importNode) { return false; } const referenceNode = (0, node_utils_1.getReferenceNode)(node); const referenceNodeIdentifier = (0, node_utils_1.getPropertyIdentifierNode)(referenceNode); if (!referenceNodeIdentifier) { return false; } const importDeclaration = (() => { if ((0, node_utils_1.isImportDeclaration)(importNode.parent)) { return importNode.parent; } const variableDeclarator = (0, node_utils_1.findClosestVariableDeclaratorNode)(importNode); if ((0, node_utils_1.isCallExpression)(variableDeclarator === null || variableDeclarator === void 0 ? void 0 : variableDeclarator.init)) { return variableDeclarator === null || variableDeclarator === void 0 ? void 0 : variableDeclarator.init; } return undefined; })(); if (!importDeclaration) { return false; } const importDeclarationName = (0, node_utils_1.getImportModuleName)(importDeclaration); if (!importDeclarationName) { return false; } const identifierName = (_a = (0, node_utils_1.getPropertyIdentifierNode)(node)) === null || _a === void 0 ? void 0 : _a.name; if (!identifierName) { return false; } const hasImportElementMatch = (0, node_utils_1.hasImportMatch)(importNode, identifierName); const hasImportModuleMatch = /testing-library/g.test(importDeclarationName) || (typeof customModuleSetting === 'string' && importDeclarationName.endsWith(customModuleSetting)); return hasImportElementMatch && hasImportModuleMatch; }; const helpers = { getTestingLibraryImportNode, getAllTestingLibraryImportNodes, getCustomModuleImportNode, getTestingLibraryImportName, getCustomModuleImportName, isTestingLibraryImported, isTestingLibraryUtil, isGetQueryVariant, isQueryQueryVariant, isFindQueryVariant, isSyncQuery, isAsyncQuery, isQuery, isCustomQuery, isBuiltInQuery, isAsyncUtil, isFireEventUtil, isUserEventUtil, isFireEventMethod, isUserEventMethod, isRenderUtil, isCreateEventUtil, isRenderVariableDeclarator, isDebugUtil, isActUtil, isPresenceAssert, isMatchingAssert, isAbsenceAssert, canReportErrors, findImportedTestingLibraryUtilSpecifier, isNodeComingFromTestingLibrary, }; const detectionInstructions = { ImportDeclaration(node) { if (typeof node.source.value !== 'string') { return; } if (/testing-library/g.test(node.source.value)) { importedTestingLibraryNodes.push(node); } const customModule = getCustomModule(); if (customModule && !importedCustomModuleNode && node.source.value.endsWith(customModule)) { importedCustomModuleNode = node; } if (!importedUserEventLibraryNode && node.source.value === USER_EVENT_PACKAGE) { importedUserEventLibraryNode = node; } if (!importedUserEventLibraryNode && node.source.value === REACT_DOM_TEST_UTILS_PACKAGE) { importedReactDomTestUtilsNode = node; } }, [`CallExpression > Identifier[name="require"]`](node) { const callExpression = node.parent; const { arguments: args } = callExpression; if (args.some((arg) => (0, node_utils_1.isLiteral)(arg) && typeof arg.value === 'string' && /testing-library/g.test(arg.value))) { importedTestingLibraryNodes.push(callExpression); } const customModule = getCustomModule(); if (!importedCustomModuleNode && args.some((arg) => customModule && (0, node_utils_1.isLiteral)(arg) && typeof arg.value === 'string' && arg.value.endsWith(customModule))) { importedCustomModuleNode = callExpression; } if (!importedCustomModuleNode && args.some((arg) => (0, node_utils_1.isLiteral)(arg) && typeof arg.value === 'string' && arg.value === USER_EVENT_PACKAGE)) { importedUserEventLibraryNode = callExpression; } if (!importedReactDomTestUtilsNode && args.some((arg) => (0, node_utils_1.isLiteral)(arg) && typeof arg.value === 'string' && arg.value === REACT_DOM_TEST_UTILS_PACKAGE)) { importedReactDomTestUtilsNode = callExpression; } }, }; const ruleInstructions = ruleCreate(context, optionsWithDefault, helpers); const enhancedRuleInstructions = {}; const allKeys = new Set(Object.keys(detectionInstructions).concat(Object.keys(ruleInstructions))); allKeys.forEach((instruction) => { enhancedRuleInstructions[instruction] = (node) => { var _a, _b; if (instruction in detectionInstructions) { (_a = detectionInstructions[instruction]) === null || _a === void 0 ? void 0 : _a.call(detectionInstructions, node); } if (canReportErrors() && ruleInstructions[instruction]) { return (_b = ruleInstructions[instruction]) === null || _b === void 0 ? void 0 : _b.call(ruleInstructions, node); } return undefined; }; }); return enhancedRuleInstructions; }; } exports.detectTestingLibraryUtils = detectTestingLibraryUtils;