Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 41 additions & 26 deletions create-ava-rule.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import enhance from 'enhance-visitors';
import {getTestModifiers, unwrapTypeExpression} from './util.js';

export default () => {
export default context => {
let isTestFile = false;
let currentTestNode;
const testIdentifiers = new Set();

function isTestFunctionCall(node) {
Expand All @@ -18,11 +16,8 @@ export default () => {
return false;
}

function getTestModifierNames(node) {
return getTestModifiers(node).map(property => property.name);
}
const getModifierNames = node => getTestModifiers(node).map(property => property.name);

/* eslint quote-props: [2, "as-needed"] */
const predefinedRules = {
ImportDeclaration(node) {
if (node.source.value !== 'ava' || node.importKind === 'type') {
Expand Down Expand Up @@ -51,37 +46,57 @@ export default () => {
testIdentifiers.add(node.id.name);
}
},
CallExpression(node) {
if (isTestFunctionCall(node.callee)) {
// Entering test function
currentTestNode = node;
}
},
'CallExpression:exit'(node) {
if (currentTestNode === node) {
// Leaving test function
currentTestNode = undefined;
}
},
'Program:exit'() {
isTestFile = false;
testIdentifiers.clear();
},
};

return {
hasTestModifier: module_ => getTestModifierNames(currentTestNode).includes(module_),
hasNoUtilityModifier() {
const modifiers = getTestModifierNames(currentTestNode);
isInTestFile: () => isTestFile,
isTestNode: node => node.type === 'CallExpression' && isTestFunctionCall(node.callee),
isInTestNode(node) {
if (node.type === 'CallExpression' && isTestFunctionCall(node.callee)) {
return node;
}

const ancestors = context.sourceCode.getAncestors(node);
for (let index = ancestors.length - 1; index >= 0; index--) {
const ancestor = ancestors[index];
if (ancestor.type === 'CallExpression' && isTestFunctionCall(ancestor.callee)) {
return ancestor;
}
}

return undefined;
},
hasTestModifier: (node, modifier) => getModifierNames(node).includes(modifier),
hasNoUtilityModifier(node) {
const modifiers = getModifierNames(node);
return !modifiers.includes('before')
&& !modifiers.includes('beforeEach')
&& !modifiers.includes('after')
&& !modifiers.includes('afterEach')
&& !modifiers.includes('macro');
},
isInTestFile: () => isTestFile,
isInTestNode: () => currentTestNode,
isTestNode: node => currentTestNode === node,
merge: customHandlers => enhance.mergeVisitors([predefinedRules, customHandlers]),
merge: customHandlers => ({
...predefinedRules,
...Object.fromEntries(Object.entries(customHandlers).map(([key, custom]) => {
const predefined = predefinedRules[key];
if (!predefined) {
return [key, custom];
}

return [key, key.endsWith(':exit')
? node => {
custom(node);
predefined(node);
}
: node => {
predefined(node);
custom(node);
}];
})),
}),
};
};
2 changes: 1 addition & 1 deletion docs/rules/no-duplicate-modifiers.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

📝 Disallow duplicate test modifiers.

❌ This rule is [deprecated](https://github.com/avajs/eslint-plugin-ava/blob/v16.0.0/docs/rules/no-duplicate-modifiers.md). Replaced by `ava/no-invalid-modifier-chain` which covers more cases.
❌ This rule is [deprecated](https://github.com/avajs/eslint-plugin-ava/blob/v16.0.1/docs/rules/no-duplicate-modifiers.md). Replaced by `ava/no-invalid-modifier-chain` which covers more cases.

🚫 This rule is _disabled_ in the ✅ `recommended` [config](https://github.com/avajs/eslint-plugin-ava#recommended-config).

Expand Down
2 changes: 1 addition & 1 deletion docs/rules/no-unknown-modifiers.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

📝 Disallow unknown test modifiers.

❌ This rule is [deprecated](https://github.com/avajs/eslint-plugin-ava/blob/v16.0.0/docs/rules/no-unknown-modifiers.md). Replaced by `ava/no-invalid-modifier-chain` which covers more cases.
❌ This rule is [deprecated](https://github.com/avajs/eslint-plugin-ava/blob/v16.0.1/docs/rules/no-unknown-modifiers.md). Replaced by `ava/no-invalid-modifier-chain` which covers more cases.

🚫 This rule is _disabled_ in the ✅ `recommended` [config](https://github.com/avajs/eslint-plugin-ava#recommended-config).

Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
"dependencies": {
"@eslint-community/eslint-utils": "^4.9.1",
"@eslint/json": "^1.0.0",
"enhance-visitors": "^1.0.0",
"espree": "^11.1.0",
"espurify": "^3.2.0",
"micro-spelling-correcter": "^1.1.1",
Expand Down
16 changes: 8 additions & 8 deletions rules/assertion-arguments.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import {visitIf} from 'enhance-visitors';
import {
getStaticValue, isOpeningParenToken, isCommaToken, findVariable,
} from '@eslint-community/eslint-utils';
import util from '../util.js';
import createAvaRule from '../create-ava-rule.js';
import util from '../util.js';

const MESSAGE_ID_TOO_FEW = 'too-few-arguments';
const MESSAGE_ID_TOO_MANY = 'too-many-arguments';
Expand Down Expand Up @@ -227,16 +226,17 @@ function isString(node) {
}

const create = context => {
const ava = createAvaRule();
const ava = createAvaRule(context);
const options = context.options[0];
const enforcesMessage = Boolean(options.message);
const shouldHaveMessage = options.message !== 'never';

return ava.merge({
CallExpression: visitIf([
ava.isInTestFile,
ava.isInTestNode,
])(node => {
CallExpression(node) {
if (!ava.isInTestFile() || !ava.isInTestNode(node)) {
return;
}

const {callee} = node;

if (
Expand Down Expand Up @@ -352,7 +352,7 @@ const create = context => {
context.report({node, messageId: MESSAGE_ID_NOT_STRING});
}
}
}),
},
});
};

Expand Down
14 changes: 7 additions & 7 deletions rules/failing-test-url.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {visitIf} from 'enhance-visitors';
import createAvaRule from '../create-ava-rule.js';
import util from '../util.js';

Expand All @@ -7,13 +6,14 @@ const MESSAGE_ID = 'failing-test-url';
const urlPattern = /https?:\/\/\S+/;

const create = context => {
const ava = createAvaRule();
const ava = createAvaRule(context);

return ava.merge({
CallExpression: visitIf([
ava.isInTestFile,
ava.isTestNode,
])(node => {
CallExpression(node) {
if (!ava.isInTestFile() || !ava.isTestNode(node)) {
return;
}

const propertyNode = util.getTestModifier(node, 'failing');
if (!propertyNode) {
return;
Expand All @@ -28,7 +28,7 @@ const create = context => {
messageId: MESSAGE_ID,
});
}
}),
},
});
};

Expand Down
14 changes: 7 additions & 7 deletions rules/hooks-order.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {visitIf} from 'enhance-visitors';
import createAvaRule from '../create-ava-rule.js';
import util from '../util.js';

Expand Down Expand Up @@ -40,7 +39,7 @@ const buildMessage = (name, orders, visited) => {
};

const create = context => {
const ava = createAvaRule();
const ava = createAvaRule(context);

const orders = buildOrders([
'before',
Expand Down Expand Up @@ -89,10 +88,11 @@ const create = context => {

const selectors = {};
for (const check of checks) {
selectors[check.selector] = visitIf([
ava.isInTestFile,
ava.isTestNode,
])(node => {
selectors[check.selector] = node => {
if (!ava.isInTestFile() || !ava.isTestNode(node)) {
return;
}

visited[check.name] = node;

const message = buildMessage(check.name, orders, visited);
Expand Down Expand Up @@ -136,7 +136,7 @@ const create = context => {
},
});
}
});
};
}

return ava.merge(selectors);
Expand Down
24 changes: 14 additions & 10 deletions rules/max-asserts.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import {visitIf} from 'enhance-visitors';
import util from '../util.js';
import createAvaRule from '../create-ava-rule.js';
import util from '../util.js';

const MESSAGE_ID = 'max-asserts';

const notAssertionMethods = new Set(['plan', 'end']);

const create = context => {
const ava = createAvaRule();
const ava = createAvaRule(context);
const {max: maxAssertions} = context.options[0];
let assertionCount = 0;
let nodeToReport;

return ava.merge({
CallExpression: visitIf([
ava.isInTestFile,
ava.isInTestNode,
])(node => {
CallExpression(node) {
if (!ava.isInTestFile() || !ava.isInTestNode(node)) {
return;
}

const {callee} = node;

if (callee.type !== 'MemberExpression') {
Expand All @@ -40,8 +40,12 @@ const create = context => {
nodeToReport = node;
}
}
}),
'CallExpression:exit': visitIf([ava.isTestNode])(() => {
},
'CallExpression:exit'(node) {
if (!ava.isTestNode(node)) {
return;
}

// Leaving test function
if (assertionCount > maxAssertions) {
context.report({
Expand All @@ -53,7 +57,7 @@ const create = context => {

assertionCount = 0;
nodeToReport = undefined;
}),
},
});
};

Expand Down
25 changes: 13 additions & 12 deletions rules/no-async-fn-without-await.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import {visitIf} from 'enhance-visitors';
import createAvaRule from '../create-ava-rule.js';
import util from '../util.js';

const MESSAGE_ID = 'no-async-fn-without-await';
const MESSAGE_ID_SUGGESTION = 'no-async-fn-without-await-suggestion';

const create = context => {
const ava = createAvaRule();
const ava = createAvaRule(context);
let testUsed = false;
let asyncTest;
let nestedFunctionDepth = 0;
Expand All @@ -32,22 +31,24 @@ const create = context => {
const isAsync = node => Boolean(node?.async);

return ava.merge({
CallExpression: visitIf([
ava.isInTestFile,
ava.isTestNode,
])(node => {
CallExpression(node) {
if (!ava.isInTestFile() || !ava.isTestNode(node)) {
return;
}

asyncTest = (isAsync(node.arguments[0]) && node.arguments[0])
|| (isAsync(node.arguments[1]) && node.arguments[1]);
}),
},
':function': enterFunction,
':function:exit': exitFunction,
AwaitExpression: registerUseOfAwait,
YieldExpression: registerUseOfAwait,
'ForOfStatement[await=true]': registerUseOfAwait,
'CallExpression:exit': visitIf([
ava.isInTestFile,
ava.isTestNode,
])(() => {
'CallExpression:exit'(node) {
if (!ava.isInTestFile() || !ava.isTestNode(node)) {
return;
}

if (asyncTest && !testUsed) {
const {sourceCode} = context;
const asyncToken = sourceCode.getFirstToken(asyncTest, token => token.value === 'async');
Expand All @@ -68,7 +69,7 @@ const create = context => {
asyncTest = undefined;
testUsed = false;
nestedFunctionDepth = 0;
}),
},
});
};

Expand Down
2 changes: 1 addition & 1 deletion rules/no-commented-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const MESSAGE_ID = 'no-commented-tests';
const commentedTestPattern = /^\s*\*?\s*(?:test|serial)\s*(?:\.\s*\w+\s*)*\(/;

const create = context => {
const ava = createAvaRule();
const ava = createAvaRule(context);

return ava.merge({
'Program:exit'() {
Expand Down
Loading
Loading