/
no-done-callback.ts
126 lines (106 loc) · 3.84 KB
/
no-done-callback.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import { AST_NODE_TYPES } from '@typescript-eslint/experimental-utils';
import { createRule, isFunction, isTestCase } from './utils';
export default createRule({
name: __filename,
meta: {
docs: {
category: 'Best Practices',
description: 'Avoid using a callback in asynchronous tests',
recommended: 'error',
suggestion: true,
},
messages: {
illegalTestCallback: 'Illegal usage of test callback',
suggestWrappingInPromise: 'Wrap in `new Promise({{ callback }} => ...`',
useAwaitInsteadOfCallback:
'Use await instead of callback in async functions',
},
schema: [],
type: 'suggestion',
},
defaultOptions: [],
create(context) {
return {
CallExpression(node) {
if (!isTestCase(node) || node.arguments.length !== 2) {
return;
}
const [, callback] = node.arguments;
if (!isFunction(callback) || callback.params.length !== 1) {
return;
}
const [argument] = callback.params;
if (argument.type !== AST_NODE_TYPES.Identifier) {
context.report({
node: argument,
messageId: 'illegalTestCallback',
});
return;
}
if (callback.async) {
context.report({
node: argument,
messageId: 'useAwaitInsteadOfCallback',
});
return;
}
context.report({
node: argument,
messageId: 'illegalTestCallback',
suggest: [
{
messageId: 'suggestWrappingInPromise',
data: { callback: argument.name },
fix(fixer) {
const { body } = callback;
const sourceCode = context.getSourceCode();
const firstBodyToken = sourceCode.getFirstToken(body);
const lastBodyToken = sourceCode.getLastToken(body);
const tokenBeforeArgument = sourceCode.getTokenBefore(argument);
const tokenAfterArgument = sourceCode.getTokenAfter(argument);
/* istanbul ignore if */
if (
!firstBodyToken ||
!lastBodyToken ||
!tokenBeforeArgument ||
!tokenAfterArgument
) {
throw new Error(
`Unexpected null when attempting to fix ${context.getFilename()} - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`,
);
}
const argumentInParens =
tokenBeforeArgument.value === '(' &&
tokenAfterArgument.value === ')';
let argumentFix = fixer.replaceText(argument, '()');
if (argumentInParens) {
argumentFix = fixer.remove(argument);
}
let newCallback = argument.name;
if (argumentInParens) {
newCallback = `(${newCallback})`;
}
let beforeReplacement = `new Promise(${newCallback} => `;
let afterReplacement = ')';
let replaceBefore = true;
if (body.type === AST_NODE_TYPES.BlockStatement) {
const keyword = 'return';
beforeReplacement = `${keyword} ${beforeReplacement}{`;
afterReplacement += '}';
replaceBefore = false;
}
return [
argumentFix,
replaceBefore
? fixer.insertTextBefore(firstBodyToken, beforeReplacement)
: fixer.insertTextAfter(firstBodyToken, beforeReplacement),
fixer.insertTextAfter(lastBodyToken, afterReplacement),
];
},
},
],
});
},
};
},
});