使用Java的Selenium WebDriver和HTML Window位置

我将Selenium WebDriver与java.awt.Robot结合使用,以更好地模拟用户与Web应用程序的交互。 是的,我知道这可能是不必要的,但我服务的客户要求它。

目前事情进展顺利,但我有一个小问题,我似乎找不到一个好的方法来获取屏幕位置的网页元素。 标题栏,菜单栏,导航栏等所有内容都会将内容推到物理屏幕上(Robot从中获取坐标),但对Selenium报告元素的位置没有影响。

当我调用: element.getLocation(); 在Selenium WebElement上,它总是给我相对于HTML内容呈现窗格的位置,而不是浏览器窗口本身。

更好的例子是: driver.findElement(By.tagName("body")).getLocation(); 无论窗口的实际屏幕位置如何,始终返回0,0。

现在我通过在最大化窗口后添加垂直和水平偏移来攻击它,但是这些在不同的浏览器之间是不一样的(例如,IE的顶部装饰占用了比Firefox更多的空间),并且对于每个用户可能是不同的如果他们有书签工具栏,搜索栏等添加。

是的,我知道我可以在全屏模式下运行,但如果可能的话,我宁愿不这样做。

有没有办法使用WebDriver以可靠的方式获取元素的物理屏幕位置?

我相信没有办法获得页面上元素的真实屏幕位置。

我也认为全屏模式是你最好的选择。

也就是说,我编写了一个RobotCalibration类,可以检测当前浏览器的实际偏移量。 它打开一个特制的页面,并使用Robot类单击它。 算法从浏览器的中心开始,然后使用二等分来查找浏览器视口的左上角。

在IE8和FF18上测试过。 适用于最大化和窗口化的浏览器。 已知问题:如果您启用了顶部书签工具栏,则可能会点击某些书签,从而重定向。 它可以很容易地处理,但如果你需要的话,我把它留给你了:)。

测试页面:

     Calibration Test        

