9

I want use wait.until(ExpectedConditions) with TWO elements. I am running a test, and I need WebDriver to wait until either of Element1 OR Element2 shows up. Then I need to pick whoever shows up first. I've tried:

WebDriverWait wait = new WebDriverWait(driver, 60); 
    wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//h2[@class='....']"))) || wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//h3[@class='... ']"))); 

        // Then I would need:

String result = driver.findElement(By.xpath("...")).getText() || driver.findElement(By.xpath("...")).getText();

To sum up, I need to wait until either of the TWO elements shows up. Then pick whoever shows up (they cannot show up simultaneously) Please Help.

Graham Russell
  • 997
  • 13
  • 24

9 Answers9

22

Now there's a native solution for that, the or method, check the doc.

You use it like so:

driverWait.until(ExpectedConditions.or(
    ExpectedConditions.presenceOfElementLocated(By.cssSelector("div.something")),
    ExpectedConditions.presenceOfElementLocated(By.cssSelector("div.anything"))));
dialex
  • 2,706
  • 8
  • 44
  • 74
  • 3
    how do i ensure for which element the condition is true? – Mak13 Jul 14 '16 at 06:46
  • @Mak13 -- use ExpectedConditions.and instead of ExpectedConditions.or. If you want to know which it was, check both options right after the "or" test. – Chad Lehman Jun 10 '20 at 17:04
8

This is the method I declared in my Helper class, it works like a charm. Just create your own ExpectedCondition and make it return any of elements found by locators:

public static ExpectedCondition<WebElement> oneOfElementsLocatedVisible(By... args)
{
    final List<By> byes = Arrays.asList(args);
    return new ExpectedCondition<WebElement>()
    {
        @Override
        public Boolean apply(WebDriver driver)
        {
            for (By by : byes)
            {
                WebElement el;
                try {el = driver.findElement(by);} catch (Exception r) {continue;}
                if (el.isDisplayed()) return el;
            }
            return false;
        }
    };
}

And then you can use it this way:

Wait wait = new WebDriverWait(driver, Timeouts.WAIT_FOR_PAGE_TO_LOAD_TIMEOUT);
WebElement webElement = (WebElement) wait.until(
        Helper.oneOfElementsLocatedVisible(
                By.xpath(SERVICE_TITLE_LOCATOR), 
                By.xpath(ATTENTION_REQUEST_ALREADY_PRESENTS_WINDOW_LOCATOR)
        )
);

Here SERVICE_TITLE_LOCATOR and ATTENTION_REQUEST_ALREADY_PRESENTS_WINDOW_LOCATOR are two static locators for page.

Graham Russell
  • 997
  • 13
  • 24
mgtriffid
  • 91
  • 1
  • 4
4

I think that your problem has a simple solution if you put "OR" into xpath.

WebDriverWait wait = new WebDriverWait(driver, 60); 
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//h2[@class='....'] | //h3[@class='... ']"))); 

Then, to print the result use for example:

if(driver.findElements(By.xpath("//h2[@class='....']")).size()>0){
       driver.findElement(By.xpath("//h2[@class='....']")).getText();
}else{
       driver.findElement(By.xpath("//h3[@class='....']")).getText();
}
Scruger
  • 149
  • 1
  • 1
  • 3
1

You can also use a CSSSelector like this:

wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("h2.someClass, h3.otherClass")));

Replace someClass and otherClass with what you had in [...] in the xpath.

flaperman
  • 23
  • 6
  • The bonus to this is that it answers the part of the question, `Then I need to pick whoever shows up first.` because `.until()` returns the found element, e.g. `WebElement e = wait.until(...);` – JeffC Dec 18 '18 at 21:54
1
 wait.until(
            ExpectedConditions.or(
                    ExpectedConditions.elementToBeClickable(By.xpath("yourXpath"),
                    ExpectedConditions.elementToBeClickable(By.xpath("yourAnotherXpath")
            )
    );
Bayram Binbir
  • 1,531
  • 11
  • 11
0

Unfortunately, there is no such a command. You can overcome this by try and catch, or I would better recommend you to use open source Ruby library Watir.

mrGe.org
  • 25
  • 5
CHEBURASHKA
  • 1,623
  • 11
  • 53
  • 85
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – Giulio Caccin Mar 15 '21 at 08:59
0

There is an alternative way to wait but it isnt using expected conditions and it uses a lambda expression instead..

wait.Until(x => driver.FindElements(By.Xpath("//h3[@class='... ']")).Count > 0 || driver.FindElements(By.Xpath("//h2[@class='... ']")).Count > 0); 
Ben
  • 667
  • 6
  • 13
0

If you have a variable number of conditions to satisfy, you can leverage a generic list such as Java's ArrayList<> as shown here, and then do the following:

//My example is waiting for any one of a list of URLs (code is java)

    public void waitUntilUrls(List<String> urls) {
        if(null != urls && urls.size() > 0) {
            System.out.println("Waiting at " + _driver.getCurrentUrl() + " for " + urls.size() + " urls");
            List<ExpectedCondition<Boolean>> conditions = new ArrayList<>();
            for (String url : urls) {
                conditions.add(ExpectedConditions.urlContains(url));
            }

            WebDriverWait wait = new WebDriverWait(_driver, 30);
            ExpectedCondition<Boolean>[] x = new ExpectedCondition[conditions.size()];
            x = conditions.toArray(x);
            wait.until(ExpectedConditions.or(x)); // "OR" selects from among your ExpectedCondition<Boolean> array
        }
    }
Chad Lehman
  • 902
  • 7
  • 10
-1

There is a simple solution for this, using an Explicit Wait:

wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//*[@id='FirstElement' or @id='SecondElement']")));

Before this I was trying to use wait.until(ExpectedConditions.or(..., which was not compatible with selenium versions before 2.53.0.

Vince Bowdren
  • 8,326
  • 3
  • 31
  • 56