@@ -1097,6 +1097,75 @@ class WebDriver extends Helper {
10971097 await this . browser . buttonDown ( 2 )
10981098 }
10991099
1100+ /**
1101+ * Performs click at specific coordinates.
1102+ * If locator is provided, the coordinates are relative to the element's top-left corner.
1103+ * If locator is not provided, the coordinates are relative to the body element.
1104+ *
1105+ * ```js
1106+ * // Click at coordinates (100, 200) relative to body
1107+ * I.clickXY(100, 200);
1108+ *
1109+ * // Click at coordinates (50, 30) relative to element's top-left corner
1110+ * I.clickXY('#someElement', 50, 30);
1111+ * ```
1112+ *
1113+ * @param {CodeceptJS.LocatorOrString|number } locator Element to click on or X coordinate if no element.
1114+ * @param {number } [x] X coordinate relative to element's top-left, or Y coordinate if locator is a number.
1115+ * @param {number } [y] Y coordinate relative to element's top-left.
1116+ * @returns {Promise<void> }
1117+ */
1118+ async clickXY ( locator , x , y ) {
1119+ // If locator is a number, treat it as X coordinate and use body as base
1120+ if ( typeof locator === 'number' ) {
1121+ const globalX = locator
1122+ const globalY = x
1123+ locator = '//body'
1124+ x = globalX
1125+ y = globalY
1126+ }
1127+
1128+ // Locate the base element
1129+ const res = await this . _locate ( withStrictLocator ( locator ) , true )
1130+ assertElementExists ( res , locator , 'Element to click' )
1131+ const el = usingFirstElement ( res )
1132+
1133+ // Get element position and size to calculate top-left corner
1134+ const location = await el . getLocation ( )
1135+ const size = await el . getSize ( )
1136+
1137+ // WebDriver clicks at center by default, so we need to offset from center to top-left
1138+ // then add our desired x, y coordinates
1139+ const offsetX = - ( size . width / 2 ) + x
1140+ const offsetY = - ( size . height / 2 ) + y
1141+
1142+ if ( this . browser . isW3C ) {
1143+ // Use performActions for W3C WebDriver
1144+ return this . browser . performActions ( [
1145+ {
1146+ type : 'pointer' ,
1147+ id : 'pointer1' ,
1148+ parameters : { pointerType : 'mouse' } ,
1149+ actions : [
1150+ {
1151+ type : 'pointerMove' ,
1152+ origin : el ,
1153+ duration : 0 ,
1154+ x : Math . round ( offsetX ) ,
1155+ y : Math . round ( offsetY ) ,
1156+ } ,
1157+ { type : 'pointerDown' , button : 0 } ,
1158+ { type : 'pointerUp' , button : 0 } ,
1159+ ] ,
1160+ } ,
1161+ ] )
1162+ }
1163+
1164+ // Fallback for non-W3C browsers
1165+ await el . moveTo ( { xOffset : Math . round ( offsetX ) , yOffset : Math . round ( offsetY ) } )
1166+ return el . click ( )
1167+ }
1168+
11001169 /**
11011170 * {{> forceRightClick }}
11021171 *
0 commit comments