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

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

package geb.navigator

import geb.Browser
import geb.Page
import java.util.regex.Pattern
import org.openqa.selenium.By
import org.openqa.selenium.WebElement
import org.openqa.selenium.RenderedWebElement
import org.openqa.selenium.internal.FindsByCssSelector
import static java.util.Collections.EMPTY_LIST
import static java.util.Collections.EMPTY_SET

class NonEmptyNavigator extends Navigator {

	static {
		def mc = new AttributeAccessingMetaClass(new ExpandoMetaClass(NonEmptyNavigator))
		mc.initialize()
		NonEmptyNavigator.metaClass = mc
	}

	private final List contextElements
	final Browser browser

	NonEmptyNavigator(Browser browser, WebElement... contextElements) {
		this.browser = browser
		this.contextElements = contextElements as List
	}

	NonEmptyNavigator(Browser browser, Collection contextElements) {
		this.browser = browser
		this.contextElements = contextElements as List
	}

	protected Navigator navigatorFor(Collection contextElements) {
		on browser, contextElements
	}

	protected Navigator navigatorFor(WebElement... contextElements) {
		on browser, contextElements
	}

	Navigator find(String selectorString) {
		if (contextElements.head() instanceof FindsByCssSelector) {
			List list = []
			contextElements.each {
				list.addAll it.findElements(By.cssSelector(selectorString))
			}
			navigatorFor list
		} else {
			navigatorFor CssSelector.findByCssSelector(allElements(), selectorString)
		}
	}

	Navigator find(Map predicates) {
		find predicates, "*"
	}

	Navigator find(Map predicates, String selector) {
		selector = optimizeSelector(selector, predicates)
		find(selector).filter(predicates)
	}

	Navigator filter(String selectorString) {
		navigatorFor contextElements.findAll { element ->
			CssSelector.matches(element, selectorString)
		}
	}

	Navigator filter(Map predicates) {
		navigatorFor contextElements.findAll { matches(it, predicates) }
	}

	Navigator filter(Map predicates, String selector) {
		filter(selector).filter(predicates)
	}

	Navigator getAt(int index) {
		navigatorFor getElement(index)
	}

	Navigator getAt(Range range) {
		navigatorFor getElements(range)
	}

	Navigator getAt(EmptyRange range) {
		EmptyNavigator.instance
	}

	Navigator getAt(Collection indexes) {
		navigatorFor getElements(indexes)
	}

	Collection allElements() {
		contextElements as WebElement[]
	}

	WebElement getElement(int index) {
		contextElements[index]
	}

	List getElements(Range range) {
		contextElements[range]
	}

	List getElements(EmptyRange range) {
		EMPTY_LIST
	}

	List getElements(Collection indexes) {
		contextElements[indexes]
	}

