import { UniDriverList , Locator , UniDriver , waitFor , getDefinitionForKeyType , delay as sleep , EnterValueOptions , NoElementWithLocatorError , isMultipleElementsWithLocatorError , MultipleElementsWithLocatorError , DriverContext , contextToWaitError , } from "@unidriver/core" ; import { Simulate } from 'react-dom/test-utils' ; type ElementOrElementFinder = ( ( ) => Element ) | Element | ( ( ) => Promise < Element > ) ; type ElementsOrElementsFinder = ( ( ) => Element [ ] ) | Element [ ] | ( ( ) => Promise < Element [ ] > ) ; const isPromise = ( a ) : a is Promise < any > => { return ! ! ( ( a as any ) . then ) ; } ; export const jsdomReactUniDriverList = ( containerOrFn , context = { selector : 'Root React list driver' } ) : UniDriverList < Element > => { const elem = async ( ) => { const elements = typeof containerOrFn === 'function' ? containerOrFn ( ) : containerOrFn ; return isPromise ( elements ) ? await elements : elements ; } ; return { get : ( idx ) => jsdomReactUniDriver ( ( ) => { return elem ( ) . then ( ( cont ) => { const elem = cont [ idx ] ; if ( ! elem ) { throw new Error ( "React base driver - element was not found" ) ; } else { return elem ; } } ) ; } , { ... context , idx } ) , text : async ( ) => ( await elem ( ) ) . map ( ( e ) => e . textContent || "" ) , count : async ( ) => ( await elem ( ) ) . length , map : async ( fn ) => { const children = Array . from ( await elem ( ) ) ; return Promise . all ( children . map ( ( e , idx ) => { return fn ( jsdomReactUniDriver ( e , { parent : context , idx , selector : context . selector , } ) , idx ) ; } ) ) ; } , filter : ( fn ) => { return jsdomReactUniDriverList ( async ( ) => { const elems = await elem ( ) ; const results = await Promise . all ( elems . map ( ( e , i ) => { const bd = jsdomReactUniDriver ( e , { parent : context , idx : i , selector : context . selector } ) ; return fn ( bd , i ) ; } ) ) ; return elems . filter ( ( _ , i ) => { return results [ i ] ; } ) ; } , context ) ; } , } ; } ; type HTMLFocusableElement = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement | HTMLButtonElement ; const elementIsFocusable = ( el ) : el is HTMLFocusableElement => { return ( el . tagName === 'INPUT' || el . tagName === 'SELECT' || el . tagName === 'TEXTAREA' || el . tagName === 'BUTTON' || el . tagName === 'svg' ) ; } ; const isCheckable = ( el ) : boolean => { return ( el . tagName === 'INPUT' && ( ( el as HTMLInputElement ) . type == 'checkbox' || ( el as HTMLInputElement ) . type == 'radio' ) ) ; } ; const slowType = async ( element , value , delay ) => { const { name , type } = element ; let currentValue = "" ; for ( let i = 0 ; i < value . length ; i ++ ) { currentValue += value [ i ] ; Simulate . change ( element as Element , { target : { name , type , value : currentValue } as HTMLInputElement } ) ; await sleep ( delay ) ; } } ; export const jsdomReactUniDriver = ( containerOrFn , context = { selector : 'Root React driver' } ) : UniDriver < Element > => { const elem = async ( ) => { const container = typeof containerOrFn === 'function' ? containerOrFn ( ) : containerOrFn ; if ( ! container ) { throw new Error ( 'React base driver - element was not found' ) ; } return container ; } ; const exists = async ( ) => { try { await elem ( ) ; return true ; } catch ( e ) { if ( isMultipleElementsWithLocatorError ( e ) ) { throw e ; } else { return false ; } } } ; const handleCheckableInput = async ( input ) => { input . checked = ! input . checked ; Simulate . input ( input ) ; Simulate . change ( input ) ; } ; return { $ : ( loc ) => { const getElement = async ( ) => { const container = await elem ( ) ; const elements = container . querySelectorAll ( loc ) ; if ( ! elements . length ) { throw new NoElementWithLocatorError ( loc ) ; } else if ( elements . length > 1 ) { throw new MultipleElementsWithLocatorError ( elements . length , loc ) ; } return elements [ 0 ] ; } ; return jsdomReactUniDriver ( getElement , { parent : context , selector : loc } ) ; } , $$ : ( selector ) => jsdomReactUniDriverList ( async ( ) => { const e = await elem ( ) ; return Array . from ( e . querySelectorAll ( selector ) ) ; } , { parent : context , selector } ) , text : async ( ) => elem ( ) . then ( ( e ) => e . textContent || '' ) , value : async ( ) => { const e = ( await elem ( ) ) as HTMLInputElement ; return e . value ; } , click : async ( ) => { const el = await elem ( ) ; const eventData = { button : 0 } ; Simulate . mouseDown ( el , eventData ) ; if ( elementIsFocusable ( el ) ) { if ( document . activeElement != el ) { if ( document . activeElement ) { Simulate . blur ( document . activeElement ) ; } if ( ! el . disabled ) { el . focus ( ) ; Simulate . focus ( el ) ; } } } Simulate . mouseUp ( el , eventData ) ; Simulate . click ( el , eventData ) ; if ( isCheckable ( el ) ) { handleCheckableInput ( el as HTMLInputElement ) ; } } , mouse : { press : async ( ) => { const el = await elem ( ) ; Simulate . mouseDown ( el ) ; } , release : async ( ) => { const el = await elem ( ) ; Simulate . mouseUp ( el ) ; } , moveTo : async ( to ) => { const el = await elem ( ) ; const { left , top } = ( await to . getNative ( ) ) . getBoundingClientRect ( ) ; Simulate . mouseMove ( el , { clientX : left , clientY : top } ) ; } , leave : async ( ) => { const el = await elem ( ) ; Simulate . mouseLeave ( el ) ; } } , hover : async ( ) => { const el = await elem ( ) ; Simulate . mouseOver ( el ) ; Simulate . mouseEnter ( el ) ; } , pressKey : async ( key ) => { const el = await elem ( ) ; const def = getDefinitionForKeyType ( key ) ; Simulate . keyDown ( el , def ) ; Simulate . keyUp ( el , def ) ; } , hasClass : async ( className ) => ( await elem ( ) ) . classList . contains ( className ) , enterValue : async ( value , options ? ) => { const el = ( await elem ( ) ) as JSX . IntrinsicElements [ 'input' ] ; if ( el . disabled || el . readOnly ) { return ; } const { name , type , onChange } = el ; if ( ! onChange ) { el . value = value ; } if ( options ?. delay ) { await slowType ( el , value , options . delay ) ; } Simulate . change ( el as Element , { target : { name , type , value } as HTMLInputElement } ) ; } , attr : async ( name ) => { const el = await elem ( ) ; return el . getAttribute ( name ) ; } , exists , isDisplayed : async ( ) => { return true ; } , wait : async ( timeout ? ) => { return waitFor ( exists , timeout , 30 , contextToWaitError ( context ) ) ; } , type : 'react' , scrollIntoView : async ( ) => { return { } ; } , getNative : ( ) => elem ( ) , _prop : async ( name ) => { const el = await elem ( ) ; return ( el as any ) [ name ] ; } } ; } ;