1- import { $ } from '@wdio/globals'
1+ import { $ , $$ } from '@wdio/globals'
22import { beforeEach , describe , expect , test , vi } from 'vitest'
33import { toHaveElementClass } from '../../../src/matchers/element/toHaveElementClass.js'
44import type { AssertionResult } from 'expect-webdriverio'
@@ -8,12 +8,11 @@ vi.mock('@wdio/globals')
88describe ( toHaveElementClass , ( ) => {
99
1010 let thisContext : { toHaveElementClass : typeof toHaveElementClass }
11- // TODO have some isNot tests
12- // let thisNotContext: { isNot: true; toHaveElementClass: typeof toHaveElementClass }
11+ let thisNotContext : { isNot : true ; toHaveElementClass : typeof toHaveElementClass }
1312
1413 beforeEach ( ( ) => {
1514 thisContext = { toHaveElementClass }
16- // thisNotContext = { isNot: true, toHaveElementClass }
15+ thisNotContext = { isNot : true , toHaveElementClass }
1716 } )
1817
1918 describe ( 'given a single element' , ( ) => {
@@ -57,6 +56,24 @@ describe(toHaveElementClass, () => {
5756 expect ( result2 . pass ) . toBe ( true )
5857 } )
5958
59+ test ( 'success with multiple asymmetric matcher' , async ( ) => {
60+ const result = await thisContext . toHaveElementClass ( el , [ expect . stringContaining ( 'some-class' ) , expect . stringContaining ( 'another-class' ) ] )
61+
62+ expect ( result . pass ) . toBe ( true )
63+ } )
64+
65+ test ( 'failure with multiple asymmetric matcher' , async ( ) => {
66+ const result = await thisContext . toHaveElementClass ( el , [ expect . stringContaining ( 'notsome-class' ) , expect . stringContaining ( 'notanother-class' ) ] )
67+
68+ expect ( result . pass ) . toBe ( false )
69+ expect ( result . message ( ) ) . toEqual ( `\
70+ Expect $(\`sel\`) to have class
71+
72+ Expected: [StringContaining "notsome-class", StringContaining "notanother-class"]
73+ Received: "some-class another-class yet-another-class"`
74+ )
75+ } )
76+
6077 test ( 'success with RegExp when class name is present' , async ( ) => {
6178 const result = await thisContext . toHaveElementClass ( el , / s O m E - c L a S s / i)
6279
@@ -87,6 +104,18 @@ Received: "some-class another-class yet-another-class"`)
87104 expect ( result . pass ) . toBe ( false )
88105 } )
89106
107+ test ( 'not - success - pass should be false' , async ( ) => {
108+ const result = await thisNotContext . toHaveElementClass ( el , [ 'not-class' , 'not-another-class' ] )
109+
110+ expect ( result . pass ) . toBe ( false ) // success, boolean is inverted later
111+ } )
112+
113+ test ( 'not - failure - pass should be true' , async ( ) => {
114+ const result = await thisNotContext . toHaveElementClass ( el , [ 'some-class' , 'not-another-class' ] )
115+
116+ expect ( result . pass ) . toBe ( true ) // failure, boolean is inverted later
117+ } )
118+
90119 describe ( 'options' , ( ) => {
91120 test ( 'should fail when class is not a string' , async ( ) => {
92121 vi . mocked ( el . getAttribute ) . mockImplementation ( async ( ) => {
@@ -154,4 +183,226 @@ Received: "some-class another-class yet-another-class"` )
154183 } )
155184 } )
156185 } )
186+
187+ describe ( 'given multiple elements' , ( ) => {
188+ let elements : ChainablePromiseArray
189+
190+ const selectorName = '$$(`sel`)'
191+ beforeEach ( async ( ) => {
192+ elements = await $$ ( 'sel' )
193+
194+ expect ( elements ) . toHaveLength ( 2 )
195+ elements . forEach ( ( el ) => {
196+ vi . mocked ( el . getAttribute ) . mockImplementation ( async ( attribute : string ) => {
197+ if ( attribute === 'class' ) {
198+ return 'some-class another-class yet-another-class'
199+ }
200+ return null as unknown as string /* casting required since wdio as bug typing see https://github.com/webdriverio/webdriverio/pull/15003 */
201+ } )
202+ } )
203+ } )
204+
205+ test ( 'success when class name is present' , async ( ) => {
206+ const beforeAssertion = vi . fn ( )
207+ const afterAssertion = vi . fn ( )
208+
209+ const result = await thisContext . toHaveElementClass ( elements , 'some-class' , { wait : 0 , beforeAssertion, afterAssertion } )
210+
211+ expect ( result . pass ) . toBe ( true )
212+ expect ( beforeAssertion ) . toBeCalledWith ( {
213+ matcherName : 'toHaveElementClass' ,
214+ expectedValue : 'some-class' ,
215+ options : { beforeAssertion, afterAssertion, wait : 0 }
216+ } )
217+ expect ( afterAssertion ) . toBeCalledWith ( {
218+ matcherName : 'toHaveElementClass' ,
219+ expectedValue : 'some-class' ,
220+ options : { beforeAssertion, afterAssertion, wait : 0 } ,
221+ result
222+ } )
223+ } )
224+
225+ test ( 'success when including surrounding spaces and asymmetric matcher' , async ( ) => {
226+ const result = await thisContext . toHaveElementClass ( elements , expect . stringContaining ( 'some-class ' ) )
227+ expect ( result . pass ) . toBe ( true )
228+
229+ const result2 = await thisContext . toHaveElementClass ( elements , expect . stringContaining ( ' another-class ' ) )
230+ expect ( result2 . pass ) . toBe ( true )
231+ } )
232+
233+ test ( 'success with multiple asymmetric matcher' , async ( ) => {
234+ const result = await thisContext . toHaveElementClass ( elements , [ expect . stringContaining ( 'some-class' ) , expect . stringContaining ( 'another-class' ) ] )
235+
236+ expect ( result . pass ) . toBe ( true )
237+ } )
238+
239+ test ( 'failure with multiple asymmetric matcher' , async ( ) => {
240+ const result = await thisContext . toHaveElementClass ( elements , [ expect . stringContaining ( 'notsome-class' ) , expect . stringContaining ( 'notanother-class' ) ] )
241+
242+ expect ( result . pass ) . toBe ( false )
243+ expect ( result . message ( ) ) . toEqual ( `\
244+ Expect ${ selectorName } to have class
245+
246+ - Expected - 2
247+ + Received + 2
248+
249+ Array [
250+ - StringContaining "notsome-class",
251+ - StringContaining "notanother-class",
252+ + "some-class another-class yet-another-class",
253+ + "some-class another-class yet-another-class",
254+ ]`
255+ )
256+ } )
257+
258+ test ( 'not - failure with multiple asymmetric matcher - pass should be true' , async ( ) => {
259+ const result = await thisNotContext . toHaveElementClass ( elements , [ expect . stringContaining ( 'some-class' ) , expect . stringContaining ( 'another-class' ) ] )
260+
261+ expect ( result . pass ) . toBe ( true ) // failure, boolean is inverted later
262+ expect ( result . message ( ) ) . toEqual ( `\
263+ Expect ${ selectorName } not to have class
264+
265+ Expected [not]: [StringContaining "some-class", StringContaining "another-class"]
266+ Received : ["some-class another-class yet-another-class", "some-class another-class yet-another-class"]`
267+ )
268+ } )
269+
270+ test ( 'success with RegExp when class name is present' , async ( ) => {
271+ const result = await thisContext . toHaveElementClass ( elements , / s O m E - c L a S s / i)
272+
273+ expect ( result . pass ) . toBe ( true )
274+ } )
275+
276+ test ( 'success if array matches with class' , async ( ) => {
277+ const result = await thisContext . toHaveElementClass ( elements , [ 'some-class' , 'yet-another-class' ] )
278+
279+ expect ( result . pass ) . toBe ( true )
280+ } )
281+
282+ test ( 'failure if the classes do not match' , async ( ) => {
283+ const result = await thisContext . toHaveElementClass ( elements , 'someclass' , { wait : 0 , message : 'Not found!' } )
284+
285+ expect ( result . pass ) . toBe ( false )
286+ expect ( result . message ( ) ) . toEqual ( `\
287+ Not found!
288+ Expect ${ selectorName } to have class
289+
290+ - Expected - 2
291+ + Received + 2
292+
293+ Array [
294+ - "someclass",
295+ - "someclass",
296+ + "some-class another-class yet-another-class",
297+ + "some-class another-class yet-another-class",
298+ ]` )
299+ } )
300+
301+ test ( 'failure if array does not match with class' , async ( ) => {
302+ const result = await thisContext . toHaveElementClass ( elements , [ 'someclass' , 'anotherclass' ] )
303+
304+ expect ( result . pass ) . toBe ( false )
305+ } )
306+
307+ test ( 'not - success - pass should be false' , async ( ) => {
308+ const result = await thisNotContext . toHaveElementClass ( elements , [ 'not-class' , 'not-another-class' ] )
309+
310+ expect ( result . pass ) . toBe ( false ) // success, boolean is inverted later
311+ } )
312+
313+ test ( 'not - failure - pass should be true' , async ( ) => {
314+ const result = await thisNotContext . toHaveElementClass ( elements , [ 'some-class' , 'not-another-class' ] )
315+
316+ expect ( result . pass ) . toBe ( true ) // failure, boolean is inverted later
317+ } )
318+
319+ describe ( 'options' , ( ) => {
320+ test ( 'should fail when class is not a string' , async ( ) => {
321+ elements . forEach ( ( el ) => {
322+ vi . mocked ( el . getAttribute ) . mockImplementation ( async ( ) => {
323+ return null as unknown as string // casting required since wdio as bug typing see
324+ } )
325+ } )
326+
327+ const result = await thisContext . toHaveElementClass ( elements , 'some-class' )
328+
329+ expect ( result . pass ) . toBe ( false )
330+ } )
331+
332+ test ( 'should pass when trimming the attribute' , async ( ) => {
333+ elements . forEach ( ( el ) => {
334+ vi . mocked ( el . getAttribute ) . mockImplementation ( async ( ) => {
335+ return ' some-class '
336+ } )
337+ } )
338+
339+ const result = await thisContext . toHaveElementClass ( elements , 'some-class' , { wait : 0 , trim : true } )
340+
341+ expect ( result . pass ) . toBe ( true )
342+ } )
343+
344+ test ( 'should pass when ignore the case' , async ( ) => {
345+ const result = await thisContext . toHaveElementClass ( elements , 'sOme-ClAsS' , { wait : 0 , ignoreCase : true } )
346+ expect ( result . pass ) . toBe ( true )
347+ } )
348+
349+ test ( 'should pass if containing' , async ( ) => {
350+ const result = await thisContext . toHaveElementClass ( elements , 'some' , { wait : 0 , containing : true } )
351+ expect ( result . pass ) . toBe ( true )
352+ } )
353+
354+ test ( 'should pass if array ignores the case' , async ( ) => {
355+ const result = await thisContext . toHaveElementClass ( elements , [ 'sOme-ClAsS' , 'anOther-ClAsS' ] , { wait : 0 , ignoreCase : true } )
356+ expect ( result . pass ) . toBe ( true )
357+ } )
358+ } )
359+
360+ describe ( 'failure when class name is not present' , ( ) => {
361+ let result : AssertionResult
362+
363+ beforeEach ( async ( ) => {
364+ result = await thisContext . toHaveElementClass ( elements , 'test' )
365+ } )
366+
367+ test ( 'failure' , ( ) => {
368+ expect ( result . pass ) . toBe ( false )
369+ expect ( result . message ( ) ) . toEqual ( `\
370+ Expect ${ selectorName } to have class
371+
372+ - Expected - 2
373+ + Received + 2
374+
375+ Array [
376+ - "test",
377+ - "test",
378+ + "some-class another-class yet-another-class",
379+ + "some-class another-class yet-another-class",
380+ ]` )
381+ } )
382+ } )
383+
384+ describe ( 'failure with RegExp when class name is not present' , ( ) => {
385+ let result : AssertionResult
386+
387+ beforeEach ( async ( ) => {
388+ result = await thisContext . toHaveElementClass ( elements , / W D I O / )
389+ } )
390+
391+ test ( 'failure' , ( ) => {
392+ expect ( result . pass ) . toBe ( false )
393+ expect ( result . message ( ) ) . toEqual ( `\
394+ Expect ${ selectorName } to have class
395+
396+ - Expected - 2
397+ + Received + 2
398+
399+ Array [
400+ - /WDIO/,
401+ - /WDIO/,
402+ + "some-class another-class yet-another-class",
403+ + "some-class another-class yet-another-class",
404+ ]` )
405+ } )
406+ } )
407+ } )
157408} )
0 commit comments