'use strict'; /* eslint "complexity": [ "error", 5 ] */ /** * @fileoverview Disallow async functions as arguments to describe */ const createAstUtils = require('../util/ast'); module.exports = { meta: { type: 'problem', docs: { description: 'Disallow async functions passed to describe' }, fixable: 'code' }, create(context) { const astUtils = createAstUtils(context.settings); const sourceCode = context.getSourceCode(); function isFunction(node) { return ( node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunctionExpression' ); } function containsDirectAwait(node) { if (node.type === 'AwaitExpression') { return true; } else if (node.type && !isFunction(node)) { return Object.keys(node).some(function (key) { if (Array.isArray(node[key])) { return node[key].some(containsDirectAwait); } else if (key !== 'parent' && node[key] && typeof node[key] === 'object') { return containsDirectAwait(node[key]); } return false; }); } return false; } function fixAsyncFunction(fixer, fn) { if (!containsDirectAwait(fn.body)) { // Remove the "async" token and all the whitespace before "function": const [ asyncToken, functionToken ] = sourceCode.getFirstTokens(fn, 2); return fixer.removeRange([ asyncToken.range[0], functionToken.range[0] ]); } return undefined; } function isAsyncFunction(node) { return node && (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') && node.async; } return { CallExpression(node) { const name = astUtils.getNodeName(node.callee); if (astUtils.isDescribe(node)) { const fnArg = node.arguments.slice(-1)[0]; if (isAsyncFunction(fnArg)) { context.report({ node: fnArg, message: `Unexpected async function in ${name}()`, fix(fixer) { return fixAsyncFunction(fixer, fnArg); } }); } } } }; } };