Skip to content

Commit 9ce2419

Browse files
committed
Add Gherkin
1 parent dfbfc76 commit 9ce2419

32 files changed

+424
-503
lines changed

README.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
# messages-cli
22
Commandline interface to work with Cucumber Messages
33

4-
54
## TODO:
6-
7-
- Support TestNG XML
8-
- Support Gherkin
95
- Publish as a CLI tool
106
- Write README.

java/minimal.feature.xml

Lines changed: 0 additions & 8 deletions
This file was deleted.

java/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@
6666
<artifactId>testng-xml-formatter</artifactId>
6767
<version>0.2.0</version>
6868
</dependency>
69+
<dependency>
70+
<groupId>io.cucumber</groupId>
71+
<artifactId>gherkin</artifactId>
72+
<version>28.0.0</version>
73+
</dependency>
6974
<dependency>
7075
<groupId>com.fasterxml.jackson.core</groupId>
7176
<artifactId>jackson-databind</artifactId>

java/src/main/java/io/cucumber/messages/cli/CommonOptions.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import java.io.OutputStream;
99
import java.nio.file.Files;
1010
import java.nio.file.Path;
11-
import java.util.function.BiFunction;
11+
import java.util.function.Function;
1212

1313
import static java.nio.file.Files.newOutputStream;
1414
import static java.nio.file.StandardOpenOption.CREATE;
@@ -19,10 +19,10 @@ final class CommonOptions {
1919
private final CommandSpec spec;
2020
private final Path source;
2121
private final Path output;
22-
private final BiFunction<String, Integer, String> fileNameGenerator;
22+
private final Function<String, String> fileNameGenerator;
2323

2424

25-
CommonOptions(CommandSpec spec, Path source, Path output, BiFunction<String, Integer, String> fileNameGenerator) {
25+
CommonOptions(CommandSpec spec, Path source, Path output, Function<String, String> fileNameGenerator) {
2626
this.spec = requireNonNull(spec);
2727
this.source = requireNonNull(source);
2828
this.output = output;
@@ -41,7 +41,7 @@ final class CommonOptions {
4141

4242
}
4343

44-
private boolean isSourceSystemIn() {
44+
boolean isSourceSystemIn() {
4545
var fileName = source.getFileName();
4646
return fileName != null && fileName.toString().equals("-");
4747
}
@@ -78,12 +78,12 @@ private Path outputPath() {
7878
if (index >= 0) {
7979
fileName = fileName.substring(0, index);
8080
}
81-
var candidate = output.resolve(fileName + ".xml");
81+
var candidate = output.resolve(fileNameGenerator.apply(fileName));
8282

8383
// Avoid overwriting existing files when we decided the file name.
8484
var counter = 1;
8585
while (Files.exists(candidate)) {
86-
candidate = output.resolve(fileNameGenerator.apply(fileName, counter++));
86+
candidate = output.resolve(fileNameGenerator.apply(fileName + "." + counter++));
8787
}
8888
return candidate;
8989
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package io.cucumber.messages.cli;
2+
3+
import java.util.function.BiFunction;
4+
5+
public class FileNameGenerator implements BiFunction<String, Integer, String> {
6+
@Override
7+
public String apply(String s, Integer integer) {
8+
return "";
9+
}
10+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package io.cucumber.messages.cli;
2+
3+
import io.cucumber.gherkin.GherkinParser;
4+
import io.cucumber.messages.MessageToNdjsonWriter;
5+
import io.cucumber.messages.types.Envelope;
6+
import picocli.CommandLine.Command;
7+
import picocli.CommandLine.Model.CommandSpec;
8+
import picocli.CommandLine.Option;
9+
import picocli.CommandLine.Parameters;
10+
import picocli.CommandLine.Spec;
11+
12+
import java.io.IOException;
13+
import java.nio.file.Path;
14+
import java.util.concurrent.Callable;
15+
16+
@Command(
17+
name = "gherkin",
18+
description = "Converts a Gherkin Document to Cucumber Messages",
19+
mixinStandardHelpOptions = true
20+
)
21+
class GherkinCommand implements Callable<Integer> {
22+
23+
@Spec
24+
private CommandSpec spec;
25+
26+
@Parameters(
27+
index = "0",
28+
paramLabel = "file",
29+
description = "The input file containing a Gherkin document. " +
30+
"Use - to read from the standard input."
31+
)
32+
private Path source;
33+
34+
@Option(
35+
names = {"-o", "--output"},
36+
arity = "0..1",
37+
paramLabel = "file",
38+
description = "The output file containing Cucumber Messages. " +
39+
"If file is a directory, a new file be " +
40+
"created by taking the name of the input file and " +
41+
"replacing the suffix with '.ndjson'. If the file is omitted " +
42+
"the current working directory is used."
43+
)
44+
private Path output;
45+
46+
@Option(
47+
names = {"-s", "--include-source"},
48+
description = "Includes the Source message in the output"
49+
)
50+
private boolean includeSource;
51+
52+
@Option(
53+
names = {"-d", "--no-include-gherkin-document"},
54+
description = "Excludes the GherkinDocument message from the output"
55+
)
56+
private boolean excludeGherkinDocument;
57+
@Option(
58+
names = {"-p", "--include-pickles"},
59+
description = "Includes the Pickle messages in the output"
60+
)
61+
private boolean includePickles;
62+
63+
private static String ndjson(String fileName) {
64+
return fileName + ".ndjson";
65+
}
66+
67+
@Override
68+
public Integer call() throws IOException {
69+
var options = new CommonOptions(spec, source, output, GherkinCommand::ndjson);
70+
71+
var parser = GherkinParser.builder()
72+
.includeSource(includeSource)
73+
.includeGherkinDocument(!excludeGherkinDocument)
74+
.includePickles(includePickles)
75+
.build();
76+
77+
var uri = options.isSourceSystemIn() ? "file:///dev/fd/0" : source.toUri().toString();
78+
try (var writer = new MessageToNdjsonWriter(options.outputPrintWriter(), Jackson.serializer())) {
79+
parser.parse(uri, options.sourceInputStream())
80+
.forEach(envelope -> write(writer, envelope));
81+
}
82+
return 0;
83+
}
84+
85+
private static void write(MessageToNdjsonWriter writer, Envelope envelope) {
86+
try {
87+
writer.write(envelope);
88+
} catch (IOException e) {
89+
throw new RuntimeException(e);
90+
}
91+
}
92+
}
Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,38 @@
11
package io.cucumber.messages.cli;
22

3-
import com.fasterxml.jackson.annotation.JsonCreator;
3+
import com.fasterxml.jackson.annotation.JsonCreator.Mode;
4+
import com.fasterxml.jackson.annotation.JsonInclude.Include;
5+
import com.fasterxml.jackson.core.JsonGenerator;
46
import com.fasterxml.jackson.databind.DeserializationFeature;
7+
import com.fasterxml.jackson.databind.SerializationFeature;
58
import com.fasterxml.jackson.databind.cfg.ConstructorDetector;
69
import com.fasterxml.jackson.databind.json.JsonMapper;
710
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
811
import com.fasterxml.jackson.module.paramnames.ParameterNamesModule;
12+
import io.cucumber.messages.MessageToNdjsonWriter;
913
import io.cucumber.messages.NdjsonToMessageIterable;
1014
import io.cucumber.messages.types.Envelope;
1115

16+
1217
class Jackson {
18+
19+
public static final JsonMapper OBJECT_MAPPER = JsonMapper.builder()
20+
.addModule(new Jdk8Module())
21+
.addModule(new ParameterNamesModule(Mode.PROPERTIES))
22+
.serializationInclusion(Include.NON_ABSENT)
23+
.constructorDetector(ConstructorDetector.USE_PROPERTIES_BASED)
24+
.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)
25+
.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
26+
.enable(DeserializationFeature.USE_LONG_FOR_INTS)
27+
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
28+
.disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET)
29+
.build();
30+
1331
static NdjsonToMessageIterable.Deserializer deserializer() {
14-
var jsonMapper = JsonMapper.builder()
15-
.addModule(new Jdk8Module())
16-
.addModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES))
17-
.constructorDetector(ConstructorDetector.USE_PROPERTIES_BASED)
18-
.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING)
19-
.enable(DeserializationFeature.USE_LONG_FOR_INTS)
20-
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
21-
.build();
22-
return json -> jsonMapper.readValue(json, Envelope.class);
32+
return json -> OBJECT_MAPPER.readValue(json, Envelope.class);
33+
}
34+
35+
public static MessageToNdjsonWriter.Serializer serializer() {
36+
return OBJECT_MAPPER::writeValue;
2337
}
2438
}

java/src/main/java/io/cucumber/messages/cli/JunitXmlCommand.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ class JunitXmlCommand implements Callable<Integer> {
5151
)
5252
private ExampleName exampleNameStrategy;
5353

54-
private static String xml(String fileName, int counter) {
55-
return fileName + "." + counter + ".xml";
54+
private static String xml(String fileName) {
55+
return fileName + ".xml";
5656
}
5757

5858
@Override

java/src/main/java/io/cucumber/messages/cli/MessagesCli.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
description = "Work with Cucumber messages",
1010
versionProvider = ManifestVersionProvider.class,
1111
subcommands = {
12+
GherkinCommand.class,
1213
JunitXmlCommand.class,
1314
TestngXmlCommand.class
1415
}

java/src/main/java/io/cucumber/messages/cli/TestngXmlCommand.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package io.cucumber.messages.cli;
22

3-
import io.cucumber.junitxmlformatter.MessagesToJunitXmlWriter;
43
import io.cucumber.messages.NdjsonToMessageIterable;
54
import io.cucumber.query.NamingStrategy.ExampleName;
65
import io.cucumber.testngxmlformatter.MessagesToTestngXmlWriter;
@@ -16,7 +15,7 @@
1615

1716
@Command(
1817
name = "testng-xml",
19-
description = "Converts Cucumber messages to JUnit XML",
18+
description = "Converts Cucumber messages to TestNG XML",
2019
mixinStandardHelpOptions = true
2120
)
2221
class TestngXmlCommand implements Callable<Integer> {
@@ -52,8 +51,8 @@ class TestngXmlCommand implements Callable<Integer> {
5251
)
5352
private ExampleName exampleNameStrategy;
5453

55-
private static String xml(String fileName, int counter) {
56-
return fileName + "." + counter + ".xml";
54+
private static String xml(String fileName) {
55+
return fileName + ".xml";
5756
}
5857

5958
@Override

0 commit comments

Comments
 (0)