blob: 671e3d24e2093c302d05fbcc06d7ab0ae46bccb0 [file] [log] [blame]
Jack Franklin8b9aa2f2020-02-12 16:35:15 +00001'use strict';
2
3const isNil = require('ramda/src/isNil');
4const find = require('ramda/src/find');
Tim van der Lippe16aca392020-11-13 11:37:13 +00005const createAstUtils = require('../util/ast');
Jack Franklin8b9aa2f2020-02-12 16:35:15 +00006
7const asyncMethods = [ 'async', 'callback', 'promise' ];
8
9function hasAsyncCallback(functionExpression) {
10 return functionExpression.params.length === 1;
11}
12
13function isAsyncFunction(functionExpression) {
14 return functionExpression.async === true;
15}
16
17function findPromiseReturnStatement(nodes) {
18 return find(function (node) {
Tim van der Lippe2c891972021-07-29 16:22:50 +010019 return (
20 node.type === 'ReturnStatement' &&
21 node.argument &&
22 node.argument.type !== 'Literal'
23 );
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000024 }, nodes);
25}
26
27function doesReturnPromise(functionExpression) {
28 const bodyStatement = functionExpression.body;
29 let returnStatement = null;
30
31 if (bodyStatement.type === 'BlockStatement') {
Tim van der Lippe2c891972021-07-29 16:22:50 +010032 returnStatement = findPromiseReturnStatement(
33 functionExpression.body.body
34 );
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000035 } else if (bodyStatement.type !== 'Literal') {
36 // allow arrow statements calling a promise with implicit return.
37 returnStatement = bodyStatement;
38 }
39
Tim van der Lippe2c891972021-07-29 16:22:50 +010040 return returnStatement !== null && typeof returnStatement !== 'undefined';
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000041}
42
Tim van der Lippe16aca392020-11-13 11:37:13 +000043module.exports = {
44 meta: {
45 type: 'suggestion',
46 docs: {
47 description: 'Disallow synchronous tests'
48 },
49 schema: [
50 {
51 type: 'object',
52 properties: {
53 allowed: {
54 type: 'array',
55 items: {
56 type: 'string',
57 enum: asyncMethods
58 },
59 minItems: 1,
60 uniqueItems: true
61 }
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000062 }
Tim van der Lippe16aca392020-11-13 11:37:13 +000063 }
64 ]
65 },
66 create(context) {
67 const astUtils = createAstUtils(context.settings);
68 const options = context.options[0] || {};
Tim van der Lippe2c891972021-07-29 16:22:50 +010069 const allowedAsyncMethods = isNil(options.allowed) ?
70 asyncMethods :
71 options.allowed;
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000072
Tim van der Lippe16aca392020-11-13 11:37:13 +000073 function check(node) {
74 if (astUtils.hasParentMochaFunctionCall(node)) {
75 // For each allowed async test method, check if it is used in the test
Tim van der Lippe2c891972021-07-29 16:22:50 +010076 const testAsyncMethods = allowedAsyncMethods.map(function (
77 method
78 ) {
Tim van der Lippe16aca392020-11-13 11:37:13 +000079 switch (method) {
80 case 'async':
81 return isAsyncFunction(node);
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000082
Tim van der Lippe16aca392020-11-13 11:37:13 +000083 case 'callback':
84 return hasAsyncCallback(node);
85
86 default:
87 return doesReturnPromise(node);
88 }
89 });
90
91 // Check that at least one allowed async test method is used in the test
Tim van der Lippe2c891972021-07-29 16:22:50 +010092 const isAsyncTest = testAsyncMethods.includes(true);
Tim van der Lippe16aca392020-11-13 11:37:13 +000093
94 if (!isAsyncTest) {
95 context.report(node, 'Unexpected synchronous test.');
96 }
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000097 }
98 }
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000099
Tim van der Lippe16aca392020-11-13 11:37:13 +0000100 return {
101 FunctionExpression: check,
102 ArrowFunctionExpression: check
103 };
104 }
Jack Franklin8b9aa2f2020-02-12 16:35:15 +0000105};