blob: f5e5ea1ec7c49e6694178e8527604ee2cd4afd61 [file] [log] [blame]
Jack Franklin8b9aa2f2020-02-12 16:35:15 +00001'use strict';
2
3const find = require('ramda/src/find');
Tim van der Lippe16aca392020-11-13 11:37:13 +00004const createAstUtils = require('../util/ast');
Jack Franklin8b9aa2f2020-02-12 16:35:15 +00005
Tim van der Lippe16aca392020-11-13 11:37:13 +00006module.exports = {
7 meta: {
8 type: 'problem',
9 docs: {
10 description: 'Enforces handling of callbacks for async tests'
11 },
12 schema: [
13 {
14 type: 'object',
15 properties: {
16 ignoreSkipped: {
17 type: 'boolean',
18 default: false
19 }
20 },
21 additionalProperties: false
22 }
23 ]
24 },
25 create(context) {
26 const astUtils = createAstUtils(context.settings);
27 const [ { ignoreSkipped = false } = {} ] = context.options;
28 const modifiersToCheck = ignoreSkipped ? [ 'only' ] : [ 'only', 'skip' ];
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000029
Tim van der Lippe16aca392020-11-13 11:37:13 +000030 function isAsyncFunction(functionExpression) {
31 return functionExpression.params.length === 1;
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000032 }
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000033
Tim van der Lippe16aca392020-11-13 11:37:13 +000034 function findParamInScope(paramName, scope) {
35 return find(function (variable) {
36 return variable.name === paramName && variable.defs[0].type === 'Parameter';
37 }, scope.variables);
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000038 }
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000039
Tim van der Lippe16aca392020-11-13 11:37:13 +000040 function isReferenceHandled(reference) {
41 const parent = context.getNodeByRangeIndex(reference.identifier.range[0]).parent;
42
43 return parent.type === 'CallExpression';
44 }
45
46 function hasHandledReferences(references) {
47 return references.some(isReferenceHandled);
48 }
49
50 function checkAsyncMochaFunction(functionExpression) {
51 const scope = context.getScope();
52 const callback = functionExpression.params[0];
53 const callbackName = callback.name;
54 const callbackVariable = findParamInScope(callbackName, scope);
55
56 if (callbackVariable && !hasHandledReferences(callbackVariable.references)) {
57 context.report(callback, 'Expected "{{name}}" callback to be handled.', { name: callbackName });
58 }
59 }
60
61 function check(node) {
62 if (astUtils.hasParentMochaFunctionCall(node, { modifiers: modifiersToCheck }) && isAsyncFunction(node)) {
63 checkAsyncMochaFunction(node);
64 }
65 }
66
67 return {
68 FunctionExpression: check,
69 ArrowFunctionExpression: check
70 };
71 }
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000072};