All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openqa.selenium.By Maven / Gradle / Ivy

Go to download

Selenium automates browsers. That's it! What you do with that power is entirely up to you.

There is a newer version: 4.19.1
Show newest version
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium;

import org.openqa.selenium.internal.FindsByClassName;
import org.openqa.selenium.internal.FindsByCssSelector;
import org.openqa.selenium.internal.FindsById;
import org.openqa.selenium.internal.FindsByLinkText;
import org.openqa.selenium.internal.FindsByName;
import org.openqa.selenium.internal.FindsByTagName;
import org.openqa.selenium.internal.FindsByXPath;

import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;

import static java.util.stream.Collectors.joining;

/**
 * Mechanism used to locate elements within a document. In order to create your own locating
 * mechanisms, it is possible to subclass this class and override the protected methods as required,
 * though it is expected that all subclasses rely on the basic finding mechanisms provided
 * through static methods of this class:
 *
 * 
 * public WebElement findElement(WebDriver driver) {
 *     WebElement element = driver.findElement(By.id(getSelector()));
 *     if (element == null)
 *       element = driver.findElement(By.name(getSelector());
 *     return element;
 * }
 * 
 */
public abstract class By {
  /**
   * @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(String id) {
    return new ById(id);
  }

  /**
   * @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(String linkText) {
    return new ByLinkText(linkText);
  }

  /**
   * @param partialLinkText The partial text to match against
   * @return a By which locates elements that contain the given link text.
   */
  public static By partialLinkText(String partialLinkText) {
    return new ByPartialLinkText(partialLinkText);
  }

  /**
   * @param name The value of the "name" attribute to search for.
   * @return A By which locates elements by the value of the "name" attribute.
   */
  public static By name(String name) {
    return new ByName(name);
  }

  /**
   * @param tagName The element's tag name.
   * @return A By which locates elements by their tag name.
   */
  public static By tagName(String tagName) {
    return new ByTagName(tagName);
  }

  /**
   * @param xpathExpression The XPath to use.
   * @return A By which locates elements via XPath.
   */
  public static By xpath(String xpathExpression) {
    return new ByXPath(xpathExpression);
  }

  /**
   * 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);
  }

  /**
   * Find elements via the driver's underlying W3C Selector engine. If the browser does not
   * implement the Selector API, a best effort is made to emulate the API. In this case, we strive
   * for at least CSS2 support, but offer no guarantees.
   *
   * @param cssSelector CSS expression.
   * @return A By which locates elements by CSS.
   */
  public static By cssSelector(String cssSelector) {
    return new ByCssSelector(cssSelector);
  }

  /**
   * Find a single element. Override this method if necessary.
   *
   * @param context A context to use to find the element.
   * @return The WebElement that matches the selector.
   */
  public WebElement findElement(SearchContext context) {
    List allElements = findElements(context);
    if (allElements == null || allElements.isEmpty()) {
      throw new NoSuchElementException("Cannot locate an element using " + toString());
    }
    return allElements.get(0);
  }

  /**
   * Find many elements.
   *
   * @param context A context to use to find the elements.
   * @return A list of WebElements matching the selector.
   */
  public abstract List findElements(SearchContext context);

  @Override
  public boolean equals(Object o) {
    if (!(o instanceof By)) {
      return false;
    }

    By that = (By) o;

    return this.toString().equals(that.toString());
  }

  @Override
  public int hashCode() {
    return toString().hashCode();
  }

  @Override
  public String toString() {
    // A stub to prevent endless recursion in hashCode()
    return "[unknown locator]";
  }

  public static class ById extends By implements Serializable {

    private static final long serialVersionUID = 5341968046120372169L;

    private final String id;

    public ById(String id) {
      if (id == null) {
        throw new IllegalArgumentException("Cannot find elements when the id is null.");
      }

      this.id = id;
    }

    @Override
    public List findElements(SearchContext context) {
      if (context instanceof FindsById) {
        return ((FindsById) context).findElementsById(id);
      }
      return ((FindsByXPath) context).findElementsByXPath(".//*[@id = '" + id + "']");
    }

    @Override
    public WebElement findElement(SearchContext context) {
      if (context instanceof FindsById) {
        return ((FindsById) context).findElementById(id);
      }
      return ((FindsByXPath) context).findElementByXPath(".//*[@id = '" + id + "']");
    }

