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

geb.navigator.CssSelector.groovy Maven / Gradle / Ivy

package geb.navigator

import org.openqa.selenium.By
import org.openqa.selenium.WebElement
import static java.util.Collections.EMPTY_LIST
import static geb.navigator.SelectorType.*

class CssSelector {

	private static final CssSelector DESCENDANT_SELECTOR = new CssSelector(SelectorType.DESCENDANT, " ")
	private static final String CSS_SELECTOR_SPECIAL_CHARS_PATTERN = '[!"#$%&\'\\(\\)*+,./:;<=>?@\\[\\]^`\\{|\\}~\\\\]'

	final SelectorType type
	final String value

	CssSelector() {
		super()
	}

	CssSelector(SelectorType type, String value) {
		this.type = type
		this.value = value
	}

	// TODO: better name

	static List findByCssSelector(Collection elements, String selectorString) {
		def result = []
		CssSelector.compile(selectorString).each { List selectorGroup ->
			def context = elements
			boolean descend = true
			selectorGroup.each { CssSelector selector ->
				if (selector.type == SelectorType.DESCENDANT) {
					descend = true
				} else {
					context = context.inject([]) { list, element ->
						list.addAll selector.apply(element, descend)
						list
					}
					descend = false
				}
			}
			result += context
		}
		return result
	}

	static String escape(String value) {
		value.replaceAll("($CSS_SELECTOR_SPECIAL_CHARS_PATTERN)", '\\\\$1')
	}
	
	static boolean matches(WebElement element, String selectorString) {
		def selectors = compile(selectorString)
		selectors.any { List selectorGroup ->
			selectorGroup.every { CssSelector selector ->
				selector.matches(element)
			}
		}
	}

	List apply(WebElement element, boolean descend) {
		if (descend) {
			return select(element)
		} else {
			return matches(element) ? [element] : EMPTY_LIST
		}
	}

	boolean matches(WebElement element) {
		// TODO: switch = failure of object orientation
		switch (type) {
			case ELEMENT:
				return element.tagName == value
			case HTML_CLASS:
				return element.getAttribute("class") =~ /(^|\s)$value($|\s)/
			case ID:
				return element.getAttribute("id") == value
			default:
				return false
		}
	}

	List select(WebElement element) {
		switch (type) {
			case ELEMENT:
				return element.findElements(By.tagName(value))
			case HTML_CLASS:
				return element.findElements(By.className(value))
			case ID:
				return element.findElements(By.id(value))
			default:
				return EMPTY_LIST
		}
	}

	String toString() {
		"${type.prefix}$value"
	}

	// TODO: should be private

	static List> compile(String groupSelector) {
		List> result = []
		groupSelector.split(",").each { String part ->
			part = part.trim()
			if (part) {
				List compiled = compileSingle(part)
				if (compiled) {
					result << compiled
				}
			}
		}
		return result
	}

	private static List compileSingle(String selector) {
		List result = []
		boolean first = true
		selector.split(/\s/).each { String part ->
			part = part.trim()
			if (part) {
				if (first) {
					first = false
				} else {
					result << DESCENDANT_SELECTOR
				}
				compileSimpleSelector(part, result)
			}
		}
		return result
	}

	private static void compileSimpleSelector(String selector, List list) {
		tokenize(selector).each { String part ->
			if (part) {
				if (part.startsWith(".")) {
					list << new CssSelector(SelectorType.HTML_CLASS, part.substring(1))
				} else if (part.startsWith("#")) {
					list << new CssSelector(SelectorType.ID, part.substring(1))
				} else {
					list << new CssSelector(SelectorType.ELEMENT, part)
				}
			}
		}
	}

	private static List tokenize(String selector) {
		List tokens = []
		int previous = 0
		int max = selector.length()
		for (int index = 0; index < max; ++index) {
			char character = selector.charAt(index)
			if (index > 0 && (character == '.' || character == '#')) {
				tokens << selector.substring(previous, index)
				previous = index
			}
		}
		tokens << selector.substring(previous)
		return tokens
	}
}

enum SelectorType {
	ELEMENT(""),
	HTML_CLASS("."),
	ID("#"),
	DESCENDANT("")

	final prefix

	SelectorType(String prefix) {
		this.prefix = prefix
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy