eslint-plugin-ava
Advanced tools
Comparing version 10.5.0 to 11.0.0
{ | ||
"name": "eslint-plugin-ava", | ||
"version": "10.5.0", | ||
"version": "11.0.0", | ||
"description": "ESLint rules for AVA", | ||
@@ -35,3 +35,4 @@ "license": "MIT", | ||
"enhance-visitors": "^1.0.0", | ||
"espree": "^7.1.0", | ||
"eslint-utils": "^2.1.0", | ||
"espree": "^7.2.0", | ||
"espurify": "^2.0.1", | ||
@@ -44,20 +45,19 @@ "import-modules": "^2.0.0", | ||
"devDependencies": { | ||
"ava": "^3.9.0", | ||
"ava": "^3.11.1", | ||
"babel-eslint": "^10.1.0", | ||
"c8": "^7.2.0", | ||
"c8": "^7.3.0", | ||
"chalk": "^4.1.0", | ||
"del": "^5.1.0", | ||
"eslint": "6.2.0", | ||
"eslint": "7.7.0", | ||
"eslint-ava-rule-tester": "^4.0.0", | ||
"eslint-plugin-eslint-plugin": "^2.3.0", | ||
"execa": "^4.0.2", | ||
"js-combinatorics": "^0.6.1", | ||
"execa": "^4.0.3", | ||
"listr": "^0.14.3", | ||
"outdent": "^0.7.1", | ||
"pify": "^5.0.0", | ||
"tempy": "^0.5.0", | ||
"xo": "^0.32.1" | ||
"tempy": "^0.6.0", | ||
"xo": "^0.33.0" | ||
}, | ||
"peerDependencies": { | ||
"eslint": ">=6.2.0" | ||
"eslint": ">=7.7.0" | ||
}, | ||
@@ -64,0 +64,0 @@ "ava": { |
'use strict'; | ||
const {visitIf} = require('enhance-visitors'); | ||
const {getStaticValue, isOpeningParenToken, isCommaToken} = require('eslint-utils'); | ||
const util = require('../util'); | ||
@@ -7,2 +8,6 @@ const createAvaRule = require('../create-ava-rule'); | ||
const expectedNbArguments = { | ||
assert: { | ||
min: 1, | ||
max: 2 | ||
}, | ||
deepEqual: { | ||
@@ -48,2 +53,6 @@ min: 2, | ||
}, | ||
notThrowsAsync: { | ||
min: 1, | ||
max: 2 | ||
}, | ||
pass: { | ||
@@ -77,2 +86,6 @@ min: 0, | ||
}, | ||
throwsAsync: { | ||
min: 1, | ||
max: 3 | ||
}, | ||
true: { | ||
@@ -92,2 +105,106 @@ min: 1, | ||
const actualExpectedAssertions = new Set([ | ||
'deepEqual', | ||
'is', | ||
'like', | ||
'not', | ||
'notDeepEqual', | ||
'throws', | ||
'throwsAsync' | ||
]); | ||
const relationalActualExpectedAssertions = new Set([ | ||
'assert', | ||
'truthy', | ||
'falsy', | ||
'true', | ||
'false' | ||
]); | ||
const comparisonOperators = new Map([ | ||
['>', '<'], | ||
['>=', '<='], | ||
['==', '=='], | ||
['===', '==='], | ||
['!=', '!='], | ||
['!==', '!=='], | ||
['<=', '>='], | ||
['<', '>'] | ||
]); | ||
const flipOperator = operator => comparisonOperators.get(operator); | ||
function isStatic(node) { | ||
const staticValue = getStaticValue(node); | ||
return staticValue !== null && typeof staticValue.value !== 'function'; | ||
} | ||
function * sourceRangesOfArguments(sourceCode, callExpression) { | ||
const openingParen = sourceCode.getTokenAfter( | ||
callExpression.callee, | ||
{filter: token => isOpeningParenToken(token)} | ||
); | ||
const closingParen = sourceCode.getLastToken(callExpression); | ||
for (const [index, argument] of callExpression.arguments.entries()) { | ||
const previousToken = index === 0 ? | ||
openingParen : | ||
sourceCode.getTokenBefore( | ||
argument, | ||
{filter: token => isCommaToken(token)} | ||
); | ||
const nextToken = index === callExpression.arguments.length - 1 ? | ||
closingParen : | ||
sourceCode.getTokenAfter( | ||
argument, | ||
{filter: token => isCommaToken(token)} | ||
); | ||
const firstToken = sourceCode.getTokenAfter( | ||
previousToken, | ||
{includeComments: true} | ||
); | ||
const lastToken = sourceCode.getTokenBefore( | ||
nextToken, | ||
{includeComments: true} | ||
); | ||
yield [firstToken.range[0], lastToken.range[1]]; | ||
} | ||
} | ||
function sourceOfBinaryExpressionComponents(sourceCode, node) { | ||
const {operator, left, right} = node; | ||
const operatorToken = sourceCode.getFirstTokenBetween( | ||
left, | ||
right, | ||
{filter: token => token.value === operator} | ||
); | ||
const previousToken = sourceCode.getTokenBefore(node); | ||
const nextToken = sourceCode.getTokenAfter(node); | ||
const leftRange = [ | ||
sourceCode.getTokenAfter(previousToken, {includeComments: true}).range[0], | ||
sourceCode.getTokenBefore(operatorToken, {includeComments: true}).range[1] | ||
]; | ||
const rightRange = [ | ||
sourceCode.getTokenAfter(operatorToken, {includeComments: true}).range[0], | ||
sourceCode.getTokenBefore(nextToken, {includeComments: true}).range[1] | ||
]; | ||
return [leftRange, operatorToken, rightRange]; | ||
} | ||
function noComments(sourceCode, ...nodes) { | ||
return nodes.every(node => { | ||
return sourceCode.getCommentsBefore(node).length === 0 && sourceCode.getCommentsAfter(node).length === 0; | ||
}); | ||
} | ||
const create = context => { | ||
@@ -120,5 +237,5 @@ const ava = createAvaRule(); | ||
const gottenArgs = node.arguments.length; | ||
const members = util.getMembers(callee).filter(name => name !== 'skip'); | ||
const firstNonSkipMember = util.getMembers(callee).find(name => name !== 'skip'); | ||
if (members[0] === 'end') { | ||
if (firstNonSkipMember === 'end') { | ||
if (gottenArgs > 1) { | ||
@@ -131,3 +248,3 @@ report(node, 'Too many arguments. Expected at most 1.'); | ||
if (members[0] === 'try') { | ||
if (firstNonSkipMember === 'try') { | ||
if (gottenArgs < 1) { | ||
@@ -140,3 +257,3 @@ report(node, 'Not enough arguments. Expected at least 1.'); | ||
const nArgs = expectedNbArguments[members[0]]; | ||
const nArgs = expectedNbArguments[firstNonSkipMember]; | ||
@@ -151,10 +268,14 @@ if (!nArgs) { | ||
report(node, `Too many arguments. Expected at most ${nArgs.max}.`); | ||
} else if (enforcesMessage && nArgs.min !== nArgs.max) { | ||
const hasMessage = gottenArgs === nArgs.max; | ||
} else { | ||
if (enforcesMessage && nArgs.min !== nArgs.max) { | ||
const hasMessage = gottenArgs === nArgs.max; | ||
if (!hasMessage && shouldHaveMessage) { | ||
report(node, 'Expected an assertion message, but found none.'); | ||
} else if (hasMessage && !shouldHaveMessage) { | ||
report(node, 'Expected no assertion message, but found one.'); | ||
if (!hasMessage && shouldHaveMessage) { | ||
report(node, 'Expected an assertion message, but found none.'); | ||
} else if (hasMessage && !shouldHaveMessage) { | ||
report(node, 'Expected no assertion message, but found one.'); | ||
} | ||
} | ||
checkArgumentOrder({node, assertion: firstNonSkipMember, context}); | ||
} | ||
@@ -165,2 +286,81 @@ }) | ||
function checkArgumentOrder({node, assertion, context}) { | ||
const [first, second] = node.arguments; | ||
if (actualExpectedAssertions.has(assertion) && second) { | ||
const [leftNode, rightNode] = [first, second]; | ||
if (isStatic(leftNode) && !isStatic(rightNode)) { | ||
context.report( | ||
makeOutOfOrder2ArgumentReport({node, leftNode, rightNode, context}) | ||
); | ||
} | ||
} else if ( | ||
relationalActualExpectedAssertions.has(assertion) && | ||
first && | ||
first.type === 'BinaryExpression' && | ||
comparisonOperators.has(first.operator) | ||
) { | ||
const [leftNode, rightNode] = [first.left, first.right]; | ||
if (isStatic(leftNode) && !isStatic(rightNode)) { | ||
context.report( | ||
makeOutOfOrder1ArgumentReport({node: first, leftNode, rightNode, context}) | ||
); | ||
} | ||
} | ||
} | ||
function makeOutOfOrder2ArgumentReport({node, leftNode, rightNode, context}) { | ||
const sourceCode = context.getSourceCode(); | ||
const [leftRange, rightRange] = sourceRangesOfArguments(sourceCode, node); | ||
const report = { | ||
message: 'Expected values should come after actual values.', | ||
loc: { | ||
start: sourceCode.getLocFromIndex(leftRange[0]), | ||
end: sourceCode.getLocFromIndex(rightRange[1]) | ||
} | ||
}; | ||
if (noComments(sourceCode, leftNode, rightNode)) { | ||
report.fix = fixer => { | ||
const leftText = sourceCode.getText().slice(...leftRange); | ||
const rightText = sourceCode.getText().slice(...rightRange); | ||
return [ | ||
fixer.replaceTextRange(leftRange, rightText), | ||
fixer.replaceTextRange(rightRange, leftText) | ||
]; | ||
}; | ||
} | ||
return report; | ||
} | ||
function makeOutOfOrder1ArgumentReport({node, leftNode, rightNode, context}) { | ||
const sourceCode = context.getSourceCode(); | ||
const [ | ||
leftRange, | ||
operatorToken, | ||
rightRange | ||
] = sourceOfBinaryExpressionComponents(sourceCode, node); | ||
const report = { | ||
message: 'Expected values should come after actual values.', | ||
loc: { | ||
start: sourceCode.getLocFromIndex(leftRange[0]), | ||
end: sourceCode.getLocFromIndex(rightRange[1]) | ||
} | ||
}; | ||
if (noComments(sourceCode, leftNode, rightNode, node)) { | ||
report.fix = fixer => { | ||
const leftText = sourceCode.getText().slice(...leftRange); | ||
const rightText = sourceCode.getText().slice(...rightRange); | ||
return [ | ||
fixer.replaceTextRange(leftRange, rightText), | ||
fixer.replaceText(operatorToken, flipOperator(node.operator)), | ||
fixer.replaceTextRange(rightRange, leftText) | ||
]; | ||
}; | ||
} | ||
return report; | ||
} | ||
const schema = [{ | ||
@@ -182,2 +382,3 @@ type: 'object', | ||
meta: { | ||
fixable: 'code', | ||
docs: { | ||
@@ -184,0 +385,0 @@ url: util.getDocsUrl(__filename) |
@@ -34,5 +34,5 @@ 'use strict'; | ||
) { | ||
const members = util.getMembers(callee).filter(name => name !== 'skip'); | ||
const firstNonSkipMember = util.getMembers(callee).find(name => name !== 'skip'); | ||
if (!util.assertionMethods.has(members[0])) { | ||
if (!util.assertionMethods.has(firstNonSkipMember)) { | ||
return; | ||
@@ -39,0 +39,0 @@ } |
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
68357
14
2453
10
144947
+ Addedeslint-utils@^2.1.0
+ Addedeslint-utils@2.1.0(transitive)
Updatedespree@^7.2.0