@@ -22,6 +22,41 @@ export const meta = {
2222} ;
2323
2424export const create = ( context ) => ( {
25+ //expect(el.classList.contains("foo")).toBe(true)
26+ [ `CallExpression[callee.object.callee.name=expect][callee.object.arguments.0.callee.object.property.name=classList][callee.object.arguments.0.callee.property.name=contains][callee.property.name=/toBe(Truthy|Falsy)?|to(Strict)?Equal/]` ] (
27+ node
28+ ) {
29+ const classValue = node . callee . object . arguments [ 0 ] . arguments [ 0 ] ;
30+ const checkedProp = node . callee . object . arguments [ 0 ] . callee . object . object ;
31+ const matcher = node . callee . property ;
32+ const [ matcherArg ] = node . arguments ;
33+ const [ expectArg ] = node . callee . object . arguments ;
34+ const isTruthy =
35+ ( matcher . name === "toBe" && matcherArg . value === true ) ||
36+ matcher . name === "toBeTruthy" ;
37+
38+ context . report ( {
39+ node : matcher ,
40+ messageId,
41+ fix ( fixer ) {
42+ return [
43+ fixer . removeRange ( [ checkedProp . range [ 1 ] , expectArg . range [ 1 ] ] ) ,
44+
45+ fixer . replaceText ( matcher , `${ isTruthy ? "" : "not." } toHaveClass` ) ,
46+ matcherArg
47+ ? fixer . replaceText (
48+ matcherArg ,
49+ context . getSourceCode ( ) . getText ( classValue )
50+ )
51+ : fixer . insertTextBeforeRange (
52+ [ node . range [ 1 ] - 1 , node . range [ 1 ] - 1 ] ,
53+ context . getSourceCode ( ) . getText ( classValue )
54+ ) ,
55+ ] ;
56+ } ,
57+ } ) ;
58+ } ,
59+
2560 //expect(el.classList[0]).toBe("bar")
2661 [ `CallExpression[callee.object.callee.name=expect][callee.object.arguments.0.object.property.name=classList][callee.property.name=/toBe$|to(Strict)?Equal|toContain/][arguments.0.type=/Literal$/]` ] (
2762 node
@@ -62,15 +97,23 @@ export const create = (context) => ({
6297 messageId,
6398 } ) ;
6499 } ,
65- //expect(el.className).toBe("bar") / toStrict?Equal / toContain
66- [ `CallExpression[callee.object.callee.name=expect][callee.object.arguments.0.property.name=/class(Name|List)/][callee.property.name=/toBe$|to(Strict)?Equal|toContain/][arguments.0.type=/Literal$/] ` ] (
100+ //expect(el.className | el.classList ).toBe("bar") / toStrict?Equal / toContain
101+ [ `CallExpression[callee.object.callee.name=expect][callee.object.arguments.0.property.name=/class(Name|List)/][callee.property.name=/toBe$|to(Strict)?Equal|toContain/]` ] (
67102 node
68103 ) {
69104 const checkedProp = node . callee . object . arguments [ 0 ] . property ;
70105 const [ classValue ] = node . arguments ;
71106 const matcher = node . callee . property ;
72107 const classNameProp = node . callee . object . arguments [ 0 ] . object ;
73108
109+ // don't report here if using `expect.foo()`
110+
111+ if (
112+ classValue . type === "CallExpression" &&
113+ classValue . callee . type === "MemberExpression" &&
114+ classValue . callee . object . name === "expect"
115+ )
116+ return ;
74117 context . report ( {
75118 node : matcher ,
76119 messageId,
@@ -91,19 +134,21 @@ export const create = (context) => ({
91134 } ) ;
92135 } ,
93136
94- //expect(el.className).toEqual(expect.stringContaining("foo")) / toStrictEqual
95- [ `CallExpression[callee.object.callee.name=expect][callee.object.arguments.0.property.name=className ][callee.property.name=/to(Strict)?Equal/][arguments.0.callee.object.name=expect][arguments.0.callee.property.name=stringContaining ]` ] (
137+ //expect(el.className | el.classList ).toEqual(expect.stringContaining("foo") | objectContaining ) / toStrictEqual
138+ [ `CallExpression[callee.object.callee.name=expect][callee.object.arguments.0.property.name=/class(Name|List)/ ][callee.property.name=/to(Strict)?Equal/][arguments.0.callee.object.name=expect]` ] (
96139 node
97140 ) {
98141 const className = node . callee . object . arguments [ 0 ] . property ;
99142 const [ classValue ] = node . arguments [ 0 ] . arguments ;
100143 const matcher = node . callee . property ;
101144 const classNameProp = node . callee . object . arguments [ 0 ] . object ;
145+ const matcherArg = node . arguments [ 0 ] . callee . property ;
102146
103147 context . report ( {
104148 node : matcher ,
105149 messageId,
106150 fix ( fixer ) {
151+ if ( matcherArg . name !== "stringContaining" ) return ;
107152 return [
108153 fixer . removeRange ( [ classNameProp . range [ 1 ] , className . range [ 1 ] ] ) ,
109154 fixer . replaceText ( matcher , "toHaveClass" ) ,
@@ -116,11 +161,10 @@ export const create = (context) => ({
116161 } ) ;
117162 } ,
118163
119- //expect(screen.getByRole("button").className).not.toBe("foo"); / toStrict?Equal / toContain
120- [ `CallExpression[callee.object.object.callee.name=expect][callee.object.object.arguments.0.property.name=className ][callee.object.property.name=not][callee.property.name=/toBe$|to(Strict)?Equal|toContain/][arguments.0.type=/Literal$ /]` ] (
164+ //expect(screen.getByRole("button").className | classList ).not.toBe("foo"); / toStrict?Equal / toContain
165+ [ `CallExpression[callee.object.object.callee.name=expect][callee.object.object.arguments.0.property.name=/class(Name|List)/ ][callee.object.property.name=not][callee.property.name=/toBe$|to(Strict)?Equal|toContain/]` ] (
121166 node
122167 ) {
123- //[callee.object.arguments.0.property.name=className][callee.property.name=/toBe$|to(Strict)?Equal|toContain/][arguments.0.type=/Literal$/]
124168 const className = node . callee . object . object . arguments [ 0 ] . property ;
125169 const [ classValue ] = node . arguments ;
126170 const matcher = node . callee . property ;
@@ -130,6 +174,9 @@ export const create = (context) => ({
130174 node : matcher ,
131175 messageId,
132176 fix ( fixer ) {
177+ if ( className . name === "classList" && matcher . name !== "toContain" )
178+ return ;
179+
133180 return [
134181 fixer . removeRange ( [ classNameProp . range [ 1 ] , className . range [ 1 ] ] ) ,
135182 fixer . replaceText ( matcher , "toHaveClass" ) ,
0 commit comments