Is it possible to programmatically determine whether W3C action commands are used?

3.1k Views Asked by At

The Selenium Javadoc for Actions.moveToElement indicate that the meanings of the xOffset and yOffset arguments are as follows.

xOffset - Offset from the top-left corner. A negative value means coordinates left from the element.
yOffset - Offset from the top-left corner. A negative value means coordinates above the element.

Consider the following program, run on Linux against Firefox Quantum.

public class FirefoxTest {
    public static void main(String[] args) {
        // Set up driver
        WebDriver driver = new FirefoxDriver();
        JavascriptExecutor js = (JavascriptExecutor) driver;

        driver.get("http://www.google.com");
        WebElement element = new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(By.name("q")));

        // Perform a move and click action to see where it lands.
        Actions moveAndClick = new Actions(driver).moveToElement(element,0,0).doubleClick();
        moveAndClick.perform();
    }
}

When the following program is run, the double-click occurs in the middle of the search box rather than at the top left corner (I know this because I injected JS to log the location of the click). Moreover, the following message is output in the terminal where the program is run.

org.openqa.selenium.interactions.Actions moveToElement
INFO: When using the W3C Action commands, offsets are from the center of element

Is it possible to programmatically determine whether the offsets are from the center or the top-left corner of the element for Actions.moveToElement?

2

There are 2 best solutions below

0
On

To start with, when invoking GeckoDriver the first set of logs confirms that dialect being W3C as follows:

org.openqa.selenium.remote.ProtocolHandshake createSession
INFO: Detected dialect: W3C

You are correct as the Java Docs for moveToElement() still mentions that:

public Actions moveToElement(WebElement target, int xOffset, int yOffset)

Description:
Moves the mouse to an offset from the top-left corner of the element. The element is scrolled into view and its location is calculated using getBoundingClientRect.

Parameters:
    target - element to move to.
    xOffset - Offset from the top-left corner. A negative value means coordinates left from the element.
    yOffset - Offset from the top-left corner. A negative value means coordinates above the element.

Returns:
A self reference.

I am able to reproduce your issue as follows:

  • Code Block:

    new Actions(driver).moveToElement(element,0,0).doubleClick().build().perform();
    
  • Trace Logs:

    Oct 16, 2018 6:06:13 PM org.openqa.selenium.interactions.Actions moveToElement
    INFO: When using the W3C Action commands, offsets are from the center of element
    1539693373141   webdriver::server   DEBUG   -> POST /session/180ab0f0-21a3-4e38-8c92-d208fac77827/actions {"actions":[{"id":"default mouse","type":"pointer","parameters":{"pointerType":"mouse"},"actions":[{"duration":100,"x":0,"y":0,"type":"pointerMove","origin":{"ELEMENT":"774efad2-7ee0-40a3-bcaa-3a4d5fcff47b","element-6066-11e4-a52e-4f735466cecf":"774efad2-7ee0-40a3-bcaa-3a4d5fcff47b"}},{"button":0,"type":"pointerDown"},{"button":0,"type":"pointerUp"},{"button":0,"type":"pointerDown"},{"button":0,"type":"pointerUp"}]}]}
    1539693373166   Marionette  TRACE   0 -> [0,5,"WebDriver:PerformActions",{"actions":[{"actions":[{"duration":100,"origin":{"element-6066-11e4-a52e-4f735466cecf":"774e ... pointerDown"},{"button":0,"type":"pointerUp"}],"id":"default mouse","parameters":{"pointerType":"mouse"},"type":"pointer"}]}]
    

As @Andreas pointed out in the discussion offsets are from the center of element instead of from the top-left corner @FlorentB. clearly pointed that:

  • As per /session/:sessionId/moveto in JsonWireProtocol specification it was mentioned:

    /session/:sessionId/moveto
        POST /session/:sessionId/moveto
        Move the mouse by an offset of the specificed element. If no element is specified, the move is relative to the current mouse cursor. If an element is provided but no offset, the mouse will be moved to the center of the element. If the element is not visible, it will be scrolled into view.
    
        URL Parameters:
        :sessionId - ID of the session to route the command to.
    
        JSON Parameters:
        element - {string} Opaque ID assigned to the element to move to, as described in the WebElement JSON Object. If not specified or is null, the offset is relative to current position of the mouse.
        xoffset - {number} X offset to move to, relative to the top-left corner of the element. If not specified, the mouse will move to the middle of the element.
        yoffset - {number} Y offset to move to, relative to the top-left corner of the element. If not specified, the mouse will move to the middle of the element.
    
  • As per the Pointer Actions section within WebDriver W3C Editor's Draft it is mentioned that:

    An object that represents a web element

    1. Let element be equal to the result of trying to get a known connected element with argument origin.

    2. Let x element and y element be the result of calculating the in-view center point of element.

    3. Let x equal x element + x offset, and y equal y element + y offset.

In-view center point

An element’s in-view center point is the origin position of the rectangle that is the intersection between the element’s first DOM client rectangle and the initial viewport. Given an element that is known to be in view, it is calculated as:

  • Let rectangle be the first element of the DOMRect sequence returned by calling getClientRects on element.
  • Let left be (max(0, min(x coordinate, x coordinate + width dimension))).
  • Let right be (min(innerWidth, max(x coordinate, x coordinate + width dimension))).
  • Let top be (max(0, min(y coordinate, y coordinate + height dimension))).
  • Let bottom be (min(innerHeight, max(y coordinate, y coordinate + height dimension))).
  • Let x be (0.5 × (left + right)).
  • Let y be (0.5 × (top + bottom)).
  • Return x and y as a pair.

So it is can be concluded that the offsets are from the center but the Jave Docs are yet to be updated.

2
On

If the ask is to click on top-left corner of an element then following code snippet will do

WebElement element = new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(By.name("q")));
        Actions moveAndClick = new 
int yOffset=element.getRect().height/-2;//top
int xOffset=element.getRect().width/-2;//left
Actions(driver).moveToElement(element,xOffset,yOffset).doubleClick().perform();