Skip to content

Commit ea62638

Browse files
authored
refactor(no-wait-for-empty-callback): migrate to v4 (#284)
* refactor(no-wait-for-empty-callback): use new rule creator and helpers * test(no-wait-for-empty-callback): improve invalid asserts * test(no-wait-for-empty-callback): increase rule coverage * refactor: improve valid names definition (PR suggestions)
1 parent dd30d02 commit ea62638

File tree

4 files changed

+136
-20
lines changed

4 files changed

+136
-20
lines changed

lib/detect-testing-library-utils.ts

+13-4
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ type IsSyncQueryFn = (node: TSESTree.Identifier) => boolean;
6363
type IsAsyncQueryFn = (node: TSESTree.Identifier) => boolean;
6464
type IsQueryFn = (node: TSESTree.Identifier) => boolean;
6565
type IsCustomQueryFn = (node: TSESTree.Identifier) => boolean;
66-
type IsAsyncUtilFn = (node: TSESTree.Identifier) => boolean;
66+
type IsAsyncUtilFn = (
67+
node: TSESTree.Identifier,
68+
validNames?: readonly typeof ASYNC_UTILS[number][]
69+
) => boolean;
6770
type IsFireEventMethodFn = (node: TSESTree.Identifier) => boolean;
6871
type IsRenderUtilFn = (node: TSESTree.Identifier) => boolean;
6972
type IsPresenceAssertFn = (node: TSESTree.MemberExpression) => boolean;
@@ -298,9 +301,15 @@ export function detectTestingLibraryUtils<
298301
* Otherwise, it means `custom-module` has been set up, so only those nodes
299302
* coming from Testing Library will be considered as valid.
300303
*/
301-
const isAsyncUtil: IsAsyncUtilFn = (node) => {
302-
return isTestingLibraryUtil(node, (identifierNodeName) =>
303-
ASYNC_UTILS.includes(identifierNodeName)
304+
const isAsyncUtil: IsAsyncUtilFn = (node, validNames = ASYNC_UTILS) => {
305+
return isTestingLibraryUtil(
306+
node,
307+
(identifierNodeName, originalNodeName) => {
308+
return (
309+
(validNames as string[]).includes(identifierNodeName) ||
310+
(validNames as string[]).includes(originalNodeName)
311+
);
312+
}
304313
);
305314
};
306315

lib/rules/no-wait-for-empty-callback.ts

+35-15
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
1+
import { ASTUtils, TSESTree } from '@typescript-eslint/experimental-utils';
12
import {
2-
ESLintUtils,
3-
TSESTree,
4-
ASTUtils,
5-
} from '@typescript-eslint/experimental-utils';
6-
import { getDocsUrl } from '../utils';
7-
import { isBlockStatement, isCallExpression } from '../node-utils';
3+
getPropertyIdentifierNode,
4+
isBlockStatement,
5+
isCallExpression,
6+
} from '../node-utils';
7+
import { createTestingLibraryRule } from '../create-testing-library-rule';
88

99
export const RULE_NAME = 'no-wait-for-empty-callback';
1010
export type MessageIds = 'noWaitForEmptyCallback';
1111
type Options = [];
1212

13-
const WAIT_EXPRESSION_QUERY =
14-
'CallExpression[callee.name=/^(waitFor|waitForElementToBeRemoved)$/]';
15-
16-
export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
13+
export default createTestingLibraryRule<Options, MessageIds>({
1714
name: RULE_NAME,
1815
meta: {
1916
type: 'suggestion',
@@ -33,11 +30,24 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
3330
defaultOptions: [],
3431

3532
// trimmed down implementation of https://github.com/eslint/eslint/blob/master/lib/rules/no-empty-function.js
36-
// TODO: var referencing any of previously mentioned?
37-
create: function (context) {
33+
create(context, _, helpers) {
34+
function isValidWaitFor(node: TSESTree.Node): boolean {
35+
const parentCallExpression = node.parent as TSESTree.CallExpression;
36+
const parentIdentifier = getPropertyIdentifierNode(parentCallExpression);
37+
38+
return helpers.isAsyncUtil(parentIdentifier, [
39+
'waitFor',
40+
'waitForElementToBeRemoved',
41+
]);
42+
}
43+
3844
function reportIfEmpty(
3945
node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression
4046
) {
47+
if (!isValidWaitFor(node)) {
48+
return;
49+
}
50+
4151
if (
4252
isBlockStatement(node.body) &&
4353
node.body.body.length === 0 &&
@@ -56,17 +66,27 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
5666
}
5767

5868
function reportNoop(node: TSESTree.Identifier) {
69+
if (!isValidWaitFor(node)) {
70+
return;
71+
}
72+
5973
context.report({
6074
node,
6175
loc: node.loc.start,
6276
messageId: 'noWaitForEmptyCallback',
77+
data: {
78+
methodName:
79+
isCallExpression(node.parent) &&
80+
ASTUtils.isIdentifier(node.parent.callee) &&
81+
node.parent.callee.name,
82+
},
6383
});
6484
}
6585

6686
return {
67-
[`${WAIT_EXPRESSION_QUERY} > ArrowFunctionExpression`]: reportIfEmpty,
68-
[`${WAIT_EXPRESSION_QUERY} > FunctionExpression`]: reportIfEmpty,
69-
[`${WAIT_EXPRESSION_QUERY} > Identifier[name="noop"]`]: reportNoop,
87+
'CallExpression > ArrowFunctionExpression': reportIfEmpty,
88+
'CallExpression > FunctionExpression': reportIfEmpty,
89+
'CallExpression > Identifier[name="noop"]': reportNoop,
7090
};
7191
},
7292
});

lib/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ const ASYNC_UTILS = [
7070
'wait',
7171
'waitForElement',
7272
'waitForDomChange',
73-
];
73+
] as const;
7474

7575
const SYNC_EVENTS = ['fireEvent', 'userEvent'];
7676

tests/lib/rules/no-wait-for-empty-callback.test.ts

+87
Original file line numberDiff line numberDiff line change
@@ -29,30 +29,97 @@ ruleTester.run(RULE_NAME, rule, {
2929
{
3030
code: `wait(() => {})`,
3131
},
32+
{
33+
code: `wait(noop)`,
34+
},
35+
{
36+
settings: { 'testing-library/utils-module': 'test-utils' },
37+
code: `
38+
import { waitFor } from 'somewhere-else'
39+
waitFor(() => {})
40+
`,
41+
},
42+
{
43+
settings: { 'testing-library/utils-module': 'test-utils' },
44+
code: `
45+
import { waitFor as renamedWaitFor } from '@testing-library/react'
46+
import { waitFor } from 'somewhere-else'
47+
waitFor(() => {})
48+
`,
49+
},
3250
],
3351

3452
invalid: [
3553
...ALL_WAIT_METHODS.map((m) => ({
3654
code: `${m}(() => {})`,
3755
errors: [
3856
{
57+
line: 1,
58+
column: 8 + m.length,
59+
messageId: 'noWaitForEmptyCallback',
60+
data: {
61+
methodName: m,
62+
},
63+
},
64+
],
65+
})),
66+
...ALL_WAIT_METHODS.map((m) => ({
67+
settings: { 'testing-library/utils-module': 'test-utils' },
68+
code: `
69+
import { ${m} } from 'test-utils';
70+
${m}(() => {});
71+
`,
72+
errors: [
73+
{
74+
line: 3,
75+
column: 16 + m.length,
76+
messageId: 'noWaitForEmptyCallback',
77+
data: {
78+
methodName: m,
79+
},
80+
},
81+
],
82+
})),
83+
...ALL_WAIT_METHODS.map((m) => ({
84+
settings: { 'testing-library/utils-module': 'test-utils' },
85+
code: `
86+
import { ${m} as renamedAsyncUtil } from 'test-utils';
87+
renamedAsyncUtil(() => {});
88+
`,
89+
errors: [
90+
{
91+
line: 3,
92+
column: 32,
3993
messageId: 'noWaitForEmptyCallback',
94+
data: {
95+
methodName: 'renamedAsyncUtil',
96+
},
4097
},
4198
],
4299
})),
43100
...ALL_WAIT_METHODS.map((m) => ({
44101
code: `${m}((a, b) => {})`,
45102
errors: [
46103
{
104+
line: 1,
105+
column: 12 + m.length,
47106
messageId: 'noWaitForEmptyCallback',
107+
data: {
108+
methodName: m,
109+
},
48110
},
49111
],
50112
})),
51113
...ALL_WAIT_METHODS.map((m) => ({
52114
code: `${m}(() => { /* I'm empty anyway */ })`,
53115
errors: [
54116
{
117+
line: 1,
118+
column: 8 + m.length,
55119
messageId: 'noWaitForEmptyCallback',
120+
data: {
121+
methodName: m,
122+
},
56123
},
57124
],
58125
})),
@@ -63,7 +130,12 @@ ruleTester.run(RULE_NAME, rule, {
63130
})`,
64131
errors: [
65132
{
133+
line: 1,
134+
column: 13 + m.length,
66135
messageId: 'noWaitForEmptyCallback',
136+
data: {
137+
methodName: m,
138+
},
67139
},
68140
],
69141
})),
@@ -73,7 +145,12 @@ ruleTester.run(RULE_NAME, rule, {
73145
})`,
74146
errors: [
75147
{
148+
line: 1,
149+
column: 14 + m.length,
76150
messageId: 'noWaitForEmptyCallback',
151+
data: {
152+
methodName: m,
153+
},
77154
},
78155
],
79156
})),
@@ -83,7 +160,12 @@ ruleTester.run(RULE_NAME, rule, {
83160
})`,
84161
errors: [
85162
{
163+
line: 1,
164+
column: 13 + m.length,
86165
messageId: 'noWaitForEmptyCallback',
166+
data: {
167+
methodName: m,
168+
},
87169
},
88170
],
89171
})),
@@ -92,7 +174,12 @@ ruleTester.run(RULE_NAME, rule, {
92174
code: `${m}(noop)`,
93175
errors: [
94176
{
177+
line: 1,
178+
column: 2 + m.length,
95179
messageId: 'noWaitForEmptyCallback',
180+
data: {
181+
methodName: m,
182+
},
96183
},
97184
],
98185
})),

0 commit comments

Comments
 (0)