2

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.

Karthik Balakrishnan
  • 4,353
  • 6
  • 37
  • 69

4 Answers4

4

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

maurycy
  • 8,455
  • 1
  • 27
  • 44
  • But the `for` loop still won't execute because `navItems.count()` is still not going to be equal to the length of `navItems` when the loop's execution commences. SIDE NOTE: I know I could just use `expectedValue.length` but I'd like to know how to use a `for` loop when the length is unknown. – Karthik Balakrishnan Jul 22 '15 at 11:58
  • I've edited my answer by creating the expectations when elements are returned – maurycy Jul 22 '15 at 12:35
2

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!

Matthew Marichiba
  • 1,942
  • 1
  • 23
  • 24
0

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()); 
Community
  • 1
  • 1
Rahul Vig
  • 716
  • 1
  • 7
  • 24
0

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);
                    });
                });
Balakrishnan
  • 279
  • 2
  • 7