org.openqa.selenium.By Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of selenium-api Show documentation
Show all versions of selenium-api Show documentation
Selenium automates browsers. That's it! What you do with that power is entirely up to you.
The 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 java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern;
import org.openqa.selenium.internal.Require;
/**
* 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. Only one class name should be used.
* If an element has multiple classes, please use {@link By#cssSelector(String)}.
*
* @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, the 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);
protected WebDriver getWebDriver(SearchContext context) {
if (context instanceof WebDriver) {
return (WebDriver) context;
}
if (!(context instanceof WrapsDriver)) {
throw new IllegalArgumentException("Context does not wrap a webdriver: " + context);
}
return ((WrapsDriver) context).getWrappedDriver();
}
protected JavascriptExecutor getJavascriptExecutor(SearchContext context) {
WebDriver driver = getWebDriver(context);
if (!(context instanceof JavascriptExecutor)) {
throw new IllegalArgumentException(
"Context does not provide a mechanism to execute JS: " + context);
}
return (JavascriptExecutor) driver;
}
@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 PreW3CLocator {
private final String id;
public ById(String id) {
super(
"id", Require.argument("Id", id).nonNull("Cannot find elements when id is null."), "#%s");
this.id = id;
}
@Override
public String toString() {
return "By.id: " + id;
}
}
public static class ByLinkText extends BaseW3CLocator {
private final String linkText;
public ByLinkText(String linkText) {
super(
"link text",
Require.argument("Link text", linkText)
.nonNull("Cannot find elements when the link text is null."));
this.linkText = linkText;
}
@Override
public String toString() {
return "By.linkText: " + linkText;
}
}
public static class ByPartialLinkText extends BaseW3CLocator {
private final String partialLinkText;
public ByPartialLinkText(String partialLinkText) {
super(
"partial link text",
Require.argument("Partial link text", partialLinkText)
.nonNull("Cannot find elements when the link text is null."));
this.partialLinkText = partialLinkText;
}
@Override
public String toString() {
return "By.partialLinkText: " + partialLinkText;
}
}
public static class ByName extends PreW3CLocator {
private final String name;
public ByName(String name) {
super(
"name",
Require.argument("Name", name).nonNull("Cannot find elements when name text is null."),
String.format("*[name='%s']", name.replace("'", "\\'")));
this.name = name;
}
@Override
public String toString() {
return "By.name: " + name;
}
}
public static class ByTagName extends BaseW3CLocator {
private final String tagName;
public ByTagName(String tagName) {
super(
"tag name",
Require.argument("Tag name", tagName)
.nonNull("Cannot find elements when the tag name is null."));
if (tagName.isEmpty()) {
throw new InvalidSelectorException("Tag name must not be blank");
}
this.tagName = tagName;
}
@Override
public String toString() {
return "By.tagName: " + tagName;
}
}
public static class ByXPath extends BaseW3CLocator {
private final String xpathExpression;
public ByXPath(String xpathExpression) {
super(
"xpath",
Require.argument("XPath", xpathExpression)
.nonNull("Cannot find elements when the XPath is null."));
this.xpathExpression = xpathExpression;
}
@Override
public String toString() {
return "By.xpath: " + xpathExpression;
}
}
public static class ByClassName extends PreW3CLocator {
private final String className;
public ByClassName(String className) {
super(
"class name",
Require.argument("Class name", className)
.nonNull("Cannot find elements when the class name expression is null."),
".%s");
if (className.matches(".*\\s.*")) {
throw new InvalidSelectorException("Compound class names not permitted");
}
this.className = className;
}
@Override
public String toString() {
return "By.className: " + className;
}
}
public static class ByCssSelector extends BaseW3CLocator {
private final String cssSelector;
public ByCssSelector(String cssSelector) {
super(
"css selector",
Require.argument("CSS selector", cssSelector)
.nonNull("Cannot find elements when the selector is null"));
this.cssSelector = cssSelector;
}
@Override
public String toString() {
return "By.cssSelector: " + cssSelector;
}
}
public interface Remotable {
Parameters getRemoteParameters();
class Parameters {
private final String using;
private final Object value;
public Parameters(String using, Object value) {
this.using = Require.nonNull("Search mechanism", using);
// There may be subclasses where the value is optional. Allow for this.
this.value = value;
}
public String using() {
return using;
}
public Object value() {
return value;
}
@Override
public String toString() {
return "[" + using + ": " + value + "]";
}
@Override
public boolean equals(Object o) {
if (!(o instanceof Parameters)) {
return false;
}
Parameters that = (Parameters) o;
return using.equals(that.using) && Objects.equals(value, that.value);
}
@Override
public int hashCode() {
return Objects.hash(using, value);
}
private Map toJson() {
Map params = new HashMap<>();
params.put("using", using);
params.put("value", value);
return Collections.unmodifiableMap(params);
}
}
}
private abstract static class BaseW3CLocator extends By implements Remotable {
private final Parameters params;
protected BaseW3CLocator(String using, String value) {
this.params = new Parameters(using, value);
}
@Override
public WebElement findElement(SearchContext context) {
Require.nonNull("Search Context", context);
return context.findElement(this);
}
@Override
public List findElements(SearchContext context) {
Require.nonNull("Search Context", context);
return context.findElements(this);
}
@Override
public final Parameters getRemoteParameters() {
return params;
}
protected final Map toJson() {
return getRemoteParameters().toJson();
}
}
private abstract static class PreW3CLocator extends By implements Remotable {
private static final Pattern CSS_ESCAPE =
Pattern.compile("([\\s'\"\\\\#.:;,!?+<>=~*^$|%&@`{}\\-\\/\\[\\]\\(\\)])");
private final Parameters remoteParams;
private final ByCssSelector fallback;
private PreW3CLocator(String using, String value, String formatString) {
this.remoteParams = new Remotable.Parameters(using, value);
this.fallback = new ByCssSelector(String.format(formatString, cssEscape(value)));
}
@Override
public WebElement findElement(SearchContext context) {
return context.findElement(fallback);
}
@Override
public List findElements(SearchContext context) {
return context.findElements(fallback);
}
@Override
public final Parameters getRemoteParameters() {
return remoteParams;
}
protected final Map toJson() {
return fallback.toJson();
}
private String cssEscape(String using) {
using = CSS_ESCAPE.matcher(using).replaceAll("\\\\$1");
if (!using.isEmpty() && Character.isDigit(using.charAt(0))) {
using = "\\" + (30 + Integer.parseInt(using.substring(0, 1))) + " " + using.substring(1);
}
return using;
}
}
}