1

I'm trying to wait for an element with certain css and inner text. I have multiple elements satisfying the css condition (element could be visible/invisible) and selenium ExpectedConditions is not behaving the way I want.

If I try to find elements by css first and then filter out myself, I sometimes miss the intended element because some elements might not have loaded.

By cssBy = By.css("CSS_HERE");
wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(cssBy);
List<WebElement> tabs = driver.findElements(cssBy);
WebElement element =
        tabs.stream().filter(tab -> tab.getText().contains("TARGET_TEXT")).findAny().get();

the above snippet sometimes misses the indented elements which satisfy the CSS but have not loaded when selenium checked for it. This results in me getting no matching element in second part.

I tried with textMatches and locator

By cssBy = By.css("CSS_HERE");
wait.until(ExpectedConditions.textMatches(cssBy, "TARGET_TEXT");
....

But I think the above snippet is selecting the first element it can find matching CSS and waits for its text to be TARGET_TEXT which is not my intention.

Any suggestions to wait for text match in case of multiple elements matching the locator?

2 Answers2

1

Instead of presenceOfAllElementsLocatedBy() you need to induce WebDriverWait for the visibilityOfAllElementsLocatedBy() and you can use the following Locator Strategy:

By cssBy = By.css("CSS_HERE");
wait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(cssBy);
List<WebElement> tabs = driver.findElements(cssBy);
WebElement element =
    tabs.stream().filter(tab -> tab.getText().contains("TARGET_TEXT")).findAny().get();
undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • While that's a nice approach. I have tab'ed layout. So some elements matching locator might not be in the active tab and hence invisible. sorry I failed to mentioned that in the question. I'll update it. But this is a good solution when everything is on screen as it doesn't require any custom expectations. Thanks – Nagendra Gupta Sep 18 '20 at 03:04
  • @NagendraGupta Ironically, [Selenium](https://stackoverflow.com/questions/54459701/what-is-selenium-and-what-is-webdriver/54482491#54482491)'s core functionality is to interact with [WebElement](https://stackoverflow.com/questions/52782684/what-is-the-difference-between-webdriver-and-webelement-in-selenium/52805139#52805139)s visible on the screen. Elements in other tabs and those are invisible needs to be handled in a different way. – undetected Selenium Sep 18 '20 at 08:51
0

You could write your own Expected Conditions method here is an example for textMatches()

  public static ExpectedCondition<List<WebElement>> textMatches(final By locator, final Pattern pattern) {
    return new ExpectedCondition<List<WebElement>>() {
        @Override
        public List<WebElement> apply(WebDriver driver) {
            String currentValue;
            List<WebElement> elements = driver.findElements(locator);
            List<WebElement> matchingElements = new ArrayList();


            for(WebElement element : elements){
                currentValue = element.getText();
                if (pattern.matcher(currentValue).find()){
                    matchingElements.add(element);
                }
            }
            return matchingElements;
        }

        @Override
        public String toString() {
            return "matching elements for  " + locator;
        }
    };
}
Alin Stelian
  • 861
  • 1
  • 6
  • 16
  • In the above snippet, returning directly on the first mismatch would fail right? say I have two elements matching the locator. The first one does not match the text pattern. won't the snippet always fail in that case?. Although I get the idea, I could just track the matched ones and return them is non-empty otherwise null. Thanks – Nagendra Gupta Sep 17 '20 at 17:54