jagdpanzer
jagdpanzer

Reputation: 713

Select WebElement by text within HTML

I've been trying to select this WebElement for a while. It's a sign up button on the front page of the web application I test. I was wondering, is there a method I could use to grab text within the HTML to define a WebElement?

<div>
  <div>
    <div style="">
      <div style="transform: translate(0px, 0px) scale(0.5, 0.5) rotate(0deg);">
        <div role="img">
          <img src="images/lsloginform/newskin/try_it_normal.png?1444658058414">
        </div>
      </div>
      <div style="transform: translate(0px, 75px) scale(1, 1) rotate(0deg);">
        <canvas height="7" width="75" style="top: 0px; left: 0px; width: 75px; height: 7px;"></canvas>
      </div>
      <div style="transform: translate(0px, 82px) scale(1, 1) rotate(0deg);">
        <div style="font-family: franklin-gothic-ext-comp-urw; font-weight: 700; font-style: normal; font-size: 15px; opacity: 1; color: rgb(63, 63, 63); transform: translate(7px, 0px) scale(1, 1) rotate(0deg);">SIGN&nbsp;UP</div>
      </div>
    </div>
    <div style="display: none;"></div>
    <div style="display: none;">
      <div style="transform: translate(0px, 0px) scale(0.5, 0.5) rotate(0deg);">
        <div role="img">
          <img src="images/lsloginform/newskin/try_it_MO.png?1444665385167">
        </div>
      </div>
      <div style="transform: translate(0px, 75px) scale(1, 1) rotate(0deg);">
        <canvas height="7" width="75" style="top: 0px; left: 0px; width: 75px; height: 7px;"></canvas>
      </div>
      <div style="transform: translate(0px, 82px) scale(1, 1) rotate(0deg);">
        <div style="font-family: franklin-gothic-ext-comp-urw; font-weight: 700; font-style: normal; font-size: 15px; opacity: 1; color: rgb(63, 63, 63); transform: translate(7px, 0px) scale(1, 1) rotate(0deg);">SIGN&nbsp;UP</div>
      </div>
      <div></div>
    </div>
    <div style="display: none;"></div>
    <div style="display: none;"></div>
  </div>
</div>

Specific example, I'd like to the xpath of the above div utilizing the "SIGN UP" text at the end of the HTML. How do I go about doing that?

The application I test on is finicky as all hell, and being able to select by that attribute would make my life much easier. The reason this is a problem is because I need 3 different methods of grabbing an xpath: One for the button as normal, one for the hover, and another because the button changes momentarily when it is clicked. Most of my automation scripts look something like:

    public class changeLanguageLogin {
        public void run(WebDriver driver) {
            WebElement changeLanguage = getWebElement(driver, "xpath", "img", "src", "select_application_language_normal");
            Actions actions = new Actions(driver);
            actions.moveToElement(changeLanguage).build().perform();
            WebElement changeLanguageMO = getWebElement(driver, "xpath", "img", "src", "select_application_language_hover");
            changeLanguageMO.click();
            WebElement changeLanguageClicked = getWebElement(driver, "xpath", "img", "src", "select_application_language_pushed");
            changeLanguageClicked.click();
        }


      private static By buildXPathExpression(String htmlElement, String attributeName, String attributeValue)
      {
          return By.xpath("//"+htmlElement+"[contains(@"+attributeName+",'"+attributeValue+"')]");
      }

      private static By buildCSSExpression(String htmlElement, String attributeName, String attributeValue)
      {
          return By.cssSelector(""+htmlElement+"["+attributeName+"='"+attributeValue+"']");
      }

      private static WebElement getWebElement(WebDriver driver, String operation, String htmlElement, String attributeName, String attributeValue)
      {
          By byObj = null;
          WebElement webElementObj = null;
          if(operation!= null)
          {
              if(operation.equals("xpath"))
              {
                  byObj = buildXPathExpression(htmlElement,attributeName,attributeValue);
              }
              else if(operation.equals("cssSelector"))
              {
                  byObj = buildCSSExpression(htmlElement,attributeName,attributeValue);
              }
          }
          if(byObj != null)
          {
              webElementObj = driver.findElement(byObj);
          }

          return webElementObj;
      }
}

Many, many, many times a day, one method of defining the xpath simply "will not work" and I have to scrounge around for another one. The goal is to not use any "hard coded" xpaths, as that was problematic when I had began the job.

So the answer I'm looking for: Define an xpath by the text in the HTML, "SIGN UP".

Upvotes: 1

Views: 172

Answers (2)

Mesut GUNES
Mesut GUNES

Reputation: 7401

For old time inlie style css, you generate the css selector depends on the style written for the div like "div[style*='paste_style_attribute']". However this method is not dependable because of duplication of same style.

For the following div:

<div style="font-family: franklin-gothic-ext-comp-urw; font-weight: 700; font-style: normal; font-size: 15px; opacity: 1; color: rgb(63, 63, 63); transform: translate(7px, 0px) scale(1, 1) rotate(0deg);">SIGN&nbsp;UP</div>

You can generate the following css selector:

"div[style*='font-family: franklin-gothic-ext-comp-urw; font-weight: 700; font-style: normal; font-size: 15px; opacity: 1; color: rgb(63, 63, 63); transform: translate(7px, 0px) scale(1, 1) rotate(0deg);']"

Java code:

driver.findElement(By.cssSelector("div[style*='font-family: franklin-gothic-ext-comp-urw; font-weight: 700; font-style: normal; font-size: 15px; opacity: 1; color: rgb(63, 63, 63); transform: translate(7px, 0px) scale(1, 1) rotate(0deg);']"))

Upvotes: 1

Jeremiah
Jeremiah

Reputation: 1145

This response will not help if you are bound to only xpath or css lookups.

Option 1

If you would be willing to add another case to your helper method I'm thinking that By.linkText may do what you're wanting?

  /**
   * @param linkText The exact text to match against
   * @return a By which locates A elements by the exact text it displays
   */
  public static By linkText(final String linkText) {
    if (linkText == null)
      throw new IllegalArgumentException(
          "Cannot find elements when link text is null.");

    return new ByLinkText(linkText);
  }

Or possibly By.partialLinkText

  /**
   * @param linkText The text to match against
   * @return a By which locates A elements that contain the given link text
   */
  public static By partialLinkText(final String linkText) {
    if (linkText == null)
      throw new IllegalArgumentException(
          "Cannot find elements when link text is null.");

    return new ByPartialLinkText(linkText);
  }

This would potentially be an issue if you're testing against a system that has been internationalized. Depending on the environment the locale of the user/browser would change the text you're looking for.

This can also be a maintenance issue if the text changes.

Option 2

If you have control of the HTML, you may consider adding id's to the elements on the page. With that then you can use the By.id lookup. From some of my preliminary research the element id is seen as one of the more reliable and consistent paths.

/**
   * @param id The value of the "id" attribute to search for
   * @return a By which locates elements by the value of the "id" attribute.
   */
  public static By id(final String id) {
    if (id == null)
      throw new IllegalArgumentException(
          "Cannot find elements with a null id attribute.");

    return new ById(id);
  }

Option 3

My final suggestion would be to try to implement your own By lookup. I would fall short on suggesting an implementation for the lookup, but if this behavior is what you're needing then you should be aware that you could potentially create a subclass to do what you're wanting.

Best of Luck.

Upvotes: 1

Related Questions