Skip to content

Commit

Permalink
Make all input data read only
Browse files Browse the repository at this point in the history
  • Loading branch information
bjuric committed Oct 25, 2024
1 parent ce05d10 commit 2270f85
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 83 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
4.0.0
=====
24 October 2024
25 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)
- Make all input data read only
- Internal engine enhancements
- Replace internal stack of data caches with a single data cache
- Manage settings internally without exposing them as system properties
- Implicits managed at lifecycle level
- Add implicit variable(s):
- `gwen.process.name`
- Add environment variable(s)
Expand Down
24 changes: 12 additions & 12 deletions src/main/scala/gwen/web/eval/WebEngine.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,9 @@ class WebEngine extends EvalEngine[WebContext] {
override def translateCompositeStep(step: Step): Option[CompositeStep[WebContext]] = {
step.expression.match {
case r"""(.+)$doStep if(?:(?!\bif\b)) (.+?)$element is( not)?$negation (displayed|hidden|checked|ticked|unchecked|unticked|enabled|disabled)$state""" =>
Some(new IfElementCondition(doStep, element, ElementState.valueOf(state), Option(negation).isDefined, this))
Some(new IfElementCondition(doStep, element, ElementState.valueOf(state), Option(negation).nonEmpty, this))
case r"""(.+?)$doStep (until|while)$operation (.+?)$element is( not)?$negation (displayed|hidden|checked|ticked|unchecked|unticked|enabled|disabled)$state""" if (doStep != "I wait" && !step.expression.matches(""".*".*(until|while).*".*""")) =>
Some(new RepeatElementState(doStep, operation, element, ElementState.valueOf(state), Option(negation).isDefined, step.delayOpt.getOrElse(Duration(1, TimeUnit.SECONDS)), step.timeoutOpt.getOrElse(Duration(1, TimeUnit.MINUTES)), this))
Some(new RepeatElementState(doStep, operation, element, ElementState.valueOf(state), Option(negation).nonEmpty, step.delayOpt.getOrElse(Duration(1, TimeUnit.SECONDS)), step.timeoutOpt.getOrElse(Duration(1, TimeUnit.MINUTES)), this))
case _ =>
super.translateCompositeStep(step) orElse {
step.expression match {
Expand Down Expand Up @@ -111,7 +111,7 @@ class WebEngine extends EvalEngine[WebContext] {
case r"""I wait until "(.+?)$javascript"""" =>
new WaitForCondition(step.orDocString(javascript), step.delayOpt.map(_.toMillis), step.timeoutOpt.map(_.toSeconds))
case r"""I wait until (.+?)$element is( not)?$negation (displayed|hidden|checked|ticked|unchecked|unticked|enabled|disabled)$state""" =>
new WaitForElementState(element, ElementState.valueOf(state), Option(negation).isDefined)
new WaitForElementState(element, ElementState.valueOf(state), Option(negation).nonEmpty)
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 navigate to "(.+?)"$url""" =>
Expand Down Expand Up @@ -145,25 +145,25 @@ class WebEngine extends EvalEngine[WebContext] {
case r"""(.+?)$element can be (clicked|right clicked|double clicked|submitted|checked|ticked|unchecked|unticked|selected|deselected|typed|entered|tabbed|cleared|moved to)$event by (?:javascript|js) "(.+?)"$expression""" =>
new BindActionHandler(element, ElementEvent.valueOf(event), step.orDocString(expression))
case r"""the page title should( not)?$negation be (blank|empty|true|false)$literal""" =>
new CompareTitle("title", ValueLiteral.valueOf(literal).value, false, ComparisonOperator.be, Option(negation).isDefined, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
new CompareTitle("title", ValueLiteral.valueOf(literal).value, false, ComparisonOperator.be, Option(negation).nonEmpty, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
case r"""the page title should( not)?$negation (be|contain|start with|end with|match regex|match xpath|match json path|match template|match template file)$operator "(.*?)"$expression""" =>
new CompareTitle("title", step.orDocString(expression), false, ComparisonOperator.valueOf(operator), Option(negation).isDefined, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
new CompareTitle("title", step.orDocString(expression), false, ComparisonOperator.valueOf(operator), Option(negation).nonEmpty, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
case r"""the page title should( not)?$negation (be|contain|start with|end with|match regex|match xpath|match json path)$operator (.+?)$attribute""" =>
new CompareTitle("title", attribute, true, ComparisonOperator.valueOf(operator), Option(negation).isDefined, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
new CompareTitle("title", attribute, true, ComparisonOperator.valueOf(operator), Option(negation).nonEmpty, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
case r"""the (alert|confirmation)$name popup message should( not)?$negation be (blank|empty|true|false)$literal""" =>
new ComparePopupMessage(name, ValueLiteral.valueOf(literal).value, false, ComparisonOperator.be, Option(negation).isDefined, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
new ComparePopupMessage(name, ValueLiteral.valueOf(literal).value, false, ComparisonOperator.be, Option(negation).nonEmpty, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
case r"""the (alert|confirmation)$name popup message should( not)?$negation (be|contain|start with|end with|match regex|match xpath|match json path|match template|match template file)$operator "(.*?)"$expression""" =>
new ComparePopupMessage(name, step.orDocString(expression), false, ComparisonOperator.valueOf(operator), Option(negation).isDefined, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
new ComparePopupMessage(name, step.orDocString(expression), false, ComparisonOperator.valueOf(operator), Option(negation).nonEmpty, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
case r"""the (alert|confirmation)$name popup message should( not)?$negation (be|contain|start with|end with|match regex|match xpath|match json path)$operator (.+?)$attribute""" =>
new ComparePopupMessage(name, attribute, true, ComparisonOperator.valueOf(operator), Option(negation).isDefined, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
new ComparePopupMessage(name, attribute, true, ComparisonOperator.valueOf(operator), Option(negation).nonEmpty, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
case r"""(.+?)$element should( not)?$negation be (displayed|hidden|checked|ticked|unchecked|unticked|enabled|disabled)$state""" =>
new CompareElementState(element, ElementState.valueOf(state), Option(negation).nonEmpty, step.message, step.timeoutOpt)
case r"""(.+?)$element( text| value)?$selection should( not)?$negation be (blank|empty|true|false)$literal""" if !element.matches(".+at (json path|xpath).+") && !element.matches(".+? file") =>
new CompareValueOrSelectionToValue(element, Option(selection).map(_.trim).map(DropdownSelection.valueOf), ValueLiteral.valueOf(literal).value, ComparisonOperator.be, Option(negation).isDefined, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
new CompareValueOrSelectionToValue(element, Option(selection).map(_.trim).map(DropdownSelection.valueOf), ValueLiteral.valueOf(literal).value, ComparisonOperator.be, Option(negation).nonEmpty, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
case r"""(.+?)$element( text| value)?$selection should( not)?$negation (be|contain|start with|end with|match regex|match xpath|match json path|match template|match template file)$operator "(.*?)"$expression""" if !element.matches(".+at (json path|xpath).+") =>
new CompareValueOrSelectionToValue(element, Option(selection).map(_.trim).map(DropdownSelection.valueOf), step.orDocString(expression), ComparisonOperator.valueOf(operator), Option(negation).isDefined, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
new CompareValueOrSelectionToValue(element, Option(selection).map(_.trim).map(DropdownSelection.valueOf), step.orDocString(expression), ComparisonOperator.valueOf(operator), Option(negation).nonEmpty, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
case r"""(.+?)$element( text| value)?$selection should( not)?$negation (be|contain|start with|end with|match regex|match xpath|match json path)$operator (.+?)$attribute""" if !attribute.matches("(absent|defined|empty|no accumulated errors)") && !attribute.contains("% similar to ") && !attribute.contains('"') && !element.matches(".+at (json path|xpath).+") =>
new CompareValueOrSelectionToBoundValue(element, Option(selection).map(_.trim).map(DropdownSelection.valueOf), attribute, ComparisonOperator.valueOf(operator), Option(negation).isDefined, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
new CompareValueOrSelectionToBoundValue(element, Option(selection).map(_.trim).map(DropdownSelection.valueOf), attribute, ComparisonOperator.valueOf(operator), Option(negation).nonEmpty, step.message, step.timeoutOpt, step.isTrim, step.isIgnoreCase)
case r"""I capture (.+?)$attribute (?:of|on|in) (.+?)$element by (?:javascript|js) "(.+?)"$expression""" =>
new CaptureElementAttribute(element, attribute, step.orDocString(expression))
case r"""I capture the current URL""" =>
Expand Down
41 changes: 36 additions & 5 deletions src/test/features/core/outlines/JoinStrings.feature
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
Feature: Join Strings

Scenario Outline: Joining <string 1> and <string 2> should yield <result>
Scenario Outline: Joining <string 1> and <string 2> should yield <result> 1

This scenario is evaluated at the point where the outline is declared.
Joining <string 1> and <string 2> should yield <result>

Given string 1 is "<string 1>"
And string 2 is "<string 2>"
Given the result is blank
When I join the two strings
Then the result should be "<result>"

@Parallel
Examples: Basic string concatenation

The header row contains the placeholder names. The body rows that
Expand All @@ -20,6 +20,37 @@ Feature: Join Strings
| any | thing | anything |

Scenario: Verify that we can join two strings in meta
Given result is ""
Given the result is blank
When I join two strings in meta
Then the result should not be ""
Then the result should be defined

Scenario Outline: Joining <text 1> and <text 2> should yield <result> with impilictly bound cells

This scenario is evaluated at the point where the outline is declared.
Joining <text 1> and <text 2> should yield <result>

Given the result is "${text 1}${text 2}"
When I capture the result
Then the result should be "<result>"
And the result should be "${result}"

Examples: Basic string concatenation

The header row contains the placeholder names. The body rows that
follow contain the data that is bound to each scenario that is evaluated.

| text 1 | text 2 | result |
| butter | fly | butterfly |
| basket | ball | basketball |

Scenario Outline: Empty examples tables should do nothing
Given the result is "${text 1}${text 2}"
When I capture the result
Then the result should be "<result>"
And the result should be "${result}"

Examples: Empty examples table

The header row contains the placeholder names. The body is empty.

| text 1 | text 2 | result |
4 changes: 2 additions & 2 deletions src/test/features/core/outlines/JoinStrings.meta
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ Feature: Join Strings Meta
This scenario is loaded into memory and evaluated whenever
the outline is referenced by a step in a feature

Given string 1 is "<string 1>"
And string 2 is "<string 2>"
Given the result is blank
When I join the two strings
Then the result should be "<result>"

@Parallel
Examples:

The header row contains the placeholder names. The body rows that
Expand Down
45 changes: 17 additions & 28 deletions src/test/features/core/rules/StringRules.feature
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,20 @@ Feature: String rules
Rule: Joining two strings together should result in a string containing both

Background: Reset strings
Given string 1 is ""
And string 2 is ""

Howdy doo

Given the result is blank
When I join the two strings
Then the result should be ""
And string 1 should not be "${gwen.feature.eval.duration}"
And string 2 should not be "${gwen.feature.eval.duration.msecs}"
And string 2 should not be "${gwen.feature.eval.duration.secs}"
Then the result should not be blank

Scenario Template: Joining <string 1> and <string 2> should yield <result>

Joining <string 1> and <string 2> should yield <result>

Given string 1 is "<string 1>"
And string 2 is "<string 2>"
Given the result is blank
When I join the two strings
Then the result should be "<result>"
And string 1 should not be "${gwen.feature.eval.duration}"
And string 2 should not be "${gwen.feature.eval.duration.msecs}"
And string 2 should not be "${gwen.feature.eval.duration.secs}"

Examples: Basic string concatenation

Expand All @@ -33,37 +28,31 @@ Feature: String rules
| any | thing | anything |

Example: Verify that we can join two strings in meta
Given the result is ""
Given the result is blank
When I join two strings in meta
Then the result should not be ""
And the result should not be "${gwen.feature.eval.duration}"
Then the result should not be blank if gwen.state.level is "feature"
And gwen.scenario.name should be "Verify that we can join two strings in meta"
And gwen.rule.name should be "Joining two strings together should result in a string containing both"

Rule: Replacing a substring in a string should result in substitution of the substring

Background: Reset strings
Given string 1 is ""
And string 2 is ""
And string 3 is ""
Given the result is blank
When I substitute string 1 for string 2 in string 3
Then the result should be ""
And string 1 should not be "${gwen.feature.eval.duration}"
And string 2 should not be "${gwen.feature.eval.duration.msecs}"
And string 2 should not be "${gwen.feature.eval.duration.secs}"
Then the result should not be blank
And gwen.rule.name should be "Replacing a substring in a string should result in substitution of the substring"

Scenario Template: Substituting <string 1> for <string 2> in <string 3> should yield <result>

Substituting <string 1> for <string 2> in <string 3> should yield <result>

Given string 1 is "<string 1>"
And string 2 is "<string 2>"
And string 3 is "<string 3>"
Given the result is blank
When I substitute string 1 for string 2 in string 3
Then the result should be "<result>"
And string 1 should not be "${gwen.feature.eval.duration}"
And string 2 should not be "${gwen.feature.eval.duration.msecs}"
And string 2 should not be "${gwen.feature.eval.duration.secs}"
And gwen.rule.name should be "Replacing a substring in a string should result in substitution of the substring"
And gwen.scenario.name should contain "-- More basic string concatenation"

Examples: Basic string concatenation
Examples: More basic string concatenation

The header row contains the placeholder names. The body rows that
follow contain the data that is bound to each scenario that is evaluated.
Expand Down
14 changes: 6 additions & 8 deletions src/test/features/core/rules/StringRules.meta
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
Feature: Strings rules Meta

Scenario: Init
Given start duration is "${gwen.feature.eval.duration}"

@StepDef
@Action
Scenario: I join the two strings
Given the result is "${string 1}${string 2}"
And gwen.feature.eval.duration should not be "${start duration}"
And gwen.stepDef.name should be "I join the two strings"
And gwen.rule.name should be "Joining two strings together should result in a string containing both"

@StepDef
@Action
Scenario Template: I join two strings in meta
Given string 1 is "<string 1>"
And string 2 is "<string 2>"
Given the result is blank
When I join the two strings
Then the result should be "<result>"
And gwen.feature.eval.duration should not be "${start duration}"
And gwen.stepDef.name should be "I join two strings in meta"
And gwen.rule.name should be "Joining two strings together should result in a string containing both"

Examples:

Expand All @@ -31,4 +29,4 @@ Feature: Strings rules Meta
@Action
Scenario: I substitute string 1 for string 2 in string 3
Given the result is defined by javascript "'${string 3}'.replace('${string 1}', '${string 2}')"
And gwen.feature.eval.duration should not be "${start duration}"
And gwen.stepDef.name should be "I substitute string 1 for string 2 in string 3"
2 changes: 1 addition & 1 deletion src/test/features/todo/LoadItemsFromJSONs.feature
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ Feature: Load Todo items from JSON files
When I load items from JSON files
And I load items from deep JSON file
And I load items from deep JSON array
And I load items by loopoing over nested JSON array
And I load items by looping over nested JSON array
And I load items from empty JSON file
Then the "Walk the dog" item should be unticked
And the "Get the milk" item should not be displayed
Expand Down
2 changes: 1 addition & 1 deletion src/test/features/todo/Todo.meta
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ Feature: TodoMVC meta
@StepDef
@Examples("src/test/features-data/TodoItems6.json")
@Action
Scenario Outline: I load items by loopoing over nested JSON array
Scenario Outline: I load items by looping over nested JSON array
When I add nested item for each item in todo.items array

@StepDef
Expand Down
Loading

0 comments on commit 2270f85

Please sign in to comment.