@@ -707,9 +707,8 @@ if (!globalThis.__headlessAgent) {
707707 element . click ( ) ;
708708 return { clicked : refFor ( element ) , role : role ( element ) , name : name ( element ) } ;
709709 } ;
710- const hover = args => {
711- const element = target ( args ) ;
712- element . scrollIntoView ( { block : 'center' , inline : 'center' , behavior : 'instant' } ) ;
710+ // Hit-test the layout after scroll, before any pointer event.
711+ const pointerHit = element => {
713712 if ( ! visible ( element ) ) fail ( 'ELEMENT_NOT_FOUND' , 'ELEMENT_NOT_VISIBLE' ) ;
714713 const rect = element . getBoundingClientRect ( ) ;
715714 const left = Math . max ( 0 , rect . left ) ;
@@ -723,10 +722,16 @@ if (!globalThis.__headlessAgent) {
723722 if ( ! hit || ( hit !== element && ! element . contains ( hit ) ) ) {
724723 fail ( 'ELEMENT_NOT_FOUND' , 'ELEMENT_OBSCURED' ) ;
725724 }
725+ return { x, y} ;
726+ } ;
727+ const hover = args => {
728+ const element = target ( args ) ;
729+ element . scrollIntoView ( { block : 'center' , inline : 'center' , behavior : 'instant' } ) ;
730+ const point = pointerHit ( element ) ;
726731 const Pointer = typeof PointerEvent === 'function' ? PointerEvent : MouseEvent ;
727- const pointer = { bubbles : true , clientX : x , clientY : y , pointerId : 1 ,
732+ const pointer = { bubbles : true , clientX : point . x , clientY : point . y , pointerId : 1 ,
728733 pointerType : 'mouse' , isPrimary : true } ;
729- const mouse = { bubbles : true , clientX : x , clientY : y } ;
734+ const mouse = { bubbles : true , clientX : point . x , clientY : point . y } ;
730735 const previous = hoveredElement instanceof Element && hoveredElement . isConnected
731736 ? hoveredElement : null ;
732737 hoveredElement = element ;
@@ -765,26 +770,24 @@ if (!globalThis.__headlessAgent) {
765770 if ( ! editable || element . disabled || element . readOnly ) throw new Error ( 'NOT_EDITABLE' ) ;
766771 }
767772 element . scrollIntoView ( { block : 'center' , inline : 'center' , behavior : 'instant' } ) ;
768- if ( action !== 'hover' ) element . focus ( { preventScroll : true } ) ;
769- if ( action === 'hover' && ! visible ( element ) ) {
770- fail ( 'ELEMENT_NOT_FOUND' , 'ELEMENT_NOT_VISIBLE' ) ;
773+ if ( action === 'hover' ) {
774+ const point = pointerHit ( element ) ;
775+ return {
776+ ref : refFor ( element ) , role : role ( element ) , name : name ( element ) ,
777+ x : point . x , y : point . y ,
778+ } ;
771779 }
780+ element . focus ( { preventScroll : true } ) ;
772781 const rect = element . getBoundingClientRect ( ) ;
773782 const left = Math . max ( 0 , rect . left ) ;
774783 const right = Math . min ( innerWidth , rect . right ) ;
775784 const top = Math . max ( 0 , rect . top ) ;
776785 const bottom = Math . min ( innerHeight , rect . bottom ) ;
777- if ( right <= left || bottom <= top ) {
778- if ( action === 'hover' ) fail ( 'ELEMENT_NOT_FOUND' , 'ELEMENT_NOT_VISIBLE' ) ;
779- throw new Error ( 'ELEMENT_NOT_VISIBLE' ) ;
780- }
786+ if ( right <= left || bottom <= top ) throw new Error ( 'ELEMENT_NOT_VISIBLE' ) ;
781787 const x = left + ( right - left ) / 2 ;
782788 const y = top + ( bottom - top ) / 2 ;
783789 const hit = document . elementFromPoint ( x , y ) ;
784- if ( ! hit || ( hit !== element && ! element . contains ( hit ) ) ) {
785- if ( action === 'hover' ) fail ( 'ELEMENT_NOT_FOUND' , 'ELEMENT_OBSCURED' ) ;
786- throw new Error ( 'ELEMENT_OBSCURED' ) ;
787- }
790+ if ( ! hit || ( hit !== element && ! element . contains ( hit ) ) ) throw new Error ( 'ELEMENT_OBSCURED' ) ;
788791 return { ref : refFor ( element ) , role : role ( element ) , name : name ( element ) , x, y} ;
789792 } ;
790793 const checkedFileInput = element => {
0 commit comments