    @Override
    public String toString() {
      return "By.id: " + id;
    }

    private Map toJson() {
      Map asJson = new HashMap<>();
      asJson.put("using", "css selector");
      asJson.put("value", Stream.of(id.split("\\s+")).map(str -> "#" + str).collect(joining(" ")));
      return Collections.unmodifiableMap(asJson);
    }
  }

  public static class ByLinkText extends By implements Serializable {

    private static final long serialVersionUID = 1967414585359739708L;

    private final String linkText;

    public ByLinkText(String linkText) {
      if (linkText == null) {
        throw new IllegalArgumentException("Cannot find elements when the link text is null.");
      }

      this.linkText = linkText;
    }

    @Override
    public List findElements(SearchContext context) {
      return ((FindsByLinkText) context).findElementsByLinkText(linkText);
    }

    @Override
    public WebElement findElement(SearchContext context) {
      return ((FindsByLinkText) context).findElementByLinkText(linkText);
    }

    @Override
    public String toString() {
      return "By.linkText: " + linkText;
    }

    private Map toJson() {
      Map asJson = new HashMap<>();
      asJson.put("using", "link text");
      asJson.put("value", linkText);
      return Collections.unmodifiableMap(asJson);
    }
  }

  public static class ByPartialLinkText extends By implements Serializable {

    private static final long serialVersionUID = 1163955344140679054L;

    private final String partialLinkText;

    public ByPartialLinkText(String partialLinkText) {
      if (partialLinkText == null) {
        throw new IllegalArgumentException("Cannot find elements when the link text is null.");
      }

      this.partialLinkText = partialLinkText;
    }

    @Override
    public List findElements(SearchContext context) {
      return ((FindsByLinkText) context).findElementsByPartialLinkText(partialLinkText);
    }

    @Override
    public WebElement findElement(SearchContext context) {
      return ((FindsByLinkText) context).findElementByPartialLinkText(partialLinkText);
    }

    @Override
    public String toString() {
      return "By.partialLinkText: " + partialLinkText;
    }

    private Map toJson() {
      Map asJson = new HashMap<>();
      asJson.put("using", "partial link text");
      asJson.put("value", partialLinkText);
      return Collections.unmodifiableMap(asJson);
    }
  }

  public static class ByName extends By implements Serializable {

    private static final long serialVersionUID = 376317282960469555L;

    private final String name;

    public ByName(String name) {
      if (name == null) {
        throw new IllegalArgumentException("Cannot find elements when name text is null.");
      }

      this.name = name;
    }

    @Override
    public List findElements(SearchContext context) {
      if (context instanceof FindsByName) {
        return ((FindsByName) context).findElementsByName(name);
      }
      return ((FindsByXPath) context).findElementsByXPath(".//*[@name = '" + name + "']");
    }

    @Override
    public WebElement findElement(SearchContext context) {
      if (context instanceof FindsByName) {
        return ((FindsByName) context).findElementByName(name);
      }
      return ((FindsByXPath) context).findElementByXPath(".//*[@name = '" + name + "']");
    }

    @Override
    public String toString() {
      return "By.name: " + name;
    }

    private Map toJson() {
      Map asJson = new HashMap<>();
      asJson.put("using", "css selector");
      asJson.put("value", String.format("*[name='%s']", name.replace("'", "\\'")));
      return Collections.unmodifiableMap(asJson);
    }
  }

  public static class ByTagName extends By implements Serializable {

    private static final long serialVersionUID = 4699295846984948351L;

    private final String tagName;

    public ByTagName(String tagName) {
      if (tagName == null) {
        throw new IllegalArgumentException("Cannot find elements when the tag name is null.");
      }

      this.tagName = tagName;
    }

    @Override
    public List findElements(SearchContext context) {
      if (context instanceof FindsByTagName) {
        return ((FindsByTagName) context).findElementsByTagName(tagName);
      }
      return ((FindsByXPath) context).findElementsByXPath(".//" + tagName);
    }

    @Override
    public WebElement findElement(SearchContext context) {
      if (context instanceof FindsByTagName) {
        return ((FindsByTagName) context).findElementByTagName(tagName);
      }
      return ((FindsByXPath) context).findElementByXPath(".//" + tagName);
    }

    @Override
    public String toString() {
      return "By.tagName: " + tagName;
    }

