From fc5956b9177070e385eb54128bb60c9ffab14072 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Sun, 2 Feb 2025 14:30:20 +0100 Subject: [PATCH 1/4] [Archetype] Replace JUnit Jupiter with AssertJ (#2969) As a JUnit Platform Engine, Cucumber does not need JUnit Jupiter. It was included as a dependency to allow users to use Assertions. This is better done with AssertJ. --- CHANGELOG.md | 1 + cucumber-archetype/README.md | 2 +- cucumber-archetype/pom.xml | 11 +++++++++++ .../src/main/resources/archetype-resources/pom.xml | 11 +++++++++-- .../src/test/java/StepDefinitions.java | 2 +- .../should-generate-project/reference/pom.xml | 11 +++++++++-- .../src/test/java/com/example/StepDefinitions.java | 2 +- 7 files changed, 33 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 667badb74d..f346027060 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [JUnit Platform Engine] Set Engine-Version-cucumber attribute ([#2963](https://github.com/cucumber/cucumber-jvm/pull/2963) M.P. Korstanje) ### Changed +- [Archetype] Replace JUnit Jupiter with AssertJ ([#2969](https://github.com/cucumber/cucumber-jvm/pull/2969) M.P. Korstanje) - [JUnit Platform Engine] Use JUnit Platform 1.11.3 (JUnit Jupiter 5.11.3) ### Added diff --git a/cucumber-archetype/README.md b/cucumber-archetype/README.md index 24bc123426..3441efb331 100644 --- a/cucumber-archetype/README.md +++ b/cucumber-archetype/README.md @@ -6,5 +6,5 @@ This is a Maven Archetype for setting up an empty Cucumber project. Used by the mvn archetype:generate \ -DarchetypeGroupId=io.cucumber \ -DarchetypeArtifactId=cucumber-archetype \ - -DarchetypeVersion=${cucumber.version} \ + -DarchetypeVersion=${cucumber.version} ``` diff --git a/cucumber-archetype/pom.xml b/cucumber-archetype/pom.xml index 58f5a6746f..4c22852e4a 100644 --- a/cucumber-archetype/pom.xml +++ b/cucumber-archetype/pom.xml @@ -16,6 +16,7 @@ 5.11.4 + 3.25.3 3.13.0 3.5.2 @@ -39,6 +40,16 @@ pom import + + + org.assertj + assertj-bom + ${assertj.version} + pom + import + diff --git a/cucumber-archetype/src/main/resources/archetype-resources/pom.xml b/cucumber-archetype/src/main/resources/archetype-resources/pom.xml index 44b0529ca4..b9df265fb4 100644 --- a/cucumber-archetype/src/main/resources/archetype-resources/pom.xml +++ b/cucumber-archetype/src/main/resources/archetype-resources/pom.xml @@ -29,6 +29,13 @@ pom import + + org.assertj + assertj-bom + ${assertj.version} + pom + import + @@ -52,8 +59,8 @@ - org.junit.jupiter - junit-jupiter + org.assertj + assertj-core test diff --git a/cucumber-archetype/src/main/resources/archetype-resources/src/test/java/StepDefinitions.java b/cucumber-archetype/src/main/resources/archetype-resources/src/test/java/StepDefinitions.java index eb4f9e9a8a..2fd2be5c40 100644 --- a/cucumber-archetype/src/main/resources/archetype-resources/src/test/java/StepDefinitions.java +++ b/cucumber-archetype/src/main/resources/archetype-resources/src/test/java/StepDefinitions.java @@ -2,7 +2,7 @@ import io.cucumber.java.en.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; public class StepDefinitions { diff --git a/cucumber-archetype/src/test/resources/projects/should-generate-project/reference/pom.xml b/cucumber-archetype/src/test/resources/projects/should-generate-project/reference/pom.xml index 1da21d59e5..1dae263f3e 100644 --- a/cucumber-archetype/src/test/resources/projects/should-generate-project/reference/pom.xml +++ b/cucumber-archetype/src/test/resources/projects/should-generate-project/reference/pom.xml @@ -29,6 +29,13 @@ pom import + + org.assertj + assertj-bom + ${assertj.version} + pom + import + @@ -52,8 +59,8 @@ - org.junit.jupiter - junit-jupiter + org.assertj + assertj-core test diff --git a/cucumber-archetype/src/test/resources/projects/should-generate-project/reference/src/test/java/com/example/StepDefinitions.java b/cucumber-archetype/src/test/resources/projects/should-generate-project/reference/src/test/java/com/example/StepDefinitions.java index b34aad7b2c..3e618a495a 100644 --- a/cucumber-archetype/src/test/resources/projects/should-generate-project/reference/src/test/java/com/example/StepDefinitions.java +++ b/cucumber-archetype/src/test/resources/projects/should-generate-project/reference/src/test/java/com/example/StepDefinitions.java @@ -2,7 +2,7 @@ import io.cucumber.java.en.*; -import static org.junit.jupiter.api.Assertions.*; +import static org.assertj.core.api.Assertions.assertThat; public class StepDefinitions { From 410d5f54dbf7ac016343dfe0e88092311b814c36 Mon Sep 17 00:00:00 2001 From: "M.P. Korstanje" Date: Sun, 2 Feb 2025 15:39:57 +0100 Subject: [PATCH 2/4] [Core] Indent pretty formatter stacktrace (#2970) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renders the stack trace in the pretty formatter with the same indent as the step that failed it. Before ``` [INFO] Running io.cucumber.skeleton.RunCucumberTest Scenario: a few cukes # io/cucumber/skeleton/belly.feature:3 Given I have 42 cukes in my belly # io.cucumber.skeleton.StepDefinitions.I_have_cukes_in_my_belly(int) org.opentest4j.AssertionFailedError: expected: "b" but was: "a" at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502) at io.cucumber.skeleton.StepDefinitions.I_have_cukes_in_my_belly(StepDefinitions.java:12) at ✽.I have 42 cukes in my belly(classpath:io/cucumber/skeleton/belly.feature:4) ``` After ``` [INFO] Running io.cucumber.skeleton.RunCucumberTest Scenario: a few cukes # io/cucumber/skeleton/belly.feature:3 Given I have 42 cukes in my belly # io.cucumber.skeleton.StepDefinitions.I_have_cukes_in_my_belly(int) org.opentest4j.AssertionFailedError: expected: "b" but was: "a" at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62) at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502) at io.cucumber.skeleton.StepDefinitions.I_have_cukes_in_my_belly(StepDefinitions.java:12) at ✽.I have 42 cukes in my belly(classpath:io/cucumber/skeleton/belly.feature:4) ``` --- CHANGELOG.md | 1 + .../cucumber/core/plugin/PrettyFormatter.java | 17 +- .../options/CommandlineOptionsParserTest.java | 6 +- .../plugin/DefaultSummaryPrinterTest.java | 4 +- .../IsEqualCompressingLineSeparators.java | 47 ++++++ .../core/plugin/JsonFormatterTest.java | 3 +- .../core/plugin/PrettyFormatterTest.java | 147 +++++++++++++----- .../core/plugin/ProgressFormatterTest.java | 6 +- .../core/plugin/RerunFormatterTest.java | 18 +-- .../cucumber/core/plugin/StubException.java | 70 ++++++++- .../core/plugin/TeamCityPluginTest.java | 18 ++- .../plugin/UnusedStepsSummaryPrinterTest.java | 4 +- 12 files changed, 258 insertions(+), 83 deletions(-) create mode 100644 cucumber-core/src/test/java/io/cucumber/core/plugin/IsEqualCompressingLineSeparators.java diff --git a/CHANGELOG.md b/CHANGELOG.md index f346027060..e31146b41f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Fixed - [Core] Include root cause when using DataTable.asList and friends ([#2949](https://github.com/cucumber/cucumber-jvm/pull/2949) M.P. Korstanje) +- [Core] Indent stacktrace in pretty formatter ([#2970](https://github.com/cucumber/cucumber-jvm/pull/2970) M.P. Korstanje) - [JUnit Platform Engine] Set Engine-Version-cucumber attribute ([#2963](https://github.com/cucumber/cucumber-jvm/pull/2963) M.P. Korstanje) ### Changed diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java index a0bb1b74c9..d6e731cd15 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/PrettyFormatter.java @@ -49,8 +49,9 @@ public final class PrettyFormatter implements ConcurrentEventListener, ColorAware { private static final String SCENARIO_INDENT = ""; - private static final String STEP_INDENT = " "; - private static final String STEP_SCENARIO_INDENT = " "; + private static final String STEP_INDENT = SCENARIO_INDENT + " "; + private static final String STEP_SCENARIO_INDENT = STEP_INDENT + " "; + private static final String STACK_TRACE_INDENT = STEP_SCENARIO_INDENT + " "; private final Map commentStartIndex = new HashMap<>(); @@ -120,7 +121,7 @@ private void preCalculateLocationIndent(TestCaseStarted event) { private void printTags(TestCaseStarted event) { List tags = event.getTestCase().getTags(); if (!tags.isEmpty()) { - out.println(PrettyFormatter.SCENARIO_INDENT + String.join(" ", tags)); + out.println(SCENARIO_INDENT + String.join(" ", tags)); } } @@ -187,21 +188,23 @@ private String formatLocationComment( private void printError(TestStepFinished event) { Result result = event.getResult(); - printError(result); + printError(STACK_TRACE_INDENT, result); } private void printError(TestRunFinished event) { Result result = event.getResult(); - printError(result); + printError(SCENARIO_INDENT, result); } - private void printError(Result result) { + private void printError(String prefix, Result result) { Throwable error = result.getError(); if (error != null) { String name = result.getStatus().name().toLowerCase(ROOT); Format format = formats.get(name); String text = printStackTrace(error); - out.println(" " + format.text(text)); + // TODO: Java 12+ use String.indent + String indented = text.replaceAll("(\r\n|\r|\n)", "$1" + prefix).trim(); + out.println(prefix + format.text(indented)); } } diff --git a/cucumber-core/src/test/java/io/cucumber/core/options/CommandlineOptionsParserTest.java b/cucumber-core/src/test/java/io/cucumber/core/options/CommandlineOptionsParserTest.java index 3fc5a35a42..f51a55f09c 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/options/CommandlineOptionsParserTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/options/CommandlineOptionsParserTest.java @@ -39,6 +39,7 @@ import java.util.regex.Pattern; import static io.cucumber.core.options.Constants.FILTER_TAGS_PROPERTY_NAME; +import static io.cucumber.core.plugin.IsEqualCompressingLineSeparators.equalCompressingLineSeparators; import static io.cucumber.core.resource.ClasspathSupport.rootPackageUri; import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; @@ -50,7 +51,6 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalToCompressingWhiteSpace; import static org.hamcrest.collection.IsEmptyCollection.empty; import static org.hamcrest.collection.IsIterableContainingInOrder.contains; import static org.hamcrest.collection.IsMapContaining.hasEntry; @@ -411,7 +411,7 @@ void ensure_less_than_1_thread_is_not_allowed() { parser .parse("--threads", "0") .build(); - assertThat(output(), equalToCompressingWhiteSpace("--threads must be > 0")); + assertThat(output(), equalCompressingLineSeparators("--threads must be > 0")); assertThat(parser.exitStatus(), is(Optional.of((byte) 0x1))); } @@ -518,7 +518,7 @@ void ensure_less_than_1_count_is_not_allowed() { parser .parse("--count", "0") .build(); - assertThat(output(), equalToCompressingWhiteSpace("--count must be > 0")); + assertThat(output(), equalCompressingLineSeparators("--count must be > 0")); assertThat(parser.exitStatus(), is(Optional.of((byte) 0x1))); } diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/DefaultSummaryPrinterTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/DefaultSummaryPrinterTest.java index 01d7ccaca3..217bb3bf18 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/DefaultSummaryPrinterTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/DefaultSummaryPrinterTest.java @@ -19,11 +19,11 @@ import java.util.Locale; import java.util.UUID; +import static io.cucumber.core.plugin.IsEqualCompressingLineSeparators.equalCompressingLineSeparators; import static java.nio.charset.StandardCharsets.UTF_8; import static java.time.Instant.ofEpochSecond; import static java.util.Collections.singletonList; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.text.IsEqualCompressingWhiteSpace.equalToCompressingWhiteSpace; class DefaultSummaryPrinterTest { @@ -56,7 +56,7 @@ void does_not_print_duplicate_snippets() { bus.send(new TestRunFinished(bus.getInstant(), new Result(Status.PASSED, Duration.ZERO, null))); - assertThat(new String(out.toByteArray(), UTF_8), equalToCompressingWhiteSpace("" + + assertThat(new String(out.toByteArray(), UTF_8), equalCompressingLineSeparators("" + "\n" + "0 Scenarios\n" + "0 Steps\n" + diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/IsEqualCompressingLineSeparators.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/IsEqualCompressingLineSeparators.java new file mode 100644 index 0000000000..300b2d20c8 --- /dev/null +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/IsEqualCompressingLineSeparators.java @@ -0,0 +1,47 @@ +package io.cucumber.core.plugin; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.hamcrest.TypeSafeMatcher; + +import java.util.Objects; + +public class IsEqualCompressingLineSeparators extends TypeSafeMatcher { + + private final String expected; + + public IsEqualCompressingLineSeparators(String expected) { + Objects.requireNonNull(expected); + this.expected = expected; + } + + public String getExpected() { + return expected; + } + + @Override + public boolean matchesSafely(String actual) { + return compressNewLines(expected).equals(compressNewLines(actual)); + } + + @Override + public void describeMismatchSafely(String item, Description mismatchDescription) { + mismatchDescription.appendText("was ").appendValue(item); + } + + @Override + public void describeTo(Description description) { + description.appendText("a string equal to ") + .appendValue(expected) + .appendText(" compressing newlines"); + } + + public String compressNewLines(String actual) { + return actual.replaceAll("[\r\n]+", "\n").trim(); + } + + public static Matcher equalCompressingLineSeparators(String expectedString) { + return new IsEqualCompressingLineSeparators(expectedString); + } + +} diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonFormatterTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonFormatterTest.java index c96d7aca1a..28d2677e4e 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonFormatterTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/JsonFormatterTest.java @@ -230,7 +230,7 @@ void should_format_scenario_with_a_failed_step() throws JSONException { .withEventBus(new TimeServiceEventBus(timeService, UUID::randomUUID)) .withBackendSupplier(new StubBackendSupplier( new StubStepDefinition("there are bananas", "StepDefs.there_are_bananas()", - new StubException()))) + new StubException("the stack trace")))) .build() .run(); @@ -1485,5 +1485,4 @@ void should_handle_several_features() throws JSONException { "]"; assertJsonEquals(expected, out); } - } diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java index 4a27d0cfe3..c64d8549c9 100755 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/PrettyFormatterTest.java @@ -24,8 +24,15 @@ import java.util.Locale; import java.util.UUID; +import static io.cucumber.core.plugin.AnsiEscapes.GREEN; +import static io.cucumber.core.plugin.AnsiEscapes.GREY; +import static io.cucumber.core.plugin.AnsiEscapes.INTENSITY_BOLD; +import static io.cucumber.core.plugin.AnsiEscapes.RED; +import static io.cucumber.core.plugin.AnsiEscapes.RESET; +import static io.cucumber.core.plugin.AnsiEscapes.YELLOW; import static io.cucumber.core.plugin.Bytes.bytes; import static io.cucumber.core.plugin.Formats.ansi; +import static io.cucumber.core.plugin.IsEqualCompressingLineSeparators.equalCompressingLineSeparators; import static io.cucumber.core.runner.TestDefinitionArgument.createArguments; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -33,7 +40,6 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.IsEqual.equalTo; -import static org.hamcrest.text.IsEqualCompressingWhiteSpace.equalToCompressingWhiteSpace; import static org.junit.jupiter.api.Assertions.assertThrows; class PrettyFormatterTest { @@ -61,7 +67,7 @@ void should_align_the_indentation_of_location_strings() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "\n" + "Scenario: scenario name # path/test.feature:2\n" + " Given first step # path/step_definitions.java:3\n" + @@ -90,7 +96,7 @@ void should_skip_missing_location_strings() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "\n" + "Scenario: scenario name # path/test.feature:2\n" + " Given first step # path/step_definitions.java:3\n" + @@ -121,7 +127,7 @@ void should_handle_background() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "\n" + "Scenario: s1 # path/test.feature:4\n" + " Given first step # path/step_definitions.java:3\n" + @@ -156,7 +162,7 @@ void should_handle_scenario_outline() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "\n" + "Scenario Outline: name 1 # path/test.feature:7\n" + " Given first step # path/step_definitions.java:3\n" + @@ -186,7 +192,7 @@ void should_print_encoded_characters() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "\n" + "Scenario: Test Characters # path/test.feature:2\n" + @@ -221,7 +227,7 @@ void should_print_tags() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "\n" + "@feature_tag @scenario_tag\n" + @@ -253,7 +259,7 @@ void should_print_table() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "\n" + "Scenario: Test Scenario # path/test.feature:2\n" + @@ -288,7 +294,7 @@ void should_print_multiple_tables() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "\n" + "Scenario: Test Scenario # path/test.feature:2\n" + @@ -315,14 +321,47 @@ void should_print_error_message_for_failed_steps() { .withAdditionalPlugins(new PrettyFormatter(out)) .withRuntimeOptions(new RuntimeOptionsBuilder().setMonochrome().build()) .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("first step", "path/step_definitions.java:3", new StubException()))) + new StubStepDefinition("first step", "path/step_definitions.java:3", + new StubException("the exception message") + .withClassName() + .withStacktrace("the stack trace")))) .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "Scenario: scenario name # path/test.feature:2\n" + " Given first step # path/step_definitions.java:3\n" + - " the stack trace\n"))); + " io.cucumber.core.plugin.StubException\n" + + " the exception message\n" + + " \tthe stack trace\n"))); + } + + @Test + void should_indent_stacktrace() { + Feature feature = TestFeatureParser.parse("path/test.feature", "" + + "Feature: feature name\n" + + " Scenario: scenario name\n" + + " Given first step\n"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Runtime.builder() + .withFeatureSupplier(new StubFeatureSupplier(feature)) + .withAdditionalPlugins(new PrettyFormatter(out)) + .withRuntimeOptions(new RuntimeOptionsBuilder().setMonochrome().build()) + .withBackendSupplier(new StubBackendSupplier( + new StubStepDefinition("first step", "path/step_definitions.java:3", + new StubException("the exception message") + .withClassName() + .withStacktrace("the stack trace")))) + .build() + .run(); + + assertThat(out, bytes(equalCompressingLineSeparators("" + + "Scenario: scenario name # path/test.feature:2\n" + + " Given first step # path/step_definitions.java:3\n" + + " io.cucumber.core.plugin.StubException\n" + + " the exception message\n" + + " \tthe stack trace\n"))); } @Test @@ -338,16 +377,20 @@ void should_print_error_message_for_before_hooks() { .withAdditionalPlugins(new PrettyFormatter(out)) .withRuntimeOptions(new RuntimeOptionsBuilder().setMonochrome().build()) .withBackendSupplier(new StubBackendSupplier( - singletonList(new StubHookDefinition(new StubException())), + singletonList(new StubHookDefinition(new StubException("the exception message") + .withClassName() + .withStacktrace("the stack trace"))), singletonList(new StubStepDefinition("first step", "path/step_definitions.java:3")), emptyList())) .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "Scenario: scenario name # path/test.feature:2\n" + - " the stack trace\n" + - " Given first step # path/step_definitions.java:3\n"))); + " io.cucumber.core.plugin.StubException\n" + + " the exception message\n" + + " \tthe stack trace\n" + + " Given first step # path/step_definitions.java:3"))); } @Test @@ -365,14 +408,18 @@ void should_print_error_message_for_after_hooks() { .withBackendSupplier(new StubBackendSupplier( emptyList(), singletonList(new StubStepDefinition("first step", "path/step_definitions.java:3")), - singletonList(new StubHookDefinition(new StubException())))) + singletonList(new StubHookDefinition(new StubException("the exception message") + .withClassName() + .withStacktrace("the stack trace"))))) .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "Scenario: scenario name # path/test.feature:2\n" + " Given first step # path/step_definitions.java:3\n" + - " the stack trace"))); + " io.cucumber.core.plugin.StubException\n" + + " the exception message\n" + + " \tthe stack trace\n"))); } @Test @@ -394,7 +441,7 @@ void should_print_output_from_before_hooks() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "Scenario: scenario name # path/test.feature:2\n" + "\n" + " printed from hook\n" + @@ -421,7 +468,7 @@ void should_print_output_from_after_hooks() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "Scenario: scenario name # path/test.feature:2\n" + " Given first step # path/step_definitions.java:3\n" + "\n" + @@ -453,7 +500,7 @@ void should_print_output_from_afterStep_hooks() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "Scenario: scenario name # path/test.feature:2\n" + " Given first step # path/step_definitions.java:3\n" + "\n" + @@ -482,8 +529,8 @@ void should_color_code_steps_according_to_the_result() { .run(); assertThat(out, bytes(containsString("" + - " " + AnsiEscapes.GREEN + "Given " + AnsiEscapes.RESET + AnsiEscapes.GREEN + "first step" - + AnsiEscapes.RESET))); + " " + GREEN + "Given " + RESET + GREEN + "first step" + + RESET))); } @Test @@ -503,7 +550,7 @@ void should_color_code_locations_as_comments() { .run(); assertThat(out, bytes(containsString("" + - AnsiEscapes.GREY + "# path/step_definitions.java:3" + AnsiEscapes.RESET))); + GREY + "# path/step_definitions.java:3" + RESET))); } @Test @@ -518,12 +565,20 @@ void should_color_code_error_message_according_to_the_result() { .withFeatureSupplier(new StubFeatureSupplier(feature)) .withAdditionalPlugins(new PrettyFormatter(out)) .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("first step", "path/step_definitions.java:3", new StubException()))) + new StubStepDefinition("first step", "path/step_definitions.java:3", + new StubException("the exception message") + .withClassName() + .withStacktrace("the stack trace")))) .build() .run(); - assertThat(out, bytes(containsString("" + - " " + AnsiEscapes.RED + "the stack trace" + AnsiEscapes.RESET))); + assertThat(out, bytes(equalCompressingLineSeparators("" + + "Scenario: scenario name " + GREY + "# path/test.feature:2" + RESET + "\n" + + " " + RED + "Given " + RESET + RED + "first step" + RESET + " " + GREY + + "# path/step_definitions.java:3" + RESET + "\n" + + " " + RED + "io.cucumber.core.plugin.StubException\n" + + " the exception message\n" + + " \tthe stack trace" + RESET + "\n"))); } @Test @@ -540,11 +595,11 @@ void should_mark_subsequent_arguments_in_steps() { String formattedText = prettyFormatter.formatStepText("Given ", stepText, formats.get("passed"), formats.get("passed_arg"), createArguments(expression.match(stepText))); - assertThat(formattedText, equalTo(AnsiEscapes.GREEN + "Given " + AnsiEscapes.RESET + - AnsiEscapes.GREEN + "text " + AnsiEscapes.RESET + - AnsiEscapes.GREEN + AnsiEscapes.INTENSITY_BOLD + "'arg1'" + AnsiEscapes.RESET + - AnsiEscapes.GREEN + " text " + AnsiEscapes.RESET + - AnsiEscapes.GREEN + AnsiEscapes.INTENSITY_BOLD + "'arg2'" + AnsiEscapes.RESET)); + assertThat(formattedText, equalTo(GREEN + "Given " + RESET + + GREEN + "text " + RESET + + GREEN + INTENSITY_BOLD + "'arg1'" + RESET + + GREEN + " text " + RESET + + GREEN + INTENSITY_BOLD + "'arg2'" + RESET)); } @Test @@ -563,9 +618,9 @@ void should_mark_nested_argument_as_part_of_full_argument() { String formattedText = prettyFormatter.formatStepText("Given ", stepText, formats.get("passed"), formats.get("passed_arg"), createArguments(expression.match(stepText))); - assertThat(formattedText, equalTo(AnsiEscapes.GREEN + "Given " + AnsiEscapes.RESET + - AnsiEscapes.GREEN + "the order is placed" + AnsiEscapes.RESET + - AnsiEscapes.GREEN + AnsiEscapes.INTENSITY_BOLD + " and not yet confirmed" + AnsiEscapes.RESET)); + assertThat(formattedText, equalTo(GREEN + "Given " + RESET + + GREEN + "the order is placed" + RESET + + GREEN + INTENSITY_BOLD + " and not yet confirmed" + RESET)); } @Test @@ -581,9 +636,9 @@ void should_mark_nested_arguments_as_part_of_enclosing_argument() { String formattedText = prettyFormatter.formatStepText("Given ", stepText, formats.get("passed"), formats.get("passed_arg"), createArguments(expression.match(stepText))); - assertThat(formattedText, equalTo(AnsiEscapes.GREEN + "Given " + AnsiEscapes.RESET + - AnsiEscapes.GREEN + "the order is placed" + AnsiEscapes.RESET + - AnsiEscapes.GREEN + AnsiEscapes.INTENSITY_BOLD + " and not yet confirmed" + AnsiEscapes.RESET)); + assertThat(formattedText, equalTo(GREEN + "Given " + RESET + + GREEN + "the order is placed" + RESET + + GREEN + INTENSITY_BOLD + " and not yet confirmed" + RESET)); } @Test @@ -604,12 +659,18 @@ void should_print_system_failure_for_failed_hooks() { emptyList(), emptyList(), emptyList(), - singletonList(new StubStaticHookDefinition(new StubException("Hook failed", "the stack trace"))))) + singletonList(new StubStaticHookDefinition(new StubException("Hook failed") + .withClassName() + .withStacktrace("the stack trace"))))) .build() .run()); - assertThat(out, bytes(containsString("" + - " " + AnsiEscapes.RED + "the stack trace" + AnsiEscapes.RESET))); + assertThat(out, bytes(equalCompressingLineSeparators("" + + "Scenario: scenario name " + GREY + "# path/test.feature:2" + RESET + "\n" + + " " + YELLOW + "Given " + RESET + YELLOW + "first step" + RESET + "\n" + + RED + "io.cucumber.core.plugin.StubException\n" + + "Hook failed\n" + + "\tthe stack trace" + RESET + "\n"))); } @Test @@ -634,7 +695,7 @@ void should_print_docstring_including_content_type() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("" + + assertThat(out, bytes(equalCompressingLineSeparators("" + "\n" + "Scenario: Test Scenario # path/test.feature:2\n" + " Given first step # path/step_definitions.java:7\n" + diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/ProgressFormatterTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/ProgressFormatterTest.java index 2a3069e354..9a1a3b632c 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/ProgressFormatterTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/ProgressFormatterTest.java @@ -18,12 +18,12 @@ import java.util.UUID; import static io.cucumber.core.plugin.Bytes.bytes; +import static io.cucumber.core.plugin.IsEqualCompressingLineSeparators.equalCompressingLineSeparators; import static io.cucumber.plugin.event.Status.FAILED; import static io.cucumber.plugin.event.Status.PASSED; import static io.cucumber.plugin.event.Status.UNDEFINED; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.text.IsEqualCompressingWhiteSpace.equalToCompressingWhiteSpace; import static org.mockito.Mockito.mock; class ProgressFormatterTest { @@ -41,7 +41,7 @@ void setup() { void prints_empty_line_for_empty_test_run() { Result result = new Result(PASSED, Duration.ZERO, null); bus.send(new TestRunFinished(Instant.now(), result)); - assertThat(out, bytes(equalToCompressingWhiteSpace("\n"))); + assertThat(out, bytes(equalCompressingLineSeparators("\n"))); } @Test @@ -49,7 +49,7 @@ void prints_empty_line_and_green_dot_for_passing_test_run() { Result result = new Result(PASSED, Duration.ZERO, null); bus.send(new TestStepFinished(Instant.now(), mock(TestCase.class), mock(PickleStepTestStep.class), result)); bus.send(new TestRunFinished(Instant.now(), result)); - assertThat(out, bytes(equalToCompressingWhiteSpace(AnsiEscapes.GREEN + "." + AnsiEscapes.RESET + "\n"))); + assertThat(out, bytes(equalCompressingLineSeparators(AnsiEscapes.GREEN + "." + AnsiEscapes.RESET + "\n"))); } @Test diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/RerunFormatterTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/RerunFormatterTest.java index acd4770587..21a75dd9bf 100755 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/RerunFormatterTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/RerunFormatterTest.java @@ -14,12 +14,12 @@ import java.io.ByteArrayOutputStream; import static io.cucumber.core.plugin.Bytes.bytes; +import static io.cucumber.core.plugin.IsEqualCompressingLineSeparators.equalCompressingLineSeparators; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.text.IsEqualCompressingWhiteSpace.equalToCompressingWhiteSpace; class RerunFormatterTest { @@ -66,7 +66,7 @@ void should_put_data_in_report_when_exit_code_is_non_zero() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("classpath:path/test.feature:2:4:6\n"))); + assertThat(out, bytes(equalCompressingLineSeparators("classpath:path/test.feature:2:4:6\n"))); } @Test @@ -89,7 +89,7 @@ void should_use_scenario_location_when_scenario_step_fails() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("file:path/test.feature:2\n"))); + assertThat(out, bytes(equalCompressingLineSeparators("file:path/test.feature:2\n"))); } @Test @@ -113,7 +113,7 @@ void should_use_scenario_location_when_background_step_fails() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("file:path/test.feature:4\n"))); + assertThat(out, bytes(equalCompressingLineSeparators("file:path/test.feature:4\n"))); } @Test @@ -139,7 +139,7 @@ void should_use_example_row_location_when_scenario_outline_fails() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("classpath:path/test.feature:8\n"))); + assertThat(out, bytes(equalCompressingLineSeparators("classpath:path/test.feature:8\n"))); } @Test @@ -165,7 +165,7 @@ void should_use_scenario_location_when_before_hook_fails() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("classpath:path/test.feature:2\n"))); + assertThat(out, bytes(equalCompressingLineSeparators("classpath:path/test.feature:2\n"))); } @Test @@ -191,7 +191,7 @@ void should_use_scenario_location_when_after_hook_fails() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("classpath:path/test.feature:2\n"))); + assertThat(out, bytes(equalCompressingLineSeparators("classpath:path/test.feature:2\n"))); } @Test @@ -217,7 +217,7 @@ void should_one_entry_for_feature_with_many_failing_scenarios() { .build() .run(); - assertThat(out, bytes(equalToCompressingWhiteSpace("classpath:path/test.feature:2:5\n"))); + assertThat(out, bytes(equalCompressingLineSeparators("classpath:path/test.feature:2:5\n"))); } @Test @@ -247,7 +247,7 @@ void should_one_entry_for_each_failing_feature() { assertThat(out, bytes( - equalToCompressingWhiteSpace("classpath:path/first.feature:2\nclasspath:path/second.feature:2\n"))); + equalCompressingLineSeparators("classpath:path/first.feature:2\nclasspath:path/second.feature:2\n"))); } } diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/StubException.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/StubException.java index 3211c34c2b..8c87ca2b69 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/StubException.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/StubException.java @@ -6,24 +6,82 @@ class StubException extends RuntimeException { private final String stacktrace; + private final String className; - StubException(String message, String stacktrace) { + private StubException(String className, String message, String stacktrace) { super(message); + this.className = className; this.stacktrace = stacktrace; } public StubException() { - this("message", "the stack trace"); + this("the stack trace"); + } + + public StubException(String message) { + this(null, message, null); + } + + public StubException withClassName() { + return new StubException(StubException.class.getName(), getMessage(), stacktrace); + } + + public StubException withStacktrace(String stacktrace) { + return new StubException(className, getMessage(), stacktrace); } @Override - public void printStackTrace(PrintWriter printWriter) { - printWriter.print(stacktrace); + public void printStackTrace(PrintWriter writer) { + printStackTrace(new PrintWriterOrStream(writer)); } @Override - public void printStackTrace(PrintStream printStream) { - printStream.print(stacktrace); + public void printStackTrace(PrintStream stream) { + printStackTrace(new PrintWriterOrStream(stream)); + } + + private void printStackTrace(PrintWriterOrStream p) { + if (className != null) { + p.println(className); + } + p.print(getMessage()); + if (stacktrace != null) { + p.println(""); + p.println("\t" + stacktrace); + } + } + + private static class PrintWriterOrStream { + private final PrintWriter writer; + private final PrintStream stream; + + private PrintWriterOrStream(PrintWriter writer) { + this.writer = writer; + this.stream = null; + } + + private PrintWriterOrStream(PrintStream stream) { + this.writer = null; + this.stream = stream; + } + + void println(String s) { + if (writer != null) { + writer.println(s); + } + if (stream != null) { + stream.println(s); + } + } + + void print(String s) { + if (writer != null) { + writer.print(s); + } + if (stream != null) { + stream.print(s); + } + } } } diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java index e965d469ed..4a433a0b4e 100755 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/TeamCityPluginTest.java @@ -191,12 +191,14 @@ void should_print_error_message_for_failed_steps() { .withAdditionalPlugins(new TeamCityPlugin(new PrintStream(out))) .withEventBus(new TimeServiceEventBus(fixed(EPOCH, of("UTC")), UUID::randomUUID)) .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("first step", new StubException("Step failed", "the stack trace")))) + new StubStepDefinition("first step", + new StubException("Step failed") + .withStacktrace("the stack trace")))) .build() .run(); assertThat(out, bytes(containsString("" + - "##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' duration = '0' message = 'Step failed' details = 'the stack trace' name = 'first step']\n"))); + "##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' duration = '0' message = 'Step failed' details = 'Step failed|n\tthe stack trace|n' name = 'first step']\n"))); } @Test @@ -255,7 +257,9 @@ void should_print_error_message_for_before_hooks() { .withAdditionalPlugins(new TeamCityPlugin(new PrintStream(out))) .withEventBus(new TimeServiceEventBus(fixed(EPOCH, of("UTC")), UUID::randomUUID)) .withBackendSupplier(new StubBackendSupplier( - singletonList(new StubHookDefinition(new StubException("Step failed", "the stack trace"))), + singletonList( + new StubHookDefinition(new StubException("Step failed") + .withStacktrace("the stack trace"))), singletonList(new StubStepDefinition("first step")), emptyList())) .build() @@ -264,7 +268,7 @@ void should_print_error_message_for_before_hooks() { assertThat(out, bytes(containsString("" + "##teamcity[testStarted timestamp = '1970-01-01T12:00:00.000+0000' locationHint = '{stubbed location with details}' captureStandardOutput = 'true' name = 'Before']\n" + - "##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' duration = '0' message = 'Step failed' details = 'the stack trace' name = 'Before']"))); + "##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' duration = '0' message = 'Step failed' details = 'Step failed|n\tthe stack trace|n' name = 'Before']"))); } @Test @@ -333,13 +337,15 @@ void should_print_system_failure_for_failed_hooks() { emptyList(), emptyList(), emptyList(), - singletonList(new StubStaticHookDefinition(new StubException("Hook failed", "the stack trace"))))) + singletonList(new StubStaticHookDefinition( + new StubException("Hook failed") + .withStacktrace("the stack trace"))))) .build() .run()); assertThat(out, bytes(containsString("" + "##teamcity[testStarted timestamp = '1970-01-01T12:00:00.000+0000' name = 'Before All/After All']\n" + - "##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' message = 'Before All/After All failed' details = 'the stack trace' name = 'Before All/After All']\n" + "##teamcity[testFailed timestamp = '1970-01-01T12:00:00.000+0000' message = 'Before All/After All failed' details = 'Hook failed|n\tthe stack trace|n' name = 'Before All/After All']\n" + "##teamcity[testFinished timestamp = '1970-01-01T12:00:00.000+0000' name = 'Before All/After All']"))); } diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinterTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinterTest.java index af319a45fa..66f5266ef4 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinterTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinterTest.java @@ -17,8 +17,8 @@ import java.util.UUID; import static io.cucumber.core.plugin.Bytes.bytes; +import static io.cucumber.core.plugin.IsEqualCompressingLineSeparators.equalCompressingLineSeparators; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.text.IsEqualCompressingWhiteSpace.equalToCompressingWhiteSpace; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -47,7 +47,7 @@ void verifyUnusedStepsPrinted() { // Verify produced output assertThat(out, - bytes(equalToCompressingWhiteSpace("1 Unused steps:\n" + "my/tummy.feature:5 # some more cukes\n"))); + bytes(equalCompressingLineSeparators("1 Unused steps:\n" + "my/tummy.feature:5 # some more cukes\n"))); } private static StepDefinition mockStepDef(String location, String pattern) { From 53d94a147640d70d3c36aa891cc71c37d9ef8271 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 2 Feb 2025 15:42:06 +0100 Subject: [PATCH 3/4] fix(deps): update messages and dependants (#2943) * fix(deps): update messages and dependants * fix: corrected renovate for #2943 --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Julien Kronegg --- cucumber-bom/pom.xml | 6 +++--- .../java/io/cucumber/core/plugin/JUnitFormatterTest.java | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cucumber-bom/pom.xml b/cucumber-bom/pom.xml index 9eb1c3cd44..e18dd99681 100644 --- a/cucumber-bom/pom.xml +++ b/cucumber-bom/pom.xml @@ -16,10 +16,10 @@ 10.0.1 18.0.1 28.0.0 - 21.7.0 - 0.5.0 + 21.8.0 + 0.7.1 24.1.0 - 12.2.0 + 13.1.0 6.1.2 0.2.0 diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/JUnitFormatterTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/JUnitFormatterTest.java index 278df9e163..525a4a327d 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/JUnitFormatterTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/JUnitFormatterTest.java @@ -33,7 +33,8 @@ void writes_report_xml() { assertThat(bytes, bytes(equalTo("" + "\n" + - "\n" + + "\n" + + "\n"))); } From 0598529f14a3e426a911bbd4613f9efd0c597010 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 2 Feb 2025 15:44:32 +0100 Subject: [PATCH 4/4] fix(deps): update dependency io.cucumber:query to v13 - abandoned (#2942) * fix(deps): update dependency io.cucumber:query to v13 * fix: corrected renovate for #2942 --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Julien Kronegg Co-authored-by: M.P. Korstanje