diff --git a/CHANGELOG.md b/CHANGELOG.md index 0fcf2a3e..39819ee5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,10 @@ See also the [CHANGELOG](https://github.com/cucumber/cucumber-jvm/blob/master/CH ### Added - [Doc] Users documentation on Scala DSL +- [Scala DSL] Support for transformers ([#32](https://github.com/cucumber/cucumber-jvm-scala/issues/32) Gaël Jourdan-Weil) + - See [Transformers](docs/transformers.md) +- [Transformers] Add optional `JacksonDefaultDataTableEntryTransformer` ([#31](https://github.com/cucumber/cucumber-jvm-scala/issues/31) Gaël Jourdan-Weil) + - See [here](docs/default_jackson_datatable_transformer.md) ### Changed diff --git a/README.md b/README.md index ef41dab9..cbf9b6bc 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ The minor version might differ because Cucumber Scala may add Scala-related feat - [Step Definitions](docs/step_definitions.md) - [Hooks](docs/hooks.md) - [Transformers](docs/transformers.md) + - [Default Jackson DataTable Transformer](docs/default_jackson_datatable_transformer.md) - [Example project](examples/README.md) - [Reference documentation for Java](https://docs.cucumber.io/docs/cucumber/) - [Changelog](CHANGELOG.md) diff --git a/docs/default_jackson_datatable_transformer.md b/docs/default_jackson_datatable_transformer.md new file mode 100644 index 00000000..f0cdbb53 --- /dev/null +++ b/docs/default_jackson_datatable_transformer.md @@ -0,0 +1,68 @@ +# Default Jackson DataTable Transformer + +Cucumber Scala provides an optional Default DataTable Transformer that uses Jackson. + +It can be used to automatically convert DataTables to case classes without defining custom converters. + +## Add Jackson dependency + +To use this optional transformer, you need to have Jackson Scala in your dependencies. + +```xml + + com.fasterxml.jackson.module + jackson-module-scala_2.13 + 2.10.3 + test + +``` + +Or: +```sbt +libraryDependencies += "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.10.3" % Test +``` + + +The current version of Cucumber Scala has been tested against Jackson Module Scala **version 2.10.3**. + +## Add the transformer + +The transformer has to be added to your glue code by extending the `JacksonDefaultDataTableEntryTransformer` trait. + +For instance: +```scala +class MySteps extends ScalaDsl with EN with JacksonDefaultDataTableEntryTransformer { + // Your usual glue code +} +``` + +### Empty string replacement + +The default empty string replacement used by the default transformer is `[empty]`. + +You can override it if you need to: +```scala +override def emptyStringReplacement: String = "[blank]" +``` + +## Example + +Then, let the transformer do its work! + +For instance, the following DataTable: +```gherkin +Given I have the following datatable +| field1 | field2 | field3 | +| 1.2 | true | abc | +| 2.3 | false | def | +| 3.4 | true | ghj | +``` + +will be automatically converted to the following case class: +```scala +case class MyCaseClass(field1: Double, field2: Boolean, field3: String) + +Given("I have the following datatable") { (data: java.util.List[MyCaseClass]) => + // Do something +} +``` diff --git a/docs/transformers.md b/docs/transformers.md index 2b1900ca..3f29a890 100644 --- a/docs/transformers.md +++ b/docs/transformers.md @@ -265,6 +265,8 @@ Default transformers are used when there is no specific transformer. They can be used with object mappers like Jackson to easily convert from well known strings to objects. +See also [Default Jackson DataTable Transformer](default_jackson_datatable_transformer.md). + ### String For instance, the following definition: @@ -304,6 +306,8 @@ Given("A step with a datatable") { (dataTable: DataTable) => } ``` +This is what to `DefaultJacksonDataTableTransformer` uses. + #### Cells For instance the following definition: diff --git a/pom.xml b/pom.xml index 49e89fc4..f81a8d7a 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,30 @@ cucumber-core ${cucumber.version} + + com.fasterxml.jackson.core + jackson-databind + ${jackson-databind.version} + provided + + + com.fasterxml.jackson.module + jackson-module-scala_2.11 + ${jackson-databind.version} + provided + + + com.fasterxml.jackson.module + jackson-module-scala_2.12 + ${jackson-databind.version} + provided + + + com.fasterxml.jackson.module + jackson-module-scala_2.13 + ${jackson-databind.version} + provided + io.cucumber cucumber-junit diff --git a/scala/pom.xml b/scala/pom.xml index efc7e796..0bbc4817 100644 --- a/scala/pom.xml +++ b/scala/pom.xml @@ -33,6 +33,11 @@ io.cucumber cucumber-core + + com.fasterxml.jackson.core + jackson-databind + provided + io.cucumber cucumber-junit @@ -43,11 +48,6 @@ junit test - - com.fasterxml.jackson.core - jackson-databind - test - diff --git a/scala/scala_2.11/pom.xml b/scala/scala_2.11/pom.xml index 06304078..dd423e6d 100644 --- a/scala/scala_2.11/pom.xml +++ b/scala/scala_2.11/pom.xml @@ -19,6 +19,13 @@ provided + + + com.fasterxml.jackson.module + jackson-module-scala_2.11 + provided + + org.scala-lang scala-library diff --git a/scala/scala_2.12/pom.xml b/scala/scala_2.12/pom.xml index 41651e9f..d3398e3f 100644 --- a/scala/scala_2.12/pom.xml +++ b/scala/scala_2.12/pom.xml @@ -19,6 +19,13 @@ provided + + + com.fasterxml.jackson.module + jackson-module-scala_2.12 + provided + + org.scala-lang scala-library diff --git a/scala/scala_2.13/pom.xml b/scala/scala_2.13/pom.xml index 59c345ff..2b4ffd3c 100644 --- a/scala/scala_2.13/pom.xml +++ b/scala/scala_2.13/pom.xml @@ -19,6 +19,13 @@ provided + + + com.fasterxml.jackson.module + jackson-module-scala_2.13 + provided + + org.scala-lang scala-library diff --git a/scala/sources/src/main/scala/io/cucumber/scala/JacksonDefaultDataTableEntryTransformer.scala b/scala/sources/src/main/scala/io/cucumber/scala/JacksonDefaultDataTableEntryTransformer.scala new file mode 100644 index 00000000..006dcc26 --- /dev/null +++ b/scala/sources/src/main/scala/io/cucumber/scala/JacksonDefaultDataTableEntryTransformer.scala @@ -0,0 +1,38 @@ +package io.cucumber.scala + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.scala.DefaultScalaModule + +/** + *

This trait register a `DefaultDataTableEntryTransformer` using Jackson `ObjectMapper`.

+ * + *

The `[empty]` string is used as default empty string replacement. You can override it if you need to.

+ * + *

Note: Jackson is not included with Cucumber Scala, you have to add the dependency: + * `com.fasterxml.jackson.module:jackson-module-scala_2.xx` + * to your project if you want to use this trait.

+ */ +trait JacksonDefaultDataTableEntryTransformer extends ScalaDsl { + + /** + * Define the string to be used as replacement for empty. + * Default is `[empty]`. + */ + def emptyStringReplacement: String = "[empty]" + + /** + * Create the Jackson ObjectMapper to be used. + * Default is a simple ObjectMapper with DefaultScalaModule registered. + */ + def createObjectMapper(): ObjectMapper = { + val objectMapper = new ObjectMapper() + objectMapper.registerModule(DefaultScalaModule) + } + + private lazy val objectMapper: ObjectMapper = createObjectMapper() + + DefaultDataTableEntryTransformer(emptyStringReplacement) { (fromValue: Map[String, String], toValueType: java.lang.reflect.Type) => + objectMapper.convertValue[AnyRef](fromValue, objectMapper.constructType(toValueType)) + } + +} diff --git a/scala/sources/src/test/resources/tests/jackson/Jackson.feature b/scala/sources/src/test/resources/tests/jackson/Jackson.feature new file mode 100644 index 00000000..09e41023 --- /dev/null +++ b/scala/sources/src/test/resources/tests/jackson/Jackson.feature @@ -0,0 +1,15 @@ +Feature: As Cucumber Scala, I want to provide a basic DataTable transformer using Jackson + + Scenario: Use the default transformer with a basic case class + Given I have the following datatable + | field1 | field2 | field3 | + | 1.2 | true | abc | + | 2.3 | false | def | + | 3.4 | true | ghj | + + Scenario: Use the default transformer with a basic case class and empty values + Given I have the following datatable, with an empty value + | field1 | field2 | field3 | + | 1.2 | true | abc | + | 2.3 | false | [blank] | + | 3.4 | true | ghj | \ No newline at end of file diff --git a/scala/sources/src/test/scala/tests/jackson/JacksonSteps.scala b/scala/sources/src/test/scala/tests/jackson/JacksonSteps.scala new file mode 100644 index 00000000..b7a934ff --- /dev/null +++ b/scala/sources/src/test/scala/tests/jackson/JacksonSteps.scala @@ -0,0 +1,31 @@ +package tests.jackson + +import io.cucumber.scala.{EN, JacksonDefaultDataTableEntryTransformer, ScalaDsl} + +import scala.collection.JavaConverters._ + +case class MyCaseClass(field1: Double, field2: Boolean, field3: String) + +class JacksonSteps extends ScalaDsl with EN with JacksonDefaultDataTableEntryTransformer { + + override def emptyStringReplacement: String = "[blank]" + + Given("I have the following datatable") { (data: java.util.List[MyCaseClass]) => + val expected = Seq( + MyCaseClass(1.2, true, "abc"), + MyCaseClass(2.3, false, "def"), + MyCaseClass(3.4, true, "ghj") + ) + assert(data.asScala == expected) + } + + Given("I have the following datatable, with an empty value") { (data: java.util.List[MyCaseClass]) => + val expected = Seq( + MyCaseClass(1.2, true, "abc"), + MyCaseClass(2.3, false, ""), + MyCaseClass(3.4, true, "ghj") + ) + assert(data.asScala == expected) + } + +} diff --git a/scala/sources/src/test/scala/tests/jackson/RunJacksonTest.scala b/scala/sources/src/test/scala/tests/jackson/RunJacksonTest.scala new file mode 100644 index 00000000..2efc18c5 --- /dev/null +++ b/scala/sources/src/test/scala/tests/jackson/RunJacksonTest.scala @@ -0,0 +1,8 @@ +package tests.jackson + +import io.cucumber.junit.{Cucumber, CucumberOptions} +import org.junit.runner.RunWith + +@RunWith(classOf[Cucumber]) +@CucumberOptions(strict = true) +class RunJacksonTest