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

com.github.markusbernhardt.seleniumlibrary.locators.ElementFinder Maven / Gradle / Ivy

There is a newer version: 4.0.0-alpha-2.0
Show newest version
package com.github.markusbernhardt.seleniumlibrary.locators;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.python.util.PythonInterpreter;

import com.github.markusbernhardt.seleniumlibrary.SeleniumLibraryNonFatalException;
import com.github.markusbernhardt.seleniumlibrary.keywords.Element;
import com.github.markusbernhardt.seleniumlibrary.utils.Python;

public class ElementFinder {

	protected final static Hashtable registeredLocationStrategies = new Hashtable();

	protected enum KeyAttrs {
		DEFAULT("@id,@name"), A("@id,@name,@href,normalize-space(descendant-or-self::text())"), IMG(
				"@id,@name,@src,@alt"), INPUT("@id,@name,@value,@src"), BUTTON(
				"@id,@name,@value,normalize-space(descendant-or-self::text())");

		protected String[] keyAttrs;

		KeyAttrs(String keyAttrs) {
			this.keyAttrs = keyAttrs.split(",");
		}

		public String[] getKeyAttrs() {
			return keyAttrs;
		}
	}

	protected interface Strategy {
		List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates);

	};

	protected enum StrategyEnum implements Strategy {
		DEFAULT {

			@Override
			public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) {
				if (findByCoordinates.criteria.startsWith("//")) {
					return XPATH.findBy(webDriver, findByCoordinates);
				}
				return findByKeyAttrs(webDriver, findByCoordinates);
			}
		},
		IDENTIFIER {

			@Override
			public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) {
				List elements = webDriver.findElements(By.id(findByCoordinates.criteria));
				elements.addAll(webDriver.findElements(By.name(findByCoordinates.criteria)));
				return filterElements(elements, findByCoordinates);
			}
		},
		ID {

			@Override
			public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) {
				return filterElements(webDriver.findElements(By.id(findByCoordinates.criteria)), findByCoordinates);
			}
		},
		NAME {

			@Override
			public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) {
				return filterElements(webDriver.findElements(By.name(findByCoordinates.criteria)), findByCoordinates);
			}
		},
		XPATH {

			@Override
			public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) {
				return filterElements(webDriver.findElements(By.xpath(findByCoordinates.criteria)), findByCoordinates);
			}
		},
		DOM {

			@Override
			public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) {
				Object result = ((JavascriptExecutor) webDriver).executeScript(String.format("return %s;",
						findByCoordinates.criteria));
				return filterElements(toList(result), findByCoordinates);
			}
		},
		LINK {

			@Override
			public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) {
				return filterElements(webDriver.findElements(By.linkText(findByCoordinates.criteria)),
						findByCoordinates);
			}
		},
		CSS {

			@Override
			public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) {
				return filterElements(webDriver.findElements(By.cssSelector(findByCoordinates.criteria)),
						findByCoordinates);
			}
		},
		TAG {

			@Override
			public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) {
				return filterElements(webDriver.findElements(By.tagName(findByCoordinates.criteria)), findByCoordinates);
			}
		},
		JQUERY {

			@Override
			public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) {

				return findByJQuerySizzle(webDriver, findByCoordinates);
			}

		},
		SIZZLE {

			@Override
			public List findBy(WebDriver webDriver, FindByCoordinates findByCoordinates) {

				return findByJQuerySizzle(webDriver, findByCoordinates);
			}

		};

	}

	protected static List findByJQuerySizzle(WebDriver webDriver, FindByCoordinates findByCoordinates) {
		String js = String.format("return jQuery('%s').get();", findByCoordinates.criteria.replace("'", "\\'"));

		Object o = ((JavascriptExecutor) webDriver).executeScript(js);
		List list = toList(o);
		return filterElements(list, findByCoordinates);
	}

	protected static List filterElements(List elements, FindByCoordinates findByCoordinates) {
		if (findByCoordinates.tag == null) {
			return elements;
		}

		List result = new ArrayList();
		for (WebElement element : elements) {
			if (elementMatches(element, findByCoordinates)) {
				result.add(element);
			}
		}
		return result;
	}

	protected static boolean elementMatches(WebElement element, FindByCoordinates findByCoordinates) {
		if (!element.getTagName().toLowerCase().equals(findByCoordinates.tag)) {
			return false;
		}

		if (findByCoordinates.constraints != null) {
			for (String name : findByCoordinates.constraints.keySet()) {
				if (!element.getAttribute(name).equals(findByCoordinates.constraints.get(name))) {
					return false;
				}
			}
		}

		return true;
	}

	protected static List findByKeyAttrs(WebDriver webDriver, FindByCoordinates findByCoordinates) {
		KeyAttrs keyAttrs = KeyAttrs.DEFAULT;
		if (findByCoordinates.tag != null) {
			try {
				keyAttrs = KeyAttrs.valueOf(findByCoordinates.tag.trim().toUpperCase());
			} catch (IllegalArgumentException e) {
				// No special keyAttrs available for this tag
			}
		}
		String xpathCriteria = Element.escapeXpathValue(findByCoordinates.criteria);
		String xpathTag = findByCoordinates.tag;
		if (findByCoordinates.tag == null) {
			xpathTag = "*";
		}
		List xpathConstraints = new ArrayList();
		if (findByCoordinates.constraints != null) {
			for (Entry entry : findByCoordinates.constraints.entrySet()) {
				xpathConstraints.add(String.format("@%s='%s'", entry.getKey(), entry.getValue()));
			}
		}
		List xpathSearchers = new ArrayList();
		for (String attr : keyAttrs.getKeyAttrs()) {
			xpathSearchers.add(String.format("%s=%s", attr, xpathCriteria));
		}
		xpathSearchers.addAll(getAttrsWithUrl(webDriver, keyAttrs, findByCoordinates.criteria));
		String xpath = String.format("//%s[%s(%s)]", xpathTag, Python.join(" and ", xpathConstraints)
				+ (xpathConstraints.size() > 0 ? " and " : ""), Python.join(" or ", xpathSearchers));

		return webDriver.findElements(By.xpath(xpath));
	}

	protected static List getAttrsWithUrl(WebDriver webDriver, KeyAttrs keyAttrs, String criteria) {
		List attrs = new ArrayList();
		String url = null;
		String xpathUrl = null;
		String[] srcHref = { "@src", "@href" };
		for (String attr : srcHref) {
			for (String keyAttr : keyAttrs.getKeyAttrs()) {
				if (attr.equals(keyAttr)) {
					if (url == null || xpathUrl == null) {
						url = getBaseUrl(webDriver) + "/" + criteria;
						xpathUrl = Element.escapeXpathValue(url);
					}
					attrs.add(String.format("%s=%s", attr, xpathUrl));
				}
			}
		}
		return attrs;
	}

	protected static String getBaseUrl(WebDriver webDriver) {
		String url = webDriver.getCurrentUrl();
		int lastIndex = url.lastIndexOf('/');
		if (lastIndex != -1) {
			url = url.substring(0, lastIndex);
		}
		return url;
	}

	public static void addLocationStrategy(String strategyName, String functionDefinition, String delimiter) {
		registeredLocationStrategies.put(strategyName.toUpperCase(), new CustomStrategy(functionDefinition, delimiter));
	}

	public static List find(WebDriver webDriver, String locator) {
		return find(webDriver, locator, null);
	}

	public static List find(WebDriver webDriver, String locator, String tag) {
		if (webDriver == null) {
			throw new SeleniumLibraryNonFatalException("ElementFinder.find: webDriver is null.");
		}
		if (locator == null) {
			throw new SeleniumLibraryNonFatalException("ElementFinder.find: locator is null.");
		}

		FindByCoordinates findByCoordinates = new FindByCoordinates();
		Strategy strategy = parseLocator(findByCoordinates, locator);
		parseTag(findByCoordinates, strategy, tag);
		return strategy.findBy(webDriver, findByCoordinates);
	}

	protected static ThreadLocal loggingPythonInterpreter = new ThreadLocal() {

		@Override
		protected PythonInterpreter initialValue() {
			PythonInterpreter pythonInterpreter = new PythonInterpreter();
			pythonInterpreter.exec("from robot.libraries.BuiltIn import BuiltIn; from robot.api import logger;");
			return pythonInterpreter;
		}
	};

	protected static void warn(String msg) {
		loggingPythonInterpreter.get().exec(
				String.format("logger.warn('%s');", msg.replace("'", "\\'").replace("\n", "\\n")));
	}

	protected static Strategy parseLocator(FindByCoordinates findByCoordinates, String locator) {
		String prefix = null;
		String criteria = locator;
		if (!locator.startsWith("//")) {
			String[] locatorParts = locator.split("[=:]", 2);
			if (locatorParts.length == 2) {
			    if (locator.charAt(locatorParts[0].length()) == "=".charAt(0)) {
			        System.out.println("*WARN* '=' is deprecated as locator strategy separator. ':' should be used instead" );
			    }
				prefix = locatorParts[0].trim().toUpperCase();
				criteria = locatorParts[1].trim();
			}
		}

		Strategy strategy = StrategyEnum.DEFAULT;
		if (prefix != null) {
			try {
				strategy = StrategyEnum.valueOf(prefix);
			} catch (IllegalArgumentException e) {
				// No standard locator type. Look for custom strategy
				CustomStrategy customStrategy = registeredLocationStrategies.get(prefix);
				if (customStrategy != null) {
					strategy = customStrategy;
				}
			}
		}
		findByCoordinates.criteria = criteria;
		return strategy;
	}

	protected static void parseTag(FindByCoordinates findByCoordinates, Strategy strategy, String tag) {
		if (tag == null) {
			return;
		}
		tag = tag.toLowerCase();
		Map constraints = new TreeMap();
		if (tag.equals("link")) {
			tag = "a";
		} else if (tag.equals("image")) {
			tag = "img";
		} else if (tag.equals("list")) {
			tag = "select";
		} else if (tag.equals("text area")) {
			tag = "textarea";
		} else if (tag.equals("radio button")) {
			tag = "input";
			constraints.put("type", "radio");
		} else if (tag.equals("checkbox")) {
			tag = "input";
			constraints.put("type", "checkbox");
		} else if (tag.equals("text field")) {
			tag = "input";
			constraints.put("type", "text");
		} else if (tag.equals("file upload")) {
			tag = "input";
			constraints.put("type", "file");
		}
		findByCoordinates.tag = tag;
		findByCoordinates.constraints = constraints;
	}

	@SuppressWarnings("unchecked")
	protected static List toList(Object o) {
		if (o instanceof List) {
			return (List) o;
		}
		List list = new ArrayList();
		if (o instanceof WebElement) {
			list.add((WebElement) o);
			return list;
		}
		return list;
	}

	protected static class FindByCoordinates {

		String criteria;
		String tag;
		Map constraints;
	}

	protected static class CustomStrategy implements Strategy {

		protected String functionDefinition;

		protected String delimiter;

		public CustomStrategy(String functionDefinition, String delimiter) {
			this.functionDefinition = functionDefinition;
			this.delimiter = delimiter;
		}

		@Override
		public List findBy(final WebDriver webDriver, final FindByCoordinates findByCoordinates) {
			return filterElements(webDriver.findElements(new By() {

				@Override
				public List findElements(SearchContext context) {
					Object[] arguments = null;
					if (delimiter == null) {
						arguments = new Object[1];
						arguments[0] = findByCoordinates.criteria;
					} else {
						String[] splittedCriteria = findByCoordinates.criteria.split(delimiter);
						arguments = new Object[splittedCriteria.length];
						for (int i = 0; i < splittedCriteria.length; i++) {
							arguments[i] = splittedCriteria[i];
						}
					}
					Object o = ((JavascriptExecutor) webDriver).executeScript(functionDefinition, arguments);
					return toList(o);
				}

			}), findByCoordinates);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy