@@ -164,42 +164,41 @@ class JsPath {
164164 ) : Promise < IExecJsPathResult < INodeVisibility > > {
165165 const objectAtPath = new ObjectAtPath ( jsPath , containerOffset ) ;
166166 try {
167- return await new Promise < IExecJsPathResult < INodeVisibility > > ( async resolve => {
168- const end = new Date ( ) ;
169- end . setTime ( end . getTime ( ) + timeoutMillis ) ;
170-
171- while ( new Date ( ) < end ) {
172- try {
173- if ( ! objectAtPath . objectAtPath ) objectAtPath . lookup ( ) ;
167+ const end = new Date ( ) ;
168+ end . setTime ( end . getTime ( ) + ( timeoutMillis || 0 ) ) ;
169+
170+ while ( new Date ( ) < end ) {
171+ try {
172+ if ( ! objectAtPath . objectAtPath ) objectAtPath . lookup ( ) ;
173+
174+ let isElementValid = ! ! objectAtPath . objectAtPath ;
175+ let visibility : INodeVisibility = {
176+ nodeExists : isElementValid ,
177+ } ;
178+ if ( isElementValid && waitForVisible ) {
179+ visibility = objectAtPath . getComputedVisibility ( ) ;
180+ isElementValid = visibility . isVisible ;
181+ }
174182
175- let isElementValid = ! ! objectAtPath . objectAtPath ;
176- let visibility : INodeVisibility = {
177- nodeExists : isElementValid ,
183+ if ( isElementValid ) {
184+ return {
185+ nodePointer : objectAtPath . extractNodePointer ( ) ,
186+ value : visibility ,
178187 } ;
179- if ( isElementValid && waitForVisible ) {
180- visibility = objectAtPath . getComputedVisibility ( ) ;
181- isElementValid = visibility . isVisible ;
182- }
183-
184- if ( isElementValid ) {
185- return resolve ( {
186- nodePointer : objectAtPath . extractNodePointer ( ) ,
187- value : visibility ,
188- } ) ;
189- }
190- } catch ( err ) {
191- // can happen if lookup path is bad
192188 }
193- // eslint-disable-next-line promise/param-names
194- await new Promise ( resolve1 => setTimeout ( resolve1 , 20 ) ) ;
195- await new Promise ( requestAnimationFrame ) ;
189+ } catch ( err ) {
190+ if ( String ( err ) . includes ( 'not a valid selector' ) ) throw err ;
191+ // can also happen if lookup path doesn't exist yet... in which case we want to keep trying
196192 }
193+ // eslint-disable-next-line promise/param-names
194+ await new Promise ( resolve1 => setTimeout ( resolve1 , 20 ) ) ;
195+ await new Promise ( requestAnimationFrame ) ;
196+ }
197197
198- resolve ( < IExecJsPathResult > {
199- nodePointer : objectAtPath . extractNodePointer ( ) ,
200- value : objectAtPath . getComputedVisibility ( ) ,
201- } ) ;
202- } ) ;
198+ return {
199+ nodePointer : objectAtPath . extractNodePointer ( ) ,
200+ value : objectAtPath . getComputedVisibility ( ) ,
201+ } ;
203202 } catch ( error ) {
204203 return objectAtPath . toReturnError ( error ) ;
205204 }
@@ -248,8 +247,8 @@ class ObjectAtPath {
248247 const element = this . closestElement ;
249248 if ( ! element ) return null ;
250249 const { x, y, width, height } = element . getBoundingClientRect ( ) ;
251- const centerX = Math . floor ( 100 * x + width / 2 ) / 100 ;
252- const centerY = Math . floor ( 100 * y + height / 2 ) / 100 ;
250+ const centerX = round ( x + width / 2 ) ;
251+ const centerY = round ( y + height / 2 ) ;
253252
254253 this . _obstructedByElement = document . elementFromPoint ( centerX , centerY ) ;
255254 return this . _obstructedByElement ;
@@ -349,38 +348,48 @@ class ObjectAtPath {
349348 }
350349
351350 public lookup ( ) {
352- this . objectAtPath = window ;
353- if ( this . jsPath [ 0 ] === 'window' ) this . jsPath . shift ( ) ;
354- this . lookupStepIndex = 0 ;
355- for ( const step of this . jsPath ) {
356- this . lookupStep = step ;
357- if ( Array . isArray ( step ) ) {
358- const [ methodName , ...args ] = step ;
359- // extract node ids as args
360- const finalArgs = args . map ( x => {
361- if ( typeof x !== 'string' ) return x ;
362- if ( ! x . startsWith ( '$$jsPath=' ) ) return x ;
363- const innerPath = JSON . parse ( x . split ( '$$jsPath=' ) . pop ( ) ) ;
364- const sub = new ObjectAtPath ( innerPath , this . containerOffset ) . lookup ( ) ;
365- return sub . objectAtPath ;
366- } ) ;
367- // handlers for getComputedStyle/Visibility/getNodeId/getBoundingRect
368- if ( methodName . startsWith ( '__' ) && methodName . endsWith ( '__' ) ) {
369- this . hasCustomMethodLookup = true ;
370- this . objectAtPath = this [ `${ methodName . replace ( / _ _ / g, '' ) } ` ] ( ...finalArgs ) ;
351+ try {
352+ // track object as we navigate so we can extract properties along the way
353+ this . objectAtPath = window ;
354+ this . lookupStepIndex = 0 ;
355+ if ( this . jsPath [ 0 ] === 'window' ) {
356+ this . jsPath . shift ( ) ;
357+ this . lookupStepIndex = 1 ;
358+ }
359+ for ( const step of this . jsPath ) {
360+ this . lookupStep = step ;
361+ if ( Array . isArray ( step ) ) {
362+ const [ methodName , ...args ] = step ;
363+ // extract node ids as args
364+ const finalArgs = args . map ( x => {
365+ if ( typeof x !== 'string' ) return x ;
366+ if ( ! x . startsWith ( '$$jsPath=' ) ) return x ;
367+ const innerPath = JSON . parse ( x . split ( '$$jsPath=' ) . pop ( ) ) ;
368+ const sub = new ObjectAtPath ( innerPath , this . containerOffset ) . lookup ( ) ;
369+ return sub . objectAtPath ;
370+ } ) ;
371+ // handlers for getComputedStyle/Visibility/getNodeId/getBoundingRect
372+ if ( methodName . startsWith ( '__' ) && methodName . endsWith ( '__' ) ) {
373+ this . hasCustomMethodLookup = true ;
374+ this . objectAtPath = this [ `${ methodName . replace ( / _ _ / g, '' ) } ` ] ( ...finalArgs ) ;
375+ } else {
376+ const methodProperty = propertyName ( methodName ) ;
377+ this . objectAtPath = this . objectAtPath [ methodProperty ] ( ...finalArgs ) ;
378+ }
379+ } else if ( typeof step === 'number' ) {
380+ this . objectAtPath = NodeTracker . getWatchedNodeWithId ( step ) ;
381+ } else if ( typeof step === 'string' ) {
382+ const prop = propertyName ( step ) ;
383+ this . objectAtPath = this . objectAtPath [ prop ] ;
371384 } else {
372- const methodProperty = propertyName ( methodName ) ;
373- this . objectAtPath = this . objectAtPath [ methodProperty ] ( ...finalArgs ) ;
385+ throw new Error ( 'unknown JsPathStep' ) ;
374386 }
375- } else if ( typeof step === 'number' ) {
376- this . objectAtPath = NodeTracker . getWatchedNodeWithId ( step ) ;
377- } else if ( typeof step === 'string' ) {
378- const prop = propertyName ( step ) ;
379- this . objectAtPath = this . objectAtPath [ prop ] ;
380- } else {
381- throw new Error ( 'unknown JsPathStep' ) ;
387+ this . lookupStepIndex += 1 ;
382388 }
383- this . lookupStepIndex += 1 ;
389+ } catch ( err ) {
390+ // don't store the invalid path if we failed at a step
391+ this . objectAtPath = null ;
392+ throw err ;
384393 }
385394
386395 return this ;
@@ -555,3 +564,7 @@ function isIterableOrArray(object) {
555564 if ( ! object || typeof object === 'string' || object instanceof String ) return false ;
556565 return ! ! object [ Symbol . iterator ] || Array . isArray ( object ) ;
557566}
567+
568+ function round ( num : number ) : number {
569+ return Math . floor ( 100 * num ) / 100 ;
570+ }
0 commit comments