UNPKG

eslint-plugin-mocha

Version:

Eslint rules for mocha.

167 lines 8.1 kB
import { getAllNames } from '../mocha/all-name-details.js'; import { getAdditionalNames, getInterface } from '../settings.js'; import { findMochaVariableCalls } from './find-mocha-variable-calls.js'; import { isCallExpression } from './node-types.js'; function isSameRange(rangeA, rangeB) { return rangeA[0] === rangeB[0] && rangeA[1] === rangeB[1]; } function isSameNode(nodeA, nodeB) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- ok return nodeA.type === nodeB.type && isSameRange(nodeA.range, nodeB.range); } function getReferenceByNode(references, node) { return references.find((reference) => { return isSameNode(reference.node, node); }); } function callVisitorIfExists(visitors, name, context) { const visitor = visitors[name]; if (typeof visitor === 'function') { visitor(context); } } function isAnyFunctionExpression(node) { return node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression'; } function callVisitorsForTestEntityCallback(visitors, name, context) { if (isCallExpression(context.node)) { const lastArgument = context.node.arguments.at(-1); // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- ok if (lastArgument !== undefined && isAnyFunctionExpression(lastArgument)) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- ok callVisitorIfExists(visitors, name, { ...context, node: lastArgument }); } } } function createContext(reference) { return { name: reference.name, node: reference.node, type: reference.nameDetails.type, modifier: reference.nameDetails.modifier, interface: reference.nameDetails.interface }; } // eslint-disable-next-line max-statements -- no good idea to split this up function callVisitors(visitors, reference, stageSuffix = '') { const context = createContext(reference); if (context.type === 'config') { return; } if (context.type === 'testCase') { callVisitorIfExists(visitors, `testCase${stageSuffix}`, context); callVisitorIfExists(visitors, `suiteOrTestCase${stageSuffix}`, context); callVisitorsForTestEntityCallback(visitors, `testCaseCallback${stageSuffix}`, context); } if (context.type === 'suite') { callVisitorIfExists(visitors, `suite${stageSuffix}`, context); callVisitorIfExists(visitors, `suiteOrTestCase${stageSuffix}`, context); callVisitorsForTestEntityCallback(visitors, `suiteCallback${stageSuffix}`, context); } if (context.type === 'hook') { callVisitorIfExists(visitors, `hook${stageSuffix}`, context); callVisitorsForTestEntityCallback(visitors, `hookCallback${stageSuffix}`, context); } callVisitorIfExists(visitors, `anyTestEntity${stageSuffix}`, context); callVisitorsForTestEntityCallback(visitors, `anyTestEntityCallback${stageSuffix}`, context); } function processExpression(visitors, calls, node, visitorName) { const reference = getReferenceByNode(calls, node.parent); const prefix = reference === undefined ? 'nonMocha' : 'mocha'; // @ts-expect-error -- ok in this case callVisitorIfExists(visitors, `${prefix}${visitorName}`, node); // @ts-expect-error -- ok in this case callVisitorIfExists(visitors, visitorName, node); } const cachedCalls = new Map(); // eslint-disable-next-line max-statements -- caching with two cache keys, requires weird dance of statements function findCallsCached(context) { const settingsCacheKey = JSON.stringify(context.settings); let callsPerSettings = cachedCalls.get(settingsCacheKey); if (callsPerSettings === undefined) { callsPerSettings = new WeakMap(); cachedCalls.set(settingsCacheKey, callsPerSettings); } const callCacheKey = context.sourceCode; let calls = callsPerSettings.get(callCacheKey); if (calls === undefined) { const additionalCustomNames = getAdditionalNames(context.settings); const interfaceToUse = getInterface(context.settings); const names = getAllNames(additionalCustomNames); calls = findMochaVariableCalls(context, names, interfaceToUse); callsPerSettings.set(callCacheKey, calls); } return calls; } export function createMochaVisitors(context, visitors) { const { nonMochaCallExpression, 'nonMochaCallExpression:exit': nonMochaCallExpressionExit, mochaMemberExpression, nonMochaMemberExpression, 'mochaMemberExpression:exit': mochaMemberExpressionExit, 'nonMochaMemberExpression:exit': nonMochaMemberExpressionExit, mochaFunctionExpression, nonMochaFunctionExpression, 'mochaFunctionExpression:exit': mochaFunctionExpressionExit, 'nonMochaFunctionExpression:exit': nonMochaFunctionExpressionExit, testCase, 'testCase:exit': testCaseExit, testCaseCallback, 'testCaseCallback:exit': testCaseCallbackExit, suite, 'suite:exit': suiteExit, suiteCallback, 'suiteCallback:exit': suiteCallbackExit, hook, 'hook:exit': hookExit, hookCallback, 'hookCallback:exit': hookCallbackExit, suiteOrTestCase, 'suiteOrTestCase:exit': suiteOrTestCaseExit, anyTestEntity, 'anyTestEntity:exit': anyTestEntityExit, anyTestEntityCallback, 'anyTestEntityCallback:exit': anyTestEntityCallbackExit, ...nonMochaVisitors } = visitors; const mochaVisitors = { nonMochaCallExpression, 'nonMochaCallExpression:exit': nonMochaCallExpressionExit, mochaMemberExpression, nonMochaMemberExpression, 'mochaMemberExpression:exit': mochaMemberExpressionExit, 'nonMochaMemberExpression:exit': nonMochaMemberExpressionExit, mochaFunctionExpression, nonMochaFunctionExpression, 'mochaFunctionExpression:exit': mochaFunctionExpressionExit, 'nonMochaFunctionExpression:exit': nonMochaFunctionExpressionExit, testCase, 'testCase:exit': testCaseExit, testCaseCallback, 'testCaseCallback:exit': testCaseCallbackExit, suite, 'suite:exit': suiteExit, suiteCallback, 'suiteCallback:exit': suiteCallbackExit, hook, 'hook:exit': hookExit, hookCallback, 'hookCallback:exit': hookCallbackExit, suiteOrTestCase, 'suiteOrTestCase:exit': suiteOrTestCaseExit, anyTestEntity, 'anyTestEntity:exit': anyTestEntityExit, anyTestEntityCallback, 'anyTestEntityCallback:exit': anyTestEntityCallbackExit }; const calls = findCallsCached(context); return { ...nonMochaVisitors, CallExpression(node) { const reference = getReferenceByNode(calls, node); if (reference === undefined) { callVisitorIfExists(mochaVisitors, 'nonMochaCallExpression', node); } else { callVisitors(mochaVisitors, reference); } // @ts-expect-error -- ok in this case callVisitorIfExists(nonMochaVisitors, 'CallExpression', node); }, 'CallExpression:exit'(node) { const reference = getReferenceByNode(calls, node); if (reference === undefined) { callVisitorIfExists(mochaVisitors, 'nonMochaCallExpression:exit', node); } else { callVisitors(mochaVisitors, reference, ':exit'); } // @ts-expect-error -- ok in this case callVisitorIfExists(nonMochaVisitors, 'CallExpression:exit', node); }, MemberExpression(node) { processExpression(visitors, calls, node, 'MemberExpression'); }, 'MemberExpression:exit'(node) { processExpression(visitors, calls, node, 'MemberExpression:exit'); }, FunctionExpression(node) { processExpression(visitors, calls, node, 'FunctionExpression'); }, 'FunctionExpression:exit'(node) { processExpression(visitors, calls, node, 'FunctionExpression:exit'); } }; } //# sourceMappingURL=mocha-visitors.js.map