从WebDriver
Selenium 2.0a2 开始,我无法检查元素是否可见。
WebDriver.findElement
返回 a WebElement
,不幸的是它不提供isVisible
方法。我可以通过使用WebElement.clear
或WebElement.click
两者都抛出一个来解决这个问题ElementNotVisibleException
,但这感觉很脏。
有更好的想法吗?
即使我回答这个问题有点晚了:
您现在可以使用WebElement.isDisplayed()
来检查元素是否可见。
注意:
元素不可见的原因有很多。Selenium 尝试覆盖其中的大部分,但在某些极端情况下它不能按预期工作。
例如,如果一个元素有or ,isDisplayed()
它会返回,但至少在我的测试中,它不能可靠地检测一个元素是否由于 CSS 定位而被另一个元素覆盖。false
display: none
opacity: 0
element instanceof RenderedWebElement
应该管用。
#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
> 0display
属性设置为none
visibility
道具设置为visible
大多数人还会要求它实际上也在视口内(这样人们就可以看到它)。
出于某种原因,纯 Java API 无法满足这种非常正常的需求,而基于它的 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
并替换const
为var
)!要使用它,请将其从Selenium 发送的File
a中读取,以便在客户端上运行。String
另一个可能值得研究的有趣且相关的脚本是selectByVisibleText。
如果您还没有使用 Selenium 执行过 JS,那么您可以稍微了解一下或浏览JavaScriptExecutor API。
通常,尝试始终使用非阻塞异步脚本(意思是#executeAsyncScript),但由于我们已经有一个同步的阻塞脚本,我们不妨使用正常的同步调用。返回的对象可以是多种类型的对象,因此请适当地进行转换。这可能是一种方法:
/**
* 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']"));
}
}
我有以下两种建议的方式:
您可以使用isDisplayed()
如下:
driver.findElement(By.id("idOfElement")).isDisplayed();
您可以定义如下所示的方法并调用它:
public boolean isElementPresent(By by) {
try {
driver.findElement(by);
return true;
}
catch (org.openqa.selenium.NoSuchElementException e) {
return false;
}
}
现在,您可以进行如下断言来检查元素是否存在:
assertTrue(isElementPresent(By.id("idOfElement")));
如果您使用的是 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
查看元素是否可见很重要,因为它Driver.FindElement
只会检查 HTML 源。但是弹出代码可能在页面 html 中,并且不可见。因此,Driver.FindElement
函数返回误报(您的测试将失败)
验证 ele 是可见的。
public static boolean isElementVisible(final By by)
throws InterruptedException {
boolean value = false;
if (driver.findElements(by).size() > 0) {
value = true;
}
return value;
}
如果您使用的是页面工厂,那么您可以尝试以下代码供您参考:
public static boolean isElementVisible(WebElement webElement, int timeOut) {
try {
WebDriverWait wait = new WebDriverWait(driver, timeOut);
wait.until(ExpectedConditions.visibilityOf(webElement));
return true;
} catch (org.openqa.selenium.NoSuchElementException e) {
return false;
}
}
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
这是我的做法(请忽略担心 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;
}
try{
if( driver.findElement(By.xpath("//div***")).isDisplayed()){
System.out.println("Element is Visible");
}
}
catch(NoSuchElementException e){
else{
System.out.println("Element is InVisible");
}
}
试试这个
public boolean isPrebuiltTestButtonVisible() {
try {
if (preBuiltTestButton.isEnabled()) {
return true;
} else {
return false;
}
} catch (Exception e) {
e.printStackTrace();
return false;
}
}