gwen.web.WebEnvContext.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gwen-web Show documentation
Show all versions of gwen-web Show documentation
Web automation engine for Gwen
/*
* Copyright 2014-2017 Brady Wood, Branko Juric
*
* Licensed 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 gwen.web
import java.io.File
import scala.util.Failure
import scala.util.Success
import scala.util.Try
import org.openqa.selenium._
import org.openqa.selenium.support.ui.Select
import org.openqa.selenium.support.ui.WebDriverWait
import gwen.Predefs.Kestrel
import gwen.dsl.Failed
import gwen.eval.{EnvContext, GwenOptions, ScopedData, ScopedDataStack}
import gwen.errors._
import scala.io.Source
import scala.sys.process._
import scala.collection.JavaConverters._
import org.openqa.selenium.interactions.Actions
import java.io.FileNotFoundException
/**
* Defines the web environment context. This includes the configured selenium web
* driver instance, feature and page scopes, and web element functions.
*
* @author Branko Juric
*/
class WebEnvContext(val options: GwenOptions, val scopes: ScopedDataStack) extends EnvContext(options, scopes)
with WebElementLocator with DriverManager {
Try(logger.info(s"GWEN_CLASSPATH = ${sys.env("GWEN_CLASSPATH")}"))
Try(logger.info(s"SELENIUM_HOME = ${sys.env("SELENIUM_HOME")}"))
/** Resets the current context and closes the web browser. */
override def reset() {
super.reset()
close()
}
/** Closes the current web driver. */
override def close() {
quit()
super.close()
}
/**
* Injects and executes a javascript on the current page.
*
* @param javascript the script expression to execute
* @param params optional parameters to the script
* @param takeScreenShot true to take screenshot after performing the function
*/
def executeScript(javascript: String, params: Any*)(implicit takeScreenShot: Boolean = false): Any =
withWebDriver { webDriver =>
webDriver.asInstanceOf[JavascriptExecutor].executeScript(javascript, params.map(_.asInstanceOf[AnyRef]) : _*) tap { result =>
if (takeScreenShot && WebSettings.`gwen.web.capture.screenshots`) {
captureScreenshot(false)
}
logger.debug(s"Evaluated javascript: $javascript, result='$result'")
if (result.isInstanceOf[Boolean] && result.asInstanceOf[Boolean]) {
Thread.sleep(WebSettings.`gwen.web.throttle.msecs`)
}
}
}
/**
* Injects and executes a javascript predicate on the current page.
*
* @param javascript the script predicate expression to execute
* @param params optional parameters to the script
*/
def executeScriptPredicate(javascript: String, params: Any*): Boolean =
executeScript(s"return $javascript", params.map(_.asInstanceOf[AnyRef]) : _*).asInstanceOf[Boolean]
/**
* Waits for a given condition to be true. Errors on time out
* after "gwen.web.wait.seconds" (default is 10 seconds)
*
* @param reason the reason for waiting (used to report timeout error)
* @param condition the boolean condition to wait for (until true)
*/
def waitUntil(reason: String)(condition: => Boolean) {
waitUntil(reason, WebSettings.`gwen.web.wait.seconds`) { condition }
}
/**
* Waits or a given condition to be true. Errors on time out
* after "gwen.web.wait.seconds" (default is 10 seconds)
*
* @param condition the boolean condition to wait for (until true)
*/
def waitUntil(condition: => Boolean) {
waitUntil(WebSettings.`gwen.web.wait.seconds`) { condition }
}
/**
* Waits for a given condition to be true for a given number of seconds.
* Errors after given timeout out seconds.
*
* @param reason the reason for waiting (used to report timeout error)
* @param timeoutSecs the number of seconds to wait before timing out
* @param condition the boolean condition to wait for (until true)
*/
def waitUntil(reason: String, timeoutSecs: Long)(condition: => Boolean) {
waitUntil(Some(reason), timeoutSecs)(condition)
}
/**
* Waits for a given condition to be true for a given number of seconds.
* Errors on given timeout out seconds.
*
* @param timeoutSecs the number of seconds to wait before timing out
* @param condition the boolean condition to wait for (until true)
*/
def waitUntil(timeoutSecs: Long)(condition: => Boolean) {
waitUntil(None, timeoutSecs)(condition)
}
/**
* Waits until a given condition is ready for a given number of seconds.
* Errors on given timeout out seconds.
*
* @param reason optional reason for waiting (used to report timeout error)
* @param timeoutSecs the number of seconds to wait before timing out
* @param condition the boolean condition to wait for (until true)
*/
private def waitUntil(reason: Option[String], timeoutSecs: Long)(condition: => Boolean) {
def doWaitUntil(webDriver: WebDriver, timeout: Long) {
new WebDriverWait(webDriver, timeout).until {
(driver: WebDriver) => condition
}
}
// some drivers intermittently throw javascript errors, so have to track timeout and retry
val start = System.nanoTime()
withWebDriver { webDriver =>
reason foreach { r => logger.info(r) }
var timeout = timeoutSecs
while (timeout > -1) {
try {
doWaitUntil(webDriver, timeout)
timeout = -1
} catch {
case e: TimeoutException=> throw e
case e: WebDriverException =>
Thread.sleep(WebSettings`gwen.web.throttle.msecs`)
timeout = timeoutSecs - ((System.nanoTime() - start) / 1000000000L)
if (timeout <= 0) throw e
}
}
}
}
/**
* Highlights and then un-highlights a browser element.
* Uses pure javascript, as suggested by https://github.com/alp82.
* The duration of the highlight lasts for `gwen.web.throttle.msecs`.
* The look and feel of the highlight is controlled by the
* `gwen.web.highlight.style` setting.
*
* @param element the element to highlight
*/
def highlight(element: WebElement): Unit = {
val msecs = WebSettings`gwen.web.throttle.msecs`; // need semi-colon (compiler bug?)
if (msecs > 0) {
val style = WebSettings.`gwen.web.highlight.style`
executeScript(s"element = arguments[0]; type = element.getAttribute('type'); if (('radio' == type || 'checkbox' == type) && element.parentElement.getElementsByTagName('input').length == 1) { element = element.parentElement; } original_style = element.getAttribute('style'); element.setAttribute('style', original_style + '; $style'); setTimeout(function() { element.setAttribute('style', original_style); }, $msecs);", element)(WebSettings.`gwen.web.capture.screenshots.highlighting`)
Thread.sleep(msecs)
}
}
/**
* Add a list of error attachments which includes the current
* screenshot and all current error attachments.
*
* @param failure the failed status
*/
override def addErrorAttachments(failure: Failed): Unit = {
super.addErrorAttachments(failure)
execute(captureScreenshot(true))
}
/**
* Performs a function on a web element and transparently re-locates elements and
* re-attempts the function if the web driver throws an exception.
*
* @param action the action string
* @param elementBinding the web element locator binding
* @param f the function to perform on the element
*/
def withWebElement[T](action: String, elementBinding: LocatorBinding)(f: WebElement => T): T =
withWebElement(Some(action), elementBinding)(f)
/**
* Performs a function on a web element and transparently re-locates elements and
* re-attempts the function if the web driver throws an exception.
*
* @param elementBinding the web element locator binding
* @param f the function to perform on the element
*/
def withWebElement[T](elementBinding: LocatorBinding)(f: WebElement => T): T =
withWebElement(None, elementBinding)(f)
/**
* Locates a web element and performs a function on it.
*
* @param action optional action string
* @param elementBinding the web element locator binding
* @param f the function to perform on the element
*/
private def withWebElement[T](action: Option[String], elementBinding: LocatorBinding)(f: WebElement => T): T = {
val wHandle = elementBinding.container.map(_ => withWebDriver(_.getWindowHandle))
try {
val webElement =
if (elementBinding.locator == "cache") {
featureScope.objects.get(elementBinding.element) match {
case Some(obj) if (obj.isInstanceOf[WebElement]) => obj.asInstanceOf[WebElement] tap { highlight(_)}
case _ => throw new NoSuchElementException(s"${elementBinding.element} not found")
}
} else {
locate(this, elementBinding)
}
action.foreach { actionString =>
logger.debug(s"${actionString match {
case "click" => "Clicking"
case "submit" => "Submitting"
case "check" => "Checking"
case "uncheck" => "Unchecking"
}} ${elementBinding.element}")
}
f(webElement) tap { _ =>
if (WebSettings.`gwen.web.capture.screenshots`) {
captureScreenshot(false)
}
}
} finally {
wHandle foreach { handle =>
withWebDriver { driver =>
driver.switchTo().window(handle)
}
}
}
}
/**
* Checks the current state of an element.
*
* @param elementBinding the locator binding of the element
* @param state the state to check
* @param negate whether or not to negate the check
*/
def checkElementState(elementBinding: LocatorBinding, state: String, negate: Boolean): Unit = {
var result = false
try {
withWebElement(elementBinding) { webElement =>
result = state match {
case "displayed" => webElement.isDisplayed
case "hidden" => !webElement.isDisplayed
case "checked" | "ticked" => webElement.isSelected
case "unchecked" | "unticked" => !webElement.isSelected
case "enabled" => webElement.isEnabled
case "disabled" => !webElement.isEnabled
}
}
} catch {
case e: NoSuchElementException =>
if ((state == "displayed" && negate) || (state == "hidden" && !negate)) result = !negate
else if (state == "displayed" || state == "hidden") result = false
else throw e
}
if (!negate) assert(result, s"${elementBinding.element} should be $state")
else assert(!result, s"${elementBinding.element} should not be $state")
bindAndWait(elementBinding.element, state, "true")
}
/**
* Gets a bound value from memory. A search for the value is made in
* the following order and the first value found is returned:
* - Web element text on the current page
* - Currently active page scope
* - The global feature scope
* - Settings
*
* @param name the name of the bound value to find
*/
override def getBoundReferenceValue(name: String): String = {
if (name == "the current URL") captureCurrentUrl()
(Try(getLocatorBinding(name)) match {
case Success(binding) =>
Try(execute(getElementText(binding)).getOrElse(None)) match {
case Success(text) => text.getOrElse(getAttribute(name))
case Failure(e) => throw e
}
case Failure(_) => getAttribute(name)
}) tap { value =>
logger.debug(s"getBoundReferenceValue($name)='$value'")
}
}
def captureCurrentUrl(): ScopedData =
featureScope.set("the current URL", execute(withWebDriver(_.getCurrentUrl()) tap { content =>
addAttachment("the current URL", "txt", content)
}).getOrElse("$[currentUrl]"))
/**
* Gets the text value of a web element on the current page.
* A search for the text is made in the following order and the first value
* found is returned:
* - Web element text
* - Web element text attribute
* - Web element value attribute
* If a value is found, its value is bound to the current page
* scope as `name/text`.
*
* @param elementBinding the web element locator binding
*/
private def getElementText(elementBinding: LocatorBinding): Option[String] =
withWebElement(elementBinding) { webElement =>
(Option(webElement.getText) match {
case None | Some("") =>
Option(webElement.getAttribute("text")) match {
case None | Some("") =>
Option(webElement.getAttribute("value")) match {
case None | Some("") =>
val value = executeScript("(function(element){return element.innerText || element.textContent || ''})(arguments[0]);", webElement).asInstanceOf[String]
if (value != null) Some(value) else Some("")
case value => value
}
case value => value
}
case value => value
}) tap { text =>
bindAndWait(elementBinding.element, "text", text.orNull)
}
} tap { value =>
logger.debug(s"getElementText(${elementBinding.element})='$value'")
}
/**
* Gets the selected text of a dropdown web element on the current page.
* If a value is found, its value is bound to the current page
* scope as `name/selectedText`.
*
* @param name the web element name
*/
private def getSelectedElementText(name: String): String = {
val elementBinding = getLocatorBinding(name)
withWebElement(elementBinding) { webElement =>
val select = new Select(webElement)
(Option(select.getAllSelectedOptions.asScala.map(_.getText()).mkString(",")) match {
case None | Some("") =>
select.getAllSelectedOptions.asScala.map(_.getAttribute("text")).mkString(",")
case Some(value) => value
}) tap { text =>
bindAndWait(elementBinding.element, "selectedText", text)
}
} tap { value =>
logger.debug(s"getSelectedElementText(${elementBinding.element})='$value'")
}
}
/**
* Gets the selected value of a dropdown web element on the current page.
* If a value is found, its value is bound to the current page
* scope as `name/selectedValue`.
*
* @param name the web element name
*/
private def getSelectedElementValue(name: String): String = {
val elementBinding = getLocatorBinding(name)
withWebElement(elementBinding) { webElement =>
new Select(webElement).getAllSelectedOptions.asScala.map(_.getAttribute("value")).mkString(",") tap { value =>
bindAndWait(elementBinding.element, "selectedValue", value)
}
} tap { value =>
logger.debug(s"getSelectedElementValue(${elementBinding.element})='$value'")
}
}
/**
* Gets an element's selected value(s).
*
* @param name the name of the element
* @param selection `text` to get selected option text, `value` to get
* selected option value
* @return the selected value or a comma seprated string containing all
* the selected values if multiple values are selected.
*/
def getElementSelection(name: String, selection: String): String = execute {
selection.trim match {
case "text" => getSelectedElementText(name)
case _ => getSelectedElementValue(name)
}
}.getOrElse(s"$$[$name $selection]")
/**
* Gets a bound attribute value from the visible scope.
*
* @param name the name of the bound attribute to find
*/
def getAttribute(name: String): String = {
val attScopes = scopes.visible.filterAtts{case (n, _) => n.startsWith(name)}
(attScopes.findEntry { case (n, v) => n.matches(s"""$name(/(text|javascript|xpath.+|regex.+|json path.+|sysproc|file|sql.+))?""") && v != "" } map {
case (n, v) =>
if (n == s"$name/text") v
else if (n == s"$name/javascript")
execute(Option(executeScript(s"return ${interpolate(v)(getBoundReferenceValue)}")).map(_.toString).getOrElse("")).getOrElse(s"$$[javascript:$v]")
else if (n.startsWith(s"$name/xpath")) {
val source = interpolate(getBoundReferenceValue(attScopes.get(s"$name/xpath/source")))(getBoundReferenceValue)
val targetType = interpolate(attScopes.get(s"$name/xpath/targetType"))(getBoundReferenceValue)
val expression = interpolate(attScopes.get(s"$name/xpath/expression"))(getBoundReferenceValue)
execute(evaluateXPath(expression, source, XMLNodeType.withName(targetType))).getOrElse(s"$$[xpath:$expression]")
}
else if (n.startsWith(s"$name/regex")) {
val source = interpolate(getBoundReferenceValue(attScopes.get(s"$name/regex/source")))(getBoundReferenceValue)
val expression = interpolate(attScopes.get(s"$name/regex/expression"))(getBoundReferenceValue)
execute(extractByRegex(expression, source)).getOrElse(s"$$[regex:$expression]")
}
else if (n.startsWith(s"$name/json path")) {
val source = interpolate(getBoundReferenceValue(attScopes.get(s"$name/json path/source")))(getBoundReferenceValue)
val expression = interpolate(attScopes.get(s"$name/json path/expression"))(getBoundReferenceValue)
execute(evaluateJsonPath(expression, source)).getOrElse(s"$$[json path:$expression]")
}
else if (n == s"$name/sysproc") execute(v.!!).map(_.trim).getOrElse(s"$$[sysproc:$v]")
else if (n == s"$name/file") {
val filepath = interpolate(v)(getBoundReferenceValue)
execute {
if (new File(filepath).exists()) {
Source.fromFile(filepath).mkString
} else throw new FileNotFoundException(s"File bound to '$name' not found: $filepath")
} getOrElse s"$$[file:$v]"
}
else if (n.startsWith(s"$name/sql")) {
val selectStmt = interpolate(attScopes.get(s"$name/sql/selectStmt"))(getBoundReferenceValue)
val dbName = interpolate(attScopes.get(s"$name/sql/dbName"))(getBoundReferenceValue)
execute(evaluateSql(selectStmt, dbName)).getOrElse(s"$$[sql:$selectStmt]")
}
else v
}).getOrElse {
execute(super.getBoundReferenceValue(name)).getOrElse {
Try(super.getBoundReferenceValue(name)).getOrElse {
Try(getLocatorBinding(name).lookup).getOrElse {
unboundAttributeError(name)
}
}
}
} tap { value =>
logger.debug(s"getAttribute($name)='$value'")
}
}
def boundAttributeOrSelection(element: String, selection: Option[String]): () => String = () => selection match {
case None => getBoundReferenceValue(element)
case Some(sel) =>
try {
getBoundReferenceValue(element + sel)
} catch {
case _: UnboundAttributeException => getElementSelection(element, sel)
case e: Throwable => throw e
}
}
/**
* Gets a web element binding.
*
* @param element the name of the web element
*/
def getLocatorBinding(element: String): LocatorBinding = {
featureScope.objects.get(element) match {
case None =>
val locatorBinding = s"$element/locator"
scopes.getOpt(locatorBinding) match {
case Some(locator) =>
val lookupBinding = interpolate(s"$element/locator/$locator")(getBoundReferenceValue)
scopes.getOpt(lookupBinding) match {
case Some(expression) =>
val expr = interpolate(expression)(getBoundReferenceValue)
val container = scopes.getOpt(interpolate(s"$element/locator/$locator/container")(getBoundReferenceValue))
if (isDryRun) {
container.foreach(c => getLocatorBinding(c))
}
LocatorBinding(element, locator, expr, container)
case None => throw new LocatorBindingException(element, s"locator lookup binding not found: $lookupBinding")
}
case None => throw new LocatorBindingException(element, s"locator binding not found: $locatorBinding")
}
case _ => LocatorBinding(element, "cache", element, None)
}
}
/**
* Binds the given element and value to a given action (element/action=value)
* and then waits for any bound post conditions to be satisfied.
*
* @param element the element to bind the value to
* @param action the action to bind the value to
* @param value the value to bind
*/
def bindAndWait(element: String, action: String, value: String) {
scopes.set(s"$element/$action", value)
// sleep if wait time is configured for this action
scopes.getOpt(s"$element/$action/wait") foreach { secs =>
logger.info(s"Waiting for $secs second(s) (post-$action wait)")
Thread.sleep(secs.toLong * 1000)
}
// wait for javascript post condition if one is configured for this action
scopes.getOpt(s"$element/$action/condition") foreach { condition =>
val javascript = scopes.get(s"$condition/javascript")
logger.debug(s"Waiting for script to return true: $javascript")
waitUntil(s"Waiting until $condition (post-$action condition)") {
executeScriptPredicate(javascript)
}
}
}
/** Gets the title of the current page in the browser.*/
def getTitle: String = withWebDriver { webDriver =>
webDriver.getTitle tap { title =>
bindAndWait("page", "title", title)
}
}
/**
* Sends a value to a web element (one character at a time).
*
* @param elementBinding the web element locator binding
* @param value the value to send
* @param clearFirst true to clear field first (if element is a text field)
* @param sendEnterKey true to send the Enter key after sending the value
*/
def sendKeys(elementBinding: LocatorBinding, value: String, clearFirst: Boolean, sendEnterKey: Boolean) {
val element = elementBinding.element
withWebElement(elementBinding) { webElement =>
if (clearFirst) {
clearText(webElement, element)
}
webElement.sendKeys(value)
bindAndWait(element, "type", value)
if (sendEnterKey) {
webElement.sendKeys(Keys.RETURN)
bindAndWait(element, "enter", "true")
}
}
}
def clearText(elementBinding: LocatorBinding) {
withWebElement(elementBinding) { clearText(_, elementBinding.element) }
}
private def clearText(webElement: WebElement, name: String) {
webElement.clear()
bindAndWait(name, "clear", "true")
}
/**
* Selects a value in a dropdown (select control) by visible text.
*
* @param elementBinding the web element locator binding
* @param value the value to select
*/
def selectByVisibleText(elementBinding: LocatorBinding, value: String) {
withWebElement(elementBinding) { webElement =>
logger.debug(s"Selecting '$value' in ${elementBinding.element} by text")
new Select(webElement).selectByVisibleText(value)
bindAndWait(elementBinding.element, "select", value)
}
}
/**
* Selects a value in a dropdown (select control) by value.
*
* @param elementBinding the web element locator binding
* @param value the value to select
*/
def selectByValue(elementBinding: LocatorBinding, value: String) {
withWebElement(elementBinding) { webElement =>
logger.debug(s"Selecting '$value' in ${elementBinding.element} by value")
new Select(webElement).selectByValue(value)
bindAndWait(elementBinding.element, "select", value)
}
}
/**
* Selects a value in a dropdown (select control) by index.
*
* @param elementBinding the web element locator binding
* @param index the index to select (first index is 1)
*/
def selectByIndex(elementBinding: LocatorBinding, index: Int) {
withWebElement(elementBinding) { webElement =>
logger.debug(s"Selecting option in ${elementBinding.element} by index: $index")
val select = new Select(webElement)
select.selectByIndex(index)
bindAndWait(elementBinding.element, "select", select.getFirstSelectedOption.getText)
}
}
def performAction(action: String, elementBinding: LocatorBinding) {
val actionBinding = scopes.getOpt(s"${elementBinding.element}/action/$action/javascript")
actionBinding match {
case Some(javascript) =>
performScriptAction(action, javascript, elementBinding)
case None =>
action match {
case "click" =>
withWebElement(action, elementBinding) { webElement =>
val clicked = executeScriptPredicate("(function(element){try{element.focus(); element.click(); return true;}catch(err){return false;}})(arguments[0]);", webElement)
if (!clicked) {
webElement.click()
}
}
case _ =>
withWebElement(action, elementBinding) { webElement =>
action match {
case "submit" => webElement.submit()
case "check" | "tick" =>
if (!webElement.isSelected) webElement.sendKeys(Keys.SPACE)
if (!webElement.isSelected) webElement.click()
case "uncheck" | "untick" =>
if (webElement.isSelected) webElement.sendKeys(Keys.SPACE)
if (webElement.isSelected) webElement.click()
}
}
}
}
bindAndWait(elementBinding.element, action, "true")
}
private def performScriptAction(action: String, javascript: String, elementBinding: LocatorBinding) {
withWebElement(action, elementBinding) { webElement =>
executeScript(s"(function(element) { $javascript })(arguments[0])", webElement)
bindAndWait(elementBinding.element, action, "true")
}
}
def performActionIn(action: String, elementBinding: LocatorBinding, contextBinding: LocatorBinding) {
def perform(webElement: WebElement, contextElement: WebElement)(buildAction: Actions => Actions) {
withWebDriver { driver =>
val moveTo = new Actions(driver).moveToElement(contextElement).moveToElement(webElement)
buildAction(moveTo).perform()
}
}
withWebElement(action, contextBinding) { contextElement =>
withWebElement(action, elementBinding) { webElement =>
action match {
case "click" => perform(webElement, contextElement) { _.click() }
case "check" | "tick" =>
if (!webElement.isSelected) perform(webElement, contextElement) { _.sendKeys(Keys.SPACE) }
if (!webElement.isSelected) perform(webElement, contextElement) { _.click() }
case "uncheck" | "untick" =>
if (webElement.isSelected) perform(webElement, contextElement) { _.sendKeys(Keys.SPACE) }
if (webElement.isSelected) perform(webElement, contextElement) { _.click() }
}
bindAndWait(elementBinding.element, action, "true")
}
}
}
/**
* Waits for text to appear in the given web element.
*
* @param elementBinding the web element locator binding
*/
def waitForText(elementBinding: LocatorBinding): Boolean =
getElementText(elementBinding).map(_.length()).getOrElse(0) > 0
/**
* Scrolls an element into view.
*
* @param elementBinding the web element locator binding
* @param scrollTo scroll element into view, options are: top or bottom
*/
def scrollIntoView(elementBinding: LocatorBinding, scrollTo: ScrollTo.Value) {
withWebElement(elementBinding) { scrollIntoView(_, scrollTo) }
}
/**
* Scrolls the given web element into view.
*
* @param webElement the web element to scroll to
* @param scrollTo scroll element into view, options are: top or bottom
*/
def scrollIntoView(webElement: WebElement, scrollTo: ScrollTo.Value) {
executeScript(s"var elem = arguments[0]; if (typeof elem !== 'undefined' && elem != null) { elem.scrollIntoView(${scrollTo == ScrollTo.top}); }", webElement)
}
/**
* Resizes the browser window to the given dimensions.
*
* @param width the width
* @param height the height
*/
def resizeWindow(width: Int, height: Int) {
logger.info(s"Resizing browser window to width $width and height $height")
withWebDriver { driver =>
driver.manage().window().setSize(new Dimension(width, height))
}
}
/**
* Maximizes the browser window.
*/
def maximizeWindow() {
logger.info("Maximising browser window")
withWebDriver { driver =>
driver.manage().window().maximize()
}
}
/**
* Gets the actual value of an attribute and compares it with an expected value or condition.
*
* @param name the name of the attribute being compared
* @param expected the expected value, regex, xpath, or json path
* @param actual the actual value of the element
* @param operator the comparison operator
* @param negate true to negate the result
* @return true if the actual value matches the expected value
*/
def compare(name: String, expected: String, actual: () => String, operator: String, negate: Boolean): Unit = {
var result = false
var actualValue = actual()
try {
waitUntil {
result = if (actualValue != null) {
super.compare(expected, actualValue, operator, negate)
} else false
result tap { r =>
if (!r) actualValue = actual()
}
}
} catch {
case _: TimeoutException => result = false
}
assert(result, s"Expected $name to ${if(negate) "not " else ""}$operator '$expected' but got '$actualValue'")
}
/**
* Adds web engine dsl steps to super implementation. The entries
* returned by this method are used for tab completion in the REPL.
*/
override def dsl: List[String] =
Source.fromInputStream(getClass.getResourceAsStream("/gwen-web.dsl")).getLines().toList ++ super.dsl
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy