diff --git a/core/src/main/java/cucumber/runtime/formatter/PrettyFormatter.java b/core/src/main/java/cucumber/runtime/formatter/PrettyFormatter.java index 504b81dbb5..33d6ed196d 100644 --- a/core/src/main/java/cucumber/runtime/formatter/PrettyFormatter.java +++ b/core/src/main/java/cucumber/runtime/formatter/PrettyFormatter.java @@ -208,22 +208,28 @@ private void printStep(TestStep testStep, Result result) { } String formatStepText(String keyword, String stepText, Format textFormat, Format argFormat, List arguments) { - int textStart = 0; + int beginIndex = 0; StringBuilder result = new StringBuilder(textFormat.text(keyword)); for (Argument argument : arguments) { // can be null if the argument is missing. if (argument.getOffset() != null) { - String text = stepText.substring(textStart, argument.getOffset()); + int argumentOffset = argument.getOffset(); + // 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.getVal() != null) { result.append(argFormat.text(argument.getVal())); - textStart = argument.getOffset() + argument.getVal().length(); + // set beginIndex to end of argument + beginIndex = argument.getOffset() + argument.getVal().length(); } } - if (textStart != stepText.length()) { - String text = stepText.substring(textStart, stepText.length()); + if (beginIndex != stepText.length()) { + String text = stepText.substring(beginIndex, stepText.length()); result.append(textFormat.text(text)); } return result.toString(); diff --git a/core/src/test/java/cucumber/runtime/JdkPatternArgumentMatcherTest.java b/core/src/test/java/cucumber/runtime/JdkPatternArgumentMatcherTest.java index a8654ecdbe..b67905fa76 100644 --- a/core/src/test/java/cucumber/runtime/JdkPatternArgumentMatcherTest.java +++ b/core/src/test/java/cucumber/runtime/JdkPatternArgumentMatcherTest.java @@ -59,6 +59,13 @@ public void canHandleVariableNumberOfArguments() { assertNull(matcher.argumentsFrom("I wait for some time").get(0).getVal()); } + @Test + public void canHandleNestedCaptureGroups() throws UnsupportedEncodingException { + assertVariables("the order is placed( and( not yet)? confirmed)?", "the order is placed and not yet confirmed", + " and not yet confirmed", 19, + " not yet", 23); + } + private void assertVariables(String regex, String string, String v1, Integer pos1, String v2, Integer pos2) throws UnsupportedEncodingException { List args = new JdkPatternArgumentMatcher(Pattern.compile(regex)).argumentsFrom(string); assertEquals(2, args.size()); diff --git a/core/src/test/java/cucumber/runtime/formatter/PrettyFormatterTest.java b/core/src/test/java/cucumber/runtime/formatter/PrettyFormatterTest.java index 7512fd049b..b2e4a85442 100755 --- a/core/src/test/java/cucumber/runtime/formatter/PrettyFormatterTest.java +++ b/core/src/test/java/cucumber/runtime/formatter/PrettyFormatterTest.java @@ -21,6 +21,7 @@ import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; public class PrettyFormatterTest { @@ -50,7 +51,7 @@ public void should_align_the_indentation_of_location_strings() throws Throwable } @Test - public void should_handle_backgound() throws Throwable { + public void should_handle_background() throws Throwable { CucumberFeature feature = feature("path/test.feature", "" + "Feature: feature name\n" + " Background: background name\n" + @@ -366,7 +367,7 @@ public void should_color_code_error_message_according_to_the_result() throws Thr } @Test - public void should_mark_arguments_in_steps() throws Throwable { + public void should_mark_subsequent_arguments_in_steps() throws Throwable { Formats formats = new AnsiFormats(); Argument arg1 = new Argument(5, "arg1"); Argument arg2 = new Argument(15, "arg2"); @@ -381,6 +382,35 @@ public void should_mark_arguments_in_steps() throws Throwable { AnsiEscapes.GREEN + AnsiEscapes.INTENSITY_BOLD + "arg2" + AnsiEscapes.RESET)); } + @Test + public void should_mark_nested_argument_as_part_of_full_argument(){ + Formats formats = new AnsiFormats(); + Argument enclosingArg = new Argument(19, " and not yet confirmed"); + Argument nestedArg = new Argument(23, " not yet "); + PrettyFormatter prettyFormatter = new PrettyFormatter(null); + + String formattedText = prettyFormatter.formatStepText("Given ", "the order is placed and not yet confirmed", formats.get("passed"), formats.get("passed_arg"), asList(enclosingArg, nestedArg)); + + 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)); + } + + @Test + public void should_mark_nested_arguments_as_part_of_enclosing_argument(){ + Formats formats = new AnsiFormats(); + Argument enclosingArg = new Argument(19, " and not yet confirmed"); + Argument nestedArg = new Argument(23, " not yet "); + Argument nestedNestedArg = new Argument(27, "yet "); + PrettyFormatter prettyFormatter = new PrettyFormatter(null); + + String formattedText = prettyFormatter.formatStepText("Given ", "the order is placed and not yet confirmed", formats.get("passed"), formats.get("passed_arg"), asList(enclosingArg, nestedArg, nestedNestedArg)); + + 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)); + } + private String runFeatureWithPrettyFormatter(final CucumberFeature feature, final Map stepsToLocation) throws Throwable { return runFeatureWithPrettyFormatter(feature, stepsToLocation, Collections.emptyMap()); }