11import {
22 AST_NODE_TYPES ,
3- TSESLint ,
43 TSESTree ,
54} from '@typescript-eslint/experimental-utils' ;
65import {
76 CallExpressionWithSingleArgument ,
87 KnownCallExpression ,
98 MaybeTypeCast ,
109 ModifierName ,
11- NotNegatableParsedModifier ,
1210 ParsedEqualityMatcherCall ,
1311 ParsedExpectMatcher ,
1412 createRule ,
@@ -57,103 +55,15 @@ type FixableIncludesCallExpression = KnownCallExpression<'includes'> &
5755 * @param {CallExpression } node
5856 *
5957 * @return {node is FixableIncludesCallExpression }
60- *
61- * @todo support `['includes']()` syntax (remove last property.type check to begin)
62- * @todo break out into `isMethodCall<Name extends string>(node: TSESTree.Node, method: Name)` util-fn
6358 */
6459const isFixableIncludesCallExpression = (
6560 node : TSESTree . Node ,
6661) : node is FixableIncludesCallExpression =>
6762 node . type === AST_NODE_TYPES . CallExpression &&
6863 node . callee . type === AST_NODE_TYPES . MemberExpression &&
6964 isSupportedAccessor ( node . callee . property , 'includes' ) &&
70- node . callee . property . type === AST_NODE_TYPES . Identifier &&
7165 hasOnlyOneArgument ( node ) ;
7266
73- const buildToContainFuncExpectation = ( negated : boolean ) =>
74- negated ? `${ ModifierName . not } .toContain` : 'toContain' ;
75-
76- /**
77- * Finds the first `.` character token between the `object` & `property` of the given `member` expression.
78- *
79- * @param {TSESTree.MemberExpression } member
80- * @param {SourceCode } sourceCode
81- *
82- * @return {Token | null }
83- */
84- const findPropertyDotToken = (
85- member : TSESTree . MemberExpression ,
86- sourceCode : TSESLint . SourceCode ,
87- ) =>
88- sourceCode . getFirstTokenBetween (
89- member . object ,
90- member . property ,
91- token => token . value === '.' ,
92- ) ;
93-
94- const getNegationFixes = (
95- node : FixableIncludesCallExpression ,
96- modifier : NotNegatableParsedModifier ,
97- matcher : ParsedBooleanEqualityMatcherCall ,
98- sourceCode : TSESLint . SourceCode ,
99- fixer : TSESLint . RuleFixer ,
100- fileName : string ,
101- ) => {
102- const [ containArg ] = node . arguments ;
103- const negationPropertyDot = findPropertyDotToken ( modifier . node , sourceCode ) ;
104-
105- const toContainFunc = buildToContainFuncExpectation (
106- followTypeAssertionChain ( matcher . arguments [ 0 ] ) . value ,
107- ) ;
108-
109- /* istanbul ignore if */
110- if ( negationPropertyDot === null ) {
111- throw new Error (
112- `Unexpected null when attempting to fix ${ fileName } - please file a github issue at https://github.com/jest-community/eslint-plugin-jest` ,
113- ) ;
114- }
115-
116- return [
117- fixer . remove ( negationPropertyDot ) ,
118- fixer . remove ( modifier . node . property ) ,
119- fixer . replaceText ( matcher . node . property , toContainFunc ) ,
120- fixer . replaceText ( matcher . arguments [ 0 ] , sourceCode . getText ( containArg ) ) ,
121- ] ;
122- } ;
123-
124- const getCommonFixes = (
125- node : FixableIncludesCallExpression ,
126- sourceCode : TSESLint . SourceCode ,
127- fileName : string ,
128- ) : Array < TSESTree . Node | TSESTree . Token > => {
129- const [ containArg ] = node . arguments ;
130- const includesCallee = node . callee ;
131-
132- const propertyDot = findPropertyDotToken ( includesCallee , sourceCode ) ;
133-
134- const closingParenthesis = sourceCode . getTokenAfter ( containArg ) ;
135- const openParenthesis = sourceCode . getTokenBefore ( containArg ) ;
136-
137- /* istanbul ignore if */
138- if (
139- propertyDot === null ||
140- closingParenthesis === null ||
141- openParenthesis === null
142- ) {
143- throw new Error (
144- `Unexpected null when attempting to fix ${ fileName } - please file a github issue at https://github.com/jest-community/eslint-plugin-jest` ,
145- ) ;
146- }
147-
148- return [
149- containArg ,
150- includesCallee . property ,
151- propertyDot ,
152- closingParenthesis ,
153- openParenthesis ,
154- ] ;
155- } ;
156-
15767// expect(array.includes(<value>)[not.]{toBe,toEqual}(<boolean>)
15868export default createRule ( {
15969 name : __filename ,
@@ -181,6 +91,7 @@ export default createRule({
18191 const {
18292 expect : {
18393 arguments : [ includesCall ] ,
94+ range : [ , expectCallEnd ] ,
18495 } ,
18596 matcher,
18697 modifier,
@@ -199,42 +110,32 @@ export default createRule({
199110 context . report ( {
200111 fix ( fixer ) {
201112 const sourceCode = context . getSourceCode ( ) ;
202- const fileName = context . getFilename ( ) ;
203-
204- const fixArr = getCommonFixes (
205- includesCall ,
206- sourceCode ,
207- fileName ,
208- ) . map ( target => fixer . remove ( target ) ) ;
209113
210- if ( modifier ) {
211- return getNegationFixes (
212- includesCall ,
213- modifier ,
214- matcher ,
215- sourceCode ,
216- fixer ,
217- fileName ,
218- ) . concat ( fixArr ) ;
219- }
220-
221- const toContainFunc = buildToContainFuncExpectation (
222- ! followTypeAssertionChain ( matcher . arguments [ 0 ] ) . value ,
223- ) ;
224-
225- const [ containArg ] = includesCall . arguments ;
226-
227- fixArr . push (
228- fixer . replaceText ( matcher . node . property , toContainFunc ) ,
229- ) ;
230- fixArr . push (
114+ // we need to negate the expectation if the current expected
115+ // value is itself negated by the "not" modifier
116+ const addNotModifier =
117+ followTypeAssertionChain ( matcher . arguments [ 0 ] ) . value ===
118+ ! ! modifier ;
119+
120+ return [
121+ // remove the "includes" call entirely
122+ fixer . removeRange ( [
123+ includesCall . callee . property . range [ 0 ] - 1 ,
124+ includesCall . range [ 1 ] ,
125+ ] ) ,
126+ // replace the current matcher with "toContain", adding "not" if needed
127+ fixer . replaceTextRange (
128+ [ expectCallEnd , matcher . node . range [ 1 ] ] ,
129+ addNotModifier
130+ ? `.${ ModifierName . not } .toContain`
131+ : '.toContain' ,
132+ ) ,
133+ // replace the matcher argument with the value from the "includes"
231134 fixer . replaceText (
232135 matcher . arguments [ 0 ] ,
233- sourceCode . getText ( containArg ) ,
136+ sourceCode . getText ( includesCall . arguments [ 0 ] ) ,
234137 ) ,
235- ) ;
236-
237- return fixArr ;
138+ ] ;
238139 } ,
239140 messageId : 'useToContain' ,
240141 node : ( modifier || matcher ) . node . property ,
0 commit comments