13

Selenium WebDriver を java.awt.Robot と組み合わせて使用​​して、Web アプリケーションとのユーザー操作をより適切にシミュレートしています。はい、おそらく不要であることはわかっていますが、私がサービスを提供する顧客はそれを求めています.

現在、物事はかなりうまくいっていますが、Web 要素の画面上の位置を取得するための良い方法が見つからないという小さな問題があります。タイトル バー、メニュー バー、ナビゲーション バーなどはすべて (ロボットが座標を取得する) 物理画面にコンテンツを押し下げますが、Selenium が要素をレポートする場所には影響しません。

Selenium WebElementを呼び出すとelement.getLocation();、ブラウザ ウィンドウ自体ではなく、HTML コンテンツ レンダー ペインに相対的な位置が常に表示されます。

driver.findElement(By.tagName("body")).getLocation();ウィンドウの実際の画面上の位置に関係なく、常に 0,0 を返します。

現在、ウィンドウを最大化した後に垂直方向と水平方向のオフセットを追加してハッキングしていますが、これらは異なるブラウザー間で同じではありません (たとえば、IE の上部の装飾は Firefox よりも多くのスペースを占有します)、ユーザーごとに異なる場合があります。ブックマーク ツールバー、検索バーなどが追加されている場合。

はい、全画面モードで実行できることは知っていますが、可能であれば実行したくありません。

WebDriver を使用して要素の画面上の物理的な位置を信頼できる方法で取得する方法はありますか?

4

4 に答える 4

8

ページ上の要素の実際の画面上の位置を取得する方法はないと思います。

また、フルスクリーンモードが最善の策だと思います。

そうは言っ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();

不明な点があれば遠慮なく質問してください。

于 2013-02-03T21:29:13.840 に答える
6

セレンには、ブラウザウィンドウに対する要素の位置を取得する方法があるようです。私のテストの 1 つで、ページが特定の位置に固定されていることを確認しようとしていましたが、これを行う最善の方法は、ウィンドウに対する要素の位置を取得することでした。これは、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 を初めて使用するので、コードに誤りがあればご容赦ください。

于 2015-08-18T17:23:12.023 に答える
0

現在の Selenium は、Web 要素の絶対位置を取得するための API を実装していません。ただし、SAFSは Web 要素の絶対位置を取得するために 1 つのバージョンを実装しています。

考え方は以下です。

  1. フレームに対する要素の位置を取得します。1 つのブラウザ Web ページに多数のフレームが存在する可能性があるためです。また、フレームは別のフレームに移植されてもよい。そのため、親フレームを持つクラスを使用して、最も外側のフレームの場所まで再帰的に検索することをお勧めします。

`

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. 画面に対するブラウザ ウィンドウの位置を追加します。

于 2015-07-09T09:07:20.387 に答える