12

I'm writing protractor tests and like it, although sometimes seem to get caught up on something that seems as if it should be simple. For example, I want to loop through all of the buttons on one of our pages that have text of 'Nominate'. There are dozens on the page, but only 1 or 2 will be visible. So I want to click the first one that is. Here's the code I'm using currently:

    var nominateButtons = element.all(by.buttonText('Nominate'));
    nominateButtons.then(function(els){
        for(var x = 0;x < els.length;x++){
            //Since isDisplayed returns a promise, I need to do it this way to get to the actual value
            els[x].isDisplayed().then(function(isVisible){
                //isVisible now has the right value                 
                if(isVisible){
                    //But now els is not defined because of the scope of the promise!!
                    els[x].click();
                }
            });
        }
    });

When I run this code, I get a 'cannot call method click of undefined' error, because els[x] is no longer in scope, but I can't seem to check the visibility without using the promise. So my question is, how can you loop through a collection of elements, check their visibility, and click the first one that is visible? (I'm trying not to use an expect to check visibility because I know that most of the buttons won't be visible)

Thanks in advance

user2233949
  • 2,053
  • 19
  • 22

1 Answers1

16

els is defined. What's not defined is x. The easy way to do it is:

var nominateButtons = element.all(by.buttonText('Nominate'));
var displayedButtons = nominateButtons.filter(function(elem) {
   return elem.isDisplayed(); 
});
displayedButtons.first().click();

or

element.all(by.buttonText('Nominate')).
  filter(function(elem) {
    return elem.isDisplayed(); 
  }).
  first().
  click();

EDIT, by the way, you shouldn't rely on this behavior (click first button of text 'Nominate') because it will lead to issues when you change your app. See if you can either select by ID, or select a more specific 'Nominate' like element(by.css('the section the nominate is under')).element(by.buttonText('Nominate'));

EDIT again: see Using protractor with loops for explanation

Community
  • 1
  • 1
hankduan
  • 5,994
  • 1
  • 29
  • 43
  • Thank you so much. I've been fighting with this all day. Not only did I not realize that x was the culprit, I wasn't aware of the first() function you referenced, so I'm very thankful. I'm aware that clicking the first element isn't a good idea, and normally I'd avoid it. This is somewhat of an odd case though, and it actually should work, although I'm aware I may end up biting my tongue on that. In any event, I really appreciate you taking the time to answer. It got my test case working finally, and taught me a couple things along the way. I'm a python guy and fairly new to JS and promises – user2233949 Jan 13 '15 at 00:17
  • You're welcome. You should accept the answer if it helped you instead of responding in a new answer though =) – hankduan Jan 13 '15 at 01:05
  • Is there a second function like first :) I want to click on all three elements that I find, one by one. first() works like charm, but how do I click the next two? – prakash krishnan Aug 29 '16 at 12:11
  • Got the code for this. It would be get(n). example would be: `element.all(by.css('value')).get(1).click();` – prakash krishnan Aug 29 '16 at 12:28