Find div element by multiple class names?

JavaCssSeleniumSelenium Webdriver

Java Problem Overview


<div class="value test" /> I'd like to identify that web element. It only has this two classes defined. I cannot do the following as className does not take a space separated value. What are alternatives?

@FindBy(className = "value test")
@CacheLookup
private WebElement test;

Java Solutions


Solution 1 - Java

I don't think barak manos's answer has fully explained it.

Imagine we have few elements as the followings:

  1. <div class="value test"></div>
  2. <div class="value test "></div>
  3. <div class="first value test last"></div>
  4. <div class="test value"></div>

How XPath matches

  • Match only 1 (exact match), barak's answer

      driver.findElement(By.xpath("//div[@class='value test']"));
    
  • Match 1, 2 and 3 (match class contains value test, class order matters)

      driver.findElement(By.xpath("//div[contains(@class, 'value test')]"));
    
  • Match 1, 2, 3 and 4 (as long as elements have class value and test)

      driver.findElement(By.xpath("//div[contains(@class, 'value') and contains(@class, 'test')]"));
    

Also, in cases like this, Css Selector is always in favor of XPath (fast, concise, native).

  • Match 1

      driver.findElement(By.cssSelector("div[class='value test']"));
    
  • Match 1, 2 and 3

      driver.findElement(By.cssSelector("div[class*='value test']"));
    
  • Match 1, 2, 3 and 4

      driver.findElement(By.cssSelector("div.value.test"));
    

Solution 2 - Java

Try this:

test = driver.findElement(By.xpath("//div[@class='value test']"));

Solution 3 - Java

Class By.ByClassName

Class By.ByClassName is defined in By.java as follows:

/**
 * Find elements based on the value of the "class" attribute. If an element has multiple classes, then
 * this will match against each of them. For example, if the value is "one two onone", then the
 * class names "one" and "two" will match.
 *
 * @param className The value of the "class" attribute to search for.
 * @return A By which locates elements by the value of the "class" attribute.
 */
public static By className(String className) {
  return new ByClassName(className);
}

This usecase

So as per the defination you can't pass multiple classes i.e. value and test as arguments to @FindBy(className = "..."). Sending multiple classes will raise an error as:

invalid selector: Compound class names not permitted

Solution

There are multiple approaches to solve this usecase as follows:

  • If the element is uniquely identified only through the classname value you can use:

      @FindBy(className = "value")
      @CacheLookup
      private WebElement test;
      
    
  • If the element is uniquely identified only through the classname test you can use:

      @FindBy(className = "test")
      @CacheLookup
      private WebElement test;
      
    
  • If both the classnames, value and test are mandatory to identify the element, you can use [tag:css-selectors] as follows:

      @FindBy(css  = ".value.test")
      @CacheLookup
      private WebElement test;
      
    
  • As an alternative you can also use [tag:xpath] as follows:

      @FindBy(xpath   = "//*[@class='value test']")
      @CacheLookup
      private WebElement test;
    

tl; dr

Invalid selector: Compound class names not permitted error using Selenium

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmembersoundView Question on Stackoverflow
Solution 1 - JavaYi ZengView Answer on Stackoverflow
Solution 2 - Javabarak manosView Answer on Stackoverflow
Solution 3 - Javaundetected SeleniumView Answer on Stackoverflow