Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#443 - DataTable arguments aren't working with cucumber-scala #455

Merged
merged 16 commits into from
Mar 10, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions scala/gen.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@

/*
* Generates the evil looking apply methods in ScalaDsl#StepBody for Function1 to Function22
*/
for (i <- 1 to 22) {
val ts = (1 to i).map("T" +).mkString(", ")
val f = "(" + ts + ") => Any"
val pf = "{ case List(" + (1 to i).map("a" +).mkString(", ") + ") => f(" + (1 to i).map(n => "t" + n + "(a" + n + ")").mkString(", ") + ") }"
val p1 = "def apply[" + ts + "](f: " + f + ")"
val p2 = "(implicit " + (1 to i).map(n => "m" + n + ":Manifest[T" + n + "], t" + n + ":Transformation[T" + n + "]").mkString(", ") + ")"
val doHandle = "doHandle(List(" + (1 to i).map("m" +).mkString(", ") + "))" + pf
val p2 = "(implicit " + (1 to i).map(n => "m" + n + ":Manifest[T" + n + "]").mkString(", ") + ")"
val register = "\n register(List(" + (1 to i).map("m" +).mkString(", ") + ")) {\n"
val pf = " case List(" + (1 to i).map("a" + _ + ":AnyRef").mkString(", ") + ") => \n f(" + (1 to i).map(n => "a" + n + ".asInstanceOf[T" + n + "]").mkString(",\n ") + ")"
val closeRegister = "\n }\n}"

println(p1 + p2 + " = " + doHandle)
println(p1 + p2 + " = { " + register + pf + closeRegister + "\n")
}
445 changes: 416 additions & 29 deletions scala/src/main/scala/cucumber/api/scala/ScalaDsl.scala

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import _root_.cucumber.api.Scenario
import _root_.cucumber.runtime.HookDefinition
import collection.JavaConverters._

