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

pon*_*zao 63 java selenium webdriver

使用WebDriverSelenium 2.0a2,我无法检查元素是否可见.

WebDriver.findElement返回a WebElement,遗憾的是它没有提供isVisible方法.我可以通过使用WebElement.clearWebElement.click两者抛出来解决这个问题ElementNotVisibleException,但这感觉很脏.

有更好的想法吗?

sle*_*ske 134

即使我回答这个问题有点迟了:

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

注意:

元素可以隐藏的原因有很多.Selenium试图覆盖其中的大多数,但是有些边缘情况不能按预期工作.

例如,isDisplayed() 返回false如果一个元素有display: noneopacity: 0,但至少在我的测试,但它不能可靠地检测如果一个元素被另一个覆盖由于CSS定位.

  • isDisplayed返回true,即使元素不可见. (5认同)
  • 我被告知这不是错误,并且可以正常运行。对于他们来说,显示的含义与对其他英语国家的含义不同。 (2认同)
  • 这并没有说明元素是否可见,仅当它存在于页面上时。它可能被其他元素隐藏和/或在视口之外。是否设置了 `display` 属性只涵盖了可能状态的一小部分,并且在很多情况下会返回误报。 (2认同)
  • @oligofren:是的,元素不可见的原因有很多,而 Selenium 确实涵盖了其中的大部分。例如,如果元素位于视口之外,或者不透明度=0,则“isDisplayed()”确实返回“false”。当然,在某些边缘情况下,它不会按预期工作,但它通常会工作,除非您在页面中做了非常花哨的事情。答案已更新。 (2认同)

hle*_*one 20

element instanceof RenderedWebElement 应该管用.

  • 真的?问题是关于Selenium(2.0a2)的特定版本.因此,使用`isDisplayed()`对于相关版本来说不是正确的答案. (11认同)
  • 这不起作用,但是这个接口委托方法isDisplayed并且它可以工作.谢谢你的提示.(1) (5认同)
  • isDisplayed 不起作用。即使明显未显示元素,它也是如此。 (5认同)
  • @Eugen我认为它已被改为`RemoteWebElement`.但是,正如@sleske所写的那样,`WebElement.isDisplayed`现在可用,并且它可能适用于这个用例(我没有那个代码可用,因此无法测试). (4认同)

Rip*_*sim 15

我有以下两种建议方式:

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

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

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

现在,您可以执行以下断言来检查元素是否存在:

assertTrue(isElementPresent(By.id("idOfElement")));
Run Code Online (Sandbox Code Playgroud)

  • 这并没有说明元素是否可见,仅当它存在于页面上时。它可能被其他元素隐藏和/或在视口之外,因此会产生误报。 (2认同)

Chr*_*les 8

如果你正在使用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
Run Code Online (Sandbox Code Playgroud)


oli*_*ren 6

简短答案:使用 #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();
Run Code Online (Sandbox Code Playgroud)

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

这是我的答案,然后才找到关于的实用方法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
    })
}
Run Code Online (Sandbox Code Playgroud)

因此,有趣的是发送到客户端上运行的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
}
Run Code Online (Sandbox Code Playgroud)

这段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']"));
    }
}
Run Code Online (Sandbox Code Playgroud)