2013-02-01 35 views
12

我將Selenium WebDriver與java.awt.Robot結合使用,以更好地模擬用戶與我們的Web應用程序的交互。是的,我知道這可能是不必要的,但我服務的客戶需要它。Selenium WebDriver和使用Java的HTML窗口位置

目前事情工作得很好,但我有一個小小的障礙,我似乎無法找到一個很好的方式來獲得Web元素的屏幕位置。諸如標題欄,菜單欄,導航欄等都將物體屏幕上的內容向下推(Robot獲取其座標),但對Selenium報告元素的位置沒有影響。

當我在Selenium WebElement上調用element.getLocation();時,它總是給我它相對於HTML內容呈現窗格的位置,而不是瀏覽器窗口本身。

更好的說明是:無論窗口的實際屏幕位置如何,driver.findElement(By.tagName("body")).getLocation();總是返回0,0。

現在我通過在最大化窗口後添加垂直和水平偏移量來黑客攻擊它,但不同瀏覽器之間的這些不一樣(例如,IE的頂級裝飾品佔用更多空間,例如Firefox),並且可能是不同的用戶,如果他們有書籤工具欄,搜索欄等添加。

是的,我知道我可以在全屏模式下運行,但我寧願不,如果可能的話。

有沒有辦法使用WebDriver以可靠的方式獲取元素的物理屏幕位置?

+2

您可能對此主題感興趣,以表達對Selenium團隊的興趣:https://code.google.com/p/selenium/issues/detail?id=5267 – kem

回答

6

我相信沒有辦法獲得頁面上元素的真實屏幕位置。

我也認爲全屏模式是你最好的選擇。

也就是說,我寫了一個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類。這是一個有點長,所以我建議你把它copypaste到您喜歡的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(); 

隨意問任何問題,如果事情還不清楚。

+0

我實際上要做的完全相同的東西,只是希望有一個更優雅的解決方案。謝謝! – Merkidemis

+0

@Merkidemis是的,我嘗試了一段時間尋找解決方案,因爲我也需要它。結束了全屏模式,由於[此錯誤](http://code.google.com/p/selenium/issues/detail?id = 4067),這使得全屏幕屏幕截圖無法使用。 –

+1

我將不得不保存這些不同的偏移量,因爲我正在測試的Web應用程序將打開新窗口,例如沒有地址欄。在會議開始時進行的校準(帶地址和菜單欄)將毫無用處。 – Merkidemis

4

有一個比Slanec answer更簡單的方法來獲得相對於絕對偏移。

首先,你需要在窗口使用javascript attached click listener一個空白頁,以獲取座標,並將它們保存到一個全局變量:

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<Object> coordinates = (List<Object>) ((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))); 
+1

這很好,我很慚愧我沒有自己想出來!尼斯。 –

0

當前Selenium沒有實現API來獲取Web元素的絕對位置。但SAFS已經實現了一個版本來獲取網頁元素的絕對位置。

的想法是如下:

  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(); 
  • 添加幀的位置,相對於瀏覽器客戶機的區域(部分排除工具欄,菜單等)。

  • 添加瀏覽器客戶區,相對於瀏覽器窗口。

  • 添加瀏覽器窗口的相對於屏幕的位置。

  • 5

    它看起來像硒有一種方法來獲取相對於您的瀏覽器窗口的元素位置。在我的一個測試中,我試圖驗證某個頁面是否固定在某個位置,並認爲這樣做的最佳方式是獲取相對於窗口的元素位置。這可以通過使用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,所以如果我的代碼有任何錯誤,請原諒我。

    相關問題