blob: 77e9c71eb453e12bdd897482af386168ceb1a252 [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: {
Tim van der Lippe0ceb4652022-01-06 14:23:36 +010047 description: 'Disallow synchronous tests',
48 url: 'https://github.com/lo1tuma/eslint-plugin-mocha/blob/master/docs/rules/no-synchronous-tests.md'
Tim van der Lippe16aca392020-11-13 11:37:13 +000049 },
50 schema: [
51 {
52 type: 'object',
53 properties: {
54 allowed: {
55 type: 'array',
56 items: {
57 type: 'string',
58 enum: asyncMethods
59 },
60 minItems: 1,
61 uniqueItems: true
62 }
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000063 }
Tim van der Lippe16aca392020-11-13 11:37:13 +000064 }
65 ]
66 },
67 create(context) {
68 const astUtils = createAstUtils(context.settings);
69 const options = context.options[0] || {};
Tim van der Lippe2c891972021-07-29 16:22:50 +010070 const allowedAsyncMethods = isNil(options.allowed) ?
71 asyncMethods :
72 options.allowed;
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000073
Tim van der Lippe16aca392020-11-13 11:37:13 +000074 function check(node) {
75 if (astUtils.hasParentMochaFunctionCall(node)) {
76 // For each allowed async test method, check if it is used in the test
Tim van der Lippe2c891972021-07-29 16:22:50 +010077 const testAsyncMethods = allowedAsyncMethods.map(function (
78 method
79 ) {
Tim van der Lippe16aca392020-11-13 11:37:13 +000080 switch (method) {
81 case 'async':
82 return isAsyncFunction(node);
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000083
Tim van der Lippe16aca392020-11-13 11:37:13 +000084 case 'callback':
85 return hasAsyncCallback(node);
86
87 default:
88 return doesReturnPromise(node);
89 }
90 });
91
92 // Check that at least one allowed async test method is used in the test
Tim van der Lippe2c891972021-07-29 16:22:50 +010093 const isAsyncTest = testAsyncMethods.includes(true);
Tim van der Lippe16aca392020-11-13 11:37:13 +000094
95 if (!isAsyncTest) {
Tim van der Lippe0ceb4652022-01-06 14:23:36 +010096 context.report({ node, message: 'Unexpected synchronous test.' });
Tim van der Lippe16aca392020-11-13 11:37:13 +000097 }
Jack Franklin8b9aa2f2020-02-12 16:35:15 +000098 }
99 }
Jack Franklin8b9aa2f2020-02-12 16:35:15 +0000100
Tim van der Lippe16aca392020-11-13 11:37:13 +0000101 return {
102 FunctionExpression: check,
103 ArrowFunctionExpression: check
104 };
105 }
Jack Franklin8b9aa2f2020-02-12 16:35:15 +0000106};