Selenium - 在 bootstrap 模态测试之后,后续测试失败并出现错误 ElementClickInterceptedError

Selenium - after bootstrap modal test, subsequent test fail with error ElementClickInterceptedError

启动 bootstrap 模式并关闭它后,selenium 无法在页面上找到任何其他元素。

下面的屏幕显示了三个按钮。其中 2 个按钮的功能是启动 bootstrap 模态并关闭它,第三个按钮(中间一个)的功能是简单地接收 "click"。

单独测试时,对所有 3 个按钮的测试都很好,但是当一起测试时,它失败了。

第一次启动模式并关闭它的测试将通过,但后续测试失败并出现 ElementClickInterceptedError。

中间有足够的隐式等待,以便模式可以正确加载,但问题仍然存在。

PS - 如果您需要最后尝试,请按照以下步骤操作 1) 复制以下 2 个文件 2) 使用 [=13= 安装 selenium webdriver ] 3) 根据您自己的文件夹更改测试中的 fileName 变量。

错误堆栈跟踪

DevTools listening on ws://127.0.0.1:50210/devtools/browser/81f6bc5f-c6f5-4255-9134-5efa67a92bed [13108:12832:0501/100716.495:ERROR:browser_switcher_service.cc(238)] XXX Init() ElementClickInterceptedError: element click intercepted: Element ... is not clickable at po int (233, 67). Other element would receive the click: ... (Session info: chrome=81.0.4044.129) at Object.throwDecodedError (D:\ip300-gk\node_modules\selenium-webdriver\lib\error.js:550:15) at parseHttpResponse (D:\ip300-gk\node_modules\selenium-webdriver\lib\http.js:565:13) at Executor.execute (D:\ip300-gk\node_modules\selenium-webdriver\lib\http.js:491:26) at processTicksAndRejections (internal/process/task_queues.js:93:5) at async Driver.execute (D:\ip300-gk\node_modules\selenium-webdriver\lib\webdriver.js:700:17) at async uitest (D:\ip300-gk\Samples\bootstrap\bs-modal-selenium\uitest.js:34:13) {
name: 'ElementClickInterceptedError',

测试脚本

const driver = require('selenium-webdriver')
const assert = require('assert').strict;
const {Builder, By, Key, until} = require('selenium-webdriver');

let fileName = "D:\ip300-gk\Samples\bootstrap\bs-modal-selenium\index.html"


function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}

(async function uitest() {
        let driver = await new Builder().forBrowser('chrome').build();
        let  element
        try {
            await driver.get(fileName)

            //Launch Modal 1 and close
            await driver.findElement(By.id('launchModalButton')).click()
            await driver.manage().setTimeouts( { implicit: 1000} )
            await driver.findElement(By.id('closeButton')).click()


            // middle button click 
             await driver.manage().setTimeouts( { implicit: 1000} )
             await driver.findElement(By.id('button')).click()

            //Launch Modal 2 and close
            await driver.manage().setTimeouts( { implicit: 1000} )
            await driver.findElement(By.id('launchModalButton_2')).click()
            await driver.manage().setTimeouts( { implicit: 1000} )
            element = await 
            driver.wait(until.elementLocated(By.id('closeButton_2')))
            await element.click()

        } catch (err) {
            console.log(err)
        } finally {
            await driver.quit();
        }
    }
)()

Bootstrap 页面

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
          integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
    <title>Selenium </title>
</head>
<body>

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
        integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
        crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"
        integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49"
        crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js"
        integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy"
        crossorigin="anonymous"></script>

<div class="container">

    <button type="button" id="launchModalButton" class="btn btn-primary mt-5" data-toggle="modal"
            data-target="#exampleModal">
        Launch modal
    </button>

    <button type="button" id="button" class="ml-3 btn btn-primary mt-5">
        Button
    </button>

    <button type="button" id="launchModalButton_2" class="ml-3 btn btn-primary mt-5" data-toggle="modal"
            data-target="#exampleModal_2">
        Launch modal 2
    </button>

    <div class="modal fade" id="exampleModal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel"
         aria-hidden="true">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="exampleModalLabel">Modal 1</h5>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    Modal 1
                </div>
                <div class="modal-footer">
                    <button id="closeButton" type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
                    <button id="saveChangesButton" type="button" class="btn btn-primary">Save changes</button>
                </div>
            </div>
        </div>
    </div>

    <div class="modal fade" id="exampleModal_2" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel_2"
         aria-hidden="true">
        <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="exampleModalLabel_2">Modal 2</h5>
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                        <span aria-hidden="true">&times;</span>
                    </button>
                </div>
                <div class="modal-body">
                    Modal 2
                </div>
                <div class="modal-footer">
                    <button id="closeButton_2" type="button" class="btn btn-secondary" data-dismiss="modal">Close
                    </button>
                    <button id="saveChangesButton_2" type="button" class="btn btn-primary">Save changes</button>
                </div>
            </div>
        </div>
    </div>
