dgorti
dgorti

Reputation: 1240

ExpectedConditions OR AND AND in DefaultWait<T>

In using C#, Selenium webdriver, I navigate to a page, which might redirect to a login screen or eventually redirect or not to the actual app page, depending on whether I have cached credentials or not. (case in point azure active dir auth).

What I am after is to find if a known element of my app page appears or the login screen of azure auth with an element id "use_another_account_link" appears. SO I want to OR the condition of ExpectedConditions without having to wait N seconds on each.

Upvotes: 3

Views: 3972

Answers (4)

Easty77
Easty77

Reputation: 580

The accepted answer is undoubtedly correct from a CSS/XPath perspective, however it may not always do what is expected when used with certain Selenium ExpectedConditions. In the case where you want to wait for a login page to display, but also need to cater for the fact that this page will display either:

  1. a userid entry field with the password field hidden, or;
  2. a password entry field with the userid field hidden (userid read from cookies)
as is the case for eBay, then the following code will timeout in both cases.
WebElement login = (new WebDriverWait(driver, Duration.ofSeconds(5))).until(
ExpectedConditions.visibilityOfElementLocated(
By.cssSelector("input#userid, input#pass")
));

Instead it is necessary to use ExpectedConditions.or to determine whether either field is both present and visible:

boolean bDisplayed = (new WebDriverWait(driver, Duration.ofSeconds(5))).until(ExpectedConditions.or(
ExpectedConditions.visibilityOfElementLocated(By.cssSelector("#userid")), ExpectedConditions.visibilityOfElementLocated(By.cssSelector("#pass"))
));

and then proceed to work out which field is visible.

Upvotes: 0

Bayram Binbir
Bayram Binbir

Reputation: 2217

Wait until at least anyone condition returns true

wait.until(
          ExpectedConditions.or(
               ExpectedConditions.visibilityOfAllElementsLocatedBy(By.name("Services")),
               ExpectedConditions.visibilityOfAllElementsLocatedBy(By.name("Products"))
               ExpectedConditions.visibilityOfAllElementsLocatedBy(By.name("Contact Us"))
          )
      );

Upvotes: 0

Florent B.
Florent B.

Reputation: 42518

You could use a CSS selector with two expessions separated by a , to implement the OR :

WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
var element = wait.Until(ExpectedConditions.ElementExists(By.CssSelector("#id1, #id2")));
if (element.GetAttribute("id") == "id1") {
    // handle element with id="id1"
} else {
    // handle element with id="id2"
}

Or an XPath with two expessions separated by a |:

var element = wait.Until(ExpectedConditions.ElementExists(By.XPath("id('id1') | id('id2')")));
if (element.GetAttribute("id") == "id1") {
    // handle element with id="id1"
} else {
    // handle element with id="id2"
}

Upvotes: 3

Paul Hicks
Paul Hicks

Reputation: 13999

This is a case where you have to provide your own Func<IWebElement, IWebElement> to IWait<IWebElement>.Until(). You can write something specific like this:

(Func<IWebElement, IWebElement>)
  (return ExpectedConditions.ElementExists(By.Id("use_another_account_link"))
  || ExpectedConditions.ElementExists(By.Id("your_login_field_id")))

Or something generic like this:

public Func<IWebElement, IWebElement> Or(Func<IWebElement, IWebElement> either,
                                         Func<IWebElement, IWebElement> or) {
  return (Func<IWebElement, IWebElement>) either || or;
}

Upvotes: 0

Related Questions