1
+ import { ESLintUtils , TSESTree } from '@typescript-eslint/experimental-utils' ;
2
+ import {
3
+ isIdentifier ,
4
+ isCallExpression ,
5
+ isMemberExpression ,
6
+ isArrowFunctionExpression ,
7
+ } from '../node-utils' ;
8
+ import { getDocsUrl , SYNC_QUERIES_COMBINATIONS } from '../utils' ;
9
+
10
+ export const RULE_NAME = 'prefer-find-by' ;
11
+
12
+ type Options = [ ] ;
13
+ export type MessageIds = 'preferFindBy' ;
14
+ // TODO check if this should be under utils.ts - there are some async utils
15
+ export const WAIT_METHODS = [ 'waitFor' , 'waitForElement' , 'wait' ]
16
+
17
+ export default ESLintUtils . RuleCreator ( getDocsUrl ) < Options , MessageIds > ( {
18
+ name : RULE_NAME ,
19
+ meta : {
20
+ type : 'suggestion' ,
21
+ docs : {
22
+ description : 'Suggest using find* instead of waitFor to wait for elements' ,
23
+ category : 'Best Practices' ,
24
+ recommended : false ,
25
+ } ,
26
+ messages : {
27
+ preferFindBy : 'Prefer {{queryVariant}}{{queryMethod}} method over using await {{fullQuery}}'
28
+ } ,
29
+ fixable : null ,
30
+ schema : [ ]
31
+ } ,
32
+ defaultOptions : [ ] ,
33
+
34
+ create ( context ) {
35
+
36
+ function reportInvalidUsage ( node : TSESTree . CallExpression , { queryVariant, queryMethod, fullQuery } : { queryVariant : string , queryMethod : string , fullQuery : string } ) {
37
+ context . report ( {
38
+ node,
39
+ messageId : "preferFindBy" ,
40
+ data : { queryVariant, queryMethod, fullQuery } ,
41
+ } ) ;
42
+ }
43
+
44
+ const sourceCode = context . getSourceCode ( ) ;
45
+
46
+ return {
47
+ 'AwaitExpression > CallExpression' ( node : TSESTree . CallExpression ) {
48
+ if ( ! isIdentifier ( node . callee ) || ! WAIT_METHODS . includes ( node . callee . name ) ) {
49
+ return
50
+ }
51
+ // ensure the only argument is an arrow function expression - if the arrow function is a block
52
+ // we skip it
53
+ const argument = node . arguments [ 0 ]
54
+ if ( ! isArrowFunctionExpression ( argument ) ) {
55
+ return
56
+ }
57
+ if ( ! isCallExpression ( argument . body ) ) {
58
+ return
59
+ }
60
+ // ensure here it's one of the sync methods that we are calling
61
+ if ( isMemberExpression ( argument . body . callee ) && isIdentifier ( argument . body . callee . property ) && isIdentifier ( argument . body . callee . object ) && SYNC_QUERIES_COMBINATIONS . includes ( argument . body . callee . property . name ) ) {
62
+ // shape of () => screen.getByText
63
+ const queryMethod = argument . body . callee . property . name
64
+ reportInvalidUsage ( node , {
65
+ queryMethod : queryMethod . split ( 'By' ) [ 1 ] ,
66
+ queryVariant : getFindByQueryVariant ( queryMethod ) ,
67
+ fullQuery : sourceCode . getText ( node )
68
+ } )
69
+ return
70
+ }
71
+ if ( isIdentifier ( argument . body . callee ) && SYNC_QUERIES_COMBINATIONS . includes ( argument . body . callee . name ) ) {
72
+ // shape of () => getByText
73
+ const queryMethod = argument . body . callee . name
74
+ reportInvalidUsage ( node , {
75
+ queryMethod : queryMethod . split ( 'By' ) [ 1 ] ,
76
+ queryVariant : getFindByQueryVariant ( queryMethod ) ,
77
+ fullQuery : sourceCode . getText ( node )
78
+ } )
79
+ return
80
+ }
81
+ }
82
+ }
83
+ }
84
+ } )
85
+
86
+ function getFindByQueryVariant ( queryMethod : string ) {
87
+ return queryMethod . includes ( 'All' ) ? 'findAllBy' : 'findBy'
88
+ }
0 commit comments