	Navigator remove(int index) {
		int size = size()
		if (!(index in -size.. classes() {
		def classNames = contextElements.head().getAttribute("class")?.tokenize()
			classNames as Set ?: EMPTY_SET
	}

	def value() {
		getInputValue(contextElements.head())
	}

	Navigator value(value) {
		setInputValues(contextElements, value)
		this
	}

	Navigator leftShift(value) {
		contextElements.each {
			it.sendKeys value
		}
		this
	}

	void click() {
		contextElements*.click()
	}

	void click(Class pageClass) {
		click()
		browser.page(pageClass)
	}

	void click(List> potentialPageClasses) {
		click()
		browser.page(potentialPageClasses)
	}

	int size() {
		contextElements.size()
	}

	boolean isEmpty() {
		size() == 0
	}

	boolean asBoolean() {
		!isEmpty()
	}

	Navigator head() {
		first()
	}

	Navigator first() {
		navigatorFor firstElement()
	}

	Navigator last() {
		navigatorFor lastElement()
	}

	Navigator tail() {
		navigatorFor contextElements.tail()
	}

	Navigator verifyNotEmpty() {
		this
	}

	String toString() {
		contextElements*.toString()
	}

	def methodMissing(String name, arguments) {
		if (!arguments) {
			navigatorFor collectElements {
				it.findElements By.name(name)
			}
		} else {
			throw new MissingMethodException(name, getClass(), arguments)
		}
	}

	def propertyMissing(String name) {
		switch (name) {
			case ~/@.+/:
				return getAttribute(name.substring(1))
			default:
				def inputs = collectElements {
					it.findElements(By.name(name))
				}

				if (inputs) {
					return getInputValues(inputs)
				} else {
					throw new MissingPropertyException(name, getClass())
				}
		}
	}

	def propertyMissing(String name, value) {
		def inputs = collectElements {
			it.findElements(By.name(name))
		}

		if (inputs) {
			setInputValues(inputs, value)
		} else {
			throw new MissingPropertyException(name, getClass())
		}
	}

	/**
	 * Optimizes the selector if the predicates contains `class` or `id` keys that map to strings. Note this method has
	 * a side-effect in that it _removes_ those keys from the predicates map.
	 */
	private String optimizeSelector(String selector, Map predicates) {
		def buffer = new StringBuilder(selector)
		if (predicates.containsKey("id") && predicates["id"] in String) {
			buffer << "#" << predicates.remove("id")
		}
		if (predicates.containsKey("class") && predicates["class"] in String) {
			predicates.remove("class").split(/\s+/).each { className ->
				buffer << "." << className
			}
		}
		if (buffer[0] == "*" && buffer.length() > 1) buffer.deleteCharAt(0)
		return buffer.toString()
	}

	private boolean matches(WebElement element, Map predicates) {
		return predicates.every { name, requiredValue ->
			def actualValue = name == "text" ? element.text : element.getAttribute(name)
			if (requiredValue instanceof Pattern) {
				actualValue ==~ requiredValue
			} else {
				actualValue == requiredValue
			}
		}
	}

	private getInputValues(Collection inputs) {
		def values = []
		inputs.each { WebElement input ->
			def value = getInputValue(input)
			if (value) values << value
		}
		return values.size() < 2 ? values[0] : values
	}

	private getInputValue(WebElement input) {
		def value = null
		if (input.tagName == "select") {
			if (input.getAttribute("multiple")) {
				value = input.findElements(By.tagName("option")).findAll { it.isSelected() }*.value
			} else {
				value = input.findElements(By.tagName("option")).find { it.isSelected() }.value
			}
		} else if (input.getAttribute("type") in ["checkbox", "radio"]) {
			if (input.isSelected()) {
				value = input.value
			}
		} else {
			value = input.value
		}
		value
	}

	private void setInputValues(Collection inputs, value) {
		inputs.each { WebElement input ->
			setInputValue(input, value)
		}
	}

	private void setInputValue(WebElement input, value) {
		if (input.tagName == "select") {
			if (!isAttributeEffectivelyFalse(input, "multiple")) {
				input.findElements(By.tagName("option")).each { WebElement option ->
					if (option.value in value) {
						option.setSelected()
					} else if (option.isSelected()) {
						option.toggle()
					}
				}
			} else {
				input.findElements(By.tagName("option")).find { it.value == value }.setSelected()
			}
		} else if (input.getAttribute("type") == "checkbox") {
			if (input.value == value || value == true) {
				input.setSelected()
			} else if (input.isSelected()) {
				input.toggle()
			}
		} else if (input.getAttribute("type") == "radio") {
			if (input.value == value) {
				input.setSelected()
			}
		} else {
			input.clear()
			input.sendKeys value
		}
	}

	// The Firefox driver at least will return a literal false when checking for certain
	// attributes. This goes against the spec of WebElement#getAttribute() but it happens
	// none the less.

	private boolean isAttributeEffectivelyFalse(WebElement input, String attribute) {
		def value = input.getAttribute(attribute)
		value == null || value == "" || value == "false" || value == false
	}

	private WebElement firstElementInContext(Closure closure) {
		def result = null
		for (int i = 0; !result && i < contextElements.size(); i++) {
			try {
				result = closure(contextElements[i])
			} catch (org.openqa.selenium.NoSuchElementException e) { }
		}
		result
	}

	private List collectElements(Closure closure) {
		List list = []
		contextElements.each {
			try {
				def value = closure(it)
				switch (value) {
					case Collection:
						list.addAll value
						break
					default:
						if (value) list << value
				}
			} catch (org.openqa.selenium.NoSuchElementException e) { }
		}
		list
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy