Skip to content Skip to sidebar Skip to footer

Protractor Stale Element Reference When Using The Each() Method

I am getting the error below as a result of using the each() method in protractor. It has worked fine in the past but is now consistently failing with this error. Failed: stale el

Solution 1:

In a nutshell, it happens because each() just fires commands simultaneously against all elements. In your case you probably need to go this way element.all(bars).getCssValue('width')).then(array => {/*do what you want with returned array here*/})

* Edited *

What you want to do is

element.all(bars).getCssValue('width')).then(array => {
    // tests is at least one element will not be "0px"let elementHasWidth = array.some(elem => elem !== "0px");
    expect(elementHasWidth).toBe(true);
})

Solution 2:

New problems like this with existing code are likely due to Protractor and Selenium evolving from Web Driver Control Flow and WebDriverJS Promise Manager to native promises. You used to be able to write code that looked like synchronous code and under the hood the toolkits would convert it to asynchronous code that waited for the DOM to load and JavaScript on the page to run. Going forward you need to convert your code to explicitly use async/await or promises. (See the reasoning behind the Selenium change here.)

Unfortunately a lot of old (pre 2019) examples, even in the Protractor documentation, are written in synchronous style, so you should be careful about following them. Even more unfortunately, you cannot mix async code with Control Flow code, and if you try, all the Control Flow will be disabled, and probably some of your tests that relied on it will start failing.

By the way, what is the value of bars? A Protractor by object works differently than a native WebDriver locator and I'm not sure they are reusable. Try using by.name('bars') or whatever instead of bars.

Your case is tricky because of all the promises involved. element.getCssValue returns a promise. Since you are trying to get a true or false value out of this, I suggest using a reducer.

let nonZero = element.all(by.name('bars')).reduce((acc, elem) => {
  return acc || elem.getCssValue('width').then( (width) => width > 0 );
}, false);

In a more complicated situation, you could use all().each() but you have to be careful to ensure that nothing you do inside each affects the DOM, because once it does, it potentially invalidates the rest of the array.

If you are potentially modifying the page with your ultimate action, then, as ugly as it may seem, you need to loop over finding the elements:

for (var i = 0; true; i++) {
  let list = element.all(by.css('.items li'));
  if (i >= await list.count();)
    break;
  list.get(i).click();
};

Solution 3:

Try using this code before your error statement:

  browser.wait(function() {
        return element.all(bars).isPresent().then(function(result) {
            return result;
        });
    }, 5000);

Solution 4:

Thanks to everyone who helped. The solution was retry again if it resulted in error. Due to the realtime nature of the element, the element reference was changing before it had got the css width.

Post a Comment for "Protractor Stale Element Reference When Using The Each() Method"