Select first visible element in protractor

2019-04-05 02:20发布

问题:

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

回答1:

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