eslint-plugin-mocha
Advanced tools
Comparing version 8.2.0 to 9.0.0
@@ -0,1 +1,34 @@ | ||
## 9.0.0 (May 26, 2021) | ||
### Breaking Changes | ||
* Drop support for node v10 ([#285](https://github.com/lo1tuma/eslint-plugin-mocha/pull/285)) | ||
### Bug Fixes | ||
* Fix false positive in no-setup-in-describe ([#293](https://github.com/lo1tuma/eslint-plugin-mocha/pull/293)) | ||
### Features | ||
* Add "all" config preset which enables all rules ([#281](https://github.com/lo1tuma/eslint-plugin-mocha/pull/281)) | ||
### Enhancements | ||
* Improve no-skipped performance ([#292](https://github.com/lo1tuma/eslint-plugin-mocha/pull/292)) | ||
* Improve no-hooks-for-single-case performance ([#291](https://github.com/lo1tuma/eslint-plugin-mocha/pull/291)) | ||
* Improve no-nested-tests performance ([#290](https://github.com/lo1tuma/eslint-plugin-mocha/pull/290)) | ||
* Improve performance of no-identical-title ([#289](https://github.com/lo1tuma/eslint-plugin-mocha/pull/289)) | ||
* Improve no-setup-in-describe performance ([#287](https://github.com/lo1tuma/eslint-plugin-mocha/pull/287)) | ||
* Improve no-mocha-arrows performance ([#288](https://github.com/lo1tuma/eslint-plugin-mocha/pull/288)) | ||
* Improve performance of no-exports rule ([#286](https://github.com/lo1tuma/eslint-plugin-mocha/pull/286)) | ||
* Enable all rules during runtime benchmark ([#282](https://github.com/lo1tuma/eslint-plugin-mocha/pull/282)) | ||
### Dependency Upgrades | ||
* Update dependencies ([#284](https://github.com/lo1tuma/eslint-plugin-mocha/pull/284)) | ||
### Build-Related | ||
* Add node v16 to CI environments ([#283](https://github.com/lo1tuma/eslint-plugin-mocha/pull/283)) | ||
## 8.2.0 (May 25, 2021) | ||
@@ -2,0 +35,0 @@ |
29
index.js
@@ -29,2 +29,31 @@ 'use strict'; | ||
configs: { | ||
all: { | ||
env: { mocha: true }, | ||
plugins: [ 'mocha' ], | ||
rules: { | ||
'mocha/handle-done-callback': 'error', | ||
'mocha/max-top-level-suites': 'error', | ||
'mocha/no-async-describe': 'error', | ||
'mocha/no-exclusive-tests': 'error', | ||
'mocha/no-exports': 'error', | ||
'mocha/no-global-tests': 'error', | ||
'mocha/no-hooks': 'error', | ||
'mocha/no-hooks-for-single-case': 'error', | ||
'mocha/no-identical-title': 'error', | ||
'mocha/no-mocha-arrows': 'error', | ||
'mocha/no-nested-tests': 'error', | ||
'mocha/no-pending-tests': 'error', | ||
'mocha/no-return-and-callback': 'error', | ||
'mocha/no-return-from-async': 'error', | ||
'mocha/no-setup-in-describe': 'error', | ||
'mocha/no-sibling-hooks': 'error', | ||
'mocha/no-skipped-tests': 'error', | ||
'mocha/no-synchronous-tests': 'error', | ||
'mocha/no-top-level-hooks': 'error', | ||
'mocha/prefer-arrow-callback': 'error', | ||
'mocha/valid-suite-description': 'error', | ||
'mocha/valid-test-description': 'error' | ||
} | ||
}, | ||
recommended: { | ||
@@ -31,0 +60,0 @@ env: { mocha: true }, |
@@ -19,2 +19,8 @@ 'use strict'; | ||
let hasTestCase = false; | ||
const isTestCase = astUtils.buildIsTestCaseAnswerer(); | ||
const isDescribe = astUtils.buildIsDescribeAnswerer(); | ||
const isMochaFunctionCall = astUtils.buildIsMochaFunctionCallAnswerer( | ||
isTestCase, | ||
isDescribe | ||
); | ||
@@ -41,3 +47,3 @@ function isCommonJsExport(node) { | ||
CallExpression(node) { | ||
if (astUtils.isMochaFunctionCall(node, context.getScope())) { | ||
if (!hasTestCase && isMochaFunctionCall(node, context)) { | ||
hasTestCase = true; | ||
@@ -47,3 +53,5 @@ } | ||
'ExportNamedDeclaration, ExportDefaultDeclaration, ExportAllDeclaration'(node) { | ||
'ExportNamedDeclaration, ExportDefaultDeclaration, ExportAllDeclaration'( | ||
node | ||
) { | ||
exportNodes.push(node); | ||
@@ -50,0 +58,0 @@ }, |
@@ -35,2 +35,5 @@ 'use strict'; | ||
const astUtils = createAstUtils(context.settings); | ||
const isTestCase = astUtils.buildIsTestCaseAnswerer(); | ||
const isDescribe = astUtils.buildIsDescribeAnswerer(); | ||
const options = context.options[0] || {}; | ||
@@ -61,3 +64,3 @@ const allowedHooks = options.allow || []; | ||
node: hookNode, | ||
message: `Unexpected use of Mocha \`${ hookNode.name }\` hook for a single test case` | ||
message: `Unexpected use of Mocha \`${hookNode.name}\` hook for a single test case` | ||
}); | ||
@@ -76,3 +79,3 @@ }); | ||
CallExpression(node) { | ||
if (astUtils.isDescribe(node)) { | ||
if (isDescribe(node)) { | ||
increaseTestCount(); | ||
@@ -83,3 +86,3 @@ layers.push(newDescribeLayer(node)); | ||
if (astUtils.isTestCase(node)) { | ||
if (isTestCase(node)) { | ||
increaseTestCount(); | ||
@@ -86,0 +89,0 @@ } |
@@ -13,3 +13,7 @@ 'use strict'; | ||
function isFirstArgLiteral(node) { | ||
return node.arguments && node.arguments[0] && node.arguments[0].type === 'Literal'; | ||
return ( | ||
node.arguments && | ||
node.arguments[0] && | ||
node.arguments[0].type === 'Literal' | ||
); | ||
} | ||
@@ -26,10 +30,14 @@ | ||
const astUtils = createAstUtils(context.settings); | ||
const isTestCase = astUtils.buildIsTestCaseAnswerer(); | ||
const isDescribe = astUtils.buildIsDescribeAnswerer(); | ||
const titleLayers = [ newLayer() ]; | ||
function handlTestCaseTitles(titles, node, title) { | ||
if (astUtils.isTestCase(node)) { | ||
if (isTestCase(node)) { | ||
if (titles.includes(title)) { | ||
context.report({ | ||
node, | ||
message: 'Test title is used multiple times in the same test suite.' | ||
message: | ||
'Test title is used multiple times in the same test suite.' | ||
}); | ||
@@ -42,3 +50,3 @@ } | ||
function handlTestSuiteTitles(titles, node, title) { | ||
if (!astUtils.isDescribe(node)) { | ||
if (!isDescribe(node)) { | ||
return; | ||
@@ -59,3 +67,3 @@ } | ||
if (astUtils.isDescribe(node)) { | ||
if (isDescribe(node)) { | ||
titleLayers.push(newLayer()); | ||
@@ -72,3 +80,3 @@ } | ||
'CallExpression:exit'(node) { | ||
if (astUtils.isDescribe(node)) { | ||
if (isDescribe(node)) { | ||
titleLayers.pop(); | ||
@@ -75,0 +83,0 @@ } |
@@ -10,2 +10,65 @@ 'use strict'; | ||
function extractSourceTextByRange(sourceCode, start, end) { | ||
return sourceCode.text.slice(start, end).trim(); | ||
} | ||
// eslint-disable-next-line max-statements | ||
function formatFunctionHead(sourceCode, fn) { | ||
const arrow = sourceCode.getTokenBefore(fn.body); | ||
const beforeArrowToken = sourceCode.getTokenBefore(arrow); | ||
let firstToken = sourceCode.getFirstToken(fn); | ||
let functionKeyword = 'function'; | ||
let params = extractSourceTextByRange( | ||
sourceCode, | ||
firstToken.range[0], | ||
beforeArrowToken.range[1] | ||
); | ||
if (fn.async) { | ||
// When 'async' specified strip the token from the params text | ||
// and prepend it to the function keyword | ||
params = params.slice(firstToken.range[1] - firstToken.range[0]).trim(); | ||
functionKeyword = 'async function'; | ||
// Advance firstToken pointer | ||
firstToken = sourceCode.getTokenAfter(firstToken); | ||
} | ||
const beforeArrowComment = extractSourceTextByRange( | ||
sourceCode, | ||
beforeArrowToken.range[1], | ||
arrow.range[0] | ||
); | ||
const afterArrowComment = extractSourceTextByRange( | ||
sourceCode, | ||
arrow.range[1], | ||
fn.body.range[0] | ||
); | ||
let paramsFullText; | ||
if (firstToken.type !== 'Punctuator') { | ||
paramsFullText = `(${params}${beforeArrowComment})${afterArrowComment}`; | ||
} else { | ||
paramsFullText = `${params}${beforeArrowComment}${afterArrowComment}`; | ||
} | ||
return `${functionKeyword}${paramsFullText} `; | ||
} | ||
function fixArrowFunction(fixer, sourceCode, fn) { | ||
if (fn.body.type === 'BlockStatement') { | ||
// When it((...) => { ... }), | ||
// simply replace '(...) => ' with 'function () ' | ||
return fixer.replaceTextRange( | ||
[ fn.range[0], fn.body.range[0] ], | ||
formatFunctionHead(sourceCode, fn) | ||
); | ||
} | ||
const bodyText = sourceCode.getText(fn.body); | ||
return fixer.replaceTextRange( | ||
fn.range, | ||
`${formatFunctionHead(sourceCode, fn)}{ return ${bodyText}; }` | ||
); | ||
} | ||
module.exports = { | ||
@@ -15,3 +78,4 @@ meta: { | ||
docs: { | ||
description: 'Disallow arrow functions as arguments to mocha functions' | ||
description: | ||
'Disallow arrow functions as arguments to mocha functions' | ||
}, | ||
@@ -23,68 +87,32 @@ fixable: 'code' | ||
const sourceCode = context.getSourceCode(); | ||
const isTestCase = astUtils.buildIsTestCaseAnswerer(); | ||
const isDescribe = astUtils.buildIsDescribeAnswerer(); | ||
const isMochaFunctionCall = astUtils.buildIsMochaFunctionCallAnswerer( | ||
isTestCase, | ||
isDescribe | ||
); | ||
function extractSourceTextByRange(start, end) { | ||
return sourceCode.text.slice(start, end).trim(); | ||
} | ||
// eslint-disable-next-line max-statements | ||
function formatFunctionHead(fn) { | ||
const arrow = sourceCode.getTokenBefore(fn.body); | ||
const beforeArrowToken = sourceCode.getTokenBefore(arrow); | ||
let firstToken = sourceCode.getFirstToken(fn); | ||
let functionKeyword = 'function'; | ||
let params = extractSourceTextByRange(firstToken.range[0], beforeArrowToken.range[1]); | ||
if (fn.async) { | ||
// When 'async' specified strip the token from the params text | ||
// and prepend it to the function keyword | ||
params = params.slice(firstToken.range[1] - firstToken.range[0]).trim(); | ||
functionKeyword = 'async function'; | ||
// Advance firstToken pointer | ||
firstToken = sourceCode.getTokenAfter(firstToken); | ||
} | ||
const beforeArrowComment = extractSourceTextByRange(beforeArrowToken.range[1], arrow.range[0]); | ||
const afterArrowComment = extractSourceTextByRange(arrow.range[1], fn.body.range[0]); | ||
let paramsFullText; | ||
if (firstToken.type !== 'Punctuator') { | ||
paramsFullText = `(${params}${beforeArrowComment})${afterArrowComment}`; | ||
} else { | ||
paramsFullText = `${params}${beforeArrowComment}${afterArrowComment}`; | ||
} | ||
return `${functionKeyword}${paramsFullText} `; | ||
} | ||
function fixArrowFunction(fixer, fn) { | ||
if (fn.body.type === 'BlockStatement') { | ||
// When it((...) => { ... }), | ||
// simply replace '(...) => ' with 'function () ' | ||
return fixer.replaceTextRange( | ||
[ fn.range[0], fn.body.range[0] ], | ||
formatFunctionHead(fn) | ||
); | ||
} | ||
const bodyText = sourceCode.text.slice(fn.body.range[0], fn.body.range[1]); | ||
return fixer.replaceTextRange( | ||
[ fn.range[0], fn.range[1] ], | ||
`${formatFunctionHead(fn)}{ return ${ bodyText }; }` | ||
); | ||
} | ||
return { | ||
CallExpression(node) { | ||
const name = astUtils.getNodeName(node.callee); | ||
if (isMochaFunctionCall(node, context)) { | ||
const amountOfArguments = node.arguments.length; | ||
if (astUtils.isMochaFunctionCall(node, context.getScope())) { | ||
const fnArg = node.arguments.slice(-1)[0]; | ||
if (fnArg && fnArg.type === 'ArrowFunctionExpression') { | ||
context.report({ | ||
node, | ||
message: `Do not pass arrow functions to ${ name }()`, | ||
fix(fixer) { | ||
return fixArrowFunction(fixer, fnArg); | ||
} | ||
}); | ||
if (amountOfArguments > 0) { | ||
const lastArgument = | ||
node.arguments[amountOfArguments - 1]; | ||
if (lastArgument.type === 'ArrowFunctionExpression') { | ||
const name = astUtils.getNodeName(node.callee); | ||
context.report({ | ||
node, | ||
message: `Do not pass arrow functions to ${name}()`, | ||
fix(fixer) { | ||
return fixArrowFunction( | ||
fixer, | ||
sourceCode, | ||
lastArgument | ||
); | ||
} | ||
}); | ||
} | ||
} | ||
@@ -91,0 +119,0 @@ } |
@@ -18,2 +18,4 @@ 'use strict'; | ||
let hookCallNestingLevel = 0; | ||
const isTestCase = astUtils.buildIsTestCaseAnswerer(); | ||
const isDescribe = astUtils.buildIsDescribeAnswerer(); | ||
@@ -27,5 +29,5 @@ function report(callExpression, message) { | ||
function isNestedTest(isTestCase, isDescribe, nestingLevel) { | ||
function isNestedTest(_isTestCase, _isDescribe, nestingLevel) { | ||
const isNested = nestingLevel > 0; | ||
const isTest = isTestCase || isDescribe; | ||
const isTest = _isTestCase || _isDescribe; | ||
@@ -35,9 +37,16 @@ return isNested && isTest; | ||
function checkForAndReportErrors(node, isTestCase, isDescribe, isHookCall) { | ||
if (isNestedTest(isTestCase, isDescribe, testNestingLevel)) { | ||
const message = isDescribe ? | ||
function checkForAndReportErrors( | ||
node, | ||
_isTestCase, | ||
_isDescribe, | ||
isHookCall | ||
) { | ||
if (isNestedTest(_isTestCase, _isDescribe, testNestingLevel)) { | ||
const message = _isDescribe ? | ||
'Unexpected suite nested within a test.' : | ||
'Unexpected test nested within another test.'; | ||
report(node, message); | ||
} else if (isNestedTest(isTestCase, isHookCall, hookCallNestingLevel)) { | ||
} else if ( | ||
isNestedTest(_isTestCase, isHookCall, hookCallNestingLevel) | ||
) { | ||
const message = isHookCall ? | ||
@@ -52,9 +61,14 @@ 'Unexpected test hook nested within a test hook.' : | ||
CallExpression(node) { | ||
const isTestCase = astUtils.isTestCase(node); | ||
const _isTestCase = isTestCase(node); | ||
const isHookCall = astUtils.isHookCall(node); | ||
const isDescribe = astUtils.isDescribe(node); | ||
const _isDescribe = isDescribe(node); | ||
checkForAndReportErrors(node, isTestCase, isDescribe, isHookCall); | ||
checkForAndReportErrors( | ||
node, | ||
_isTestCase, | ||
_isDescribe, | ||
isHookCall | ||
); | ||
if (isTestCase) { | ||
if (_isTestCase) { | ||
testNestingLevel += 1; | ||
@@ -67,3 +81,3 @@ } else if (isHookCall) { | ||
'CallExpression:exit'(node) { | ||
if (astUtils.isTestCase(node)) { | ||
if (isTestCase(node)) { | ||
testNestingLevel -= 1; | ||
@@ -70,0 +84,0 @@ } else if (astUtils.isHookCall(node)) { |
@@ -10,2 +10,30 @@ 'use strict'; | ||
function isNestedInDescribeBlock(nesting) { | ||
return ( | ||
nesting.length && | ||
!nesting.includes(PURE) && | ||
nesting.lastIndexOf(FUNCTION) < nesting.lastIndexOf(DESCRIBE) | ||
); | ||
} | ||
function reportCallExpression(context, callExpression) { | ||
const message = 'Unexpected function call in describe block.'; | ||
context.report({ | ||
message, | ||
node: callExpression.callee | ||
}); | ||
} | ||
function reportMemberExpression(context, memberExpression) { | ||
const message = | ||
'Unexpected member expression in describe block. ' + | ||
'Member expressions may call functions via getters.'; | ||
context.report({ | ||
message, | ||
node: memberExpression | ||
}); | ||
} | ||
module.exports = { | ||
@@ -21,46 +49,21 @@ meta: { | ||
const astUtils = createAstUtils(context.settings); | ||
const isDescribe = astUtils.buildIsDescribeAnswerer(); | ||
const isTestCase = astUtils.buildIsTestCaseAnswerer(); | ||
function isPureNode(node) { | ||
return astUtils.isHookCall(node) || | ||
astUtils.isTestCase(node) || | ||
astUtils.isSuiteConfigCall(node); | ||
return ( | ||
astUtils.isHookCall(node) || | ||
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); | ||
} else if (isNestedInDescribeBlock(nesting)) { | ||
reportCallExpression(context, node); | ||
} | ||
} | ||
function isDescribe(node) { | ||
return astUtils.isDescribe(node); | ||
} | ||
function isParentDescribe(node) { | ||
@@ -90,4 +93,7 @@ return isDescribe(node.parent); | ||
MemberExpression(node) { | ||
if (!isDescribe(node.parent) && isNestedInDescribeBlock()) { | ||
reportMemberExpression(node); | ||
if ( | ||
!isDescribe(node.parent) && | ||
isNestedInDescribeBlock(nesting) | ||
) { | ||
reportMemberExpression(context, node); | ||
} | ||
@@ -107,2 +113,13 @@ }, | ||
FunctionExpression(node) { | ||
if (nesting.length && !isParentDescribe(node)) { | ||
nesting.push(FUNCTION); | ||
} | ||
}, | ||
'FunctionExpression:exit'(node) { | ||
if (nesting.length && !isParentDescribe(node)) { | ||
nesting.pop(); | ||
} | ||
}, | ||
ArrowFunctionExpression(node) { | ||
@@ -109,0 +126,0 @@ if (nesting.length && !isParentDescribe(node)) { |
@@ -14,10 +14,14 @@ 'use strict'; | ||
const astUtils = createAstUtils(context.settings); | ||
const options = { modifiers: [ 'skip' ], modifiersOnly: true }; | ||
const isTestCase = astUtils.buildIsTestCaseAnswerer(options); | ||
const isDescribe = astUtils.buildIsDescribeAnswerer(options); | ||
return { | ||
CallExpression(node) { | ||
const options = { modifiers: [ 'skip' ], modifiersOnly: true }; | ||
if (astUtils.isDescribe(node, options) || astUtils.isTestCase(node, options)) { | ||
if (isDescribe(node) || isTestCase(node)) { | ||
const callee = node.callee; | ||
const nodeToReport = callee.type === 'MemberExpression' ? callee.property : callee; | ||
const nodeToReport = | ||
callee.type === 'MemberExpression' ? | ||
callee.property : | ||
callee; | ||
@@ -24,0 +28,0 @@ context.report({ |
@@ -19,3 +19,7 @@ 'use strict'; | ||
return find(function (node) { | ||
return node.type === 'ReturnStatement' && node.argument && node.argument.type !== 'Literal'; | ||
return ( | ||
node.type === 'ReturnStatement' && | ||
node.argument && | ||
node.argument.type !== 'Literal' | ||
); | ||
}, nodes); | ||
@@ -29,3 +33,5 @@ } | ||
if (bodyStatement.type === 'BlockStatement') { | ||
returnStatement = findPromiseReturnStatement(functionExpression.body.body); | ||
returnStatement = findPromiseReturnStatement( | ||
functionExpression.body.body | ||
); | ||
} else if (bodyStatement.type !== 'Literal') { | ||
@@ -36,4 +42,3 @@ // allow arrow statements calling a promise with implicit return. | ||
return returnStatement !== null && | ||
typeof returnStatement !== 'undefined'; | ||
return returnStatement !== null && typeof returnStatement !== 'undefined'; | ||
} | ||
@@ -67,3 +72,5 @@ | ||
const options = context.options[0] || {}; | ||
const allowedAsyncMethods = isNil(options.allowed) ? asyncMethods : options.allowed; | ||
const allowedAsyncMethods = isNil(options.allowed) ? | ||
asyncMethods : | ||
options.allowed; | ||
@@ -73,3 +80,5 @@ function check(node) { | ||
// For each allowed async test method, check if it is used in the test | ||
const testAsyncMethods = allowedAsyncMethods.map(function (method) { | ||
const testAsyncMethods = allowedAsyncMethods.map(function ( | ||
method | ||
) { | ||
switch (method) { | ||
@@ -88,5 +97,3 @@ case 'async': | ||
// Check that at least one allowed async test method is used in the test | ||
const isAsyncTest = testAsyncMethods.some(function (value) { | ||
return value === true; | ||
}); | ||
const isAsyncTest = testAsyncMethods.includes(true); | ||
@@ -93,0 +100,0 @@ if (!isAsyncTest) { |
@@ -58,3 +58,6 @@ 'use strict'; | ||
*/ | ||
if (variable.name === 'arguments' && variable.identifiers.length === 0) { | ||
if ( | ||
variable.name === 'arguments' && | ||
variable.identifiers.length === 0 | ||
) { | ||
variableObject = variable; | ||
@@ -73,5 +76,7 @@ break; | ||
function propertyIndicatesBind(property) { | ||
return !property.computed && | ||
property.type === 'Identifier' && | ||
property.name === 'bind'; | ||
return ( | ||
!property.computed && | ||
property.type === 'Identifier' && | ||
property.name === 'bind' | ||
); | ||
} | ||
@@ -85,6 +90,8 @@ | ||
function isBindThis(node, currentNode) { | ||
return node.object === currentNode && | ||
return ( | ||
node.object === currentNode && | ||
propertyIndicatesBind(node.property) && | ||
node.parent.type === 'CallExpression' && | ||
node.parent.callee === node; | ||
node.parent.callee === node | ||
); | ||
} | ||
@@ -100,4 +107,7 @@ | ||
function hasDuplicateParams(paramsList) { | ||
return paramsList.every((param) => param.type === 'Identifier') && | ||
paramsList.length !== new Set(paramsList.map((param) => param.name)).size; | ||
return ( | ||
paramsList.every((param) => param.type === 'Identifier') && | ||
paramsList.length !== | ||
new Set(paramsList.map((param) => param.name)).size | ||
); | ||
} | ||
@@ -146,2 +156,8 @@ | ||
const sourceCode = context.getSourceCode(); | ||
const isTestCase = astUtils.buildIsTestCaseAnswerer(); | ||
const isDescribe = astUtils.buildIsDescribeAnswerer(); | ||
const isMochaFunctionCall = astUtils.buildIsMochaFunctionCallAnswerer( | ||
isTestCase, | ||
isDescribe | ||
); | ||
@@ -175,4 +191,5 @@ /** | ||
retv.isLexicalThis = | ||
parent.parent.arguments.length === 1 && | ||
parent.parent.arguments[0].type === 'ThisExpression'; | ||
parent.parent.arguments.length === 1 && | ||
parent.parent.arguments[0].type === | ||
'ThisExpression'; | ||
parent = parent.parent; | ||
@@ -191,3 +208,6 @@ } else { | ||
// Checks whether the node is a mocha function callback. | ||
if (retv.isCallback && astUtils.isMochaFunctionCall(parent, context.getScope())) { | ||
if ( | ||
retv.isCallback && | ||
isMochaFunctionCall(parent, context) | ||
) { | ||
retv.isMochaCallback = true; | ||
@@ -234,3 +254,2 @@ } | ||
return { | ||
// Reset internal state. | ||
@@ -294,4 +313,7 @@ Program() { | ||
if (callbackInfo.isCallback && | ||
(!allowUnboundThis || !scopeInfo.this || callbackInfo.isLexicalThis) && | ||
if ( | ||
callbackInfo.isCallback && | ||
(!allowUnboundThis || | ||
!scopeInfo.this || | ||
callbackInfo.isLexicalThis) && | ||
!scopeInfo.meta && | ||
@@ -304,3 +326,7 @@ !callbackInfo.isMochaCallback | ||
fix(fixer) { | ||
if (!callbackInfo.isLexicalThis && scopeInfo.this || hasDuplicateParams(node.params)) { | ||
if ( | ||
!callbackInfo.isLexicalThis && | ||
scopeInfo.this || | ||
hasDuplicateParams(node.params) | ||
) { | ||
/* | ||
@@ -319,9 +345,13 @@ * If the callback function does not have .bind(this) and contains a reference to | ||
sourceCode.getTokenBefore(node.body, 1); | ||
const paramsRightParen = sourceCode.getTokenBefore(node.body); | ||
const paramsRightParen = sourceCode.getTokenBefore( | ||
node.body | ||
); | ||
const asyncKeyword = node.async ? 'async ' : ''; | ||
const paramsFullText = sourceCode.text.slice( | ||
paramsLeftParen.range[0], paramsRightParen.range[1] | ||
paramsLeftParen.range[0], | ||
paramsRightParen.range[1] | ||
); | ||
const arrowFunctionText = | ||
`${asyncKeyword}${paramsFullText} => ${sourceCode.getText(node.body)}`; | ||
const arrowFunctionText = `${asyncKeyword}${paramsFullText} => ${sourceCode.getText( | ||
node.body | ||
)}`; | ||
@@ -332,3 +362,5 @@ /* | ||
*/ | ||
const replacedNode = callbackInfo.isLexicalThis ? node.parent.parent : node; | ||
const replacedNode = callbackInfo.isLexicalThis ? | ||
node.parent.parent : | ||
node; | ||
@@ -340,7 +372,14 @@ /* | ||
*/ | ||
const needsParens = replacedNode.parent.type !== 'CallExpression' && | ||
replacedNode.parent.type !== 'ConditionalExpression'; | ||
const replacementText = needsParens ? `(${arrowFunctionText})` : arrowFunctionText; | ||
const needsParens = | ||
replacedNode.parent.type !== 'CallExpression' && | ||
replacedNode.parent.type !== | ||
'ConditionalExpression'; | ||
const replacementText = needsParens ? | ||
`(${arrowFunctionText})` : | ||
arrowFunctionText; | ||
return fixer.replaceText(replacedNode, replacementText); | ||
return fixer.replaceText( | ||
replacedNode, | ||
replacementText | ||
); | ||
} | ||
@@ -347,0 +386,0 @@ }); |
@@ -16,4 +16,12 @@ 'use strict'; | ||
const hooks = [ | ||
'before', 'after', 'beforeEach', 'afterEach', 'beforeAll', 'afterAll', | ||
'setup', 'teardown', 'suiteSetup', 'suiteTeardown' | ||
'before', | ||
'after', | ||
'beforeEach', | ||
'afterEach', | ||
'beforeAll', | ||
'afterAll', | ||
'setup', | ||
'teardown', | ||
'suiteSetup', | ||
'suiteTeardown' | ||
]; | ||
@@ -30,3 +38,3 @@ const suiteConfig = [ 'timeout', 'slow', 'retries' ]; | ||
if (node.type === 'MemberExpression') { | ||
return `${getNodeName(node.object) }.${ getPropertyName(node.property)}`; | ||
return `${getNodeName(node.object)}.${getPropertyName(node.property)}`; | ||
} | ||
@@ -37,5 +45,3 @@ return node.name; | ||
function isHookIdentifier(node) { | ||
return node && | ||
node.type === 'Identifier' && | ||
hooks.includes(node.name); | ||
return node && node.type === 'Identifier' && hooks.includes(node.name); | ||
} | ||
@@ -56,7 +62,12 @@ | ||
return reference && reference.resolved && reference.resolved.defs.length > 0; | ||
return ( | ||
reference && reference.resolved && reference.resolved.defs.length > 0 | ||
); | ||
} | ||
function isCallToShadowedReference(node, scope) { | ||
const identifier = node.callee.type === 'MemberExpression' ? node.callee.object : node.callee; | ||
const identifier = | ||
node.callee.type === 'MemberExpression' ? | ||
node.callee.object : | ||
node.callee; | ||
@@ -74,5 +85,9 @@ return isShadowed(scope, identifier); | ||
function buildIsDescribeAnswerer(options) { | ||
function buildIsDescribeAnswerer(options = {}) { | ||
const { modifiers = [ 'skip', 'only' ], modifiersOnly = false } = options; | ||
const describeAliases = getSuiteNames({ modifiersOnly, modifiers, additionalCustomNames }); | ||
const describeAliases = getSuiteNames({ | ||
modifiersOnly, | ||
modifiers, | ||
additionalCustomNames | ||
}); | ||
@@ -88,3 +103,7 @@ return (node) => isFunctionCallWithName(node, describeAliases); | ||
const { modifiers = [ 'skip', 'only' ], modifiersOnly = false } = options; | ||
const testCaseNames = getTestCaseNames({ modifiersOnly, modifiers, additionalCustomNames }); | ||
const testCaseNames = getTestCaseNames({ | ||
modifiersOnly, | ||
modifiers, | ||
additionalCustomNames | ||
}); | ||
@@ -116,12 +135,25 @@ return (node) => isFunctionCallWithName(node, testCaseNames); | ||
function isMochaFunctionCall(node, scope) { | ||
if (isCallToShadowedReference(node, scope)) { | ||
return false; | ||
function buildIsMochaFunctionCallAnswerer(_isTestCase, _isDescribe) { | ||
function isMochaFunctionCall(node) { | ||
return _isTestCase(node) || _isDescribe(node) || isHookCall(node); | ||
} | ||
return isTestCase(node) || isDescribe(node) || isHookCall(node); | ||
return (node, context) => { | ||
if (isMochaFunctionCall(node)) { | ||
const scope = context.getScope(); | ||
if (!isCallToShadowedReference(node, scope)) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
} | ||
function hasParentMochaFunctionCall(functionExpression, options) { | ||
return isTestCase(functionExpression.parent, options) || isHookCall(functionExpression.parent); | ||
return ( | ||
isTestCase(functionExpression.parent, options) || | ||
isHookCall(functionExpression.parent) | ||
); | ||
} | ||
@@ -146,3 +178,2 @@ | ||
getNodeName, | ||
isMochaFunctionCall, | ||
isHookCall, | ||
@@ -154,3 +185,4 @@ isSuiteConfigCall, | ||
buildIsDescribeAnswerer, | ||
buildIsTestCaseAnswerer | ||
buildIsTestCaseAnswerer, | ||
buildIsMochaFunctionCallAnswerer | ||
}; | ||
@@ -157,0 +189,0 @@ } |
{ | ||
"name": "eslint-plugin-mocha", | ||
"version": "8.2.0", | ||
"version": "9.0.0", | ||
"description": "Eslint rules for mocha.", | ||
"engines": { | ||
"node": ">=10.0.0" | ||
"node": ">=12.0.0" | ||
}, | ||
@@ -25,16 +25,16 @@ "main": "index.js", | ||
"dependencies": { | ||
"eslint-utils": "^2.1.0", | ||
"eslint-utils": "^3.0.0", | ||
"ramda": "^0.27.1" | ||
}, | ||
"devDependencies": { | ||
"chai": "^4.2.0", | ||
"chai": "^4.3.4", | ||
"coveralls": "^3.1.0", | ||
"eslint": "^7.5.0", | ||
"eslint": "^7.27.0", | ||
"eslint-config-holidaycheck": "^0.13.1", | ||
"eslint-plugin-node": "^11.1.0", | ||
"eslint-plugin-unicorn": "^21.0.0", | ||
"mocha": "^8.1.0", | ||
"eslint-plugin-unicorn": "^32.0.1", | ||
"mocha": "^8.4.0", | ||
"nyc": "^15.1.0", | ||
"pr-log": "^4.0.0", | ||
"semver": "^7.3.4" | ||
"semver": "^7.3.5" | ||
}, | ||
@@ -41,0 +41,0 @@ "peerDependencies": { |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
95345
2003
+ Addedeslint-utils@3.0.0(transitive)
+ Addedeslint-visitor-keys@2.1.0(transitive)
- Removedeslint-utils@2.1.0(transitive)
- Removedeslint-visitor-keys@1.3.0(transitive)
Updatedeslint-utils@^3.0.0