From a58fc6eacd38c796872ddc8073c29b5bd0e96ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Jourdan-Weil?= Date: Sun, 5 Oct 2025 14:32:19 +0200 Subject: [PATCH 1/3] feat: support Jackson 3.x --- CHANGELOG.md | 3 ++ build.sbt | 27 +++++++++-- ...son3DefaultDataTableEntryTransformer.scala | 48 +++++++++++++++++++ ...ksonDefaultDataTableEntryTransformer.scala | 5 +- docs/build.md | 3 +- docs/default_jackson_datatable_transformer.md | 32 +++++++++++-- .../test/resources/jackson/Jackson.feature | 0 .../test/resources/junit-platform.properties | 0 .../src/test/scala/jackson/JacksonSteps.scala | 0 .../test/scala/jackson/RunJacksonTest.scala | 0 .../test/resources/jackson3/Jackson3.feature | 15 ++++++ .../test/resources/junit-platform.properties | 3 ++ .../test/scala/jackson3/Jackson3Steps.scala | 40 ++++++++++++++++ .../test/scala/jackson3/RunJackson3Test.scala | 18 +++++++ 14 files changed, 183 insertions(+), 11 deletions(-) create mode 100644 cucumber-scala/src/main/scala/io/cucumber/scala/Jackson3DefaultDataTableEntryTransformer.scala rename integration-tests/{jackson => jackson2}/src/test/resources/jackson/Jackson.feature (100%) rename integration-tests/{jackson => jackson2}/src/test/resources/junit-platform.properties (100%) rename integration-tests/{jackson => jackson2}/src/test/scala/jackson/JacksonSteps.scala (100%) rename integration-tests/{jackson => jackson2}/src/test/scala/jackson/RunJacksonTest.scala (100%) create mode 100644 integration-tests/jackson3/src/test/resources/jackson3/Jackson3.feature create mode 100644 integration-tests/jackson3/src/test/resources/junit-platform.properties create mode 100644 integration-tests/jackson3/src/test/scala/jackson3/Jackson3Steps.scala create mode 100644 integration-tests/jackson3/src/test/scala/jackson3/RunJackson3Test.scala diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e7c23d1..0768188d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ See also the [CHANGELOG](https://github.com/cucumber/cucumber-jvm/blob/master/CH ### Added +- [Scala] Support for Jackson 3.x with `Jackson3DefaultDataTableEntryTransformer` trait + - Jackson 2.x is still supported with `JacksonDefaultDataTableEntryTransformer` trait + ### Changed ### Deprecated diff --git a/build.sbt b/build.sbt index 3a3e0e6f..e11393da 100644 --- a/build.sbt +++ b/build.sbt @@ -42,6 +42,7 @@ scalaVersion := scala213 val cucumberVersion = "7.30.0" val jacksonVersion = "2.20.0" +val jackson3Version = "3.0.0" val mockitoScalaVersion = "1.17.45" val junit4Version = "4.13.2" @@ -83,7 +84,8 @@ lazy val root = (project in file(".")) .aggregate( cucumberScala.projectRefs ++ integrationTestsCommon.projectRefs ++ - integrationTestsJackson.projectRefs ++ + integrationTestsJackson2.projectRefs ++ + integrationTestsJackson3.projectRefs ++ integrationTestsPicoContainer.projectRefs ++ examplesJunit4.projectRefs ++ examplesJunit5.projectRefs: _* @@ -99,6 +101,7 @@ lazy val cucumberScala = (projectMatrix in file("cucumber-scala")) "io.cucumber" % "cucumber-core" % cucumberVersion, // Users have to provide it (for JacksonDefaultDataTableTransformer) "com.fasterxml.jackson.module" %% "jackson-module-scala" % jacksonVersion % Provided, + "tools.jackson.module" %% "jackson-module-scala" % jackson3Version % Provided, "org.junit.jupiter" % "junit-jupiter" % junitBom.key.value % Test, ("org.mockito" %% "mockito-scala" % mockitoScalaVersion % Test) .cross(CrossVersion.for3Use2_13) @@ -158,12 +161,12 @@ lazy val integrationTestsCommon = .dependsOn(cucumberScala % Test) .jvmPlatform(scalaVersions = Seq(scala3, scala213, scala212)) -lazy val integrationTestsJackson = - (projectMatrix in file("integration-tests/jackson")) +lazy val integrationTestsJackson2 = + (projectMatrix in file("integration-tests/jackson2")) .settings(commonSettings) .settings(junit5SbtSupport) .settings( - name := "integration-tests-jackson", + name := "integration-tests-jackson2", libraryDependencies ++= Seq( "org.junit.platform" % "junit-platform-suite" % junitBom.key.value % Test, "io.cucumber" % "cucumber-junit-platform-engine" % cucumberVersion % Test, @@ -174,6 +177,22 @@ lazy val integrationTestsJackson = .dependsOn(cucumberScala % Test) .jvmPlatform(scalaVersions = Seq(scala3, scala213, scala212)) +lazy val integrationTestsJackson3 = + (projectMatrix in file("integration-tests/jackson3")) + .settings(commonSettings) + .settings(junit5SbtSupport) + .settings( + name := "integration-tests-jackson3", + libraryDependencies ++= Seq( + "org.junit.platform" % "junit-platform-suite" % junitBom.key.value % Test, + "io.cucumber" % "cucumber-junit-platform-engine" % cucumberVersion % Test, + "tools.jackson.module" %% "jackson-module-scala" % jackson3Version % Test + ), + publishArtifact := false + ) + .dependsOn(cucumberScala % Test) + .jvmPlatform(scalaVersions = Seq(scala3, scala213, scala212)) + lazy val integrationTestsPicoContainer = (projectMatrix in file("integration-tests/picocontainer")) .settings(commonSettings) diff --git a/cucumber-scala/src/main/scala/io/cucumber/scala/Jackson3DefaultDataTableEntryTransformer.scala b/cucumber-scala/src/main/scala/io/cucumber/scala/Jackson3DefaultDataTableEntryTransformer.scala new file mode 100644 index 00000000..3b6a4b70 --- /dev/null +++ b/cucumber-scala/src/main/scala/io/cucumber/scala/Jackson3DefaultDataTableEntryTransformer.scala @@ -0,0 +1,48 @@ +package io.cucumber.scala + +import tools.jackson.databind.ObjectMapper +import tools.jackson.databind.json.JsonMapper +import tools.jackson.module.scala.ScalaModule + +/**

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: `tools.jackson.module:jackson-module-scala` to your project if + * you want to use this trait.

+ * + *

For Jackson 2.x, use `JacksonDefaultDataTableEntryTransformer` + * instead.

+ */ +trait Jackson3DefaultDataTableEntryTransformer 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 JsonMapper + * with ScalaModule (including all builtin modules) registered. + */ + def createObjectMapper(): ObjectMapper = { + val scalaModule = ScalaModule + .builder() + .addAllBuiltinModules() + .build() + JsonMapper.builder().addModule(scalaModule).build() + } + + 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/cucumber-scala/src/main/scala/io/cucumber/scala/JacksonDefaultDataTableEntryTransformer.scala b/cucumber-scala/src/main/scala/io/cucumber/scala/JacksonDefaultDataTableEntryTransformer.scala index 22965051..c8410d05 100644 --- a/cucumber-scala/src/main/scala/io/cucumber/scala/JacksonDefaultDataTableEntryTransformer.scala +++ b/cucumber-scala/src/main/scala/io/cucumber/scala/JacksonDefaultDataTableEntryTransformer.scala @@ -10,8 +10,11 @@ import com.fasterxml.jackson.module.scala.DefaultScalaModule * 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 + * dependency: `com.fasterxml.jackson.module:jackson-module-scala` to your * project if you want to use this trait.

+ * + *

For Jackson 3.x, use `Jackson3DefaultDataTableEntryTransformer` + * instead.

*/ trait JacksonDefaultDataTableEntryTransformer extends ScalaDsl { diff --git a/docs/build.md b/docs/build.md index 434d8169..abb6ee14 100644 --- a/docs/build.md +++ b/docs/build.md @@ -12,7 +12,8 @@ The project contains several subprojects: - `cucumber-scala`: contains the codebase of the Cucumber Scala implementation - `integration-tests`: contains integration tests projects - `common`: general integration tests - - `jackson`: Jackson integration specific tests + - `jackson2`: Jackson 2.x integration specific tests + - `jackson3`: Jackson 3.x integration specific tests - `picocontainer`: Picocontainer integration specific tests - `examples`: contains a sample project diff --git a/docs/default_jackson_datatable_transformer.md b/docs/default_jackson_datatable_transformer.md index 6c5d59dd..f4382c8f 100644 --- a/docs/default_jackson_datatable_transformer.md +++ b/docs/default_jackson_datatable_transformer.md @@ -6,32 +6,54 @@ It can be used to automatically convert DataTables to case classes without defin ## Add Jackson dependency +### Jackson 2.x + 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.13.3 + 2.20.0 test ``` Or: ```sbt -libraryDependencies += "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.13.3" % Test +libraryDependencies += "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.20.0" % Test ``` - The current version of Cucumber Scala has been tested against Jackson Module Scala **version 2.20.0**. +### Jackson 3.x + +To use this optional transformer, you need to have Jackson Scala in your dependencies. + +```xml + + tools.jackson.module + jackson-module-scala_2.13 + 3.0.0 + test + +``` + +Or: +```sbt +libraryDependencies += "tools.jackson.module" %% "jackson-module-scala" % "3.0.0" % Test +``` + +The current version of Cucumber Scala has been tested against Jackson Module Scala **version 3.0.0**. + ## Add the transformer -The transformer has to be added to your glue code by extending the `JacksonDefaultDataTableEntryTransformer` trait. +The transformer has to be added to your glue code by extending the `JacksonDefaultDataTableEntryTransformer` (Jackson 2.x) +or `Jackson3DefaultDataTableEntryTransformer` (Jackson 3.x) trait. For instance: ```scala -class MySteps extends ScalaDsl with EN with JacksonDefaultDataTableEntryTransformer { +class MySteps extends ScalaDsl with EN with Jackson3DefaultDataTableEntryTransformer { // Your usual glue code } ``` diff --git a/integration-tests/jackson/src/test/resources/jackson/Jackson.feature b/integration-tests/jackson2/src/test/resources/jackson/Jackson.feature similarity index 100% rename from integration-tests/jackson/src/test/resources/jackson/Jackson.feature rename to integration-tests/jackson2/src/test/resources/jackson/Jackson.feature diff --git a/integration-tests/jackson/src/test/resources/junit-platform.properties b/integration-tests/jackson2/src/test/resources/junit-platform.properties similarity index 100% rename from integration-tests/jackson/src/test/resources/junit-platform.properties rename to integration-tests/jackson2/src/test/resources/junit-platform.properties diff --git a/integration-tests/jackson/src/test/scala/jackson/JacksonSteps.scala b/integration-tests/jackson2/src/test/scala/jackson/JacksonSteps.scala similarity index 100% rename from integration-tests/jackson/src/test/scala/jackson/JacksonSteps.scala rename to integration-tests/jackson2/src/test/scala/jackson/JacksonSteps.scala diff --git a/integration-tests/jackson/src/test/scala/jackson/RunJacksonTest.scala b/integration-tests/jackson2/src/test/scala/jackson/RunJacksonTest.scala similarity index 100% rename from integration-tests/jackson/src/test/scala/jackson/RunJacksonTest.scala rename to integration-tests/jackson2/src/test/scala/jackson/RunJacksonTest.scala diff --git a/integration-tests/jackson3/src/test/resources/jackson3/Jackson3.feature b/integration-tests/jackson3/src/test/resources/jackson3/Jackson3.feature new file mode 100644 index 00000000..09e41023 --- /dev/null +++ b/integration-tests/jackson3/src/test/resources/jackson3/Jackson3.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/integration-tests/jackson3/src/test/resources/junit-platform.properties b/integration-tests/jackson3/src/test/resources/junit-platform.properties new file mode 100644 index 00000000..d756c5ba --- /dev/null +++ b/integration-tests/jackson3/src/test/resources/junit-platform.properties @@ -0,0 +1,3 @@ +# Workaround for https://github.com/sbt/sbt-jupiter-interface/issues/142 +# See also https://github.com/cucumber/cucumber-jvm/pull/3023 +cucumber.junit-platform.discovery.as-root-engine=false \ No newline at end of file diff --git a/integration-tests/jackson3/src/test/scala/jackson3/Jackson3Steps.scala b/integration-tests/jackson3/src/test/scala/jackson3/Jackson3Steps.scala new file mode 100644 index 00000000..78a07862 --- /dev/null +++ b/integration-tests/jackson3/src/test/scala/jackson3/Jackson3Steps.scala @@ -0,0 +1,40 @@ +package jackson3 + +import io.cucumber.scala.{ + EN, + Jackson3DefaultDataTableEntryTransformer, + ScalaDsl +} + +import scala.jdk.CollectionConverters._ + +case class MyCaseClass(field1: Double, field2: Boolean, field3: String) + +class Jackson3Steps + extends ScalaDsl + with EN + with Jackson3DefaultDataTableEntryTransformer { + + 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/integration-tests/jackson3/src/test/scala/jackson3/RunJackson3Test.scala b/integration-tests/jackson3/src/test/scala/jackson3/RunJackson3Test.scala new file mode 100644 index 00000000..5ef0515e --- /dev/null +++ b/integration-tests/jackson3/src/test/scala/jackson3/RunJackson3Test.scala @@ -0,0 +1,18 @@ +package jackson3 + +import io.cucumber.junit.platform.engine.Constants +import org.junit.platform.suite.api.{ + ConfigurationParameter, + IncludeEngines, + SelectPackages, + Suite +} + +@Suite +@IncludeEngines(Array("cucumber")) +@SelectPackages(Array("jackson3")) +@ConfigurationParameter( + key = Constants.GLUE_PROPERTY_NAME, + value = "jackson3" +) +class RunJackson3Test From 53a570fbebd30d1be58efe3a50061366b491bd5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Jourdan-Weil?= Date: Sun, 5 Oct 2025 14:33:11 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9D=20docs:=20add=20missing=20item?= =?UTF-8?q?=20in=20changelog=20for=208.34.0=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0768188d..1f82acee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ See also the [CHANGELOG](https://github.com/cucumber/cucumber-jvm/blob/master/CH ### Changed - [Core] Update `cucumber-core` dependency to [7.30.0](https://github.com/cucumber/cucumber-jvm/blob/main/CHANGELOG.md) +- Upgrade Scala versions to 2.13.17 ## [8.33.0] (2025-09-22) From 4d4a72a751857c806bb322390dd6659ee9d1ce12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=ABl=20Jourdan-Weil?= Date: Sun, 5 Oct 2025 15:32:11 +0200 Subject: [PATCH 3/3] ci: stop testing against Java 8 and 11 This is required to be able to test our support to Jackson 3. Cucumber Core also stopped testing against these versions. --- .github/workflows/build.yml | 5 ++--- .github/workflows/version-policy-check.yml | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb4eb61e..7bb9bb7d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,10 +18,9 @@ jobs: matrix: os: [ ubuntu-latest ] java: - - 8.0 - - 11 - 17 - 21 + - 25 runs-on: ${{ matrix.os }} steps: @@ -40,7 +39,7 @@ jobs: uses: actions/setup-java@v5 with: java-version: ${{ matrix.java }} - distribution: 'adopt' + distribution: 'temurin' - uses: sbt/setup-sbt@v1 diff --git a/.github/workflows/version-policy-check.yml b/.github/workflows/version-policy-check.yml index ce51b1de..18fbcc29 100644 --- a/.github/workflows/version-policy-check.yml +++ b/.github/workflows/version-policy-check.yml @@ -32,7 +32,7 @@ jobs: uses: actions/setup-java@v5 with: java-version: ${{ matrix.java }} - distribution: 'adopt' + distribution: 'temurin' - uses: sbt/setup-sbt@v1