tool
tool

Reputation: 356

Creating promises in a forEach loop and using them without nesting

I am using selenium webdriver and I get specific elements by id. After getting the elements with the correct id, I try to pick 1 element from the list which has a unique attribute value.

I have managed to get to the values I need, but I'm not happy with the solutions I found.

Situation:

var attributeName = "bla";
var id = "icon";    
var iconElementsPromise = driver.findElements(By.id(id)); // returns a promise containing an array with WebElements

Solution 1: nested then's: This just looks so wrong, but it works.

  iconElementsPromise
    .then(function(iconElements) {
      iconElements.forEach(function(element, index) {
        element.getAttribute(attributeName)
          .then(function(attribute) {
            console.log("attribute: " + attribute);
          });
      });
    });

Solution 2: just 1 nested then: It looks better but now I need to create an array of Promises, which is ok... but I want NO Nesting at all

  iconElementsPromise
    .then(function(iconElements) {
      var promises = [];
      iconElements.forEach(function(element, index) {
        promises.push(element.getAttribute("tag"));
      });
      return promises;
    })
    .then(function(promises) {
      Promise.all(promises)
        .then(function(attributes) {
          console.log("attributes: " + attributes);
        });
    });

Solution 3:By returning the Promise.all(promises) I can stay on the same indet-level and don't nest the thens.

  iconElementsPromise
    .then(function(iconElements) {
      var promises = [];
      iconElements.forEach(function(element, index) {
        promises.push(element.getAttribute(attributeName));
      });
      return promises;
    })
    .then(function(promises) {
      return Promise.all(promises);
    })
    .then(function(attributes) {
      console.log("attributes: " + attributes);
    });

Solution 1 has 2 thens and gets each attribute

Solution 2 and 3 have 3 thens each, and get me the attributes array

Getting each attribute or just the array is ok.

I do believe that Solution 3 is more or less what I want. But the code is rather long. I get the feeling that there must be a better, more readable and shorter way to get the attributes.

So my question is: **What is the best way to get the attributes using promises? **

Examples appreciated.

Upvotes: 1

Views: 477

Answers (2)

Florent B.
Florent B.

Reputation: 42518

If your goal is to pick 1 element having a unique attribute, then it would be easier to include this attribute in a locator:

// select with an XPath the first element having an attribute "bla" and a parent with the "icon" id
var element = driver.findElement(By.xpath("id('icon')/*[@bla]"));

// select with a CSS selector the first element having the attribute "bla" and a parent with the "icon" id
var element = driver.findElement(By.cssSelector("#icon > [bla]"));

But if you really want all the attributes, then a short solution is to use webdriver.promise.map :

var webdriver = require('selenium-webdriver');
var promise = webdriver.promise;
var By = webdriver.By;
var Key = webdriver.Key;
var EC = webdriver.until;

var driver = new webdriver.Builder()
  .withCapabilities({'browserName': 'firefox'})
  .build();

driver.get('http://stackoverflow.com/');

driver.findElements(By.xpath('//a')).then(function(elts) {
  promise.map(elts, function(elt) {
    return elt.getAttribute("href");
  }).then(function(links) {
    console.log(links);
  })
});

Upvotes: 1

Bhabishya Kumar
Bhabishya Kumar

Reputation: 731

A little shorter version of 3 using map to reduce one then while still keeping it readable

iconElementsPromise
.then(function(iconElements) {
  return Promise.all(iconElements.map(function(element){
      return element.getAttribute(attributeName);
  }));
})
.then(function(attributes) {
  console.log("attributes: " + attributes);
});

Upvotes: 2

Related Questions