Selenium WebDriver 和 HTML Window 位置使用 Java

2022-09-04 20:09:24

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

目前,事情进展得相当顺利,但是我有一个小障碍,因为我似乎找不到一个好方法来获取Web元素在屏幕上的位置。标题栏,菜单栏,导航栏等内容都会将内容向下推送到物理屏幕上(Robot从中获取坐标),但对Selenium报告元素的位置没有影响。

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

更好的说明是:始终返回 0,0,而不考虑窗口的实际屏幕位置。driver.findElement(By.tagName("body")).getLocation();

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

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

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


答案 1

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

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

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

在 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>

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

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();

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


答案 2

看起来硒现在有一种方法可以获得相对于浏览器窗口的元素位置。在我的一个测试中,我试图验证页面是否锚定在某个位置,并认为最好的方法是获得相对于窗口的元素位置。这可以使用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的新手,所以如果我的代码有任何错误,请原谅我。


推荐