diff --git a/CHANGELOG.md b/CHANGELOG.md index 93d4cbf664..31e9c4fbaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,8 +18,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - [JUnit Platform Engine] Use JUnit's `EngineDiscoveryRequestResolver` to resolve classpath based resources. ([#2835](https://github.com/cucumber/cucumber-jvm/pull/2835) M.P. Korstanje) - [JUnit Platform Engine] Use JUnit Platform 1.13.3 (JUnit Jupiter 5.13.3) +- [Core] Use a message based [Pretty Formatter](https://github.com/cucumber/pretty-formatter) ([#2835](https://github.com/cucumber/cucumber-jvm/pull/3012) M.P. Korstanje) - [Core] Update dependency io.cucumber:gherkin to v33.0.0 -- [Core] Update dependency io.cucumber:messages to v28.0.0 +- [Core] Update dependency io.cucumber:messages to v28.2.0 +- [Core] Update dependency io.cucumber:html-formatter to v21.13.0 +- [Core] Update dependency io.cucumber:junit-xml-formatter to v0.8.0 +- [Core] Update dependency io.cucumber:query to v13.4.0 +- [Core] Update dependency io.cucumber:testng-xml-formatter to v0.4.1 ### Fixed - [JUnit Platform Engine] Log discovery issues for feature files with parse errors. ([#2835](https://github.com/cucumber/cucumber-jvm/pull/2835) M.P. Korstanje) diff --git a/cucumber-bom/pom.xml b/cucumber-bom/pom.xml index 9c74967fac..eeec57fe24 100644 --- a/cucumber-bom/pom.xml +++ b/cucumber-bom/pom.xml @@ -16,12 +16,13 @@ 10.0.1 18.0.1 33.0.0 - 21.12.0 - 0.7.1 + 21.13.0 + 0.8.0 28.0.0 - 13.3.0 + 0.2.0 + 13.4.0 6.1.2 - 0.3.1 + 0.4.0 @@ -57,6 +58,11 @@ messages ${messages.version} + + io.cucumber + pretty-formatter + ${pretty-formatter.version} + io.cucumber query diff --git a/cucumber-core/pom.xml b/cucumber-core/pom.xml index 73e2106aab..039ced8d81 100644 --- a/cucumber-core/pom.xml +++ b/cucumber-core/pom.xml @@ -64,6 +64,10 @@ io.cucumber messages + + io.cucumber + pretty-formatter + io.cucumber testng-xml-formatter diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/AnsiEscapes.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/AnsiEscapes.java index 61ef287bb3..6fbba67caa 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/AnsiEscapes.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/AnsiEscapes.java @@ -13,6 +13,7 @@ final class AnsiEscapes { static final AnsiEscapes DEFAULT = color(9); static final AnsiEscapes GREY = color(90); static final AnsiEscapes INTENSITY_BOLD = color(1); + static final AnsiEscapes INTENSITY_BOLD_OFF = color(22); static final AnsiEscapes UNDERLINE = color(4); private static final char ESC = 27; private static final char BRACKET = '['; 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 d6e731cd15..699e046f12 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 @@ -1,44 +1,19 @@ package io.cucumber.core.plugin; -import io.cucumber.core.exception.CucumberException; -import io.cucumber.core.gherkin.DataTableArgument; -import io.cucumber.core.gherkin.DocStringArgument; -import io.cucumber.datatable.DataTable; -import io.cucumber.datatable.DataTableFormatter; -import io.cucumber.docstring.DocString; -import io.cucumber.docstring.DocStringFormatter; +import io.cucumber.messages.types.Envelope; import io.cucumber.plugin.ColorAware; import io.cucumber.plugin.ConcurrentEventListener; -import io.cucumber.plugin.event.Argument; -import io.cucumber.plugin.event.EmbedEvent; import io.cucumber.plugin.event.EventPublisher; -import io.cucumber.plugin.event.PickleStepTestStep; -import io.cucumber.plugin.event.Result; -import io.cucumber.plugin.event.StepArgument; -import io.cucumber.plugin.event.TestCase; -import io.cucumber.plugin.event.TestCaseStarted; -import io.cucumber.plugin.event.TestRunFinished; -import io.cucumber.plugin.event.TestStepFinished; -import io.cucumber.plugin.event.WriteEvent; +import io.cucumber.prettyformatter.MessagesToPrettyWriter; -import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.io.StringReader; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import static io.cucumber.core.exception.ExceptionUtils.printStackTrace; -import static io.cucumber.core.plugin.Formats.ansi; -import static io.cucumber.core.plugin.Formats.monochrome; -import static java.lang.Math.max; -import static java.util.Locale.ROOT; +import static io.cucumber.prettyformatter.MessagesToPrettyWriter.PrettyFeature.INCLUDE_FEATURE_LINE; +import static io.cucumber.prettyformatter.MessagesToPrettyWriter.PrettyFeature.INCLUDE_RULE_LINE; +import static io.cucumber.prettyformatter.Theme.cucumber; +import static io.cucumber.prettyformatter.Theme.none; /** * Prints a pretty report of the scenario execution as it happens. @@ -48,269 +23,47 @@ */ public final class PrettyFormatter implements ConcurrentEventListener, ColorAware { - private static final String 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<>(); - - private final UTF8PrintWriter out; - private Formats formats = ansi(); + private final OutputStream out; + private MessagesToPrettyWriter writer; public PrettyFormatter(OutputStream out) { - this.out = new UTF8PrintWriter(out); - } - - @Override - public void setEventPublisher(EventPublisher publisher) { - publisher.registerHandlerFor(TestCaseStarted.class, this::handleTestCaseStarted); - publisher.registerHandlerFor(TestStepFinished.class, this::handleTestStepFinished); - publisher.registerHandlerFor(WriteEvent.class, this::handleWrite); - publisher.registerHandlerFor(EmbedEvent.class, this::handleEmbed); - publisher.registerHandlerFor(TestRunFinished.class, this::handleTestRunFinished); - } - - private void handleTestCaseStarted(TestCaseStarted event) { - out.println(); - preCalculateLocationIndent(event); - printTags(event); - printScenarioDefinition(event); - out.flush(); - } - - private void handleTestStepFinished(TestStepFinished event) { - printStep(event); - printError(event); - out.flush(); - } - - private void handleWrite(WriteEvent event) { - out.println(); - printText(event); - out.println(); - out.flush(); - } - - private void handleEmbed(EmbedEvent event) { - out.println(); - printEmbedding(event); - out.println(); - out.flush(); - } - - private void handleTestRunFinished(TestRunFinished event) { - printError(event); - out.close(); - } - - private void preCalculateLocationIndent(TestCaseStarted event) { - TestCase testCase = event.getTestCase(); - Integer longestStep = testCase.getTestSteps().stream() - .filter(PickleStepTestStep.class::isInstance) - .map(PickleStepTestStep.class::cast) - .map(PickleStepTestStep::getStep) - .map(step -> formatPlainStep(step.getKeyword(), step.getText()).length()) - .max(Comparator.naturalOrder()) - .orElse(0); - - int scenarioLength = formatScenarioDefinition(testCase).length(); - commentStartIndex.put(testCase.getId(), max(longestStep, scenarioLength) + 1); - } - - private void printTags(TestCaseStarted event) { - List tags = event.getTestCase().getTags(); - if (!tags.isEmpty()) { - out.println(SCENARIO_INDENT + String.join(" ", tags)); - } - } - - private void printScenarioDefinition(TestCaseStarted event) { - TestCase testCase = event.getTestCase(); - String definitionText = formatScenarioDefinition(testCase); - String path = relativize(testCase.getUri()).getSchemeSpecificPart(); - String locationIndent = calculateLocationIndent(event.getTestCase(), SCENARIO_INDENT + definitionText); - out.println(SCENARIO_INDENT + definitionText + locationIndent - + formatLocation(path + ":" + testCase.getLocation().getLine())); - } - - private void printStep(TestStepFinished event) { - if (event.getTestStep() instanceof PickleStepTestStep) { - PickleStepTestStep testStep = (PickleStepTestStep) event.getTestStep(); - String keyword = testStep.getStep().getKeyword(); - String stepText = testStep.getStep().getText(); - String status = event.getResult().getStatus().name().toLowerCase(ROOT); - String formattedStepText = formatStepText(keyword, stepText, formats.get(status), - formats.get(status + "_arg"), testStep.getDefinitionArgument()); - String locationComment = formatLocationComment(event, testStep, keyword, stepText); - out.println(STEP_INDENT + formattedStepText + locationComment); - StepArgument stepArgument = testStep.getStep().getArgument(); - if (stepArgument instanceof DataTableArgument) { - DataTableFormatter tableFormatter = DataTableFormatter - .builder() - .prefixRow(STEP_SCENARIO_INDENT) - .escapeDelimiters(false) - .build(); - DataTableArgument dataTableArgument = (DataTableArgument) stepArgument; - DataTable table = DataTable.create(dataTableArgument.cells()); - try { - tableFormatter.formatTo(table, out); - } catch (IOException e) { - throw new CucumberException(e); - } - } else if (stepArgument instanceof DocStringArgument) { - DocStringFormatter docStringFormatter = DocStringFormatter - .builder() - .indentation(STEP_SCENARIO_INDENT) - .build(); - DocStringArgument docStringArgument = (DocStringArgument) stepArgument; - DocString docString = DocString.create(docStringArgument.getContent(), - docStringArgument.getContentType()); - try { - docStringFormatter.formatTo(docString, out); - } catch (IOException e) { - throw new CucumberException(e); - } - } - } - } - - private String formatLocationComment( - TestStepFinished event, PickleStepTestStep testStep, String keyword, String stepText - ) { - String codeLocation = testStep.getCodeLocation(); - if (codeLocation == null) { - return ""; - } - String locationIndent = calculateLocationIndent(event.getTestCase(), formatPlainStep(keyword, stepText)); - return locationIndent + formatLocation(codeLocation); + this.out = out; + this.writer = createBuilder().build(out); } - private void printError(TestStepFinished event) { - Result result = event.getResult(); - printError(STACK_TRACE_INDENT, result); + private static MessagesToPrettyWriter.Builder createBuilder() { + String cwdUri = new File("").toURI().toString(); + return MessagesToPrettyWriter.builder() + .feature(INCLUDE_FEATURE_LINE, false) + .feature(INCLUDE_RULE_LINE, false) + .theme(cucumber()) + .removeUriPrefix(cwdUri); } - private void printError(TestRunFinished event) { - Result result = event.getResult(); - printError(SCENARIO_INDENT, 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); - // TODO: Java 12+ use String.indent - String indented = text.replaceAll("(\r\n|\r|\n)", "$1" + prefix).trim(); - out.println(prefix + format.text(indented)); - } - } - - private void printText(WriteEvent event) { - // Prevent interleaving when multiple threads write to System.out - StringBuilder builder = new StringBuilder(); - try (BufferedReader lines = new BufferedReader(new StringReader(event.getText()))) { - String line; - while ((line = lines.readLine()) != null) { - builder.append(STEP_SCENARIO_INDENT) - .append(line) - // Add system line separator - \n won't do it! - .append(System.lineSeparator()); - } - } catch (IOException e) { - throw new CucumberException(e); - } - out.append(builder); - } - - private void printEmbedding(EmbedEvent event) { - String line = "Embedding " + event.getName() + " [" + event.getMediaType() + " " + event.getData().length - + " bytes]"; - out.println(STEP_SCENARIO_INDENT + line); - } - - private String formatPlainStep(String keyword, String stepText) { - return STEP_INDENT + keyword + stepText; - } - - private String formatScenarioDefinition(TestCase testCase) { - return testCase.getKeyword() + ": " + testCase.getName(); + @Override + public void setEventPublisher(EventPublisher publisher) { + publisher.registerHandlerFor(Envelope.class, this::write); } - static URI relativize(URI uri) { - if (!"file".equals(uri.getScheme())) { - return uri; - } - if (!uri.isAbsolute()) { - return uri; - } - + private void write(Envelope event) { try { - URI root = new File("").toURI(); - URI relative = root.relativize(uri); - // Scheme is lost by relativize - return new URI("file", relative.getSchemeSpecificPart(), relative.getFragment()); - } catch (URISyntaxException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - private String calculateLocationIndent(TestCase testStep, String prefix) { - Integer commentStartAt = commentStartIndex.getOrDefault(testStep.getId(), 0); - int padding = commentStartAt - prefix.length(); - - if (padding < 0) { - return " "; - } - StringBuilder builder = new StringBuilder(padding); - for (int i = 0; i < padding; i++) { - builder.append(" "); + writer.write(event); + } catch (IOException e) { + throw new IllegalStateException(e); } - return builder.toString(); - } - private String formatLocation(String location) { - return formats.get("comment").text("# " + location); - } - - String formatStepText( - String keyword, String stepText, Format textFormat, Format argFormat, List arguments - ) { - int beginIndex = 0; - StringBuilder result = new StringBuilder(textFormat.text(keyword)); - for (Argument argument : arguments) { - // can be null if the argument is missing. - if (argument.getValue() != null) { - int argumentOffset = argument.getStart(); - // a nested argument starts before the enclosing argument ends; - // ignore it when formatting - if (argumentOffset < beginIndex) { - continue; - } - String text = stepText.substring(beginIndex, argumentOffset); - result.append(textFormat.text(text)); - } - // val can be null if the argument isn't there, for example - // @And("(it )?has something") - if (argument.getValue() != null) { - String text = stepText.substring(argument.getStart(), argument.getEnd()); - result.append(argFormat.text(text)); - // set beginIndex to end of argument - beginIndex = argument.getEnd(); - } - } - if (beginIndex != stepText.length()) { - String text = stepText.substring(beginIndex); - result.append(textFormat.text(text)); + // TODO: Plugins should implement the closable interface + // and be closed by Cucumber + if (event.getTestRunFinished().isPresent()) { + writer.close(); } - return result.toString(); } @Override public void setMonochrome(boolean monochrome) { - formats = monochrome ? monochrome() : ansi(); + if (monochrome) { + writer = createBuilder().theme(none()).build(out); + } } } diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java index bc23a6168c..e3063116f3 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java @@ -7,15 +7,16 @@ import io.cucumber.plugin.event.TestCaseFinished; import io.cucumber.plugin.event.TestRunFinished; +import java.io.File; import java.io.OutputStream; import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.Map; import static io.cucumber.core.feature.FeatureWithLines.create; -import static io.cucumber.core.plugin.PrettyFormatter.relativize; /** * Formatter for reporting all failed test cases and print their locations @@ -61,4 +62,21 @@ private Collection getFailedTestCaseLines(URI uri) { return featureAndFailedLinesMapping.computeIfAbsent(uri, k -> new ArrayList<>()); } + static URI relativize(URI uri) { + if (!"file".equals(uri.getScheme())) { + return uri; + } + if (!uri.isAbsolute()) { + return uri; + } + + try { + URI root = new File("").toURI(); + URI relative = root.relativize(uri); + // Scheme is lost by relativize + return new URI("file", relative.getSchemeSpecificPart(), relative.getFragment()); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + } } diff --git a/cucumber-core/src/test/java/io/cucumber/core/backend/StubStepDefinition.java b/cucumber-core/src/test/java/io/cucumber/core/backend/StubStepDefinition.java index c09c631296..d47e8d06a1 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/backend/StubStepDefinition.java +++ b/cucumber-core/src/test/java/io/cucumber/core/backend/StubStepDefinition.java @@ -3,6 +3,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Type; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -15,6 +16,7 @@ public class StubStepDefinition implements StepDefinition { private final String expression; private final Throwable exception; private final Located location; + private SourceReference sourceReference; public StubStepDefinition(String pattern, String location, Type... types) { this(pattern, location, null, types); @@ -35,6 +37,18 @@ public StubStepDefinition(String pattern, String location, Throwable exception, this.exception = exception; } + public StubStepDefinition(String pattern, SourceReference sourceReference, Type... types) { + this(pattern, sourceReference, null, types); + } + + public StubStepDefinition(String pattern, SourceReference sourceReference, Throwable exception, Type... types) { + this.parameterInfos = Stream.of(types).map(StubParameterInfo::new).collect(Collectors.toList()); + this.expression = pattern; + this.location = new StubLocation(""); + this.sourceReference = sourceReference; + this.exception = exception; + } + @Override public boolean isDefinedAt(StackTraceElement stackTraceElement) { return false; @@ -70,6 +84,11 @@ public String getPattern() { return expression; } + @Override + public Optional getSourceReference() { + return Optional.ofNullable(sourceReference); + } + private static final class StubParameterInfo implements ParameterInfo { private final Type type; diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/PrettyFormatterStepDefinition.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/PrettyFormatterStepDefinition.java new file mode 100644 index 0000000000..09259f4e90 --- /dev/null +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/PrettyFormatterStepDefinition.java @@ -0,0 +1,66 @@ +package io.cucumber.core.plugin; + +import io.cucumber.core.backend.SourceReference; + +import java.lang.reflect.Method; + +class PrettyFormatterStepDefinition { + SourceReference source; + + PrettyFormatterStepDefinition() { + source = SourceReference.fromStackTraceElement(new Exception().getStackTrace()[0]); + } + + public void one() { + + } + + public void two() { + + } + + public void three() { + + } + + public void oneArgument(String a) { + + } + + public void twoArguments(Integer a, Integer b) { + + } + + static SourceReference oneReference() { + return getSourceReference("one"); + } + + static SourceReference twoReference() { + return getSourceReference("two"); + } + + static SourceReference threeReference() { + return getSourceReference("three"); + } + + static SourceReference twoArgumentsReference() { + return getSourceReference("twoArguments", Integer.class, Integer.class); + } + + static SourceReference oneArgumentsReference() { + return getSourceReference("oneArgument", String.class); + } + + private static SourceReference getSourceReference(String methodName, Class... p) { + try { + Method method = PrettyFormatterStepDefinition.class.getMethod(methodName, p); + return SourceReference.fromMethod(method); + } catch (NoSuchMethodException e) { + throw new IllegalStateException(e); + } + } + + static SourceReference getStackSourceReference() { + return new PrettyFormatterStepDefinition().source; + } +} 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 c64d8549c9..dba69af43e 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 @@ -1,82 +1,27 @@ package io.cucumber.core.plugin; -import io.cucumber.core.backend.StepDefinition; -import io.cucumber.core.backend.StubHookDefinition; -import io.cucumber.core.backend.StubStaticHookDefinition; import io.cucumber.core.backend.StubStepDefinition; -import io.cucumber.core.eventbus.EventBus; import io.cucumber.core.feature.TestFeatureParser; import io.cucumber.core.gherkin.Feature; import io.cucumber.core.options.RuntimeOptionsBuilder; import io.cucumber.core.runtime.Runtime; import io.cucumber.core.runtime.StubBackendSupplier; import io.cucumber.core.runtime.StubFeatureSupplier; -import io.cucumber.core.runtime.TimeServiceEventBus; -import io.cucumber.core.stepexpression.StepExpression; -import io.cucumber.core.stepexpression.StepExpressionFactory; -import io.cucumber.core.stepexpression.StepTypeRegistry; -import io.cucumber.datatable.DataTable; -import io.cucumber.docstring.DocString; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; -import java.time.Clock; -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; -import static java.util.Collections.singletonList; -import static org.hamcrest.CoreMatchers.containsString; +import static io.cucumber.core.plugin.PrettyFormatterStepDefinition.oneReference; +import static io.cucumber.core.plugin.PrettyFormatterStepDefinition.threeReference; +import static io.cucumber.core.plugin.PrettyFormatterStepDefinition.twoReference; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.core.IsEqual.equalTo; -import static org.junit.jupiter.api.Assertions.assertThrows; class PrettyFormatterTest { - private final EventBus bus = new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID); - - @Test - void should_align_the_indentation_of_location_strings() { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: feature name\n" + - " Scenario: scenario name\n" + - " Given first step\n" + - " When second step\n" + - " Then third 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 StubStepDefinition("second step", "path/step_definitions.java:7"), - new StubStepDefinition("third step", "path/step_definitions.java:11"))) - .build() - .run(); - - assertThat(out, bytes(equalCompressingLineSeparators("" + - "\n" + - "Scenario: scenario name # path/test.feature:2\n" + - " Given first step # path/step_definitions.java:3\n" + - " When second step # path/step_definitions.java:7\n" + - " Then third step # path/step_definitions.java:11\n"))); - } - @Test - void should_skip_missing_location_strings() { + void writes_pretty_report() { Feature feature = TestFeatureParser.parse("path/test.feature", "" + "Feature: feature name\n" + " Scenario: scenario name\n" + @@ -90,619 +35,17 @@ void should_skip_missing_location_strings() { .withAdditionalPlugins(new PrettyFormatter(out)) .withRuntimeOptions(new RuntimeOptionsBuilder().setMonochrome().build()) .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("first step", "path/step_definitions.java:3"), - new StubStepDefinition("second step", (String) null), - new StubStepDefinition("third step", "path/step_definitions.java:11"))) - .build() - .run(); - - assertThat(out, bytes(equalCompressingLineSeparators("" + - "\n" + - "Scenario: scenario name # path/test.feature:2\n" + - " Given first step # path/step_definitions.java:3\n" + - " When second step\n" + - " Then third step # path/step_definitions.java:11\n"))); - } - - @Test - void should_handle_background() { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: feature name\n" + - " Background: background name\n" + - " Given first step\n" + - " Scenario: s1\n" + - " Then second step\n" + - " Scenario: s2\n" + - " Then third 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 StubStepDefinition("second step", "path/step_definitions.java:7"), - new StubStepDefinition("third step", "path/step_definitions.java:11"))) - .build() - .run(); - - assertThat(out, bytes(equalCompressingLineSeparators("" + - "\n" + - "Scenario: s1 # path/test.feature:4\n" + - " Given first step # path/step_definitions.java:3\n" + - " Then second step # path/step_definitions.java:7\n" + - "\n" + - "Scenario: s2 # path/test.feature:6\n" + - " Given first step # path/step_definitions.java:3\n" + - " Then third step # path/step_definitions.java:11\n"))); - } - - @Test - void should_handle_scenario_outline() { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: feature name\n" + - " Scenario Outline: \n" + - " Given first step\n" + - " Then step\n" + - " Examples: examples name\n" + - " | name | arg |\n" + - " | name 1 | second |\n" + - " | name 2 | third |\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 StubStepDefinition("second step", "path/step_definitions.java:7"), - new StubStepDefinition("third step", "path/step_definitions.java:11"))) - .build() - .run(); - - assertThat(out, bytes(equalCompressingLineSeparators("" + - "\n" + - "Scenario Outline: name 1 # path/test.feature:7\n" + - " Given first step # path/step_definitions.java:3\n" + - " Then second step # path/step_definitions.java:7\n" + - "\n" + - "Scenario Outline: name 2 # path/test.feature:8\n" + - " Given first step # path/step_definitions.java:3\n" + - " Then third step # path/step_definitions.java:11\n"))); - } - - @Test - void should_print_encoded_characters() { - - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Test feature\n" + - " Scenario: Test Characters\n" + - " Given first step\n" + - " | URLEncoded | %71s%22i%22%3A%7B%22D |\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:7", DataTable.class))) - .build() - .run(); - - assertThat(out, bytes(equalCompressingLineSeparators("" + - - "\n" + - "Scenario: Test Characters # path/test.feature:2\n" + - " Given first step # path/step_definitions.java:7\n" + - " | URLEncoded | %71s%22i%22%3A%7B%22D |\n"))); - } - - @Test - void should_print_tags() { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "@feature_tag\n" + - "Feature: feature name\n" + - " @scenario_tag\n" + - " Scenario: scenario name\n" + - " Then first step\n" + - " @scenario_outline_tag\n" + - " Scenario Outline: scenario outline name\n" + - " Then step\n" + - " @examples_tag\n" + - " Examples: examples name\n" + - " | arg |\n" + - " | second |\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:7"), - new StubStepDefinition("second step", "path/step_definitions.java:11"))) - .build() - .run(); - - assertThat(out, bytes(equalCompressingLineSeparators("" + - - "\n" + - "@feature_tag @scenario_tag\n" + - "Scenario: scenario name # path/test.feature:4\n" + - " Then first step # path/step_definitions.java:7\n" + - "\n" + - "@feature_tag @scenario_outline_tag @examples_tag\n" + - "Scenario Outline: scenario outline name # path/test.feature:12\n" + - " Then second step # path/step_definitions.java:11\n"))); - } - - @Test - void should_print_table() { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Test feature\n" + - " Scenario: Test Scenario\n" + - " Given first step\n" + - " | key1 | key2 |\n" + - " | value1 | value2 |\n" + - " | another1 | another2 |\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:7", DataTable.class))) - .build() - .run(); - - assertThat(out, bytes(equalCompressingLineSeparators("" + - - "\n" + - "Scenario: Test Scenario # path/test.feature:2\n" + - " Given first step # path/step_definitions.java:7\n" + - " | key1 | key2 |\n" + - " | value1 | value2 |\n" + - " | another1 | another2 |\n"))); - } - - @Test - void should_print_multiple_tables() { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Test feature\n" + - " Scenario: Test Scenario\n" + - " Given first step\n" + - " | key1 | key2 |\n" + - " | value1 | value2 |\n" + - " | another1 | another2 |\n" + - " Given second step\n" + - " | key3 | key4 |\n" + - " | value3 | value4 |\n" + - " | another3 | another4 |\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:7", DataTable.class), - new StubStepDefinition("second step", "path/step_definitions.java:15", DataTable.class))) - .build() - .run(); - - assertThat(out, bytes(equalCompressingLineSeparators("" + - - "\n" + - "Scenario: Test Scenario # path/test.feature:2\n" + - " Given first step # path/step_definitions.java:7\n" + - " | key1 | key2 |\n" + - " | value1 | value2 |\n" + - " | another1 | another2 |\n" + - " Given second step # path/step_definitions.java:15\n" + - " | key3 | key4 |\n" + - " | value3 | value4 |\n" + - " | another3 | another4 |\n"))); - } - - @Test - void should_print_error_message_for_failed_steps() { - 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 - 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 - void should_print_error_message_for_before_hooks() { - 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( - 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(equalCompressingLineSeparators("" + - "Scenario: scenario name # path/test.feature:2\n" + - " io.cucumber.core.plugin.StubException\n" + - " the exception message\n" + - " \tthe stack trace\n" + - " Given first step # path/step_definitions.java:3"))); - } - - @Test - void should_print_error_message_for_after_hooks() { - 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( - emptyList(), - singletonList(new StubStepDefinition("first step", "path/step_definitions.java:3")), - singletonList(new StubHookDefinition(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 - void should_print_output_from_before_hooks() { - 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( - singletonList(new StubHookDefinition(testCaseState -> testCaseState.log("printed from hook"))), - singletonList(new StubStepDefinition("first step", "path/step_definitions.java:3")), - emptyList())) + new StubStepDefinition("first step", oneReference()), + new StubStepDefinition("second step", twoReference()), + new StubStepDefinition("third step", threeReference()))) .build() .run(); assertThat(out, bytes(equalCompressingLineSeparators("" + - "Scenario: scenario name # path/test.feature:2\n" + - "\n" + - " printed from hook\n" + "\n" + - " Given first step # path/step_definitions.java:3\n"))); - } - - @Test - void should_print_output_from_after_hooks() { - 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( - emptyList(), - singletonList(new StubStepDefinition("first step", "path/step_definitions.java:3")), - singletonList(new StubHookDefinition(testCaseState -> testCaseState.log("printed from hook"))))) - .build() - .run(); - - assertThat(out, bytes(equalCompressingLineSeparators("" + "Scenario: scenario name # path/test.feature:2\n" + - " Given first step # path/step_definitions.java:3\n" + - "\n" + - " printed from hook\n"))); - } - - @Test - void should_print_output_from_afterStep_hooks() { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: feature name\n" + - " Scenario: scenario name\n" + - " Given first step\n" + - " When second step\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(new PrettyFormatter(out)) - .withRuntimeOptions(new RuntimeOptionsBuilder().setMonochrome().build()) - .withBackendSupplier(new StubBackendSupplier( - emptyList(), - emptyList(), - asList( - new StubStepDefinition("first step", "path/step_definitions.java:3"), - new StubStepDefinition("second step", "path/step_definitions.java:4")), - singletonList( - new StubHookDefinition(testCaseState -> testCaseState.log("printed from afterstep hook"))), - emptyList())) - .build() - .run(); - - assertThat(out, bytes(equalCompressingLineSeparators("" + - "Scenario: scenario name # path/test.feature:2\n" + - " Given first step # path/step_definitions.java:3\n" + - "\n" + - " printed from afterstep hook\n" + - "\n" + - " When second step # path/step_definitions.java:4\n" + - "\n" + - " printed from afterstep hook" + - "\n"))); - } - - @Test - void should_color_code_steps_according_to_the_result() { - 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)) - .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("first step", "path/step_definitions.java:3"))) - .build() - .run(); - - assertThat(out, bytes(containsString("" + - " " + GREEN + "Given " + RESET + GREEN + "first step" - + RESET))); - } - - @Test - void should_color_code_locations_as_comments() { - 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)) - .withBackendSupplier(new StubBackendSupplier( - new StubStepDefinition("first step", "path/step_definitions.java:3"))) - .build() - .run(); - - assertThat(out, bytes(containsString("" + - GREY + "# path/step_definitions.java:3" + RESET))); - } - - @Test - void should_color_code_error_message_according_to_the_result() { - 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)) - .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 " + 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 - void should_mark_subsequent_arguments_in_steps() { - Formats formats = ansi(); - - StepTypeRegistry registry = new StepTypeRegistry(Locale.ENGLISH); - StepExpressionFactory stepExpressionFactory = new StepExpressionFactory(registry, bus); - StepDefinition stepDefinition = new StubStepDefinition("text {string} text {string}", String.class); - StepExpression expression = stepExpressionFactory.createExpression(stepDefinition); - - PrettyFormatter prettyFormatter = new PrettyFormatter(new ByteArrayOutputStream()); - String stepText = "text 'arg1' text 'arg2'"; - String formattedText = prettyFormatter.formatStepText("Given ", stepText, formats.get("passed"), - formats.get("passed_arg"), createArguments(expression.match(stepText))); - - assertThat(formattedText, equalTo(GREEN + "Given " + RESET + - GREEN + "text " + RESET + - GREEN + INTENSITY_BOLD + "'arg1'" + RESET + - GREEN + " text " + RESET + - GREEN + INTENSITY_BOLD + "'arg2'" + RESET)); - } - - @Test - void should_mark_nested_argument_as_part_of_full_argument() { - Formats formats = ansi(); - - StepTypeRegistry registry = new StepTypeRegistry(Locale.ENGLISH); - StepExpressionFactory stepExpressionFactory = new StepExpressionFactory(registry, bus); - StepDefinition stepDefinition = new StubStepDefinition("^the order is placed( and (not yet )?confirmed)?$", - String.class); - StepExpression expression = stepExpressionFactory.createExpression(stepDefinition); - - PrettyFormatter prettyFormatter = new PrettyFormatter(new ByteArrayOutputStream()); - String stepText = "the order is placed and not yet confirmed"; - - String formattedText = prettyFormatter.formatStepText("Given ", stepText, formats.get("passed"), - formats.get("passed_arg"), createArguments(expression.match(stepText))); - - assertThat(formattedText, equalTo(GREEN + "Given " + RESET + - GREEN + "the order is placed" + RESET + - GREEN + INTENSITY_BOLD + " and not yet confirmed" + RESET)); - } - - @Test - void should_mark_nested_arguments_as_part_of_enclosing_argument() { - Formats formats = ansi(); - PrettyFormatter prettyFormatter = new PrettyFormatter(new ByteArrayOutputStream()); - StepTypeRegistry registry = new StepTypeRegistry(Locale.ENGLISH); - StepExpressionFactory stepExpressionFactory = new StepExpressionFactory(registry, bus); - StepDefinition stepDefinition = new StubStepDefinition("^the order is placed( and (not( yet)? )?confirmed)?$", - String.class); - StepExpression expression = stepExpressionFactory.createExpression(stepDefinition); - String stepText = "the order is placed and not yet confirmed"; - String formattedText = prettyFormatter.formatStepText("Given ", stepText, formats.get("passed"), - formats.get("passed_arg"), createArguments(expression.match(stepText))); - - assertThat(formattedText, equalTo(GREEN + "Given " + RESET + - GREEN + "the order is placed" + RESET + - GREEN + INTENSITY_BOLD + " and not yet confirmed" + RESET)); - } - - @Test - void should_print_system_failure_for_failed_hooks() { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: feature name\n" + - " Scenario: scenario name\n" + - " Given first step\n"); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - assertThrows(StubException.class, () -> Runtime.builder() - .withFeatureSupplier(new StubFeatureSupplier(feature)) - .withAdditionalPlugins(new PrettyFormatter(out)) - .withBackendSupplier(new StubBackendSupplier( - emptyList(), - emptyList(), - emptyList(), - emptyList(), - emptyList(), - emptyList(), - singletonList(new StubStaticHookDefinition(new StubException("Hook failed") - .withClassName() - .withStacktrace("the stack trace"))))) - .build() - .run()); - - 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 - void should_print_docstring_including_content_type() { - Feature feature = TestFeatureParser.parse("path/test.feature", "" + - "Feature: Test feature\n" + - " Scenario: Test Scenario\n" + - " Given first step\n" + - " \"\"\"json\n" + - " {\"key1\": \"value1\",\n" + - " \"key2\": \"value2\",\n" + - " \"another1\": \"another2\"}\n" + - " \"\"\"\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:7", DocString.class))) - .build() - .run(); - - assertThat(out, bytes(equalCompressingLineSeparators("" + - "\n" + - "Scenario: Test Scenario # path/test.feature:2\n" + - " Given first step # path/step_definitions.java:7\n" + - " \"\"\"json\n" + - " {\"key1\": \"value1\",\n" + - " \"key2\": \"value2\",\n" + - " \"another1\": \"another2\"}\n" + - " \"\"\"\n"))); + " Given first step # io.cucumber.core.plugin.PrettyFormatterStepDefinition.one()\n" + + " When second step # io.cucumber.core.plugin.PrettyFormatterStepDefinition.two()\n" + + " Then third step # io.cucumber.core.plugin.PrettyFormatterStepDefinition.three()\n"))); } }