RobotCalibration类。 它有点长,所以我建议你把它复制到你最喜欢的IDE中并在那里进行探索:

 import java.awt.AWTException; import java.awt.Dimension; import java.awt.Point; import java.awt.Robot; import java.awt.Toolkit; import java.awt.event.InputEvent; import java.nio.file.Paths; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.ie.InternetExplorerDriver; public class RobotCalibration { public static Point calibrate(WebDriver driver) { return new RobotCalibration(driver).calibrate(); } /** Time for which to wait for the page response. */ private static final long TIMEOUT = 1000; private final WebDriver driver; private final Robot r; private final Point browserCenter; private int leftX; private int rightX; private int midX; private int topY; private int bottomY; private int midY; private RobotCalibration(WebDriver driver) { this.driver = driver; try { driver.manage().window().getSize(); } catch (UnsupportedOperationException headlessBrowserException) { throw new IllegalArgumentException("Calibrating a headless browser makes no sense.", headlessBrowserException); } try { this.r = new Robot(); } catch (AWTException headlessEnvironmentException) { throw new IllegalStateException("Robot won't work on headless environments.", headlessEnvironmentException); } Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); org.openqa.selenium.Dimension browserSize = driver.manage().window().getSize(); org.openqa.selenium.Point browserPos = driver.manage().window().getPosition(); // a maximized browser returns negative position // a maximized browser returns size larger than actual screen size // you can't click outside the screen leftX = Math.max(0, browserPos.x); rightX = Math.min(leftX + browserSize.width, screenSize.width - 1); midX = (leftX + rightX) /2; topY = Math.max(0, browserPos.y); bottomY = Math.min(topY + browserSize.height, screenSize.height - 1); midY = (topY + bottomY) /2; browserCenter = new Point(midX, midY); } private Point calibrate() { driver.get(Paths.get("files/RobotCalibration.html").toUri().toString()); // find left border while (leftX < rightX) { click(midX, midY); if (clickWasSuccessful()) { rightX = midX; } else { leftX = midX + 1; // close any menu we could have opened click(browserCenter.x, browserCenter.y); } midX = (leftX + rightX) /2; } // find top border while (topY < bottomY) { click(midX, midY); if (clickWasSuccessful()) { bottomY = midY; } else { topY = midY + 1; // close any menu we could have opened click(browserCenter.x, browserCenter.y); } midY = (topY + bottomY) /2; } if (!isCalibrated()) { throw new IllegalStateException("Couldn't calibrate the Robot."); } return new Point(midX, midY); } /** clicks on the specified location */ private void click(int x, int y) { r.mouseMove(x, y); r.mousePress(InputEvent.BUTTON1_DOWN_MASK); r.mouseRelease(InputEvent.BUTTON1_DOWN_MASK); // for some reason, my IE8 can't properly register clicks that are close // to each other faster than click every half a second if (driver instanceof InternetExplorerDriver) { sleep(500); } } private static void sleep(int millis) { try { Thread.sleep(millis); } catch (InterruptedException ignored) { // nothing to do } } private int counter = 0; /** @return whether the click on a page was successful */ private boolean clickWasSuccessful() { counter++; long targetTime = System.currentTimeMillis() + TIMEOUT; while (System.currentTimeMillis() < targetTime) { int pageCounter = Integer.parseInt(driver.findElement(By.id("counter")).getAttribute("value")); if (counter == pageCounter) { return true; } } return false; } /** @return whether the top left corner has already been clicked at */ private boolean isCalibrated() { long targetTime = System.currentTimeMillis() + TIMEOUT; while (System.currentTimeMillis() < targetTime) { if (driver.findElement(By.id("done")).getAttribute("value").equals("yep")) { return true; } } return false; } } 

样品用法:

 WebDriver driver = new InternetExplorerDriver(); Point p = RobotCalibration.calibrate(driver); System.out.println("Left offset: " + px + ", top offset: " + py); driver.quit(); 

如果不清楚,请随意提出任何问题。

看起来selenium现在可以相对于浏览器窗口获取元素位置。 在我的一个测试中,我试图validation页面是否锚定到某个位置,并认为执行此操作的最佳方法是获取相对于窗口的元素位置。 这可以使用getCoordinates()。inViewPort()方法完成。

以下是获取WebElement相对位置的示例代码:

 import org.openqa.selenium.By; import org.openqa.selenium.Point; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.internal.Locatable; public class RelativeElementLocation { private static Point locationPoint; private static Locatable elementLocation; WebDriver driver = new FirefoxDriver(); WebElement element; public Point location() throws Throwable { driver.get("Insert your URL here"); element = driver.findElement(By.id("Insert id/css/xpath/ here")); elementLocation = (Locatable) element; locationPoint = elementLocation.getCoordinates().inViewPort(); System.out.println(locationPoint); return locationPoint; } } 

inViewPort()方法的唯一警告是,如果你试图获取当前不在页面视图中的元素的相对元素位置,该方法将滚动页面以使元素在视图中然后给你相对位置。 意思是,如果你试图得到一个元素的相对位置在浏览器页面视图之上,它不会给你一个负y坐标,而是向上滚动到那个元素并使y坐标为零。

希望这有帮助,我是Selenium和Java的新手,所以如果我的代码中有任何错误,请原谅我。

有一种比Slanec更简单的方法来获得相对于绝对偏移的方法。

首先,你需要一个带有javascript 附加点击监听器的空白页面来获取坐标并将它们保存到一个全局变量中:

 window.coordinates = []; window.attachEvent('click', function() { window.coordinates = [event.pageX, event.pageY]; }); 

然后单击并通过JavascriptExecutor获取相对点击位置:

 // browser center Dimension size = driver.manage().window().getSize(); Point point = driver.manage().window().getPosition() .translate(size.width / 2, size.height / 2); // Click through robot click(point.x, point.y); Thread.sleep(500); // read coordinates List coordinates = (List) ((JavascriptExecutor) driver) .executeScript("return window.coordinates;"); if (coordinates.size() != 2) throw new IllegalStateException("Javascript did not receive the click"); // point contains screen coordinates of upper left page point point.translate(-((Integer)coordinates.get(0)), -((Integer)coordinates.get(1))); 

目前的Selenium没有实现API来获取web元素的绝对位置。 但是SAFS已经实现了一个版本来获取web元素的绝对位置。

思路如下:

  1. 获取元素对于框架的位置。 因为一个浏览器网页中可能有很多帧。 而且,框架可以植入另一个框架中。 因此,最好使用具有父帧的类来递归搜索到最外面的帧位置。

`

 class FrameElement { FrameElement parent; Point location; FrameElement(FrameElement parent, WebElement frame) throws ... { if(parent != null) { this.parent = parent; location = frame.getLocation().moveBy(parent.getLocation().x, parent.getLocation().y); } else { location = frame.getLocation(); } } public Point getLocation() {return location;} } Point p = webelement.getLocation(); 
  1. 相对于浏览器客户端区域(部件排除工具栏,菜单等)添加框架的位置。

  2. 相对于浏览器窗口添加浏览器客户端区域。

  3. 添加浏览器窗口相对于屏幕的位置。