    private Map toJson() {
      Map asJson = new HashMap<>();
      asJson.put("using", "tag name");
      asJson.put("value", tagName);
      return Collections.unmodifiableMap(asJson);
    }
  }

  public static class ByXPath extends By implements Serializable {

    private static final long serialVersionUID = -6727228887685051584L;

    private final String xpathExpression;

    public ByXPath(String xpathExpression) {
      if (xpathExpression == null) {
        throw new IllegalArgumentException(
            "Cannot find elements when the XPath is null.");
      }

      this.xpathExpression = xpathExpression;
    }

    @Override
    public List findElements(SearchContext context) {
      return ((FindsByXPath) context).findElementsByXPath(xpathExpression);
    }

    @Override
    public WebElement findElement(SearchContext context) {
      return ((FindsByXPath) context).findElementByXPath(xpathExpression);
    }

    @Override
    public String toString() {
      return "By.xpath: " + xpathExpression;
    }

    private Map toJson() {
      Map asJson = new HashMap<>();
      asJson.put("using", "xpath");
      asJson.put("value", xpathExpression);
      return Collections.unmodifiableMap(asJson);
    }
  }

  public static class ByClassName extends By implements Serializable {

    private static final long serialVersionUID = -8737882849130394673L;

    private final String className;

    public ByClassName(String className) {
      if (className == null) {
        throw new IllegalArgumentException(
            "Cannot find elements when the class name expression is null.");
      }

      this.className = className;
    }

    @Override
    public List findElements(SearchContext context) {
      if (context instanceof FindsByClassName) {
        return ((FindsByClassName) context).findElementsByClassName(className);
      }
      return ((FindsByXPath) context).findElementsByXPath(
          ".//*[" + containingWord("class", className) + "]");
    }

    @Override
    public WebElement findElement(SearchContext context) {
      if (context instanceof FindsByClassName) {
        return ((FindsByClassName) context).findElementByClassName(className);
      }
      return ((FindsByXPath) context).findElementByXPath(
          ".//*[" + containingWord("class", className) + "]");
    }

    /**
     * Generate a partial XPath expression that matches an element whose specified attribute
     * contains the given CSS word. So to match <div class='foo bar'> you would say "//div[" +
     * containingWord("class", "foo") + "]".
     *    private Map toJson() {
      Map asJson = new HashMap<>();
      asJson.put("using", "link text");
      asJson.put("value", linkText);
      return Collections.unmodifiableMap(asJson);
    }

     * @param attribute name
     * @param word name
     * @return XPath fragment
     */
    private String containingWord(String attribute, String word) {
      return "contains(concat(' ',normalize-space(@" + attribute + "),' '),' " + word + " ')";
    }

    @Override
    public String toString() {
      return "By.className: " + className;
    }

    private Map toJson() {

      Map asJson = new HashMap<>();
      asJson.put("using", "css selector");
      asJson.put("value", Stream.of(className.split("\\s+")).map(str -> "." + str).collect(joining(" ")));
      return Collections.unmodifiableMap(asJson);
    }

  }

  public static class ByCssSelector extends By implements Serializable {

    private static final long serialVersionUID = -3910258723099459239L;

    private final String cssSelector;

    public ByCssSelector(String cssSelector) {
      if (cssSelector == null) {
        throw new IllegalArgumentException("Cannot find elements when the selector is null");
      }
      
      this.cssSelector = cssSelector;
    }

    @Override
    public WebElement findElement(SearchContext context) {
      if (context instanceof FindsByCssSelector) {
        return ((FindsByCssSelector) context).findElementByCssSelector(cssSelector);
      }

      throw new WebDriverException(
          "Driver does not support finding an element by selector: " + cssSelector);
    }

    @Override
    public List findElements(SearchContext context) {
      if (context instanceof FindsByCssSelector) {
        return ((FindsByCssSelector) context).findElementsByCssSelector(cssSelector);
      }

      throw new WebDriverException(
          "Driver does not support finding elements by selector: " + cssSelector);
    }

    @Override
    public String toString() {
      return "By.cssSelector: " + cssSelector;
    }

    private Map toJson() {
      Map asJson = new HashMap<>();
      asJson.put("using", "css selector");
      asJson.put("value", cssSelector);
      return Collections.unmodifiableMap(asJson);
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy