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
JavaScript
;
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,
},
});
}
}
},
};
},
});