function getNodeName(node) { if (node.type === 'MemberExpression') { return getNodeName(node.object) + '.' + getPropertyName(node.property); } return node.name; }
n/a
function getPropertyName(property) { return property.name || property.value; }
n/a
function isDescribe(node, additionalSuiteNames) { return node && node.type === 'CallExpression' && describeAliases.concat(additionalSuiteNames).indexOf(getNodeName(node.callee)) > -1; }
...
suiteLimit = defaultSuiteLimit;
} else {
suiteLimit = options.limit;
}
return {
CallExpression: function (node) {
if (astUtil.isDescribe(node, additionalSuiteNames(settings))) {
stack.push(node);
}
},
'CallExpression:exit': function (node) {
if (astUtil.isDescribe(node, additionalSuiteNames(settings))) {
if (stack.length === 1) {
...
function isHookIdentifier(node) { return node && node.type === 'Identifier' && hooks.indexOf(node.name) !== -1; }
...
return;
}
if (astUtil.isTestCase(node)) {
layers[layers.length - 1].testCount += 1;
}
if (astUtil.isHookIdentifier(node.callee)) {
layers[layers.length - 1].hookNodes.push(node.callee);
}
},
'CallExpression:exit': popLayer,
'Program:exit': popLayer
};
...
function isTestCase(node) { return node && node.type === 'CallExpression' && testCaseNames.indexOf(getNodeName(node.callee)) > -1; }
...
CallExpression: function (node) {
if (astUtil.isDescribe(node, additionalSuiteNames(settings))) {
layers[layers.length - 1].testCount += 1;
layers.push(newDescribeLayer(node));
return;
}
if (astUtil.isTestCase(node)) {
layers[layers.length - 1].testCount += 1;
}
if (astUtil.isHookIdentifier(node.callee)) {
layers[layers.length - 1].hookNodes.push(node.callee);
}
},
...
handle-done-callback = function (context) {
var possibleAsyncFunctionNames = [
'it',
'it.only',
'test',
'test.only',
'specify',
'specify.only',
'before',
'after',
'beforeEach',
'afterEach'
];
function getCalleeName(callee) {
if (callee.type === 'MemberExpression') {
return callee.object.name + '.' + callee.property.name;
}
return callee.name;
}
function hasParentMochaFunctionCall(functionExpression) {
var name;
if (functionExpression.parent && functionExpression.parent.type === 'CallExpression') {
name = getCalleeName(functionExpression.parent.callee);
return possibleAsyncFunctionNames.indexOf(name) > -1;
}
return false;
}
function isAsyncFunction(functionExpression) {
return functionExpression.params.length === 1;
}
function findParamInScope(paramName, scope) {
return R.find(function (variable) {
return variable.name === paramName && variable.defs[0].type === 'Parameter';
}, scope.variables);
}
function isReferenceHandled(reference) {
var parent = context.getNodeByRangeIndex(reference.identifier.range[0]).parent;
return parent.type === 'CallExpression';
}
function hasHandledReferences(references) {
return references.some(isReferenceHandled);
}
function checkAsyncMochaFunction(functionExpression) {
var scope = context.getScope(),
callback = functionExpression.params[0],
callbackName = callback.name,
callbackVariable = findParamInScope(callbackName, scope);
if (callbackVariable && !hasHandledReferences(callbackVariable.references)) {
context.report(callback, 'Expected "function eslint-plugin-mocha.rules.handle-done-callback" callback to be handled.', { name: callbackName });
}
}
function check(node) {
if (hasParentMochaFunctionCall(node) && isAsyncFunction(node)) {
checkAsyncMochaFunction(node);
}
}
return {
FunctionExpression: check,
ArrowFunctionExpression: check
};
}
n/a
max-top-level-suites = function (context) { var stack = [], topLevelDescribes = [], options = context.options[0] || {}, settings = context.settings, suiteLimit; if (R.isNil(options.limit)) { suiteLimit = defaultSuiteLimit; } else { suiteLimit = options.limit; } return { CallExpression: function (node) { if (astUtil.isDescribe(node, additionalSuiteNames(settings))) { stack.push(node); } }, 'CallExpression:exit': function (node) { if (astUtil.isDescribe(node, additionalSuiteNames(settings))) { if (stack.length === 1) { topLevelDescribes.push(node); } stack.pop(node); } }, 'Program:exit': function () { if (topLevelDescribes.length > suiteLimit) { context.report({ node: topLevelDescribes[suiteLimit], message: 'The number of top-level suites is more than ' + suiteLimit + '.' }); } } }; }
n/a
no-exclusive-tests = function (context) { var mochaTestFunctions = [ 'it', 'describe', 'suite', 'test', 'context', 'specify' ], settings = context.settings, additionalTestFunctions = getAdditionalTestFunctions(settings); mochaTestFunctions = mochaTestFunctions.concat(additionalTestFunctions); function matchesMochaTestFunction(object) { var name = astUtils.getNodeName(object); return mochaTestFunctions.indexOf(name) !== -1; } function isPropertyNamedOnly(property) { return property && astUtils.getPropertyName(property) === 'only'; } function isCallToMochasOnlyFunction(callee) { return callee.type === 'MemberExpression' && matchesMochaTestFunction(callee.object) && isPropertyNamedOnly(callee.property); } return { CallExpression: function (node) { var callee = node.callee; if (callee && isCallToMochasOnlyFunction(callee)) { context.report({ node: callee.property, message: 'Unexpected exclusive mocha test.' }); } } }; }
n/a
no-global-tests = function (context) { var testFunctionNames = [ 'it', 'it.only', 'it.skip', 'test', 'test.only', 'test.skip', 'specify', 'specify.only', 'specify.skip' ]; function getFnName(callee) { if (callee.type === 'MemberExpression') { if (callee.computed) { return callee.object.name + '.' + callee.property.value; } return callee.object.name + '.' + callee.property.name; } return callee.name; } function isGlobalScope(scope) { return scope.type === 'global' || scope.type === 'module'; } return { CallExpression: function (node) { var callee = node.callee, fnName = getFnName(callee), scope = context.getScope(); if (testFunctionNames.indexOf(fnName) !== -1 && isGlobalScope(scope)) { context.report(callee, 'Unexpected global mocha test.'); } } }; }
n/a
no-hooks = function (context) { return { CallExpression: function (node) { if (astUtil.isHookIdentifier(node.callee)) { context.report({ node: node.callee, message: 'Unexpected use of Mocha `' + node.callee.name + '` hook' }); } } }; }
n/a
no-hooks-for-single-case = function (context) { var options = context.options[0] || {}, allowedHooks = options.allow || [], settings = context.settings, layers = []; function popLayer(node) { var layer = layers[layers.length - 1]; if (layer.describeNode === node) { if (layer.testCount <= 1) { layer.hookNodes .filter(function (hookNode) { return allowedHooks.indexOf(hookNode.name) === -1; }) .forEach(function (hookNode) { context.report({ node: hookNode, message: 'Unexpected use of Mocha `' + hookNode.name + '` hook for a single test case' }); }); } layers.pop(); } } return { Program: function (node) { layers.push(newDescribeLayer(node)); }, CallExpression: function (node) { if (astUtil.isDescribe(node, additionalSuiteNames(settings))) { layers[layers.length - 1].testCount += 1; layers.push(newDescribeLayer(node)); return; } if (astUtil.isTestCase(node)) { layers[layers.length - 1].testCount += 1; } if (astUtil.isHookIdentifier(node.callee)) { layers[layers.length - 1].hookNodes.push(node.callee); } }, 'CallExpression:exit': popLayer, 'Program:exit': popLayer }; }
n/a
no-identical-title = function (context) { var titleLayers = [ newLayer() ], settings = context.settings; return { CallExpression: function (node) { var currentLayer = titleLayers[titleLayers.length - 1], title; if (astUtil.isDescribe(node, additionalSuiteNames(settings))) { titleLayers.push(newLayer()); } if (!isFirstArgLiteral(node)) { return; } title = node.arguments[0].value; handlTestCaseTitles(context, currentLayer.testTitles, node, title); handlTestSuiteTitles(context, currentLayer.describeTitles, node, title); }, 'CallExpression:exit': function (node) { if (astUtil.isDescribe(node, additionalSuiteNames(settings))) { titleLayers.pop(); } } }; }
n/a
no-mocha-arrows = function (context) { function getCalleeName(callee) { if (callee.type === 'MemberExpression') { return callee.object.name + '.' + callee.property.name; } return callee.name; } function isLikelyMochaGlobal(scope, name) { return !R.find(R.propEq('name', name), scope.variables); } function fixArrowFunction(fixer, fn) { var sourceCode = context.getSourceCode(), paramsLeftParen = sourceCode.getFirstToken(fn), paramsRightParen = sourceCode.getTokenBefore(sourceCode.getTokenBefore(fn.body)), paramsFullText = sourceCode.text.slice(paramsLeftParen.range[0], paramsRightParen.range[1]), functionKeyword = 'function', bodyText; if (fn.async) { // When 'async' specified, take care about the keyword. functionKeyword = 'async function'; // Strip 'async (...)' to ' (...)' paramsFullText = paramsFullText.slice(5); } if (fn.params.length > 0) { paramsFullText = '(' + sourceCode.text.slice(fn.params[0].start, R.last(fn.params).end) + ')'; } if (fn.body.type === 'BlockStatement') { // When it((...) => { ... }), // simply replace '(...) => ' with 'function () ' return fixer.replaceTextRange( [ fn.start, fn.body.start ], functionKeyword + paramsFullText + ' ' ); } bodyText = sourceCode.text.slice(fn.body.range[0], fn.body.range[1]); return fixer.replaceTextRange( [ fn.start, fn.end ], functionKeyword + paramsFullText + ' { return ' + bodyText + '; }' ); } return { CallExpression: function (node) { var name = getCalleeName(node.callee), fnArg; if (name && mochaFunctionNames.indexOf(name) > -1) { fnArg = node.arguments.slice(-1)[0]; if (fnArg && fnArg.type === 'ArrowFunctionExpression') { if (isLikelyMochaGlobal(context.getScope(), name)) { context.report({ node: node, message: 'Do not pass arrow functions to ' + name + '()', fix: function (fixer) { return fixArrowFunction(fixer, fnArg); } }); } } } } }; }
n/a
function noNestedTests(context) { var testNestingLevel = 0, settings = context.settings; function report(callExpression, isTestCase) { var message = isTestCase ? 'Unexpected test nested within another test.' : 'Unexpected suite nested within a test.'; context.report({ message: message, node: callExpression.callee }); } return { CallExpression: function (node) { var isTestCase = astUtils.isTestCase(node), isDescribe = astUtils.isDescribe(node, additionalSuiteNames(settings)); if (testNestingLevel > 0 && (isTestCase || isDescribe)) { report(node, isTestCase); } if (isTestCase) { testNestingLevel += 1; } }, 'CallExpression:exit': function (node) { if (astUtils.isTestCase(node)) { testNestingLevel -= 1; } } }; }
n/a
no-pending-tests = function (context) { var mochaTestFunctionNames = [ 'it', 'test', 'specify' ]; function isMochaTest(callee) { return callee.type === 'Identifier' && mochaTestFunctionNames.indexOf(callee.name) !== -1; } function isPendingMochaTest(node) { return isMochaTest(node.callee) && node.arguments.length === 1 && node.arguments[0].type === 'Literal'; } return { CallExpression: function (node) { if (node.callee && isPendingMochaTest(node)) { context.report({ node: node, message: 'Unexpected pending mocha test.' }); } } }; }
n/a
no-return-and-callback = function (context) { function check(node) { if (node.params.length === 0 || !hasParentMochaFunctionCall(node)) { return; } if (!reportIfShortArrowFunction(context, node)) { reportIfFunctionWithBlock(context, node, node.params[0].name); } } return { FunctionExpression: check, ArrowFunctionExpression: check }; }
n/a
no-sibling-hooks = function (context) { var isUsed = [], settings = context.settings; return { Program: function (node) { isUsed.push(newDescribeLayer(node)); }, CallExpression: function (node) { var name = node.callee && node.callee.name; if (astUtil.isDescribe(node, additionalSuiteNames(settings))) { isUsed.push(newDescribeLayer(node)); return; } if (!astUtil.isHookIdentifier(node.callee)) { return; } if (isUsed[isUsed.length - 1][name]) { context.report({ node: node.callee, message: 'Unexpected use of duplicate Mocha `' + name + '` hook' }); } isUsed[isUsed.length - 1][name] = true; }, 'CallExpression:exit': function (node) { if (isUsed[isUsed.length - 1].describeNode === node) { isUsed.pop(); } } }; }
n/a
no-skipped-tests = function (context) { var settings = context.settings, additionalTestFunctions = getAdditionalTestFunctions(settings), additionalXFunctions = getAdditionalXFunctions(settings); mochaTestFunctions = [ 'it', 'describe', 'suite', 'test', 'context', 'specify' ].concat(additionalTestFunctions); mochaXFunctions = [ 'xit', 'xdescribe', 'xcontext', 'xspecify' ].concat(additionalXFunctions); return { CallExpression: function (node) { var callee = node.callee; if (callee && isCallToMochasSkipFunction(callee)) { context.report({ node: callee.property, message: 'Unexpected skipped mocha test.', fix: createSkipAutofixFunction(callee) }); } else if (callee && isCallToMochaXFunction(callee)) { context.report({ node: callee, message: 'Unexpected skipped mocha test.', fix: createXAutofixFunction(callee) }); } } }; }
n/a
no-synchronous-tests = function (context) { var possibleAsyncFunctionNames = [ 'it', 'it.only', 'test', 'test.only', 'specify', 'specify.only', 'before', 'after', 'beforeEach', 'afterEach' ]; function getCalleeName(callee) { if (callee.type === 'MemberExpression') { return callee.object.name + '.' + callee.property.name; } return callee.name; } function hasParentMochaFunctionCall(functionExpression) { var name; if (functionExpression.parent && functionExpression.parent.type === 'CallExpression') { name = getCalleeName(functionExpression.parent.callee); return possibleAsyncFunctionNames.indexOf(name) > -1; } return false; } function hasAsyncCallback(functionExpression) { return functionExpression.params.length === 1; } function findPromiseReturnStatement(nodes) { return R.find(function (node) { return node.type === 'ReturnStatement' && node.argument && node.argument.type !== 'Literal'; }, nodes); } function checkPromiseReturn(functionExpression) { var bodyStatement = functionExpression.body, returnStatement = null; if (bodyStatement.type === 'BlockStatement') { returnStatement = findPromiseReturnStatement(functionExpression.body.body); } else if (bodyStatement.type === 'CallExpression') { // allow arrow statements calling a promise with implicit return. returnStatement = bodyStatement; } if (!returnStatement) { context.report(functionExpression, 'Unexpected synchronous test.'); } } function check(node) { if (hasParentMochaFunctionCall(node) && !hasAsyncCallback(node)) { checkPromiseReturn(node); } } return { FunctionExpression: check, ArrowFunctionExpression: check }; }
n/a
no-top-level-hooks = function (context) { var settings = context.settings, testSuiteStack = []; return { CallExpression: function (node) { if (astUtil.isDescribe(node, additionalSuiteNames(settings))) { testSuiteStack.push(node); return; } if (!astUtil.isHookIdentifier(node.callee)) { return; } if (testSuiteStack.length === 0) { context.report({ node: node.callee, message: 'Unexpected use of Mocha `' + node.callee.name + '` hook outside of a test suite' }); } }, 'CallExpression:exit': function (node) { if (testSuiteStack[testSuiteStack.length - 1] === node) { testSuiteStack.pop(); } } }; }
n/a
valid-suite-description = function (context) { var pattern = new RegExp(context.options[0]), suiteNames = context.options[1] ? context.options[1] : defaultSuiteNames; return { CallExpression: function (node) { var callee = node.callee, args; if (callee && callee.name && suiteNames.indexOf(callee.name) > -1) { args = node.arguments; if (args && args[0] && typeof args[0].value === 'string' && !pattern.test(args[0].value)) { context.report(node, 'Invalid "' + callee.name + '()" description found.'); } } } }; }
n/a
valid-test-description = function (context) { var pattern = context.options[0] ? new RegExp(context.options[0]) : /^should/, testNames = context.options[1] ? context.options[1] : defaultTestNames; return { CallExpression: function (node) { var callee = node.callee, args; if (callee && callee.name && testNames.indexOf(callee.name) > -1) { args = node.arguments; if (args && args[0] && typeof args[0].value === 'string' && !pattern.test(args[0].value)) { context.report(node, 'Invalid "' + callee.name + '()" description found.'); } } } }; }
n/a
additionalSuiteNames = function (settings) { var value = settings['mocha/' + propertyName], mochaSettings = settings.mocha || {}; return value || mochaSettings[propertyName] || []; }
n/a
getAdditionalTestFunctions = function (settings) { var value = settings['mocha/' + propertyName], mochaSettings = settings.mocha || {}; return value || mochaSettings[propertyName] || []; }
n/a
getAdditionalXFunctions = function (settings) { var value = settings['mocha/' + propertyName], mochaSettings = settings.mocha || {}; return value || mochaSettings[propertyName] || []; }
n/a