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