Working with Array of Elements in Protractor

12 / Feb / 2016 by Vishal Tyagi 1 comments

What is ElementArrayFinder?

ElementArrayFinder is an array of WebElements which is used to set up a chain of conditions that identify an array of elements. This allows you to perform actions (i.e. click, getText) on them as you would on an array of WebElements. The action will apply to every element identified by the ElementArrayFinder.

 

What is the issue working with ElementArrayFinder in Protractor?

While working with array of WebElements (ElementArrayFinder) in Protractor, I came across few exceptions while scripting e-to-e tests. One of them in “stale element reference exception”. This exception occurs because Protractor has lost the reference of the element that it is looking for or the element has been deleted entirely.

Being a Selenium (Java) guy I used similar techniques of handling lists of webelements what I normally do in Selenium Webdriver but sooner realize that I was on wrong track because I am working with JavaScript which is asynchronous in nature.

So let me explain you the case where I am wrong. As we all know that Java is synchronous so using for loop on arraylist of webelement is quit easy. Every instruction in program executed only when its previous one got finished. So I was trying same with Protractor but getting “stale element reference exception”. After debugging I found out that all this is happening because of Promises.

Lets take one example:

Promises (i.e. element(by…), element.all(by…)) execute their then functions when the underlying value becomes ready. What this means is that all the promises are first scheduled and then the then functions are run as the results become ready.

When you run something like this:

for (var j = 0; j < 3; ++j) {

console.log('1) j is: ', j);

getPromise().then(function() {

console.log('2) j is: ', j);

someArray[j] // 'j' always takes the value of 3

})

}

console.log('*  finished looping. j is: ', j);

 

What happens is that getPromise().then(function() {…}) returns immediately, before the promise is resolved and without executing the function inside the then. So first the loop runs through 3 times, scheduling all the getPromise() calls. Then, as the promises resolve, the corresponding then’s are run.

The result on console would look something like this:

1) j is: 0 // schedules first `getPromise()`

1) j is: 1 // schedules second `getPromise()`

1) j is: 2 // schedules third `getPromise()`

*  finished looping. j is: 3

2) j is: 3 // first `then` function runs, but j is already 3 now.

2) j is: 3 // second `then` function runs, but j is already 3 now.

2) j is: 3 // third `then` function runs, but j is already 3 now.

 

What’s the solution?

So, the question is how do you loop across array of webelements?

Well there are few ways which you can use to solve this problem:

  1. By functions provided by Protractor such as maps, filters, each.
  2. By using closure

 

For now lets concentrate on solution as mention in point 1 i.e map function provided by Protractor:

var list1= element.all(by.css('div#topmenu>ul>li:nth-of-type(2)>div>div>div'));

list1.map(function(links){

return links;

}).then(function(links){

for (var i = 0; i < links.length; i++) {

links[i].$('h3>a').getText().then(function(text){

console.log('text of elem1 is: '+text);

if(text === ‘pink’){

links[i].$('h3>a').click();

}

})

}

})

What happens in above example is, map iterate through each element within the ElementArrayFinder and store them in links array which inturn can be used with for loop to perform various actions on each element like in above code, getting text of each element and then based on comparison, performing clicking operation.

Hope the above solution works. Please let me know if you have any queries / additions in the comment box below.

Tag -

FOUND THIS USEFUL? SHARE IT

comments (1 “Working with Array of Elements in Protractor”)

Leave a comment -