import { readFileSync } from 'fs' ; import * as path from 'path' ; import { ElementHandle , EvaluateFn , JSHandle , Page } from 'puppeteer' ; import waitForExpect from 'wait-for-expect' ; import { IConfigureOptions , IQueryUtils , IScopedQueryUtils } from './typedefs' ; const domLibraryAsString = readFileSync ( path . join ( __dirname , '../dom-testing-library.js' ) , 'utf8' ) . replace ( /process.env/g , '{}' ) ; function mapArgument ( o ) { return convertProxyToRegExp ( o , 0 ) ; } function convertProxyToRegExp ( o , depth ) { if ( typeof o !== 'object' || ! o || depth > 2 ) return o ; if ( ! o . __regex || typeof o . __flags !== 'string' ) { const copy = { ... o } ; for ( const key of Object . keys ( copy ) ) { copy [ key ] = convertProxyToRegExp ( copy [ key ] , depth + 1 ) ; } return copy ; } return new RegExp ( o . __regex , o . __flags ) ; } function convertRegExpToProxy ( o , depth ) { if ( typeof o !== 'object' || ! o || depth > 2 ) return o ; if ( Object . prototype . toString . call ( o ) !== '[object RegExp]' ) { const copy = { ... o } ; for ( const key of Object . keys ( copy ) ) { copy [ key ] = convertRegExpToProxy ( copy [ key ] , depth + 1 ) ; } return copy ; } return { __regex : o . source , __flags : o . flags } ; } const delegateFnBodyToExecuteInPageInitial = `
  ${ domLibraryAsString };
  ${ convertProxyToRegExp . toString ( ) };

  const mappedArgs = args.map(${ mapArgument . toString ( ) });
  const moduleWithFns = fnName in __dom_testing_library__ ?
    __dom_testing_library__ :
    __dom_testing_library__.__moduleExports;
  return moduleWithFns[fnName](container, ...mappedArgs);
` ; let delegateFnBodyToExecuteInPage = delegateFnBodyToExecuteInPageInitial ; type DOMReturnType = ElementHandle | ElementHandle [ ] | null ; type ContextFn = ( ... args : any [ ] ) => ElementHandle ; async function createElementHandleArray ( handle ) { const lengthHandle = await handle . getProperty ( 'length' ) ; if ( ! lengthHandle ) throw new Error ( `Failed to assess length property` ) ; const length = ( await lengthHandle . jsonValue ( ) ) as number ; const elements = [ ] ; for ( let i = 0 ; i < length ; i ++ ) { const jsElement = await handle . getProperty ( i . toString ( ) ) ; if ( ! jsElement ) throw new Error ( `Failed to assess ${ i . toString ( ) } property` ) ; const element = await createElementHandle ( jsElement ) ; if ( element ) elements . push ( element ) ; } return elements ; } async function createElementHandle ( handle ) { const element = handle . asElement ( ) ; if ( element ) return element ; await handle . dispose ( ) ; return null ; } async function covertToElementHandle ( handle , asArray ) { return asArray ? createElementHandleArray ( handle ) : createElementHandle ( handle ) ; } function processNodeText ( handles ) { return handles . containerHandle . executionContext ( ) . evaluate ( handles . evaluateFn , handles . containerHandle , 'getNodeText' ) ; } async function processQuery ( handles ) { const { containerHandle , evaluateFn , fnName , argsToForward } = handles ; try { const handle = await containerHandle . executionContext ( ) . evaluateHandle ( evaluateFn , containerHandle , fnName , ... argsToForward ) ; return await covertToElementHandle ( handle , fnName . includes ( 'All' ) ) ; } catch ( err ) { err . message = err . message . replace ( '[fnName]' , `[${ fnName }]` ) ; err . stack = err . stack . replace ( '[fnName]' , `[${ fnName }]` ) ; throw err ; } } interface IHandleSet { containerHandle : ElementHandle ; evaluateFn : EvaluateFn ; fnName : string ; argsToForward : any [ ] ; } function createDelegateFor < T = DOMReturnType > ( fnName , contextFn ? , processHandleFn ? ) { processHandleFn = processHandleFn || processQuery ; return async function ( ... args ) : Promise < T > { const containerHandle = contextFn ? contextFn . apply ( this , args ) : this ; const evaluateFn = new Function ( 'container, fnName, ...args' , delegateFnBodyToExecuteInPage ) ; let argsToForward = args ; if ( containerHandle === args [ 0 ] ) { argsToForward = argsToForward . slice ( 1 ) ; } argsToForward = argsToForward . map ( convertRegExpToProxy ) ; return processHandleFn ! ( { fnName , containerHandle , evaluateFn , argsToForward } ) ; } ; } export async function getDocument ( _page ? ) { const page = _page || this ; const documentHandle = await page . mainFrame ( ) . evaluateHandle ( 'document' ) ; const document = documentHandle . asElement ( ) ; if ( ! document ) throw new Error ( 'Could not find document' ) ; return document ; } export function wait ( callback , { timeout = 4500 , interval = 50 } = { } ) { return waitForExpect ( callback , timeout , interval ) ; } export const waitFor = wait ; export function configure ( options ) { if ( ! options ) { return ; } const { testIdAttribute } = options ; if ( testIdAttribute ) { delegateFnBodyToExecuteInPage = delegateFnBodyToExecuteInPageInitial . replace ( /testIdAttribute: (['|"])data-testid(['|"])/g , `testIdAttribute: $1${ testIdAttribute }$2` ) ; } } export function getQueriesForElement < T > ( object , contextFn ? ) { const o = object as any ; if ( ! contextFn ) contextFn = ( ) => o ; const functionNames = [ 'queryByPlaceholderText' , 'queryAllByPlaceholderText' , 'getByPlaceholderText' , 'getAllByPlaceholderText' , 'findByPlaceholderText' , 'findAllByPlaceholderText' , 'queryByText' , 'queryAllByText' , 'getByText' , 'getAllByText' , 'findByText' , 'findAllByText' , 'queryByLabelText' , 'queryAllByLabelText' , 'getByLabelText' , 'getAllByLabelText' , 'findByLabelText' , 'findAllByLabelText' , 'queryByAltText' , 'queryAllByAltText' , 'getByAltText' , 'getAllByAltText' , 'findByAltText' , 'findAllByAltText' , 'queryByTestId' , 'queryAllByTestId' , 'getByTestId' , 'getAllByTestId' , 'findByTestId' , 'findAllByTestId' , 'queryByTitle' , 'queryAllByTitle' , 'getByTitle' , 'getAllByTitle' , 'findByTitle' , 'findAllByTitle' , 'queryByRole' , 'queryAllByRole' , 'getByRole' , 'getAllByRole' , 'findByRole' , 'findAllByRole' , 'queryByDisplayValue' , 'queryAllByDisplayValue' , 'getByDisplayValue' , 'getAllByDisplayValue' , 'findByDisplayValue' , 'findAllByDisplayValue' , ] ; functionNames . forEach ( functionName => { o [ functionName ] = createDelegateFor ( functionName , contextFn ) ; } ) ; o . getQueriesForElement = ( ) => getQueriesForElement ( o , ( ) => o ) ; o . getNodeText = createDelegateFor < string > ( 'getNodeText' , contextFn , processNodeText ) ; return o ; } export const within = getQueriesForElement ; export const queries = { } ; getQueriesForElement ( queries , el => el ) ;