'use strict'; const createAstUtils = require('../util/ast'); const FUNCTION = 1; const DESCRIBE = 2; // "Pure" nodes are hooks (like `beforeEach`) or `it` calls const PURE = 3; module.exports = { meta: { type: 'suggestion', docs: { description: 'Disallow setup in describe blocks' } }, create(context) { const nesting = []; const astUtils = createAstUtils(context.settings); function isPureNode(node) { return astUtils.isHookCall(node) || astUtils.isTestCase(node) || astUtils.isSuiteConfigCall(node); } function reportCallExpression(callExpression) { const message = 'Unexpected function call in describe block.'; context.report({ message, node: callExpression.callee }); } function reportMemberExpression(memberExpression) { const message = 'Unexpected member expression in describe block. ' + 'Member expressions may call functions via getters.'; context.report({ message, node: memberExpression }); } function isNestedInDescribeBlock() { return nesting.length && !nesting.includes(PURE) && nesting.lastIndexOf(FUNCTION) < nesting.lastIndexOf(DESCRIBE); } function handleCallExpressionInDescribe(node) { if (isPureNode(node)) { nesting.push(PURE); } else if (isNestedInDescribeBlock()) { reportCallExpression(node); } } function isDescribe(node) { return astUtils.isDescribe(node); } function isParentDescribe(node) { return isDescribe(node.parent); } return { CallExpression(node) { if (isDescribe(node)) { nesting.push(DESCRIBE); return; } // don't process anything else if the first describe hasn't been processed if (!nesting.length) { return; } handleCallExpressionInDescribe(node); }, 'CallExpression:exit'(node) { if (isDescribe(node) || nesting.length && isPureNode(node)) { nesting.pop(); } }, MemberExpression(node) { if (!isDescribe(node.parent) && isNestedInDescribeBlock()) { reportMemberExpression(node); } }, FunctionDeclaration() { if (nesting.length) { nesting.push(FUNCTION); } }, 'FunctionDeclaration:exit'() { if (nesting.length) { nesting.pop(); } }, ArrowFunctionExpression(node) { if (nesting.length && !isParentDescribe(node)) { nesting.push(FUNCTION); } }, 'ArrowFunctionExpression:exit'(node) { if (nesting.length && !isParentDescribe(node)) { nesting.pop(); } } }; } };