eslint-plugin-testing-library
Version:
ESLint plugin to follow best practices and anticipate common mistakes when writing tests with Testing Library
432 lines (431 loc) • 17.2 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findClosestCallExpressionNode = findClosestCallExpressionNode;
exports.findClosestVariableDeclaratorNode = findClosestVariableDeclaratorNode;
exports.findClosestFunctionExpressionNode = findClosestFunctionExpressionNode;
exports.findClosestCallNode = findClosestCallNode;
exports.hasThenProperty = hasThenProperty;
exports.hasChainedThen = hasChainedThen;
exports.isPromiseIdentifier = isPromiseIdentifier;
exports.isPromiseAll = isPromiseAll;
exports.isPromiseAllSettled = isPromiseAllSettled;
exports.isPromisesArrayResolved = isPromisesArrayResolved;
exports.isPromiseHandled = isPromiseHandled;
exports.getVariableReferences = getVariableReferences;
exports.getInnermostFunctionScope = getInnermostFunctionScope;
exports.getFunctionReturnStatementNode = getFunctionReturnStatementNode;
exports.getPropertyIdentifierNode = getPropertyIdentifierNode;
exports.getDeepestIdentifierNode = getDeepestIdentifierNode;
exports.getReferenceNode = getReferenceNode;
exports.getFunctionName = getFunctionName;
exports.getImportModuleName = getImportModuleName;
exports.getAssertNodeInfo = getAssertNodeInfo;
exports.hasClosestExpectResolvesRejects = hasClosestExpectResolvesRejects;
exports.getInnermostReturningFunction = getInnermostReturningFunction;
exports.hasImportMatch = hasImportMatch;
exports.getStatementCallExpression = getStatementCallExpression;
exports.isEmptyFunction = isEmptyFunction;
exports.findImportSpecifier = findImportSpecifier;
const scope_manager_1 = require("@typescript-eslint/scope-manager");
const utils_1 = require("@typescript-eslint/utils");
const utils_2 = require("../utils");
const is_node_of_type_1 = require("./is-node-of-type");
__exportStar(require("./is-node-of-type"), exports);
const ValidLeftHandSideExpressions = [
utils_1.AST_NODE_TYPES.CallExpression,
utils_1.AST_NODE_TYPES.ClassExpression,
utils_1.AST_NODE_TYPES.ClassDeclaration,
utils_1.AST_NODE_TYPES.FunctionExpression,
utils_1.AST_NODE_TYPES.Literal,
utils_1.AST_NODE_TYPES.TemplateLiteral,
utils_1.AST_NODE_TYPES.MemberExpression,
utils_1.AST_NODE_TYPES.ArrayExpression,
utils_1.AST_NODE_TYPES.ArrayPattern,
utils_1.AST_NODE_TYPES.ClassExpression,
utils_1.AST_NODE_TYPES.FunctionExpression,
utils_1.AST_NODE_TYPES.Identifier,
utils_1.AST_NODE_TYPES.JSXElement,
utils_1.AST_NODE_TYPES.JSXFragment,
utils_1.AST_NODE_TYPES.JSXOpeningElement,
utils_1.AST_NODE_TYPES.MetaProperty,
utils_1.AST_NODE_TYPES.ObjectExpression,
utils_1.AST_NODE_TYPES.ObjectPattern,
utils_1.AST_NODE_TYPES.Super,
utils_1.AST_NODE_TYPES.ThisExpression,
utils_1.AST_NODE_TYPES.TSNullKeyword,
utils_1.AST_NODE_TYPES.TaggedTemplateExpression,
utils_1.AST_NODE_TYPES.TSNonNullExpression,
utils_1.AST_NODE_TYPES.TSAsExpression,
utils_1.AST_NODE_TYPES.ArrowFunctionExpression,
];
function findClosestCallExpressionNode(node, shouldRestrictInnerScope = false) {
if ((0, is_node_of_type_1.isCallExpression)(node)) {
return node;
}
if (!(node === null || node === void 0 ? void 0 : node.parent)) {
return null;
}
if (shouldRestrictInnerScope &&
!ValidLeftHandSideExpressions.includes(node.parent.type)) {
return null;
}
return findClosestCallExpressionNode(node.parent, shouldRestrictInnerScope);
}
function findClosestVariableDeclaratorNode(node) {
if (!node) {
return null;
}
if (utils_1.ASTUtils.isVariableDeclarator(node)) {
return node;
}
return findClosestVariableDeclaratorNode(node.parent);
}
function findClosestFunctionExpressionNode(node) {
if (!node) {
return null;
}
if ((0, is_node_of_type_1.isArrowFunctionExpression)(node) ||
(0, is_node_of_type_1.isFunctionExpression)(node) ||
(0, is_node_of_type_1.isFunctionDeclaration)(node)) {
return node;
}
return findClosestFunctionExpressionNode(node.parent);
}
function findClosestCallNode(node, name) {
if (!node.parent) {
return null;
}
if ((0, is_node_of_type_1.isCallExpression)(node) &&
utils_1.ASTUtils.isIdentifier(node.callee) &&
node.callee.name === name) {
return node;
}
else {
return findClosestCallNode(node.parent, name);
}
}
function hasThenProperty(node) {
return ((0, is_node_of_type_1.isMemberExpression)(node) &&
utils_1.ASTUtils.isIdentifier(node.property) &&
node.property.name === 'then');
}
function hasChainedThen(node) {
const parent = node.parent;
if ((0, is_node_of_type_1.isCallExpression)(parent) && parent.parent) {
return hasThenProperty(parent.parent);
}
return !!parent && hasThenProperty(parent);
}
function isPromiseIdentifier(node) {
return utils_1.ASTUtils.isIdentifier(node) && node.name === 'Promise';
}
function isPromiseAll(node) {
return ((0, is_node_of_type_1.isMemberExpression)(node.callee) &&
isPromiseIdentifier(node.callee.object) &&
utils_1.ASTUtils.isIdentifier(node.callee.property) &&
node.callee.property.name === 'all');
}
function isPromiseAllSettled(node) {
return ((0, is_node_of_type_1.isMemberExpression)(node.callee) &&
isPromiseIdentifier(node.callee.object) &&
utils_1.ASTUtils.isIdentifier(node.callee.property) &&
node.callee.property.name === 'allSettled');
}
function isPromisesArrayResolved(node) {
const closestCallExpression = findClosestCallExpressionNode(node, true);
if (!closestCallExpression) {
return false;
}
return (!!closestCallExpression.parent &&
(0, is_node_of_type_1.isArrayExpression)(closestCallExpression.parent) &&
(0, is_node_of_type_1.isCallExpression)(closestCallExpression.parent.parent) &&
(isPromiseAll(closestCallExpression.parent.parent) ||
isPromiseAllSettled(closestCallExpression.parent.parent)));
}
function isPromiseHandled(nodeIdentifier) {
const closestCallExpressionNode = findClosestCallExpressionNode(nodeIdentifier, true);
const callRootExpression = closestCallExpressionNode == null
? null
: getRootExpression(closestCallExpressionNode);
const suspiciousNodes = [nodeIdentifier, callRootExpression].filter((node) => node != null);
return suspiciousNodes.some((node) => {
if (!node.parent)
return false;
if (utils_1.ASTUtils.isAwaitExpression(node.parent))
return true;
if ((0, is_node_of_type_1.isArrowFunctionExpression)(node.parent) ||
(0, is_node_of_type_1.isReturnStatement)(node.parent))
return true;
if (hasClosestExpectResolvesRejects(node.parent))
return true;
if (hasChainedThen(node))
return true;
if (isPromisesArrayResolved(node))
return true;
});
}
function getRootExpression(expression) {
const { parent } = expression;
if (parent == null)
return expression;
switch (parent.type) {
case utils_1.AST_NODE_TYPES.ConditionalExpression:
return getRootExpression(parent);
case utils_1.AST_NODE_TYPES.LogicalExpression: {
let rootExpression;
switch (parent.operator) {
case '??':
case '||':
rootExpression = getRootExpression(parent);
break;
case '&&':
rootExpression =
parent.right === expression
? getRootExpression(parent)
: expression;
break;
}
return rootExpression !== null && rootExpression !== void 0 ? rootExpression : expression;
}
case utils_1.AST_NODE_TYPES.SequenceExpression:
return parent.expressions[parent.expressions.length - 1] === expression
? getRootExpression(parent)
: expression;
case utils_1.AST_NODE_TYPES.ChainExpression:
return getRootExpression(parent);
default:
return expression;
}
}
function getVariableReferences(context, node) {
var _a, _b, _c;
if (utils_1.ASTUtils.isVariableDeclarator(node)) {
return (_c = (_b = (_a = (0, utils_2.getDeclaredVariables)(context, node)[0]) === null || _a === void 0 ? void 0 : _a.references) === null || _b === void 0 ? void 0 : _b.slice(1)) !== null && _c !== void 0 ? _c : [];
}
return [];
}
function getInnermostFunctionScope(context, asyncQueryNode) {
const innermostScope = utils_1.ASTUtils.getInnermostScope((0, utils_2.getScope)(context, asyncQueryNode), asyncQueryNode);
if (innermostScope.type === scope_manager_1.ScopeType.function &&
utils_1.ASTUtils.isFunction(innermostScope.block)) {
return innermostScope;
}
return null;
}
function getFunctionReturnStatementNode(functionNode) {
if ((0, is_node_of_type_1.isBlockStatement)(functionNode.body)) {
const returnStatementNode = functionNode.body.body.find((statement) => (0, is_node_of_type_1.isReturnStatement)(statement));
if (!returnStatementNode) {
return null;
}
return returnStatementNode.argument;
}
else if (functionNode.expression) {
return functionNode.body;
}
return null;
}
function getPropertyIdentifierNode(node) {
if (utils_1.ASTUtils.isIdentifier(node)) {
return node;
}
if ((0, is_node_of_type_1.isMemberExpression)(node)) {
return getPropertyIdentifierNode(node.object);
}
if ((0, is_node_of_type_1.isCallExpression)(node)) {
return getPropertyIdentifierNode(node.callee);
}
if ((0, is_node_of_type_1.isExpressionStatement)(node)) {
return getPropertyIdentifierNode(node.expression);
}
if (utils_1.ASTUtils.isAwaitExpression(node)) {
return getPropertyIdentifierNode(node.argument);
}
return null;
}
function getDeepestIdentifierNode(node) {
if (utils_1.ASTUtils.isIdentifier(node)) {
return node;
}
if ((0, is_node_of_type_1.isMemberExpression)(node) && utils_1.ASTUtils.isIdentifier(node.property)) {
return node.property;
}
if ((0, is_node_of_type_1.isCallExpression)(node)) {
return getDeepestIdentifierNode(node.callee);
}
if (utils_1.ASTUtils.isAwaitExpression(node)) {
return getDeepestIdentifierNode(node.argument);
}
return null;
}
function getReferenceNode(node) {
if (node.parent &&
((0, is_node_of_type_1.isMemberExpression)(node.parent) || (0, is_node_of_type_1.isCallExpression)(node.parent))) {
return getReferenceNode(node.parent);
}
return node;
}
function getFunctionName(node) {
var _a, _b;
return ((_b = (_a = utils_1.ASTUtils.getFunctionNameWithKind(node)
.match(/('\w+')/g)) === null || _a === void 0 ? void 0 : _a[0].replace(/'/g, '')) !== null && _b !== void 0 ? _b : '');
}
function getImportModuleName(node) {
if ((0, is_node_of_type_1.isImportDeclaration)(node) && typeof node.source.value === 'string') {
return node.source.value;
}
if ((0, is_node_of_type_1.isCallExpression)(node) &&
(0, is_node_of_type_1.isLiteral)(node.arguments[0]) &&
typeof node.arguments[0].value === 'string') {
return node.arguments[0].value;
}
return undefined;
}
function getAssertNodeInfo(node) {
const emptyInfo = { matcher: null, isNegated: false };
if (!(0, is_node_of_type_1.isCallExpression)(node.object) ||
!utils_1.ASTUtils.isIdentifier(node.object.callee)) {
return emptyInfo;
}
if (node.object.callee.name !== 'expect') {
return emptyInfo;
}
let matcher = utils_1.ASTUtils.getPropertyName(node);
const isNegated = matcher === 'not';
if (isNegated) {
matcher =
node.parent && (0, is_node_of_type_1.isMemberExpression)(node.parent)
? utils_1.ASTUtils.getPropertyName(node.parent)
: null;
}
if (!matcher) {
return emptyInfo;
}
return { matcher, isNegated };
}
const matcherNamesHandlePromise = [
'resolves',
'rejects',
'toResolve',
'toReject',
];
function hasClosestExpectResolvesRejects(node) {
if ((0, is_node_of_type_1.isCallExpression)(node) &&
utils_1.ASTUtils.isIdentifier(node.callee) &&
node.parent &&
(0, is_node_of_type_1.isMemberExpression)(node.parent) &&
node.callee.name === 'expect') {
const expectMatcher = node.parent.property;
return (utils_1.ASTUtils.isIdentifier(expectMatcher) &&
matcherNamesHandlePromise.includes(expectMatcher.name));
}
if (!node.parent) {
return false;
}
return hasClosestExpectResolvesRejects(node.parent);
}
function getInnermostReturningFunction(context, node) {
const functionScope = getInnermostFunctionScope(context, node);
if (!functionScope) {
return undefined;
}
const returnStatementNode = getFunctionReturnStatementNode(functionScope.block);
if (!returnStatementNode) {
return undefined;
}
const returnStatementIdentifier = getDeepestIdentifierNode(returnStatementNode);
if ((returnStatementIdentifier === null || returnStatementIdentifier === void 0 ? void 0 : returnStatementIdentifier.name) !== node.name) {
return undefined;
}
return functionScope.block;
}
function hasImportMatch(importNode, identifierName) {
if (utils_1.ASTUtils.isIdentifier(importNode)) {
return importNode.name === identifierName;
}
return importNode.local.name === identifierName;
}
function getStatementCallExpression(statement) {
if ((0, is_node_of_type_1.isExpressionStatement)(statement)) {
const { expression } = statement;
if ((0, is_node_of_type_1.isCallExpression)(expression)) {
return expression;
}
if (utils_1.ASTUtils.isAwaitExpression(expression) &&
(0, is_node_of_type_1.isCallExpression)(expression.argument)) {
return expression.argument;
}
if ((0, is_node_of_type_1.isAssignmentExpression)(expression)) {
if ((0, is_node_of_type_1.isCallExpression)(expression.right)) {
return expression.right;
}
if (utils_1.ASTUtils.isAwaitExpression(expression.right) &&
(0, is_node_of_type_1.isCallExpression)(expression.right.argument)) {
return expression.right.argument;
}
}
}
if ((0, is_node_of_type_1.isReturnStatement)(statement) && (0, is_node_of_type_1.isCallExpression)(statement.argument)) {
return statement.argument;
}
if ((0, is_node_of_type_1.isVariableDeclaration)(statement)) {
for (const declaration of statement.declarations) {
if ((0, is_node_of_type_1.isCallExpression)(declaration.init)) {
return declaration.init;
}
}
}
return undefined;
}
function isEmptyFunction(node) {
if (utils_1.ASTUtils.isFunction(node) && (0, is_node_of_type_1.isBlockStatement)(node.body)) {
return node.body.body.length === 0;
}
return false;
}
function findImportSpecifier(specifierName, node) {
if ((0, is_node_of_type_1.isImportDeclaration)(node)) {
const namedExport = node.specifiers.find((n) => {
return ((0, is_node_of_type_1.isImportSpecifier)(n) &&
utils_1.ASTUtils.isIdentifier(n.imported) &&
[n.imported.name, n.local.name].includes(specifierName));
});
if (namedExport) {
return namedExport;
}
return node.specifiers.find((n) => (0, is_node_of_type_1.isImportNamespaceSpecifier)(n));
}
else {
if (!utils_1.ASTUtils.isVariableDeclarator(node.parent)) {
return undefined;
}
const requireNode = node.parent;
if (utils_1.ASTUtils.isIdentifier(requireNode.id)) {
return requireNode.id;
}
if (!(0, is_node_of_type_1.isObjectPattern)(requireNode.id)) {
return undefined;
}
const property = requireNode.id.properties.find((n) => (0, is_node_of_type_1.isProperty)(n) &&
utils_1.ASTUtils.isIdentifier(n.key) &&
n.key.name === specifierName);
if (!property) {
return undefined;
}
return property.key;
}
}
;