Karthik Balakrishnan
Karthik Balakrishnan

Reputation: 4383

Looping over elements and testing them individually in protractor

Is there a way to implement loops so that the following code can be reduced?

    var navItems = element.all(by.repeater('item in mc.navItems'));
    expect(navItems.count()).toEqual(4);

    expect(navItems.get(0).getText()).toEqual('page1'.toUpperCase());
    navItems.get(0).click();
    expect(browser.getLocationAbsUrl()).toEqual('/page1');

    expect(navItems.get(1).getText()).toEqual('page2'.toUpperCase());
    navItems.get(1).click();
    expect(browser.getLocationAbsUrl()).toEqual('/page2');

    expect(navItems.get(2).getText()).toEqual('page3'.toUpperCase());
    navItems.get(2).click();
    expect(browser.getLocationAbsUrl()).toEqual('/page3');

    expect(navItems.get(3).getText()).toEqual('page4'.toUpperCase());
    navItems.get(3).click();
    expect(browser.getLocationAbsUrl()).toEqual('/page4');

I've tried multiple ways but they all fail because I'm not sure how to get them to wait until the promised value is returned.

Here's one way that fails:

var navItems = element.all(by.repeater('item in mc.navItems'));
for(var i = 0; i < navItems.count(); i++) {
    expect(navItems.get(i).getText()).toEqual(expectedValue[i].toUpperCase());
    navItems.get(i).click();
    expect(browser.getLocationAbsUrl()).toEqual('/' + expectedValue[i]);
}

where expectedValue = ['page1', 'page2', 'page3', 'page4'];

The problem with this is that the for loop evaluates the condition even before the promised value is returned and so the contents of the loop are never executed.

I'm sure I can use a then and attach a callback function to count() but that would only make things messy because I'd have to make navItems a global variable so that it can be accessed by the callback function and I also need to make sure that navItems is populated before the callback function is executed.

I'm aware that the expect() method takes care of promises in a nice clean way, I would like to implement something like this.

Upvotes: 2

Views: 5217

Answers (4)

Balakrishnan
Balakrishnan

Reputation: 299

Step 1 : first get the list of elements

let listOfElements = element.all(by.repeater('item in mc.navItems'));    

Step 2: Iterate the element using below line

listOfElements.each(function (element, index) {
                    element.getText().then(function (text) {
                        console.log(index, text);
                    });
                });

Upvotes: 0

Matthew Marichiba
Matthew Marichiba

Reputation: 2092

I don't know if the each() method was available at the time this question was asked, but these days I'm pretty sure each() is the best approach.

In the stated context, the answer would look something like:

var navItems = element.all(by.repeater('item in mc.navItems'));

navItems.each((element, index) => {
  expect(element.getText()).toEqual(('page'+index).toUpperCase());
  element.click();
  expect(browser.getLocationAbsUrl()).toEqual('/page'+index);
});

Much better than calling out each element individually!

Upvotes: 2

Rahul Vig
Rahul Vig

Reputation: 736

I think the problem that you have is not with the delay in return of promise but with closure. Take a look here-Using protractor with loops You can also use a recursion loop with if instead of using for as it is simpler-

function loop(i){
     if(i>=navItems.count())
              {
                return null;
              }
              else
              {
                expect(navItems.get(i).getText()).toEqual(expectedValue[i].toUpperCase());
        navItems.get(i).click();
        expectations.push(expect(browser.getLocationAbsUrl()).toEqual('/' + expectedValue[i]));
                })
            return loop(i+1)
          }
        }return loop(navItems.count()); 

Upvotes: 0

maurycy
maurycy

Reputation: 8465

I'm using protractor with cucumber and what I do is something like this

element.all(by.repeater('item in mc.navItems')).then(function (elements) {
  navItems = elements
  var expectations = []
  for (var i = 0; i < navItems.count(); i++) {
    expect(navItems.get(i).getText()).toEqual(expectedValue[i].toUpperCase());
    navItems.get(i).click();
    expectations.push(expect(browser.getLocationAbsUrl()).toEqual('/' + expectedValue[i]));
  }
  Q.all(expectations).should.notify(callback)
})

so the test will wait for all expectations to execute before notifying callback

Upvotes: 4

Related Questions