Selenium并不总是识别何时从DOM中删除元素

时间:2017-02-15 22:25:06

标签: java selenium webdriver automated-tests selenium-chromedriver

我在Mac 10.12.3上使用chrome 56和chromedriver 2.27时遇到了问题,虽然我尝试了几个不同版本的驱动程序和几个不同版本的浏览器,并且遇到了同样的问题。在UI中,我可以非常清楚地看到阻挡层已经清除,但是硒仍然认为它没有。等待阻止层清除后(只看浏览器),如果我捕获了driver.getPageSource()的结果,我就得到了旧的'页面源,而不是新的页面源(删除了阻止层)。当我将旧页面源视为html doc时,我可以看到阻塞层。当我查看测试结束时拍摄的屏幕截图时,显然没有阻挡层,并且对DOM的手动检查显示该元素已被移除。不知何故,selenium似乎正在缓存旧的页面源,并且无法识别何时从DOM中删除了一个元素。我似乎无法在不完全重新加载页面的情况下强制它刷新缓存的(?)html。我想避免重新加载页面,因为这会使阻塞层是否被正确删除的测试失效。

我尝试使用阻塞层元素获取一些信息(使用像element.isDisplayed()或element.getLocation()这样的良性信息),这些信息仍然表现得好像元素仍然存在。

如何处理此问题的任何建议将不胜感激。

3 个答案:

答案 0 :(得分:2)

如果您的目标是点击该项目而不管阻止图层的状态,您可以使用内联javascript来点击元素。示例代码如下。

    try {
            e.click();
        } catch (org.openqa.selenium.WebDriverException E1) {
            ((JavascriptExecutor) driver).executeScript("arguments[0].click();", e.findElements(By.xpath(".//a")).get(0));
        }

答案 1 :(得分:1)

在通过GWT弹出面板运行Selenium测试时,我遇到了同样的问题。

有时动画无法完成,弹出窗口和后面的玻璃留在DOM上。

发生这种情况时,我尝试从dom中删除这两个元素,但结果却很奇怪(从dom中删除了其他元素!!)。

最后,我实现了这些函数,它们隐藏了这两个元素并更改了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);
    }
}

答案 2 :(得分:0)

我在扩展/折叠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());
            }
        }
    }
}