I am running node.js and Selenium WebDriverJS. One of my tests is failing with the following error:
UnknownError: unknown error: Runtime.evaluate threw exception: Error: element is not attached to the page document
I understand that this is essentially a StaleElementReferenceException, but I have not been able to find a reliable workaround. I have tried the following without success:
waiting for the element to appear on the page before finding and clicking on the element
waitForElement: function (selector, timeout) {
if (typeof(timeout) === 'undefined') { timeout = 3000; }
driver.wait(function() {
return driver.findElements(selector).then(function(list) {
return list.length > 0;
});
}, timeout);
}
driver.sleep(1000)
) before finding and clicking on the element.findElement()
) before clicking on the elementusing a promise chain to catch any errors and try re-clicking on the element
driver.getTitle().then(function(title) {
driver.findElement(webdriver.By.xpath(...)).click();
}).thenCatch(function(e) {
driver.findElement(webdriver.By.xpath(...)).click();
});
using a promise chain with a recursive function to keep trying to re-click the element
var getStaleElement = function(selector, callback) {
var element = driver.findElement(selector);
callback(element);
}).thenCatch(function(e) {
getStaleElement(selector, callback);
});
var clickSelf = function(ele) { return ele.click() };
driver.getTitle().then(function(title) {
driver.findElement(webdriver.By.xpath(...)).click();
}).thenCatch(function(e) {
getStaleElement(webdriver.By.xpath(...), clickSelf);
});
.then()
in place of .thenCatch()
It seems like Selenium is not able to catch this specific error. I used print statements to confirm that other errors such as NoSuchElementError are caught by .thenCatch()
. Is there a workaround that will allow me to deal with stale elements?
I had a similar problem, so I made the below work-around, you can give it a try ...
/*
* params.config - {
* opposite - {Boolean} - if true, will wait till negative result is reached/ error is thrown.
* maxWaitTime - {Number} - if this time exceeds, just throw an error and leave.
* waitTime - {Number} - wait time between two checks.
* expectValue - {Boolean} - where you just want it to run without error, or it should expect a value
* expectedValue - {Object} - object value it should or should not match.
* }
* params.fn - a function that returns a promise that we want to keep checking till desire value is reached
*/
function waiter(fn, config){
config = config || {};
var deffered = Driver.promise.defer(),
wt = config.waitTime || 100,
mwt = config.maxWaitTime || 3000,
timeoutReached = false,
pCall = function(){
fn().then(pThen, pCatch);
},
pThen = function(data){
if(timeoutReached) return;
if(config.expectValue){
if(config.opposite){
if(data == config.expectedValue){
setTimeout(pCall, wt);
}else{
clearTimeout(vTimeout);
deffered.fulfill(true);
}
}else{
if(data == config.expectedValue){
clearTimeout(vTimeout);
deffered.fulfill(true);
}else{
setTimeout(pCall, wt);
}
}
}else{
deffered.fulfill(true);
}
},
pCatch = function(err){
if(timeoutReached) return;
if(config.opposite){
deffered.fulfill(true);
}else{
setTimeout(pCall, wt);
}
};
pCall();
var vTimeout = setTimeout(function(){
timeoutReached = true;
if(config.opposite){
deffered.fulfill(true);
}else{
deffered.reject(new Error('timed-out'));
}
}, mwt);
return deffered.promise;
}
example usage( for your case):
var myPromise = function(){
return driver.findElement(webdriver.By.xpath(...)).click();
};
//default use
waiter(myPromise).then(function(){
console.log('finally...');
}).catch(fucntion(err){
console.log('not working: ', err);
});
// with custom timeout after 10 seconds
waiter(myPromise, {maxWaitTime: 10000}).then(function(){
console.log('finally...');
}).catch(fucntion(err){
console.log('not working: ', err);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With