![](/img/trans.png)
[英]How to handle a web element getting removed from DOM using Selenium Webdriver?
[英]Selenium does not always recognize when an element is removed from the DOM
我在 mac 10.12.3 上使用带有 chromedriver 2.27 的 chrome 56 时遇到问题,尽管我尝试了几个不同版本的驱动程序和几个不同版本的浏览器,但遇到了同样的问题。 在 UI 中,我可以很清楚地看到一个阻塞层已经清除了,但是 selenium 仍然认为它没有。 等待阻塞层清除后(仅查看浏览器),如果我捕获 driver.getPageSource() 的结果,我将获得“旧”页面源,而不是新页面源(已删除阻塞层)。 当我将旧页面源代码作为 html 文档查看时,我可以看到阻塞层。 当我查看测试结束时截取的屏幕截图时,显然没有阻塞层,并且手动检查 DOM 显示该元素已被删除。 不知何故,selenium 似乎正在缓存旧的页面源,并且无法识别何时从 DOM 中删除了元素。 我似乎无法强制它在不完全重新加载页面的情况下刷新缓存的(?)html。 我想避免重新加载页面,因为这会使对阻塞层是否被正确删除的测试无效。
我已经尝试从阻塞层元素中获取一些信息(使用诸如 element.isDisplayed() 或 element.getLocation() 之类的良性内容),但这些似乎仍然表现得好像元素仍然存在一样。
任何有关如何处理此问题的建议将不胜感激。
如果您的目标是单击项目而不考虑阻塞层的状态,您可以使用内联 javascript 单击一个元素。 下面的示例代码。
try {
e.click();
} catch (org.openqa.selenium.WebDriverException E1) {
((JavascriptExecutor) driver).executeScript("arguments[0].click();", e.findElements(By.xpath(".//a")).get(0));
}
我在使用 GWT 弹出面板运行 Selenium 测试时遇到了同样的问题。
有时动画不会完成,弹出窗口和后面的玻璃留在 DOM 上。
当这种情况发生时,我试图从 dom 中删除这两个元素,但得到了奇怪的结果(dom 中的其他元素被删除了!!?)。
最后,我实现了这些隐藏这 2 个元素并更改 className 的函数,以便它们不再打扰我们(请参阅下面的代码)
waitForPopupOpeningAnimationFinished() :在应该打开弹出窗口的点击后启动
waitForPopupClosingAnimationFinished() :在单击弹出按钮后启动,应关闭弹出窗口
// 200 is the animation time of the popup, we wait a lot more, see com.google.gwt.user.client.ui.PopupPanel.ANIMATION_DURATION
public static final int POPUP_ANIMATION_WAIT_TIME = 300;
public static final String CLASS_GWT_DIALOG_BOX = "gwt-DialogBox";
public static final String X_POPUP = "//div[@class='" + CLASS_GWT_DIALOG_BOX + "']";
public static final By BY_POPUP = By.xpath(X_POPUP);
public static final String X_POPUP_TOTALLY_OPENED_LOCATOR = "//div[contains(@class, '" + CLASS_GWT_DIALOG_BOX + "') and contains(@style, 'clip: rect(auto, auto, auto, auto);')]";
public static final By BY_POPUP_TOTALLY_OPENED_LOCATOR = By.xpath(X_POPUP_TOTALLY_OPENED_LOCATOR);
private static final String CLASS_GWT_POPUP_PANEL_GLASS = "gwt-PopupPanelGlass";
private static final By BY_GLASS_PANEL = By.xpath("//div[contains(@class, '" + CLASS_GWT_POPUP_PANEL_GLASS + "')]");
public void waitForPopupOpeningAnimationFinished() {
LOGGER.info("waitForPopupOpeningAnimationFinished");
try {
waiter.withTimeout(Duration.ofMillis(POPUP_ANIMATION_WAIT_TIME)).until(ExpectedConditions.visibilityOfElementLocated(BY_POPUP_TOTALLY_OPENED_LOCATOR));
} catch (TimeoutException e) {
LOGGER.info("Forcing popup to be visible");
try {
js.executeScript("var elementsToBeRemoved = document.getElementsByClassName('" + CLASS_GWT_DIALOG_BOX + "');" //
+ "if(elementsToBeRemoved.length>0){" //
+ "elementsToBeRemoved[0].style.overflow='visible';" //
+ "elementsToBeRemoved[0].style.clip='rect(auto,auto,auto,auto)';" //
+ "}");
} catch (JavascriptException e2) {
LOGGER.warn("Could not find popup to force to be open", e2.getCause());
}
}
}
public void waitForPopupClosingAnimationFinished() {
try {
LOGGER.info("waitForPopupClosingAnimationFinished");
waiter.withTimeout(Duration.ofMillis(POPUP_ANIMATION_WAIT_TIME)).until(ExpectedConditions.invisibilityOfElementLocated(BY_POPUP));
waiter.withTimeout(Duration.ofMillis(POPUP_ANIMATION_WAIT_TIME)).until(ExpectedConditions.invisibilityOfElementLocated(BY_GLASS_PANEL));
} catch (TimeoutException e) {
LOGGER.info("Waiting for popup closing did not work as expected, forcing it");
javascriptRemovalOfElementByClassName(CLASS_GWT_DIALOG_BOX);
javascriptRemovalOfElementByClassName(CLASS_GWT_POPUP_PANEL_GLASS);
}
}
private void javascriptRemovalOfElementByClassName(String className) {
LOGGER.info("javascriptRemovalOfElementByClassName : {}", className);
try {
js.executeScript("var elementsToBeRemoved = document.getElementsByClassName('" + className + "');" //
+ "if(elementsToBeRemoved.length>0){" //
+ "elementsToBeRemoved[0].style.visibility = 'hidden';" //
+ "elementsToBeRemoved[0].style.display = 'none';" //
+ "elementsToBeRemoved[0].className='shouldHaveBeenRemovedFromDom';" //
+ "}");
LOGGER.info("Successfully removal of element with class {}", className);
} catch (JavascriptException jse) {
LOGGER.info("Apparently element with class {}} does not exists, we are good to go : {}", className, jse.toString());
}
}
public void waitForPopupClosingAnimationFinished() {
try {
LOGGER.info("waitForPopupClosingAnimationFinished");
waiter.withTimeout(Duration.ofMillis(POPUP_ANIMATION_WAIT_TIME)).until(ExpectedConditions.invisibilityOfElementLocated(BY_POPUP));
waiter.withTimeout(Duration.ofMillis(POPUP_ANIMATION_WAIT_TIME)).until(ExpectedConditions.invisibilityOfElementLocated(BY_GLASS_PANEL));
} catch (TimeoutException e) {
LOGGER.info("Waiting for popup closing did not work as expected, forcing it");
javascriptRemovalOfElementByClassName(CLASS_GWT_DIALOG_BOX);
javascriptRemovalOfElementByClassName(CLASS_GWT_POPUP_PANEL_GLASS);
}
}
我在展开/折叠 TreeNode 动画时也遇到了同样的问题,因此我实现了在节点展开/折叠后启动的以下功能:
private static final String X_EXPANDING_TREE_NODE =
"//div[contains(@role, 'treeitem') and @aria-expanded='true']/div[contains(@style, 'overflow: hidden;') and contains(@style, 'height:') and contains(@style, 'position: relative;')]/div[contains(@style, 'top:') and contains(@style, 'position: relative;')]";
private static final By BY_EXPANDING_TREE_NODE = By.xpath(X_EXPANDING_TREE_NODE);
private static final String X_COLLAPSING_TREE_NODE =
"//div[contains(@role, 'treeitem') and @aria-expanded='false']/following:div[contains(@style, 'overflow: hidden;') and contains(@style, 'height:') and contains(@style, 'position: relative;')]/div[contains(@style, 'top:') and contains(@style, 'position: relative;')]";
private static final By BY_COLLAPSING_TREE_NODE = By.xpath(X_COLLAPSING_TREE_NODE);
/**
* When a report tree node is expanding or collapsing is loading, we can see stuff like
* <div style="overflow: hidden; height: 1px; position: relative;">
* <div style="top: -122px; position: relative;">
* <p>
* We wait for it do disappear
* <p>
* or we force it to animation finished like this for expanding:
*
* <div style="overflow: hidden;">
* <div style="">
*
* or we force it to animation finished like this for collapsing:
*
* <div style="overflow: hidden; display : none;">
* <div style="">
*/
public void waitForTreeNodeExpandedAndCollapsed() {
waitForTreeNodeExpanded();
waitForTreeNodeCollapsed();
}
public void waitForTreeNodeExpanded(){
waitForTreeNode(true);
}
public void waitForTreeNodeCollapsed(){
waitForTreeNode(false);
}
private void waitForTreeNode(boolean expanding) {
try {
waiter.withTimeout(Duration.ofSeconds(2)).until(ExpectedConditions.invisibilityOfElementLocated(expanding?BY_EXPANDING_TREE_NODE:BY_COLLAPSING_TREE_NODE ));
} catch (TimeoutException e) {
boolean noJavascriptException = false;
while (!noJavascriptException) { // While javascript throw error like InvalidStateError, we continue
try {
LOGGER.info("Waiting for tree expand or collapse did not work as expected, forcing it");
js.executeScript("while(true){" //
+ "var iterator = document.evaluate(\"" + (expanding?X_EXPANDING_TREE_NODE:X_COLLAPSING_TREE_NODE) + "\",document, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null );" //
+ "var thisNode = iterator.iterateNext();" //
+ "if(thisNode) {" //
+ "thisNode.parentNode.style='overflow: hidden;" + (expanding?"":"display: none;") + "';"//
+ "thisNode.style='';"
+ "thisNode = iterator.iterateNext();" //
+ "}else {" //
+ "break;" //
+ "}" //
+ "}");
noJavascriptException = true;
} catch (Exception e2) {
LOGGER.info("Javascript pb", e2.getCause());
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.