eslint-plugin-mocha
Version:
Eslint rules for mocha.
86 lines • 3.52 kB
JavaScript
import { createMochaVisitors } from '../ast/mocha-visitors.js';
import { isBlockStatement, isFunction } from '../ast/node-types.js';
import { isRecord } from '../record.js';
const asyncMethods = ['async', 'callback', 'promise'];
function hasAsyncCallback(functionExpression) {
return isFunction(functionExpression) && functionExpression.params.length === 1;
}
function isAsyncFunction(functionExpression) {
return isFunction(functionExpression) && functionExpression.async === true;
}
function findPromiseReturnStatement(nodes) {
return nodes.find((node) => {
return (node.type === 'ReturnStatement' &&
node.argument !== null &&
node.argument?.type !== 'Literal');
});
}
function doesReturnPromise(functionExpression) {
if (!isFunction(functionExpression)) {
return false;
}
const bodyStatement = functionExpression.body;
let returnStatement = null;
if (isBlockStatement(bodyStatement)) {
returnStatement = findPromiseReturnStatement(bodyStatement.body);
}
else if (bodyStatement.type !== 'Literal') {
// allow arrow statements calling a promise with implicit return.
returnStatement = bodyStatement;
}
return returnStatement !== null && returnStatement !== undefined;
}
export const noSynchronousTestsRule = {
meta: {
type: 'suggestion',
docs: {
description: 'Disallow synchronous tests',
url: 'https://github.com/lo1tuma/eslint-plugin-mocha/blob/main/docs/rules/no-synchronous-tests.md'
},
schema: [
{
type: 'object',
properties: {
allowed: {
type: 'array',
items: {
type: 'string',
enum: asyncMethods
},
minItems: 1,
uniqueItems: true
}
}
}
]
},
create(context) {
const [firstOption] = context.options;
const options = isRecord(firstOption) ? firstOption : {};
const allowedAsyncMethods = options.allowed === undefined
? asyncMethods
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- we have json schema validation in place so we know this is a string
: options.allowed;
return createMochaVisitors(context, {
anyTestEntityCallback(visitorContext) {
// For each allowed async test method, check if it is used in the test
const testAsyncMethods = allowedAsyncMethods.map((method) => {
switch (method) {
case 'async':
return isAsyncFunction(visitorContext.node);
case 'callback':
return hasAsyncCallback(visitorContext.node);
default:
return doesReturnPromise(visitorContext.node);
}
});
// Check that at least one allowed async test method is used in the test
const isAsyncTest = testAsyncMethods.includes(true);
if (!isAsyncTest) {
context.report({ node: visitorContext.node, message: 'Unexpected synchronous test.' });
}
}
});
}
};
//# sourceMappingURL=no-synchronous-tests.js.map