2013-02-01 18 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,所以如果我的代码有任何错误,请原谅我。

    相关问题