class ScalaHookDefinition(f:Scenario => Unit, order:Int, tags:Seq[String]) extends HookDefinition {
class ScalaHookDefinition(f:Scenario => Unit,
order:Int,
tags:Seq[String]) extends HookDefinition {

val tagExpression = new TagExpression(tags.asJava)

def getLocation(detail: Boolean) = "TODO: Implement getLocation in similar fashion to ScalaStepDefinition"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,77 @@ import _root_.cucumber.runtime.StepDefinition
import _root_.cucumber.runtime.JdkPatternArgumentMatcher
import _root_.cucumber.runtime.ParameterInfo
import collection.JavaConversions._
import cucumber.api.Transform

class ScalaStepDefinition(frame:StackTraceElement, name:String, pattern:String, parameterInfos:List[Class[_]], f:List[Any] => Any) extends StepDefinition {
/**
* Implementation of step definition for scala.
*
* @param frame Representation of a stack frame containing information about the context in which a
* step was defined. Allows retrospective queries about the definition of a step.
*
* @param name The name of the step definition class, e.g. cucumber.runtime.scala.test.CukesStepDefinitions
*
* @param pattern The regex matcher that defines the cucumber step, e.g. /I eat (.*) cukes$/

* @param parameterInfos
*
* @param f Function body of a step definition. This is what actually runs the code within the step def.
*/
class ScalaStepDefinition(frame:StackTraceElement,
name:String,
pattern:String,
parameterInfos:List[Class[_]],
f:List[Any] => Any) extends StepDefinition {

/**
* Compiled pattern matcher for the cucumber step regex.
*/
private val argumentMatcher = new JdkPatternArgumentMatcher(Pattern.compile(pattern))

/**
* Returns a list of arguments. Return null if the step definition
* doesn't match at all. Return an empty List if it matches with 0 arguments
* and bigger sizes if it matches several.
*/
def matchedArguments(step: Step) = argumentMatcher.argumentsFrom(step.getName)

/**
* The source line where the step definition is defined.
* Example: foo/bar/Zap.brainfuck:42
*
* @param detail true if extra detailed location information should be included.
*/
def getLocation(detail: Boolean) = frame.getFileName + ":" + frame.getLineNumber

/**
* How many declared parameters this stepdefinition has. Returns null if unknown.
*/
def getParameterCount() = parameterInfos.size()

// TODO: get rid of Transform.scala and leave transformation to be done by core. The correct implementation is commented out
// below until this is fixed.
// def getParameterType(index: Int, javaType: Type) = new ParameterInfo(parameterInfos.get(index), null)
def getParameterType(index: Int, javaType: Type) = new ParameterInfo(classOf[String], null, null, null)
/**
* The parameter type at index n. A hint about the raw parameter type is passed to make
* it easier for the implementation to make a guess based on runtime information.
* As Scala is a statically typed language, the javaType parameter is ignored
*/
def getParameterType(index: Int, javaType: Type) = {
new ParameterInfo(parameterInfos.get(index), null, null, null)
}

/**
* Invokes the step definition. The method should raise a Throwable
* if the invocation fails, which will cause the step to fail.
*/
def execute(i18n: I18n, args: Array[AnyRef]) { f(args.toList) }

/**
* Return true if this matches the location. This is used to filter
* stack traces.
*/
def isDefinedAt(stackTraceElement: StackTraceElement) = stackTraceElement == frame

/**
* @return the pattern associated with this instance. Used for error reporting only.
*/
def getPattern = pattern

}
34 changes: 0 additions & 34 deletions scala/src/main/scala/cucumber/runtime/scala/Transform.scala

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,74 @@ Feature: Cukes

Scenario: in the belly
Given I have 4 "cukes" in my belly
Then I am "happy"
Then I am "happy"

Scenario: Int in the belly
Given I have eaten an int 100
Then I should have one hundred in my belly

Scenario: Long in the belly
Given I have eaten a long 100
Then I should have long one hundred in my belly

Scenario: String in the belly
Given I have eaten "numnumnum"
Then I should have numnumnum in my belly

Scenario: Double in the belly
Given I have eaten 1.5 doubles
Then I should have one and a half doubles in my belly

Scenario: Float in the belly
Given I have eaten 1.5 floats
Then I should have one and a half floats in my belly

Scenario: Short in the belly
Given I have eaten a short 100
Then I should have short one hundred in my belly

Scenario: Byte in the belly
Given I have eaten a byte 2
Then I should have two byte in my belly

Scenario: BigDecimal in the belly
Given I have eaten 1.5 big decimals
Then I should have one and a half big decimals in my belly

Scenario: BigInt in the belly
Given I have eaten 10 big int
Then I should have a ten big int in my belly

Scenario: Char in the belly
Given I have eaten char 'C'
Then I should have character C in my belly

Scenario: Boolean in the belly
Given I have eaten boolean true
Then I should have truth in my belly

Scenario: DataTable in the belly
Given I have the following foods :
| FOOD | CALORIES |
| cheese | 500 |
| burger | 1000 |
| fries | 750 |
Then I am "definitely happy"
And have eaten 2250.0 calories today

Scenario: DataTable with args in the belly
Given I have a table the sum of all rows should be 400 :
| ROW |
| 20 |
| 80 |
| 300 |

Scenario: Argh! a snake - to be custom mapped
Given I see in the distance ... =====>
Then I have a snake of length 6 moving east
And I see in the distance ... <====================
Then I have a snake of length 21 moving west

Scenario: Custom object with string constructor
Given I have a person Bob
Then he should say "Hello, I'm Bob!"
24 changes: 2 additions & 22 deletions scala/src/test/scala/cucumber/api/scala/ScalaDslTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import Assert._
import _root_.gherkin.I18n
import _root_.gherkin.formatter.model.Tag
import collection.JavaConverters._

import _root_.cucumber.runtime.scala.Transform
import cucumber.api.Scenario

class ScalaDslTest {
Expand Down Expand Up @@ -127,7 +125,7 @@ class ScalaDslTest {

assertEquals(1, Dummy.stepDefinitions.size)
val step = Dummy.stepDefinitions.head
assertEquals("ScalaDslTest.scala:123", step.getLocation(true)) // be careful with formatting or this test will break
assertEquals("ScalaDslTest.scala:121", step.getLocation(true)) // be careful with formatting or this test will break
assertEquals("x", step.getPattern)
step.execute(new I18n("en"), Array())
assertTrue(called)
Expand All @@ -147,27 +145,9 @@ class ScalaDslTest {

assertEquals(1, Dummy.stepDefinitions.size)
val step = Dummy.stepDefinitions(0)
step.execute(new I18n("en"), Array("5", "green"))
step.execute(new I18n("en"), Array(new java.lang.Integer(5), "green"))
assertEquals(5, thenumber)
assertEquals("green", thecolour)
}

@Test
def transformation {
case class Person(name:String)

var person:Person = null

object Dummy extends ScalaDsl with EN {

implicit val transformPerson = Transform(Person(_))

Given("Person (\\s+)"){ p:Person =>
person = p
}
}

Dummy.stepDefinitions(0).execute(new I18n("en"), Array("Aslak"))
assertEquals(Person("Aslak"), person)
}
}
13 changes: 13 additions & 0 deletions scala/src/test/scala/cucumber/runtime/scala/model/Person.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package cucumber.runtime.scala.model

/**
* Test model for a "Person"
* @param name of person
*/
case class Person(name:String) {

def hello = {
"Hello, I'm " + name + "!"
}

}
13 changes: 13 additions & 0 deletions scala/src/test/scala/cucumber/runtime/scala/model/Snake.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package cucumber.runtime.scala.model

import cucumber.runtime.scala.transform.SnakeConverter

/**
* Test model "Snake" to exercise the custom mapper functionality
* @param length of the snake in characters
* @param direction in which snake is moving 'west, 'east, etc
*/
@cucumber.deps.com.thoughtworks.xstream.annotations.XStreamConverter(classOf[SnakeConverter])
case class Snake(length:Int, direction:Symbol) {

}
Loading