import { report } from '../report' ; import { sleep } from '../sleep' ; export interface WaitOptions { type { number } memberof timeoutInMilliseconds : number ; type { number } memberof stabilityInMilliseconds : number ; } export interface WaitUntilOptions extends WaitOptions { type { boolean } memberof throwOnTimeout : boolean ; type { boolean } memberof verbose : boolean ; type { boolean } memberof wrapPredicateExecutionInsideTryCatch : boolean ; } export const defaultWaitUntilOptions = { stabilityInMilliseconds : 300 , throwOnTimeout : true , timeoutInMilliseconds : 30000 , verbose : false , wrapPredicateExecutionInsideTryCatch : false , } ; export const noWaitNoThrowOptions = { stabilityInMilliseconds : 0 , throwOnTimeout : false , timeoutInMilliseconds : 0 , verbose : false , wrapPredicateExecutionInsideTryCatch : true , } ; export param { ( ) => Promise < boolean > } predicate param { ( string | ( ( ) => Promise < string > ) ) } errorMessage param { WaitUntilOptions } options returns { Promise < void > } export async function waitUntil ( predicate , errorMessage , options = defaultWaitUntilOptions ) { report ( 'wait options:' , options . verbose ) ; report ( JSON . stringify ( options , null , 2 ) , options . verbose ) ; const timeout = options . timeoutInMilliseconds ; const interval = options . stabilityInMilliseconds <= 3 ? 1 : options . stabilityInMilliseconds / 3 ; const nbIntervals = timeout <= 0 ? 1 : timeout / interval ; const stabilityCounterMaxValue = timeout <= 0 ? 1 : options . stabilityInMilliseconds / interval ; let stabilityCounterCurrentValue = 0 ; for ( let index = 0 ; index < nbIntervals ; index ++ ) { await sleep ( interval ) ; const result = await safeExecutePredicate ( predicate , options . wrapPredicateExecutionInsideTryCatch ) ; report ( `predicate returned ${ result } after waiting ${ ( index + 1 ) * interval } ms` , options . verbose ) ; if ( result === true ) { stabilityCounterCurrentValue += 1 ; } if ( result === false ) { stabilityCounterCurrentValue = 0 ; } if ( result && stabilityCounterCurrentValue >= stabilityCounterMaxValue ) { report ( `predicate always returned ${ result } during ${ stabilityCounterCurrentValue > 0 ? stabilityCounterCurrentValue * interval : interval } ms: no need to wait anymore.` , options . verbose ) ; return ; } } report ( `predicate could not converge to a stable result or always returned false during ${ options . timeoutInMilliseconds } ms.` , options . verbose ) ; if ( ! options . throwOnTimeout ) { report ( 'exiting waiting mechanism without throwing' , options . verbose ) ; return ; } if ( typeof errorMessage === 'string' ) { throw new Error ( errorMessage ) ; } throw new Error ( await errorMessage ( ) ) ; } async function safeExecutePredicate ( predicate , noThrow ) { try { return await predicate ( ) ; } catch ( error ) { if ( noThrow ) { return false ; } throw error ; } }