我将Selenium WebDriver与java.awt.Robot结合使用,以更好地模拟用户与Web应用程序的交互。是的,我知道可能没有必要,但是我所服务的客户都需要它。
当前,一切运行良好,但是我有一个小问题,就是我似乎找不到找到将Web元素显示在屏幕上位置的好方法。诸如标题栏,菜单栏,导航栏之类的东西都将内容向下推到物理屏幕上(Robot从中获取坐标),但对Selenium报告元素所在的位置没有影响。
当我element.getLocation();在Selenium WebElement上调用:时,它总是为我提供相对于HTML内容呈现窗格的位置,而不是浏览器窗口本身的位置。
element.getLocation();
一个更好的示例是:driver.findElement(By.tagName("body")).getLocation();无论窗口的实际屏幕位置如何,始终返回0,0。
driver.findElement(By.tagName("body")).getLocation();
现在,我通过在最大化窗口后添加垂直和水平偏移量来破解它,但是不同的浏览器之间这些偏移量并不相同(例如,IE的顶部装饰所占的空间要比Firefox更大),并且每个用户可能会有所不同如果其中添加了书签工具栏,搜索栏等。
是的,我知道我可以在全屏模式下运行,但如果可能的话,我宁愿不运行。
有没有一种方法可以使用WebDriver以可靠的方式获取元素在屏幕上的物理位置?
我相信无法获得页面上元素的真实屏幕位置。
我也认为全屏模式是您最好的选择。
就是说,我写了一个RobotCalibration可以检测当前浏览器实际偏移量的类。它会打开一个特制页面,并使用Robot该类单击它。该算法从浏览器的中心开始,然后使用二等分线找到浏览器视口的左上角。
RobotCalibration
Robot
在IE8和FF18上测试。适用于最大化和窗口浏览器。已知问题:如果启用了顶部的书签工具栏,则它可能会单击某些书签,因此会重定向。它可以很容易地处理,但是如果您需要它,我留给您:)。
测试页面:
<!DOCTYPE html> <html lang="en" onclick="document.getElementById('counter').value++"> <head> <meta charset="utf-8" /> <title>Calibration Test</title> </head> <body> <img height="1" width="1" style="position: absolute; left: 0; top: 0;" onclick="document.getElementById('done').value = 'yep'" /> <input type="text" id="counter" value="0" /> <input type="text" id="done" value="nope" /> </body> </html>
该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: " + p.x + ", top offset: " + p.y); driver.quit();
如果有不清楚的地方,请随时提出任何问题。