Reputation: 1035
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.
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">×</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">×</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
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
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.
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.
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);
});
};
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();
}
}
)()
You can find a couple of related discussions in:
Upvotes: 0
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