如何使用WebDriver检查元素是否可见


70

随着WebDriver硒2.0a2我有麻烦检查如果一个元素是可见的。

WebDriver.findElement返回WebElement,不幸的是没有提供isVisible方法。我可以使用WebElement.clearWebElement.click两者都抛出一个来解决这个问题ElementNotVisibleException,但这感觉很脏。

还有更好的主意吗?

Answers:


22

element instanceof RenderedWebElement 应该管用。


5
这不起作用,但是此接口委托方法isDisplayed起作用。感谢您的提示。(+1)
盖姆

1
这对硒2.0仍然有用吗?我认为不需要RenderedWebElement(类本身已消失)。
欧根(Eugen)

4
@Eugen我认为它已更改为RemoteWebElement。但是,是的,如@sleske所写WebElement.isDisplayed,现在可以使用,并且它可能会在此用例中正常工作(我再也没有可用的代码了,因此无法测试)。
ponzao 2011年

14
真的?问题是关于硒(2.0a2)的特定版本。因此,isDisplayed()对于所讨论的版本,使用不是正确的答案。
hleinone 2012年

5
isDisplayed不起作用。即使元素未明确显示也是如此。
史蒂夫·斯台

143

即使我回答这个问题有些晚:

现在,您可以WebElement.isDisplayed()用来检查元素是否可见。

注意事项

元素不可见的原因有很多。Selenium尝试涵盖了大多数尝试,但是在某些极端情况下,Selenium无法按预期工作。

例如,如果某个元素具有或,isDisplayed() 返回,但是至少在我的测试中,由于CSS定位,它不能可靠地检测到某个元素是否被另一个元素覆盖。falsedisplay: noneopacity: 0


6
即使元素不可见,isDisplayed也会返回true。
史蒂夫·斯台

@SteveStaple:那不应该发生。如果是这样,请考虑提交错误。
sleske

4
我被告知这不是错误,并且可以正常工作。对于他们来说,显示的含义与对其他英语国家的含义不同。
史蒂夫·斯台

2
这并不表示该元素是否可见,仅当该元素存在于页面上时。它可能被其他元素隐藏和/或位于视口之外。是否display设置道具仅覆盖了部分可能的状态,并且在很多情况下会返回误报。
oligofren

1
@oligofren:是的,元素可能不可见的原因有很多-Selenium确实涵盖了其中的大部分。例如,如果元素在视口之外或具有opacity = 0 ,isDisplayed() 返回falsetrue。当然,在某些极端情况下,它无法按预期运行,但通常可以运行,除非您在页面中做一些非常漂亮的事情。答案已更新。
sleske

16

我有以下2种建议的方式:

  1. 您可以isDisplayed()如下使用:

    driver.findElement(By.id("idOfElement")).isDisplayed();
    
  2. 您可以定义如下所示的方法并调用它:

    public boolean isElementPresent(By by) {
      try {
        driver.findElement(by);
        return true;
      }
    catch (org.openqa.selenium.NoSuchElementException e) {
        return false;
      }
    }
    

现在,您可以按以下方式进行断言以检查元素是否存在:

assertTrue(isElementPresent(By.id("idOfElement")));

但这总是要等到超时发生
wutzebaer

@Anna:这是我的荣幸-谢谢。
Ripon Al Wasim

2
这并不表示该元素是否可见,仅当该元素存在于页面上时。它可能被其他元素隐藏和/或位于视口之外,因此产生误报。
oligofren

15

简短答案:使用 #visibilityOfElementLocated

使用isDisplayed或类似的答案均不正确。他们只检查display属性是否不是none,而不检查元素是否实际可见!Selenium在ExpectedConditions类中添加了一堆静态实用程序方法。在这种情况下,可以使用其中两个:

用法

