UNPKG

eslint-plugin-testing-library

Version:

ESLint plugin to follow best practices and anticipate common mistakes when writing tests with Testing Library

135 lines (134 loc) 6.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.RULE_NAME = void 0; const utils_1 = require("@typescript-eslint/utils"); const create_testing_library_rule_1 = require("../create-testing-library-rule"); exports.RULE_NAME = 'prefer-user-event-setup'; const USER_EVENT_PACKAGE = '@testing-library/user-event'; const USER_EVENT_NAME = 'userEvent'; const SETUP_METHOD_NAME = 'setup'; exports.default = (0, create_testing_library_rule_1.createTestingLibraryRule)({ name: exports.RULE_NAME, meta: { type: 'suggestion', docs: { description: 'Suggest using userEvent with setup() instead of direct methods', recommendedConfig: { dom: false, angular: false, react: false, vue: false, svelte: false, marko: false, }, }, messages: { preferUserEventSetup: 'Prefer using userEvent with setup() instead of direct {{method}}() call. Use: const user = userEvent.setup(); await user.{{method}}(...)', }, schema: [], }, defaultOptions: [], create(context, options, helpers) { const userEventSetupVars = new Set(); const setupFunctions = new Map(); let userEventIdentifier = null; function isUserEventSetupCall(node) { return (node.type === utils_1.AST_NODE_TYPES.CallExpression && node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression && node.callee.object.type === utils_1.AST_NODE_TYPES.Identifier && node.callee.object.name === userEventIdentifier && node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier && node.callee.property.name === SETUP_METHOD_NAME); } return { ImportDeclaration(node) { if (node.source.value === USER_EVENT_PACKAGE) { const defaultImport = node.specifiers.find((spec) => spec.type === utils_1.AST_NODE_TYPES.ImportDefaultSpecifier); if (defaultImport) { userEventIdentifier = defaultImport.local.name; } const namedImport = node.specifiers.find((spec) => spec.type === utils_1.AST_NODE_TYPES.ImportSpecifier && spec.imported.type === utils_1.AST_NODE_TYPES.Identifier && spec.imported.name === USER_EVENT_NAME); if (namedImport && namedImport.type === utils_1.AST_NODE_TYPES.ImportSpecifier) { userEventIdentifier = namedImport.local.name; } } }, VariableDeclarator(node) { if (!userEventIdentifier || !node.init) return; if (isUserEventSetupCall(node.init) && node.id.type === utils_1.AST_NODE_TYPES.Identifier) { userEventSetupVars.add(node.id.name); } if (node.id.type === utils_1.AST_NODE_TYPES.ObjectPattern && node.init.type === utils_1.AST_NODE_TYPES.CallExpression && node.init.callee.type === utils_1.AST_NODE_TYPES.Identifier) { const functionName = node.init.callee.name; const setupProps = setupFunctions.get(functionName); if (setupProps) { for (const prop of node.id.properties) { if (prop.type === utils_1.AST_NODE_TYPES.Property && prop.key.type === utils_1.AST_NODE_TYPES.Identifier && setupProps.has(prop.key.name) && prop.value.type === utils_1.AST_NODE_TYPES.Identifier) { userEventSetupVars.add(prop.value.name); } } } } }, FunctionDeclaration(node) { if (!userEventIdentifier || !node.id) return; if (node.body && node.body.type === utils_1.AST_NODE_TYPES.BlockStatement) { for (const statement of node.body.body) { if (statement.type === utils_1.AST_NODE_TYPES.ReturnStatement) { const ret = statement; if (ret.argument && ret.argument.type === utils_1.AST_NODE_TYPES.ObjectExpression) { const props = new Set(); for (const prop of ret.argument.properties) { if (prop.type === utils_1.AST_NODE_TYPES.Property && prop.key.type === utils_1.AST_NODE_TYPES.Identifier && prop.value && isUserEventSetupCall(prop.value)) { props.add(prop.key.name); } } if (props.size > 0) { setupFunctions.set(node.id.name, props); } } } } } }, CallExpression(node) { if (!userEventIdentifier) return; if (node.callee.type === utils_1.AST_NODE_TYPES.MemberExpression && node.callee.property.type === utils_1.AST_NODE_TYPES.Identifier && helpers.isUserEventMethod(node.callee.property)) { const methodName = node.callee.property.name; if (methodName === SETUP_METHOD_NAME) { return; } const isSetupInstance = node.callee.object.type === utils_1.AST_NODE_TYPES.Identifier && userEventSetupVars.has(node.callee.object.name); if (!isSetupInstance) { context.report({ node: node.callee, messageId: 'preferUserEventSetup', data: { method: methodName, }, }); } } }, }; }, });