</div>

</body>
</html>

您不需要每次都使用 implicitwait。在进行点击操作之前,您需要等待的是元素的可见性。

等待条件下有一个elementIsVisible方法可用。我修改了代码以检查元素是否存在,然后在单击之前检查元素的可见性。

(async function uitest() {
    let driver = await new Builder().forBrowser('chrome').build();
    let element
    try {
        await driver.get(fileName)

        //Launch Modal 1 and close
        await driver.findElement(By.id('launchModalButton')).click()
        let closeButton1 = await driver.wait(until.elementLocated(By.id('closeButton')))
        closeButton1 = await driver.wait(until.elementIsVisible(closeButton1))
        await closeButton1.click()

        // middle button click 
        await driver.findElement(By.id('button')).click()

        //Launch Modal 2 and close
        await driver.findElement(By.id('launchModalButton_2')).click()
        let closeButton2 = await driver.wait(until.elementLocated(By.id('closeButton_2')))
        closeButton2 = await driver.wait(until.elementIsVisible(closeButton2))
        await closeButton2.click()

    } catch (err) {
        console.log(err)
    } finally {
        await driver.quit();
    }
}
)()

这个错误信息...

[13108:12832:0501/100716.495:ERROR:browser_switcher_service.cc(238)] XXX Init() ElementClickInterceptedError: element click intercepted: Element ... is not clickable at po int (233, 67). 
Other element would receive the click: ... (Session info: chrome=81.0.4044.129)

...表示当您在其上调用 click() 时所需的元素不可点击。


正如您提到的 启动模式并关闭它的测试将通过,但随后的测试失败并出现 ElementClickInterceptedError 本质上意味着当您在第二次调用 click() element Button 在那个时间点 Modal Dialog Box 包含文本为 Launch modal 的元素仍然模糊了所需的元素。因此,单击 所需元素失败。

详情

may not be effective with modern application built using JavaScript, Angular, ReactJS, jQuery, AJAX, Vue.js, Ember.js开始,等等

实际上你需要replace implicit wait with explicit wait


selenium-webdriver/lib/until

  • elementLocated:创建一个循环条件,直到找到具有给定定位器的元素。

    /**
     * Creates a condition that will loop until an element is
     * {@link ./webdriver.WebDriver#findElement found} with the given locator.
     *
     * @param {!(By|Function)} locator The locator to use.
     * @return {!WebElementCondition} The new condition.
     */
    exports.elementLocated = function elementLocated(locator) {
      locator = by.checkedLocator(locator);
      let locatorStr =
          typeof locator === 'function' ? 'by function()' : locator + '';
      return new WebElementCondition('for element to be located ' + locatorStr,
          function(driver) {
        return driver.findElements(locator).then(function(elements) {
          return elements[0];
        });
          });
    };
    
  • elementIsVisible: 创建一个等待给定元素可见的条件。

    /**
     * Creates a condition that will wait for the given element to become visible.
     *
     * @param {!./webdriver.WebElement} element The element to test.
     * @return {!WebElementCondition} The new condition.
     * @see ./webdriver.WebDriver#isDisplayed
     */
    exports.elementIsVisible = function elementIsVisible(element) {
      return new WebElementCondition('until element is visible', function() {
        return element.isDisplayed().then(v => v ? element : null);
      });
    };
    

解决方案

所以用显式等待替换隐式等待的有效代码块将是:

(async function uitest() {
    let driver = await new Builder().forBrowser('chrome').build();
    let  element
    try {
        await driver.get(fileName)

        //Click Launch Modal
        let launchModal = await driver.wait(until.elementLocated(By.id('launchModalButton')))
        launchModal = await driver.wait(until.elementIsVisible(launchModal))
        await launchModal.click()

        //Close Launch Modal
        let closeModal = await driver.wait(until.elementLocated(By.id('closeButton')))
        closeModal = await driver.wait(until.elementIsVisible(closeModal))
        await closeModal.click()

        //Click Button
        let button = await driver.wait(until.elementLocated(By.id('button')))
        button = await driver.wait(until.elementIsVisible(button))
        await button.click()

        //Click Launch Modal 2 and close
        let launchModal2 = await driver.wait(until.elementLocated(By.id('launchModalButton_2')))
        launchModal2 = await driver.wait(until.elementIsVisible(launchModal2))
        await launchModal2.click()

        //Close Launch Modal 2
        let closeButton2 = await driver.wait(until.elementLocated(By.id('closeButton_2')))
        closeButton2 = await driver.wait(until.elementIsVisible(closeButton2))
        await closeButton2.click()          

    } catch (err) {
        console.log(err)
    } finally {
        await driver.quit();
    }
    }
)()

参考资料

您可以在以下位置找到一些相关讨论:

要绕过 ElementNotClickable(这是网络驱动程序逻辑),您只需使用 JavaScript click。

var element = await driver.findElement(By.id('button'))
driver.executeScript('arguments[0].click();', element)