Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Selenium WebDriverJS thenCatch not catching StaleElementException

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:

  1. 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);
    }
    
  2. waiting an explicit time period (driver.sleep(1000)) before finding and clicking on the element
  3. finding the element multiple times (using .findElement()) before clicking on the element
  4. using 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();
    });
    
  5. 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);
    });
    
  6. methods 4 and 5 using the errback argument of .then() in place of .thenCatch()
  7. combinations of the above

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?

like image 982
eytac Avatar asked Oct 31 '22 00:10

eytac


1 Answers

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);
});
like image 123
mido Avatar answered Nov 08 '22 07:11

mido