Skip to content

Commit

Permalink
Replace internal stack of data caches with a single data cache (inter…
Browse files Browse the repository at this point in the history
…nal enhancement)
  • Loading branch information
bjuric committed Oct 15, 2024
1 parent 0fc845c commit 36f0893
Show file tree
Hide file tree
Showing 35 changed files with 433 additions and 674 deletions.
10 changes: 8 additions & 2 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
4.0.0
=====
12 October 2024
15 October 2024
- Introduce Gwen processes to support multiple launch configurations
- `-p|--process` option
- Require Java 17+ instead of Java 11+
- Use logback instead of log4j for logging
- Support `empty` literal in DSLs wherever `blank` is accepted
- Replace internal stack of data caches with a single data cache (internal enhancement)
- Add implicit variable(s):
- `gwen.process.name`
- Add environment variable(s)
Expand All @@ -16,9 +17,14 @@
- Report portal integration
- IE browser integration
- `gwen-web-accept-untrusted-certs` setting (use `acceptInsecureCerts` capability instead)
- Core DSL steps
- DSL steps
- `I wait <duration> second[s] when <element> is <actioned>`
- `I wait until <condition> when <element> is <actioned>`
- `I navigate to the <page>`
- `I am on the <page>`
- `the <page> url is "<url>"`
- `the url will be "<url>`
- `the url will be defined by <property|setting> "<name>"`
- Core DSL steps in favour of base step with `@Timeout` and `@Delay` annotations
- `<step> <until|while> <condition> using <delay> <delayUnit> delay`
- `<step> <until|while> <condition> using <timeout> <timeoutUnit> timeout`
Expand Down
3 changes: 0 additions & 3 deletions src/main/resources/gwen-web.dsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,6 @@
<name> is blank
<name> is true
<name> is false
I am on the <page>
the <page> url is "<url>"
I navigate to the <page>
I navigate to "<url>"
I scroll to the top of <element>
I scroll to the bottom of <element>
Expand Down
14 changes: 7 additions & 7 deletions src/main/scala/gwen/web/eval/WebContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ class WebContext(options: GwenOptions, envState: EnvState, driverManager: Driver
if (WebSettings.videoEnabled) {
addVideo(new File(GwenSettings.`gwen.video.dir`, s"$sessionId.mp4"))
}
envState.scopes.topScope.set(`gwen.web.sessionId`, sessionId)
envState.topScope.set(`gwen.web.sessionId`, sessionId)
}
}

override def sessionClosed(event: WebSessionEvent): Unit = {
envState.scopes.topScope.set(`gwen.web.sessionId`, null)
envState.topScope.set(`gwen.web.sessionId`, null)
}

def webElementlocator = new WebElementLocator(this)
Expand Down Expand Up @@ -207,7 +207,7 @@ class WebContext(options: GwenOptions, envState: EnvState, driverManager: Driver
topScope.set(name, url)
}