@Test
// visibilityOfElementLocated has been statically imported
public demo(){
    By searchButtonSelector = By.className("search_button");
    WebDriverWait wait = new WebDriverWait(driver, 10);
    driver.get(homeUrl);

    WebElement searchButton = wait.until(                
            visibilityOfElementLocated
            (searchButtonSelector)); 

    //clicks the search button 
    searchButton.click();

在客户端上运行的自定义可见性检查

这是我的答案,然后才了解关于的实用方法ExpectedConditions。它可能仍然很重要,因为我认为它的作用不止上述方法,后者仅检查元素的高度和宽度。

本质上:这不能由Java和findElementBy*方法WebElement#isDisplayed来解决,而且不能单独解决,因为它们只能告诉您元素是否存在,而不是元素是否实际可见。OP尚未定义可见的含义,但通常需要

  • 它的opacity> 0
  • 它的display属性设置为除none
  • visibility道具设为visible
  • 没有其他元素隐藏它(这是最上面的元素)

大多数人也会要求它实际上也必须在视口内(这样一个人就能看到它)。

出于某种原因,纯Java API无法满足这种非常正常的需求,而基于Selenium构建的Selenium的前端通常会实现的某些变体isVisible,这就是为什么我知道这是可能的。浏览Node框架WebDriver.IO的源代码isVisible,我找到的源代码,现在isVisibleInViewport在5.0-beta中将其重命名为更恰当的名称。

基本上,它们将自定义命令作为调用来实现,该调用委托给在客户端上运行的javascript并完成实际工作!这是“服务器”位:

export default function isDisplayedInViewport () {
    return getBrowserObject(this).execute(isDisplayedInViewportScript, {
        [ELEMENT_KEY]: this.elementId, // w3c compatible
        ELEMENT: this.elementId // jsonwp compatible
    })
}

因此,有趣的是发送到客户端上运行的javascript:

/**
 * check if element is visible and within the viewport
 * @param  {HTMLElement} elem  element to check
 * @return {Boolean}           true if element is within viewport
 */
export default function isDisplayedInViewport (elem) {
    const dde = document.documentElement

    let isWithinViewport = true
    while (elem.parentNode && elem.parentNode.getBoundingClientRect) {
        const elemDimension = elem.getBoundingClientRect()
        const elemComputedStyle = window.getComputedStyle(elem)
        const viewportDimension = {
            width: dde.clientWidth,
            height: dde.clientHeight
        }

        isWithinViewport = isWithinViewport &&
                           (elemComputedStyle.display !== 'none' &&
                            elemComputedStyle.visibility === 'visible' &&
                            parseFloat(elemComputedStyle.opacity, 10) > 0 &&
                            elemDimension.bottom > 0 &&
                            elemDimension.right > 0 &&
                            elemDimension.top < viewportDimension.height &&
                            elemDimension.left < viewportDimension.width)

        elem = elem.parentNode
    }

    return isWithinViewport
}

这片JS的其实是可以复制的(几乎)逐字到您自己的代码库(删除export default和替换constvar在非常绿浏览器的情况下)!要使用它,请将其读FileStringSelenium可以发送的,以在客户端上运行。

另一个值得研究的有趣且相关的脚本是selectByVisibleText

如果您尚未使用Selenium执行JS,则可以对此进行一小步浏览或浏览JavaScriptExecutor API

通常,尝试始终使用非阻塞的异步脚本(即#executeAsyncScript),但是由于我们已经有一个同步的阻塞脚本,因此我们也可以使用普通的同步调用。返回的对象可以是许多类型的Object,因此请适当地进行转换。这可能是这样做的一种方式:

/** 
 * Demo of a java version of webdriverio's isDisplayedInViewport
 * https://github.com/webdriverio/webdriverio/blob/v5.0.0-beta.2/packages/webdriverio/src/commands/element/isDisplayedInViewport.js
 * The super class GuiTest just deals with setup of the driver and such
 */
class VisibleDemoTest extends GuiTest {
    public static String readScript(String name) {
        try {
            File f = new File("selenium-scripts/" + name + ".js");
            BufferedReader reader = new BufferedReader( new FileReader( file ) );
            return reader.lines().collect(Collectors.joining(System.lineSeparator()));
        } catch(IOError e){
            throw new RuntimeError("No such Selenium script: " + f.getAbsolutePath()); 
        }
    }

    public static Boolean isVisibleInViewport(RemoteElement e){
        // according to the Webdriver spec a string that identifies an element
        // should be deserialized into the corresponding web element,
        // meaning the 'isDisplayedInViewport' function should receive the element, 
        // not just the string we passed to it originally - how this is done is not our concern
        //
        // This is probably when ELEMENT and ELEMENT_KEY refers to in the wd.io implementation
        //
        // Ref https://w3c.github.io/webdriver/#dfn-json-deserialize
        return js.executeScript(readScript("isDisplayedInViewport"), e.getId());
    }

    public static Boolean isVisibleInViewport(String xPath){
        driver().findElementByXPath("//button[@id='should_be_visible']");
    }

    @Test
    public demo_isVisibleInViewport(){
        // you can build all kinds of abstractions on top of the base method
        // to make it more Selenium-ish using retries with timeouts, etc
        assertTrue(isVisibleInViewport("//button[@id='should_be_visible']"));
        assertFalse(isVisibleInViewport("//button[@id='should_be_hidden']"));
    }
}

9

如果您使用的是C#,它将是driver.Displayed。这是我自己的项目中的一个示例:

if (!driver.FindElement(By.Name("newtagfield")).Displayed)      //if the tag options is not displayed
    driver.FindElement(By.Id("expand-folder-tags")).Click();    //make sure the folder and tags options are visible

可见与显示的不一样。那只是检查元素是否将CSSdisplay属性设置为以外的其他属性none。它仍然可能被其他元素隐藏,高度为零,等等。
oligofren

2

请务必查看该元素是否可见,因为Driver.FindElement只会检查HTML源。但是弹出代码可能在页面html中,并且不可见。因此,Driver.FindElement函数返回假阳性(您的测试将失败)


1
如果该元素存在于HTML代码中,则即使该元素被隐藏,findElement也将通过。或说不可见(如下拉菜单)。问题是要查找元素是否可见。
some_other_guy 2012年

@CallumRogers检查我的答案。它提供了一个Java版本,说明基于Selenium的Webdriver.io NodeJS框架是如何实现的。基本上,您需要在客户端上运行JS来确保。
oligofren

0

验证ele是否可见。

public static boolean isElementVisible(final By by)
    throws InterruptedException {
        boolean value = false;

        if (driver.findElements(by).size() > 0) {
            value = true;
        }
        return value;
    }

7
与OP无关,但是您可以简化功能。怎么样:返回driver.findElements(by).size()> 0;
雷蒙德·巴洛

只是检查您是否可以找到它,而不是它是否可见。它将找到一个隐藏的元素。
oligofren

-1

这是我的操作方法(请忽略担心的Logger类调用):

public boolean isElementExist(By by) {
    int count = driver.findElements(by).size();
    if (count>=1) {
        Logger.LogMessage("isElementExist: " + by + " | Count: " + count, Priority.Medium);
        return true;
    }
    else {
        Logger.LogMessage("isElementExist: " + by + " | Could not find element", Priority.High);
        return false;
    }   
}

public boolean isElementNotExist(By by) {
    int count = driver.findElements(by).size();
    if (count==0) {
        Logger.LogMessage("ElementDoesNotExist: " + by, Priority.Medium);
        return true;
    }
    else {
        Logger.LogMessage("ElementDoesExist: " + by, Priority.High);
        return false;
    }   
}

public boolean isElementVisible(By by) {
    try {
        if (driver.findElement(by).isDisplayed()) {
            Logger.LogMessage("Element is Displayed: " + by, Priority.Medium);
            return true;
        }
    }
    catch(Exception e) {       
        Logger.LogMessage("Element is Not Displayed: " + by, Priority.High);
        return false;
    }       

    return false;
}

我也一样 直接使用isDisplayed()会引发异常,因此可以进行检查,但不能成为驱动这样的工作流程的条件。
张涛

-1
public boolean isElementFound( String text) {
        try{
            WebElement webElement = appiumDriver.findElement(By.xpath(text));
            System.out.println("isElementFound : true :"+text + "true");
        }catch(NoSuchElementException e){
            System.out.println("isElementFound : false :"+text);
            return false;
        }
        return true;
    }

    text is the xpath which you would be passing when calling the function.
the return value will be true if the element is present else false if element is not pressent

2
请为您的答案提供更多详细信息。这是如何工作的,如何解决OP的问题?
阿诺德·丹尼尔斯

这不是检查元素是否存在,而是检查它是否可见
oligofren

-1
    try{
        if( driver.findElement(By.xpath("//div***")).isDisplayed()){
          System.out.println("Element is Visible");
        }
}
catch(NoSuchElementException e){
   else{
     System.out.println("Element is InVisible");
        }
}

尽管此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的其他上下文,可以提高其长期价值。
Vishal Chhodwani

这并不表示该元素是否可见,仅当该元素存在于页面上时。它可能被其他元素隐藏和/或位于视口之外。
oligofren

-2

尝试这个

public boolean isPrebuiltTestButtonVisible() {
    try {

        if (preBuiltTestButton.isEnabled()) {

            return true;

        } else {

            return false;
        }

    } catch (Exception e) {

        e.printStackTrace();
        return false;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.