0

In the code below, I'm using a repeater to get the values from ng-repeat and getting the column by cat.name which gives me an array of all the cat names. So I'm doing a for loop to get to a particular name, say Meow, I want to store the index value, so that I can validate whether the corresponding row, the cat's age to be equal to 10. The 2 console.log of index inside the for loop results in different values, the first one ranges from 0-10 (considering the length of the array as 10) and the next always results in 10. It logs "10" 10 times. Now, I cannot get the index to validate the corresponding row, since i is always 10. Can someone correct me where I'm going wrong and also answer me with the modified code. I guess I'm going wrong in chaining the promise

  element(by.repeater(cat in cats).column(cat.name)).then(function(fields) {
    for (var i in fields) {                // promise returns an array fields
      console.log(i);                      // values range from 0 to length of the fields(say 10)
      fields[i].getText().then(function(fieldValue) {
        console.log(i);                    // values are always 10 (i.e the length of the array)
        if(fieldValue === 'Meow') {
          var catAge= element(by.repeater('cat in cats').row(i)).element(by.model('cat.age')); // row(i) always gives the last value
          expect(catAge.getAttribute('value')).toBe('10');
        }
      })
    }
  });
9094US
  • 7
  • 7
  • 1
    Possible duplicate of [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – James Jan 24 '18 at 15:31
  • From what I'm seeing, you're both looping and looking for a certain element (text with `Meow`). Are you aware of the [each()](http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.each) and [filter()](http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.filter) functions? Might fix your issue and would also make your code significantly cleaner/more readable – Gunderson Jan 24 '18 at 17:59

1 Answers1

0

The issue come from below snippet, the first console.log() is executed sync, but the second console.log() will be executed async, because it inside getText().then(), as we know all Protractor api is async and return a Promise.

for (var i in fields) { 
      console.log(i); 
      fields[i].getText().then(function(fieldValue) {
        console.log(i); 

So the actual execution procedure of above for loop should be like this:

when i=1
    first console.log() execute done, it print out 1
    fields[i].getText() execute done, it return a Promise,
    and push the promise into Protractor control flow,
    actually getText() not to read the text from page,
    because it's execute async.


when i=2
    first console.log() execute done, it print out 2
    fields[i].getText() execute done, it return a Promise,
    and push the promise into Protractor control flow,
    actually getText() not to read the text from page,
    because it's execute async.

....

when i=10
    the loop end,  
    you get 1, 2 .. 9, 10 printed out
    Protractor control flow get a promise queue
    Now Protractor control flow start to execute the promise queue

    Protractor push out the fist promise in queue
    Important!!! the i=10 now
    fields[i].getText() will be fields[10].getText()
    so you will get fields[10].getText() for 10 times

Option 1) use javascript closure as Jamines comment said, a little changes on your current code

element(by.repeater(cat in cats).column(cat.name)).then(function(fields) {
  for (var i in fields) {
    console.log(i);

    (function(index){ // change part

      fields[index].getText().then(function(fieldValue) {
        console.log(index);  
        if(fieldValue === 'Meow') {
          var catAge= element(by.repeater('cat in cats').row(index)).element(by.model('cat.age'));
          expect(catAge.getAttribute('value')).toBe('10');
        }
      })

    })(i); // change part

  }
});

Option 2 use Protractor filter()

element(by.repeater(cat in cats).column(cat.name)).then(function(fields) {
  for (var i in fields) {
    console.log(i);
    var matchedIndex = -1;

    fields
      .filter(function(item, index){
        if( matchedIndex > -1) { 
          // means had found the matched one, directly return false to
          // skip read text from page to reduce exection time
          return false;
        }
        else {
          return item.getText().then(function(name){
             if(name === 'Meow') {
               matchedIndex = index;
               return true;
             }
             return false;
          })

        }
      })
      .then(function(){
        console.log('matchedIndex: ' + matchedIndex);  
        var catAge= element(by.repeater('cat in cats').row(matchedIndex)).element(by.model('cat.age'));
        return expect(catAge.getAttribute('value')).toBe('10');
      });
  }
});
yong
  • 13,357
  • 1
  • 16
  • 27