val locatorEntry = scopes.visibleEntry(name) { _ => true } map { (n, _) => n.startsWith(LocatorKey.baseKey(name)) }
val locatorEntry = topScope.namedEntry(name) { _ => true } map { (n, _) => n.startsWith(LocatorKey.baseKey(name)) }
if (locatorEntry.exists(_ == true) || locatorEntry.isEmpty) {
getLocatorBinding(name, optional = true).map(_.withTimeout(timeout)) match {
case Some(binding) =>
Expand All @@ -233,7 +233,7 @@ class WebContext(options: GwenOptions, envState: EnvState, driverManager: Driver
*/
def getCachedOrBoundValue(name: String): String = {
getCachedWebElement(s"${JSBinding.key(name)}/param/webElement") map { webElement =>
val javascript = interpolate(scopes.get(JSBinding.key(name)))
val javascript = interpolate(topScope.get(JSBinding.key(name)))
val jsFunction = jsFunctionWrapper("element", "arguments[0]", s"return $javascript")
Option(applyJS(jsFunction, webElement)).map(_.toString).getOrElse("")
} getOrElse {
Expand Down Expand Up @@ -302,13 +302,13 @@ class WebContext(options: GwenOptions, envState: EnvState, driverManager: Driver
def evaluatePostAction(element: String, action: String): Unit = {

// sleep if wait time is configured for this action
scopes.getOpt(s"$element/$action/wait") foreach { secs =>
topScope.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 =>
topScope.getOpt(s"$element/$action/condition") foreach { condition =>
val bCondition = BooleanCondition(condition, false, WebSettings.`gwen.web.wait.seconds`, this)
logger.info(s"waiting until ${bCondition.name} (post-$action condition)")
waitUntil(s"waiting for true return from condition: ${bCondition.name}") {
Expand Down Expand Up @@ -877,7 +877,7 @@ class WebContext(options: GwenOptions, envState: EnvState, driverManager: Driver
private [web] def createActions(driver: WebDriver): Actions = new Actions(driver)

def performAction(action: ElementAction, binding: LocatorBinding): Unit = {
val actionBinding = scopes.getOpt(JSBinding.key(s"${binding.name}/action/$action"))
val actionBinding = topScope.getOpt(JSBinding.key(s"${binding.name}/action/$action"))
actionBinding match {
case Some(javascript) =>
performScriptAction(action, javascript, binding, s"trying to $action ${binding.displayName}")
Expand Down
10 changes: 0 additions & 10 deletions src/main/scala/gwen/web/eval/WebEngine.scala
Original file line number Diff line number Diff line change
Expand Up @@ -114,22 +114,12 @@ class WebEngine extends EvalEngine[WebContext] {
new WaitForElementState(element, ElementState.valueOf(state), Option(negation).isDefined)
case r"""I wait until (.+?)$condition""" if !condition.matches(".+ file (exists|not exists|does not exist|is empty|is not empty)") =>
new WaitForBoundCondition(condition, step.delayOpt.map(_.toMillis), step.timeoutOpt.map(_.toSeconds))
case r"""I am on the (.+?)$page""" =>
new CreatePageScope(page)
case r"""I navigate to the (.+?)$page""" =>
new NavigateToPageInScope(page)
case r"""I navigate to "(.+?)"$url""" =>
new NavigateToUrl(step.orDocString(url))
case r"""I scroll to the (top|bottom)$position of the page""" =>
new ScrollPage(ScrollTo.valueOf(position))
case r"""I scroll to the (top|bottom)$position of (.+?)$element""" =>
new ScrollToElement(element, ScrollTo.valueOf(position))
case r"""the url will be defined by (?:property|setting) "(.+?)"$name""" =>
new BindUrl(Settings.get(step.orDocString(name)), None)
case r"""the url will be "(.+?)"$url""" =>
new BindUrl(step.orDocString(url), None)
case r"""the (.+?)$page url is "(.+?)"$url""" =>
new BindUrl(step.orDocString(url), Some(page))
case r"""(.+?)$element can be located by (id|name|tag name|tag|css selector|css|xpath|class name|class|link text|partial link text|javascript|js)$selectorType "(.+?)"$expression at index (\d+)$index in (.+?)$container""" =>
new BindElementLocator(element, SelectorType.parse(selectorType), expression, Some((RelativeSelectorType.in, container, None)), step.timeoutOpt.map(_.toSeconds), Some(index.toInt))
case r"""(.+?)$element can be located by (id|name|tag name|tag|css selector|css|xpath|class name|class|link text|partial link text|javascript|js)$selectorType "(.+?)"$expression in (.+?)$container""" =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,21 @@ class LocatorBindingResolver(ctx: WebContext) extends LazyLogging {
ctx.topScope.getObject(name) match {
case None =>
val locatorKey = LocatorKey.baseKey(name)
ctx.scopes.getOpt(locatorKey) match {
ctx.topScope.getOpt(locatorKey) match {
case Some(boundValue) =>
val selectors = boundValue.split(",") flatMap { boundValue =>
val selectorType = Try(SelectorType.parse(boundValue)) getOrElse {
locatorBindingError(s"Unsupported selector type defined for: $name")
}
val selectorKey = ctx.interpolate(LocatorKey.selectorKey(name, selectorType))
ctx.scopes.getOpt(selectorKey) match {
ctx.topScope.getOpt(selectorKey) match {
case Some(expression) =>
val selector = ctx.interpolate(expression)
val rKeys = RelativeSelectorType.values map { rSelector =>
LocatorKey.relativeKey(name, selectorType, rSelector)
}
val rKeyAndElement: Option[(String, String)] = (rKeys flatMap { rKey =>
ctx.scopes.getOpt(ctx.interpolate(rKey)) map { rElement =>
ctx.topScope.getOpt(ctx.interpolate(rKey)) map { rElement =>
(rKey, rElement)
}
}).headOption
Expand All @@ -87,13 +87,13 @@ class LocatorBindingResolver(ctx: WebContext) extends LazyLogging {
val rSelector = RelativeSelectorType.valueOf(rKey.substring(rKey.lastIndexOf("/") + 1))
val rBinding = getBinding(rElement, false).get
val rKeyWithinPixels = LocatorKey.relativeKeyWithinPixels(name, selectorType, rSelector)
val withinPixels = ctx.scopes.getOpt(ctx.interpolate(rKeyWithinPixels)).map(_.toInt)
val withinPixels = ctx.topScope.getOpt(ctx.interpolate(rKeyWithinPixels)).map(_.toInt)
(rSelector, rBinding, withinPixels)
}
val timeout = ctx.scopes.getOpt(ctx.interpolate(LocatorKey.timeoutSecsKey(name, selectorType))).map { timeoutSecs =>
val timeout = ctx.topScope.getOpt(ctx.interpolate(LocatorKey.timeoutSecsKey(name, selectorType))).map { timeoutSecs =>
Duration.create(timeoutSecs.toLong, TimeUnit.SECONDS)
}
val index = ctx.scopes.getOpt(ctx.interpolate(LocatorKey.indexKey(name, selectorType))).map(_.toInt)
val index = ctx.topScope.getOpt(ctx.interpolate(LocatorKey.indexKey(name, selectorType))).map(_.toInt)
Some(Selector(selectorType, selector, relative, timeout, index))
case None =>
if (optional) None else locatorBindingError(s"Undefined selector for: $name")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class BindActionHandler(element: String, event: ElementEvent, javascript: String
step tap { _ =>
checkStepRules(step, BehaviorType.Context, ctx)
ctx.getLocatorBinding(element)
ctx.scopes.set(JSBinding.key(s"$element/action/${ElementEvent.actionOf(event)}"), javascript)
ctx.topScope.set(JSBinding.key(s"$element/action/${ElementEvent.actionOf(event)}"), javascript)
}
}

Expand Down
28 changes: 14 additions & 14 deletions src/main/scala/gwen/web/eval/lambda/unit/BindElementLocator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -39,52 +39,52 @@ class BindElementLocator(name: String, selectorType: SelectorType, expression: S
ctx.getLocatorBinding(rElement)
}

ctx.scopes.set(LocatorKey.baseKey(name), selectorType.toString)
ctx.scopes.set(LocatorKey.selectorKey(name, selectorType), expression)
ctx.topScope.set(LocatorKey.baseKey(name), selectorType.toString)
ctx.topScope.set(LocatorKey.selectorKey(name, selectorType), expression)

if (relative.isEmpty) {
RelativeSelectorType.values foreach { rSelectorType =>
val rKey = LocatorKey.relativeKey(name, selectorType, rSelectorType)
ctx.scopes.getOpt(rKey) foreach { _ =>
ctx.scopes.set(rKey, null)
ctx.topScope.getOpt(rKey) foreach { _ =>
ctx.topScope.set(rKey, null)
}
if (rSelectorType == RelativeSelectorType.near) {
val rKeyWithinPixels = LocatorKey.relativeKeyWithinPixels(name, selectorType, rSelectorType)
ctx.scopes.getOpt(rKeyWithinPixels) foreach { _ =>
ctx.scopes.set(rKeyWithinPixels, null)
ctx.topScope.getOpt(rKeyWithinPixels) foreach { _ =>
ctx.topScope.set(rKeyWithinPixels, null)
}
}
}
} else {
relative foreach { (rSelectorType, rElement, rPixels) =>
val rKey = LocatorKey.relativeKey(name, selectorType, rSelectorType)
ctx.scopes.set(rKey, rElement)
ctx.topScope.set(rKey, rElement)
rPixels foreach { withinPixels =>
val rKeyWithinPixels = LocatorKey.relativeKeyWithinPixels(name, selectorType, rSelectorType)
ctx.scopes.set(rKeyWithinPixels, withinPixels.toString)
ctx.topScope.set(rKeyWithinPixels, withinPixels.toString)
}
}
}

val iKey = LocatorKey.indexKey(name, selectorType)
if (index.isEmpty) {
ctx.scopes.getOpt(iKey) foreach { _ =>
ctx.scopes.set(iKey, null)
ctx.topScope.getOpt(iKey) foreach { _ =>
ctx.topScope.set(iKey, null)
}
} else {
index foreach { idx =>
ctx.scopes.set(iKey, idx.toString)
ctx.topScope.set(iKey, idx.toString)
}
}

val tKey = LocatorKey.timeoutSecsKey(name, selectorType)
if (timeoutSecs.isEmpty) {
ctx.scopes.getOpt(tKey) foreach { _ =>
ctx.scopes.set(tKey, null)
ctx.topScope.getOpt(tKey) foreach { _ =>
ctx.topScope.set(tKey, null)
}
} else {
timeoutSecs foreach { secs =>
ctx.scopes.set(tKey, secs.toString)
ctx.topScope.set(tKey, secs.toString)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class BindMultipleElementLocators(name: String, container: Option[String], timeo
}
}
if (selectors.nonEmpty) {
ctx.scopes.set(LocatorKey.baseKey(name), selectors.mkString(","))
ctx.topScope.set(LocatorKey.baseKey(name), selectors.mkString(","))
}
}
}
Expand Down
40 changes: 0 additions & 40 deletions src/main/scala/gwen/web/eval/lambda/unit/BindUrl.scala

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class CaptureElementAttribute(element: String, attribute: String, javascript: St
override def apply(parent: GwenNode, step: Step, ctx: WebContext): Step = {
checkStepRules(step, BehaviorType.Action, ctx)
val binding = ctx.getLocatorBinding(element)
ctx.scopes.set(JSBinding.key(attribute), javascript)
ctx.topScope.set(JSBinding.key(attribute), javascript)
try {
ctx.perform {
ctx.topScope.pushObject(s"${JSBinding.key(attribute)}/param/webElement", binding.resolve())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class CaptureElementScreenshot(element: String, target: Option[String]) extends
target match {
case Some(name) =>
ctx.captureElementScreenshot(binding, name) foreach { file =>
ctx.scopes.set(name, file.getAbsolutePath)
ctx.topScope.set(name, file.getAbsolutePath)
}
case None =>
ctx.captureElementScreenshot(binding)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class CaptureScreenshot(target: Option[String]) extends UnitStep[WebContext] {
target match {
case Some(name) =>
ctx.captureScreenshot(true, name) foreach { file =>
ctx.scopes.set(name, file.getAbsolutePath)
ctx.topScope.set(name, file.getAbsolutePath)
}
case None =>
ctx.captureScreenshot(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ class CompareValueOrSelectionToValue(element: String, selection: Option[Dropdown
val formattedActual = () => Formatting.format(ctx.boundAttributeOrSelection(element, selection, timeout), trim, ignoreCase)
step tap { _ =>
ctx.perform {
if (ctx.scopes.findEntry { case (n, _) => n.startsWith(element) } forall { case (n, _) => n != element }) {
if (ctx.topScope.findEntry { case (n, _) => n.startsWith(element) } forall { case (n, _) => n != element }) {
val nameSuffix = selection.map(sel => s" $sel")
ctx.compare(s"$element${nameSuffix.getOrElse("")}", Formatting.format(expected, trim, ignoreCase), formattedActual, operator, negate, nameSuffix, message, timeout.map(_.toSeconds), step.assertionMode)
} else {
val actualValue = ctx.scopes.getOpt(element).getOrElse(actual())
val actualValue = ctx.topScope.getOpt(element).getOrElse(actual())
val result = ctx.compare(element, Formatting.format(expected, trim, ignoreCase), Formatting.format(actualValue, trim, ignoreCase), operator, negate)
result match {
case Success(assertion) =>
Expand Down
37 changes: 0 additions & 37 deletions src/main/scala/gwen/web/eval/lambda/unit/CreatePageScope.scala

This file was deleted.

Loading

0 comments on commit 36f0893

Please sign in to comment.