org.openqa.selenium.htmlunit.HtmlUnitElementFinder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of htmlunit3-driver Show documentation
Show all versions of htmlunit3-driver Show documentation
WebDriver compatible driver for HtmlUnit headless browser
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.htmlunit;
import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.htmlunit.Page;
import org.htmlunit.SgmlPage;
import org.htmlunit.cssparser.parser.CSSException;
import org.htmlunit.html.DomElement;
import org.htmlunit.html.DomNode;
import org.htmlunit.html.DomNodeList;
import org.htmlunit.html.HtmlAnchor;
import org.htmlunit.html.HtmlElement;
import org.htmlunit.html.HtmlPage;
import org.openqa.selenium.By;
import org.openqa.selenium.By.Remotable.Parameters;
import org.openqa.selenium.InvalidSelectorException;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.locators.RelativeLocator;
import org.openqa.selenium.support.locators.RelativeLocator.RelativeBy;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* @author Martin Bartoš
* @author Ronald Brill
*/
public class HtmlUnitElementFinder {
private static final String INVALIDXPATHERROR = "The xpath expression '%s' cannot be evaluated";
private static final String INVALIDSELECTIONERROR =
"The xpath expression '%s' selected an object of type '%s' instead of a WebElement";
private static final String FIND_ELEMENTS_JS;
static {
try {
final String location =
String.format(
"/%s/%s",
HtmlUnitDriver.class.getPackage().getName().replace(".", "/"), "findElements.js");
try (InputStream stream = HtmlUnitDriver.class.getResourceAsStream(location)) {
FIND_ELEMENTS_JS = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
}
}
catch (final IOException e) {
throw new UncheckedIOException(e);
}
}
private final Map, HtmlUnitElementLocator> finders_ = new HashMap<>();
HtmlUnitElementFinder() {
finders_.put(By.id("a").getClass(), new FindByID());
finders_.put(By.name("a").getClass(), new FindByName());
finders_.put(By.linkText("a").getClass(), new FindByLinkText());
finders_.put(By.partialLinkText("a").getClass(), new FindByPartialLinkText());
finders_.put(By.className("a").getClass(), new FindByClassName());
finders_.put(By.cssSelector("a").getClass(), new FindByCssSelector());
finders_.put(By.tagName("a").getClass(), new FindByTagName());
finders_.put(By.xpath("//a").getClass(), new FindByXPath());
finders_.put(RelativeLocator.with(By.id("id")).getClass(), new FindByRelativeLocator());
}
public WebElement findElement(final HtmlUnitDriver driver, final By locator) {
final HtmlUnitElementLocator elementLocator = finders_.get(locator.getClass());
if (elementLocator == null) {
return locator.findElement(driver);
}
return elementLocator.findElement(driver, locator);
}
public List findElements(final HtmlUnitDriver driver, final By locator) {
final HtmlUnitElementLocator elementLocator = finders_.get(locator.getClass());
if (elementLocator == null) {
return locator.findElements(driver);
}
return elementLocator.findElements(driver, locator);
}
public WebElement findElement(final HtmlUnitWebElement element, final By locator) {
final HtmlUnitElementLocator elementLocator = finders_.get(locator.getClass());
if (elementLocator == null) {
return locator.findElement(element);
}
return elementLocator.findElement(element, locator);
}
public List findElements(final HtmlUnitWebElement element, final By locator) {
final HtmlUnitElementLocator elementLocator = finders_.get(locator.getClass());
if (elementLocator == null) {
return locator.findElements(element);
}
return elementLocator.findElements(element, locator);
}
public static class FindByID extends HtmlUnitElementLocator {
@Override
public WebElement findElement(final HtmlUnitDriver driver, final By locator) {
final SgmlPage lastPage = getLastPage(driver);
if (!(lastPage instanceof HtmlPage)) {
throw new IllegalStateException("Cannot find elements by id for " + lastPage);
}
final String id = getValue(locator);
final DomElement element = ((HtmlPage) lastPage).getElementById(id);
if (element == null) {
throw new NoSuchElementException("Unable to locate element with ID: '" + id + "'");
}
return driver.toWebElement(element);
}
@Override
public List findElements(final HtmlUnitDriver driver, final By locator) {
final SgmlPage lastPage = getLastPage(driver);
if (!(lastPage instanceof HtmlPage)) {
throw new IllegalStateException("Cannot find elements by id for " + lastPage);
}
final List allElements = ((HtmlPage) lastPage).getElementsById(getValue(locator));
return convertRawDomElementsToWebElements(driver, allElements);
}
@Override
public WebElement findElement(final HtmlUnitWebElement element, final By locator) {
final String id = getValue(locator);
return new FindByXPath().findElement(element, By.xpath(".//*[@id = '" + id + "']"));
}
@Override
public List findElements(final HtmlUnitWebElement element, final By locator) {
final String id = getValue(locator);
return new FindByXPath().findElements(element, By.xpath(".//*[@id = '" + id + "']"));
}
}
public static class FindByName extends HtmlUnitElementLocator {
@Override
public List findElements(final HtmlUnitDriver driver, final By locator) {
final SgmlPage lastPage = getLastPage(driver);
if (!(lastPage instanceof HtmlPage)) {
throw new IllegalStateException("Cannot find elements by id for " + lastPage);
}
final List allElements = ((HtmlPage) lastPage).getElementsByName(getValue(locator));
return convertRawDomElementsToWebElements(driver, allElements);
}
@Override
public List findElements(final HtmlUnitWebElement element, final By locator) {
final String name = getValue(locator);
return new FindByXPath().findElements(element, By.xpath(".//*[@name = '" + name + "']"));
}
}
public static class FindByLinkText extends HtmlUnitElementLocator {
@Override
public List findElements(final HtmlUnitDriver driver, final By locator) {
final SgmlPage lastPage = getLastPage(driver);
if (!(lastPage instanceof HtmlPage)) {
throw new IllegalStateException("Cannot find links for " + lastPage);
}
final String expectedText = getValue(locator);
final List anchors = ((HtmlPage) lastPage).getAnchors();
final List toReturn = new ArrayList<>();
for (final HtmlAnchor anchor : anchors) {
if (expectedText.equals(anchor.asNormalizedText())) {
toReturn.add(driver.toWebElement(anchor));
}
}
return toReturn;
}
@Override
public List findElements(final HtmlUnitWebElement element, final By locator) {
final String expectedText = getValue(locator);
final List extends HtmlElement> htmlElements = element.getElement().getElementsByTagName("a");
final List toReturn = new ArrayList<>();
for (final DomElement e : htmlElements) {
if (expectedText.equals(e.asNormalizedText())) {
toReturn.add(element.getDriver().toWebElement(e));
}
}
return toReturn;
}
}
public static class FindByPartialLinkText extends HtmlUnitElementLocator {
@Override
public List findElements(final HtmlUnitDriver driver, final By locator) {
final SgmlPage lastPage = getLastPage(driver);
if (!(lastPage instanceof HtmlPage)) {
throw new IllegalStateException("Cannot find links for " + lastPage);
}
final String expectedText = getValue(locator);
final List anchors = ((HtmlPage) lastPage).getAnchors();
final List toReturn = new ArrayList<>();
for (final HtmlAnchor anchor : anchors) {
if (anchor.asNormalizedText().contains(expectedText)) {
toReturn.add(driver.toWebElement(anchor));
}
}
return toReturn;
}
@Override
public List findElements(final HtmlUnitWebElement element, final By locator) {
final String expectedText = getValue(locator);
final DomNodeList anchors = element.getElement().getElementsByTagName("a");
final List toReturn = new ArrayList<>();
for (final HtmlElement anchor : anchors) {
if (anchor.asNormalizedText().contains(expectedText)) {
toReturn.add(element.getDriver().toWebElement(anchor));
}
}
return toReturn;
}
}
public static class FindByClassName extends HtmlUnitElementLocator {
private String checkValue(final By locator) {
final String value = getValue(locator);
if (value.indexOf(' ') != -1) {
throw new NoSuchElementException("Compound class names not permitted");
}
return value;
}
@Override
public List findElements(final HtmlUnitDriver driver, final By locator) {
return new FindByCssSelector().findElements(driver, By.cssSelector("." + checkValue(locator)));
}
@Override
public List findElements(final HtmlUnitWebElement element, final By locator) {
return new FindByCssSelector().findElements(element, By.cssSelector("." + checkValue(locator)));
}
}
public static class FindByCssSelector extends HtmlUnitElementLocator {
@Override
public WebElement findElement(final HtmlUnitDriver driver, final By locator) {
final DomNode node;
try {
node = getLastPage(driver).querySelector(getValue(locator));
}
catch (final CSSException ex) {
throw new NoSuchElementException("Unable to locate element using css", ex);
}
if (node instanceof DomElement) {
return driver.toWebElement((DomElement) node);
}
throw new NoSuchElementException("Returned node (" + node + ") was not a DOM element");
}
@Override
public List findElements(final HtmlUnitDriver driver, final By locator) {
final DomNodeList allNodes;
try {
allNodes = getLastPage(driver).querySelectorAll(getValue(locator));
}
catch (final CSSException ex) {
throw new NoSuchElementException("Unable to locate element using css", ex);
}
final List toReturn = new ArrayList<>();
for (final DomNode node : allNodes) {
if (node instanceof DomElement) {
toReturn.add(driver.toWebElement((DomElement) node));
}
else {
throw new NoSuchElementException("Returned node was not a DOM element");
}
}
return toReturn;
}
@Override
public List findElements(final HtmlUnitWebElement element, final By locator) {
final DomNodeList allNodes;
try {
allNodes = element.getElement().querySelectorAll(getValue(locator));
}
catch (final CSSException ex) {
throw new NoSuchElementException("Unable to locate element using css", ex);
}
final List toReturn = new ArrayList<>();
for (final DomNode node : allNodes) {
if (node instanceof DomElement) {
toReturn.add(element.getDriver().toWebElement((DomElement) node));
}
else {
throw new NoSuchElementException("Returned node was not a DOM element");
}
}
return toReturn;
}
@Override
public WebElement findElement(final HtmlUnitWebElement element, final By locator) {
final DomNode node;
try {
node = element.getElement().querySelector(getValue(locator));
}
catch (final CSSException ex) {
throw new NoSuchElementException("Unable to locate element using css", ex);
}
if (node instanceof DomElement) {
return element.getDriver().toWebElement((DomElement) node);
}
throw new NoSuchElementException("Returned node (" + node + ") was not a DOM element");
}
}
public static class FindByTagName extends HtmlUnitElementLocator {
@Override
public WebElement findElement(final HtmlUnitDriver driver, final By locator) {
final NodeList allElements = getLastPage(driver).getElementsByTagName(getValue(locator));
if (allElements.getLength() > 0) {
return driver.toWebElement((HtmlElement) allElements.item(0));
}
throw new NoSuchElementException("Unable to locate element with name: " + getValue(locator));
}
@Override
public List findElements(final HtmlUnitDriver driver, final By locator) {
final String name = getValue(locator);
if ("".equals(name)) {
throw new InvalidSelectorException("Unable to locate element by xpath for " + getLastPage(driver));
}
final SgmlPage lastPage;
try {
lastPage = getLastPage(driver);
}
catch (final IllegalStateException e) {
return Collections.emptyList();
}
final NodeList allElements = lastPage.getElementsByTagName(name);
final List toReturn = new ArrayList<>(allElements.getLength());
for (int i = 0; i < allElements.getLength(); i++) {
final Node item = allElements.item(i);
if (item instanceof DomElement) {
toReturn.add(driver.toWebElement((DomElement) item));
}
}
return toReturn;
}
@Override
public WebElement findElement(final HtmlUnitWebElement element, final By locator) {
final NodeList allElements = element.getElement().getElementsByTagName(getValue(locator));
if (allElements.getLength() > 0) {
return element.getDriver().toWebElement((HtmlElement) allElements.item(0));
}
throw new NoSuchElementException("Unable to locate element with name: " + getValue(locator));
}
@Override
public List findElements(final HtmlUnitWebElement element, final By locator) {
final NodeList allElements = element.getElement().getElementsByTagName(getValue(locator));
final List toReturn = new ArrayList<>(allElements.getLength());
for (int i = 0; i < allElements.getLength(); i++) {
final Node item = allElements.item(i);
if (item instanceof DomElement) {
toReturn.add(element.getDriver().toWebElement((DomElement) item));
}
}
return toReturn;
}
}
public static class FindByXPath extends HtmlUnitElementLocator {
@Override
public WebElement findElement(final HtmlUnitDriver driver, final By locator) {
final Object node;
final SgmlPage lastPage = getLastPage(driver);
final String value = getValue(locator);
try {
node = lastPage.getFirstByXPath(value);
}
catch (final Exception ex) {
// The xpath expression cannot be evaluated, so the expression is invalid
throw new InvalidSelectorException(String.format(INVALIDXPATHERROR, value), ex);
}
if (node == null) {
throw new NoSuchElementException("Unable to locate a node using " + value);
}
if (node instanceof DomElement) {
return driver.toWebElement((DomElement) node);
}
// The xpath expression selected something different than a WebElement.
// The selector is therefore invalid
throw new InvalidSelectorException(String.format(INVALIDSELECTIONERROR, value, node.getClass()));
}
@Override
public List findElements(final HtmlUnitDriver driver, final By locator) {
final SgmlPage lastPage;
try {
lastPage = getLastPage(driver);
}
catch (final IllegalStateException e) {
return Collections.emptyList();
}
final String value = getValue(locator);
final List> nodes;
try {
nodes = lastPage.getByXPath(value);
}
catch (final RuntimeException ex) {
// The xpath expression cannot be evaluated, so the expression is invalid
throw new InvalidSelectorException(String.format(INVALIDXPATHERROR, value), ex);
}
final List toReturn = new ArrayList<>(nodes.size());
for (final Object node : nodes) {
// There exist elements in the nodes list which could not be converted to
// WebElements.
// A valid xpath selector should only select WebElements.
if (!(node instanceof DomElement)) {
// We only want to know the type of one invalid element so that we can give this
// information in the exception. We can throw the exception immediately.
throw new InvalidSelectorException(String.format(INVALIDSELECTIONERROR, value, node.getClass()));
}
toReturn.add(driver.toWebElement((DomElement) node));
}
return toReturn;
}
@Override
public WebElement findElement(final HtmlUnitWebElement element, final By locator) {
final String value = getValue(locator);
final Object node;
try {
node = element.getElement().getFirstByXPath(value);
}
catch (final Exception ex) {
// The xpath expression cannot be evaluated, so the expression is invalid
throw new InvalidSelectorException(String.format(INVALIDXPATHERROR, value), ex);
}
if (node == null) {
throw new NoSuchElementException("Unable to find an element with xpath " + value);
}
if (node instanceof HtmlElement) {
return element.getDriver().toWebElement((HtmlElement) node);
}
// The xpath selector selected something different than a WebElement. The
// selector is therefore
// invalid
throw new InvalidSelectorException(
String.format(INVALIDSELECTIONERROR, value, node.getClass().toString()));
}
@Override
public List findElements(final HtmlUnitWebElement element, final By locator) {
final String value = getValue(locator);
final List toReturn = new ArrayList<>();
final List> domElements;
try {
domElements = element.getElement().getByXPath(value);
}
catch (final Exception ex) {
// The xpath expression cannot be evaluated, so the expression is invalid
throw new InvalidSelectorException(String.format(INVALIDXPATHERROR, value), ex);
}
for (final Object e : domElements) {
if (e instanceof DomElement) {
toReturn.add(element.getDriver().toWebElement((DomElement) e));
}
else {
// The xpath selector selected something different than a WebElement. The
// selector is
// therefore invalid
throw new InvalidSelectorException(
String.format(INVALIDSELECTIONERROR, value, e.getClass().toString()));
}
}
return toReturn;
}
}
public abstract static class HtmlUnitElementLocator {
public WebElement findElement(final HtmlUnitDriver driver, final By locator) {
final List toReturn = findElements(driver, locator);
if (!toReturn.isEmpty()) {
return toReturn.get(0);
}
throw new NoSuchElementException("Unable to locate element");
}
public abstract List findElements(HtmlUnitDriver driver, By locator);
public WebElement findElement(final HtmlUnitWebElement element, final By locator) {
final List toReturn = findElements(element, locator);
if (!toReturn.isEmpty()) {
return toReturn.get(0);
}
throw new NoSuchElementException("Unable to locate element");
}
public abstract List findElements(HtmlUnitWebElement element, By locator);
protected static By.Remotable getRemotable(final By locator) {
if (!(locator instanceof By.Remotable)) {
throw new IllegalStateException("Cannot convert locator to Remotable");
}
return (By.Remotable) locator;
}
protected SgmlPage getLastPage(final HtmlUnitDriver driver) {
final Page lastPage = driver.getCurrentWindow().lastPage();
if (!(lastPage instanceof SgmlPage)) {
throw new IllegalStateException("Current page is not a SgmlPage");
}
return (SgmlPage) lastPage;
}
protected static String getValue(final By locator) {
final By.Remotable remote = getRemotable(locator);
return (String) remote.getRemoteParameters().value();
}
}
private static List convertRawDomElementsToWebElements(
final HtmlUnitDriver driver, final List nodes) {
final List toReturn = new ArrayList<>(nodes.size());
for (final DomElement node : nodes) {
toReturn.add(driver.toWebElement(node));
}
return toReturn;
}
public static class FindByRelativeLocator extends HtmlUnitElementLocator {
@Override
public List findElements(final HtmlUnitDriver driver, final By locator) {
return (List) driver.executeScript(FIND_ELEMENTS_JS, asParameter(locator));
}
@Override
public List findElements(final HtmlUnitWebElement element, final By locator) {
// TODO Auto-generated method stub
return null;
}
private static Object asParameter(final By locator) {
final Parameters params = ((RelativeBy) locator).getRemoteParameters();
return Map.of(params.using(), params.value());
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy