Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Selenium click only the visible element, if the xpath returns 2 WebElements

I have a responsive design, so many items I need to interact with have 2 WebElements. One for Desktop and one for Mobile, and I am trying to use PageFactory. Here's what I have now to identify and interact with the element.

//this returns 2 webelements, one for desktop and one for mobile
    @FindBy(xpath = "//selector-dropdown/p")
    private WebElement dropdown;

    public void ClickDropdown() throws InterruptedException {       
        WebDriverWait wait = new WebDriverWait(driver, 15);
        wait.until(ExpectedConditions.visibilityOf(dropdown)).click();

    }

I was under the impression that ExpectedConditions.visibilityOf(WebElement) would find the first element visible. Right now when I open the app on Desktop, it finds the element (Desktop is first in the DOM). But on Mobile, it times out waiting for visibility, because it is stuck waiting for the first one to become visible.

My alternative is using @FindBy to declare every element twice, and making an if statement to decide which path to take. Is this extra work the only way to make it work?

like image 790
dsidler Avatar asked Mar 22 '17 15:03

dsidler


1 Answers

Your assumption "... ExpectedConditions.visibilityOf(WebElement) would find the first element visible." is wrong! You will need to declare your WebElement as a List, find all of them, and pick the one that is visible. Here is a sample that I used successfully in the past:

@FindBy(xpath = "//input[@ng-model='loginData.username']")
private List<WebElement> txtUsername;

public String getUsername() {
    driverWait.until(CustomExpectedConditions.presenceOfAllElements(txtUsername));
    for (WebElement oneUsername : txtUsername) {
        if (oneUsername.isDisplayed())
            return oneUsername.getAttribute("value");
    }
    return null;
}

public void setUsername(String username) {
    driverWait.until(CustomExpectedConditions.presenceOfAllElements(txtUsername));
    for (WebElement oneUsername : txtUsername) {
        if (oneUsername.isDisplayed()) {
            oneUsername.clear();
            oneUsername.sendKeys(username);
            break;
        }
    }
}

Based on discussion elsewhere, here is the CustomExpectedConditions:

public class CustomExpectedConditions {
    /**
     * Based on {@link ExpectedConditions#presenceOfAllElementsLocatedBy(org.openqa.selenium.By)}.
     * 
     * @param elements
     * @return
     */
    public static ExpectedCondition<List<WebElement>> presenceOfAllElements(
            final List<WebElement> elements) {
        return new ExpectedCondition<List<WebElement>>() {

            @Override
            public List<WebElement> apply(WebDriver driver) {
                // List<WebElement> elements = findElements(locator, driver);
                return elements.size() > 0 ? elements : null;
            }
        };
    }
}
like image 73
SiKing Avatar answered Sep 28 '22 07:09

SiKing