@@ -20,45 +20,76 @@ const getElementMethods = () => ({
2020 getHTML : vi . spyOn ( { getHTML : async ( ) => { return '<Html/>' } } , 'getHTML' ) ,
2121 getComputedLabel : vi . spyOn ( { getComputedLabel : async ( ) => 'Computed Label' } , 'getComputedLabel' ) ,
2222 getComputedRole : vi . spyOn ( { getComputedRole : async ( ) => 'Computed Role' } , 'getComputedRole' ) ,
23+ // Null is not part of the type, to fix in wdio one day
24+ getAttribute : vi . spyOn ( { getAttribute : async ( _attr : string ) => null as unknown as string } , 'getAttribute' ) ,
2325 getSize : vi . spyOn ( { getSize : async ( prop ?: 'width' | 'height' ) => {
2426 if ( prop === 'width' ) { return 100 }
2527 if ( prop === 'height' ) { return 50 }
2628 return { width : 100 , height : 50 } satisfies Size
2729 } } , 'getSize' ) as unknown as WebdriverIO . Element [ 'getSize' ] ,
2830} satisfies Partial < WebdriverIO . Element > )
2931
30- function $ ( _selector : string ) {
31- const element = {
32+ const elementFactory = ( _selector : string , index ?: number ) : WebdriverIO . Element => {
33+ const partialElement = {
3234 selector : _selector ,
3335 ...getElementMethods ( ) ,
36+ index,
3437 $,
3538 $$
36- } satisfies Partial < WebdriverIO . Element > as unknown as WebdriverIO . Element
37- element . getElement = async ( ) => Promise . resolve ( element )
38- return element as unknown as ChainablePromiseElement
39+ } satisfies Partial < WebdriverIO . Element >
40+
41+ const element = partialElement as unknown as WebdriverIO . Element
42+ element . getElement = vi . fn ( ) . mockResolvedValue ( element )
43+ return element
44+ }
45+
46+ function $ ( _selector : string ) {
47+ const element = elementFactory ( _selector )
48+
49+ // Wdio framework does return a Promise-wrapped element, so we need to mimic this behavior
50+ const chainablePromiseElement = Promise . resolve ( element ) as unknown as ChainablePromiseElement
51+
52+ // Ensure `'getElement' in chainableElement` is false while allowing to use `await chainableElement.getElement()`
53+ const runtimeChainableElement = new Proxy ( chainablePromiseElement , {
54+ get ( target , prop ) {
55+ if ( prop in element ) {
56+ return element [ prop as keyof WebdriverIO . Element ]
57+ }
58+ const value = Reflect . get ( target , prop )
59+ return typeof value === 'function' ? value . bind ( target ) : value
60+ }
61+ } )
62+ return runtimeChainableElement
3963}
4064
4165function $$ ( selector : string ) {
42- const length = ( this ) ?. _length || 2
43- const elements = Array ( length ) . fill ( null ) . map ( ( _ , index ) => {
44- const element = {
45- selector,
46- index,
47- ...getElementMethods ( ) ,
48- $,
49- $$
50- } satisfies Partial < WebdriverIO . Element > as unknown as WebdriverIO . Element
51- element . getElement = async ( ) => Promise . resolve ( element )
52- return element
53- } ) satisfies WebdriverIO . Element [ ] as unknown as WebdriverIO . ElementArray
66+ const length = ( this as any ) ?. _length || 2
67+ const elements : WebdriverIO . Element [ ] = Array ( length ) . fill ( null ) . map ( ( _ , index ) => elementFactory ( selector , index ) )
68+
69+ const elementArray = elements as unknown as WebdriverIO . ElementArray
70+
71+ elementArray . foundWith = '$$'
72+ elementArray . props = [ ]
73+ elementArray . props . length = length
74+ elementArray . selector = selector
75+ elementArray . getElements = async ( ) => elementArray
76+ elementArray . length = length
77+
78+ // Wdio framework does return a Promise-wrapped element, so we need to mimic this behavior
79+ const chainablePromiseArray = Promise . resolve ( elementArray ) as unknown as ChainablePromiseArray
80+
81+ // Ensure `'getElements' in chainableElements` is false while allowing to use `await chainableElement.getElements()`
82+ const runtimeChainablePromiseArray = new Proxy ( chainablePromiseArray , {
83+ get ( target , prop ) {
84+ if ( elementArray && prop in elementArray ) {
85+ return elementArray [ prop as keyof WebdriverIO . ElementArray ]
86+ }
87+ const value = Reflect . get ( target , prop )
88+ return typeof value === 'function' ? value . bind ( target ) : value
89+ }
90+ } )
5491
55- elements . foundWith = '$$'
56- elements . props = [ ]
57- elements . props . length = length
58- elements . selector = selector
59- elements . getElements = async ( ) => elements
60- elements . length = length
61- return elements as unknown as ChainablePromiseArray
92+ return runtimeChainablePromiseArray
6293}
6394
6495export const browser = {
0 commit comments