GLK
GLK

Reputation: 1035

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

After launching a bootstrap modal and closing it, selenium is not able to find any other element on the page.

Three buttons are shown in screen below. Out of which function of 2 buttons is to launch a bootstrap modal and close it, and function of third button ( middle one ) is to simply receive a "click".

When tested individually, test for all 3 buttons works well, but when tested collectively it fails.

First time a test which launch a modal and close it, will pass, but subsequent test fails with ElementClickInterceptedError.

There are sufficient implicit waits in between so that modal can load properly, still issue persist.

PS - In case you need to try at your end, follow these steps 1) copy below 2 files 2) install selenium webdriver using npm install selenium-webdriver 3) change fileName variable in test as per your own folder.

Image 1 - buttons

Image 2 - modal 1

Error Stacktrace

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',

Test Script

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 page

<!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>

Upvotes: 6

Views: 2129

Answers (3)

Roei Sabag
Roei Sabag

Reputation: 111

To bypass ElementNotClickable (this is web-driver logic), you can just use JavaScript click.

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

Upvotes: 0

undetected Selenium
undetected Selenium

Reputation: 193188

This error message...

[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)

...implies that the desired element wasn't clickable when you invoked click() on it.


As you mentioned test which launch a modal and close it, will pass, but subsequent test fails with ElementClickInterceptedError essentially means that when you invoked click() on the second element Button at that point of time the Modal Dialog Box containing the element with text as Launch modal was still obsecuring the desired element. Hence the click on the desired element failed.

Details

To start with implicit wait may not be effective with modern application built using JavaScript, Angular, ReactJS, jQuery, AJAX, Vue.js, Ember.js, etc.

Effectively you need to replace implicit wait with explicit wait.


selenium-webdriver/lib/until

  • elementLocated: Creates a condition that will loop until an element is found with the given locator.

    /**
     * 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.

    /**
     * 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);
      });
    };
    

Solution

So your effective code block replacing implicit wait with explicit wait will be:

(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();
    }
    }
)()

References

You can find a couple of related discussions in:

Upvotes: 0

Madhan
Madhan

Reputation: 5818

You don't need to use the implicitwait everytime. What you need to wait for is the visibility of the elements before doing the click operations.

There is a elementIsVisible method available in the wait condition. I've modified the code to check for the presence of the element and then the visibility of the element before doing the click.

(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();
    }
}
)()

Upvotes: 4

Related Questions