Skip to content

Commit f046ca4

Browse files
authored
add support for cucumber jvm 6.9.0 (via #493)
1 parent 067cd89 commit f046ca4

File tree

7 files changed

+149
-122
lines changed

7 files changed

+149
-122
lines changed

allure-cucumber6-jvm/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ description = "Allure CucumberJVM 6.0"
22

33
val agent: Configuration by configurations.creating
44

5-
val cucumberVersion = "6.1.1"
6-
val cucumberGherkinVersion = "5.1.0"
5+
val cucumberVersion = "6.9.0"
6+
val cucumberGherkinVersion = "15.0.2"
77

88
dependencies {
99
agent("org.aspectj:aspectjweaver")

allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/AllureCucumber6Jvm.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,10 @@
1515
*/
1616
package io.qameta.allure.cucumber6jvm;
1717

18-
import gherkin.ast.Examples;
19-
import gherkin.ast.Feature;
20-
import gherkin.ast.ScenarioDefinition;
21-
import gherkin.ast.ScenarioOutline;
22-
import gherkin.ast.TableRow;
18+
import io.cucumber.messages.Messages.GherkinDocument.Feature;
19+
import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario;
20+
import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario.Examples;
21+
import io.cucumber.messages.Messages.GherkinDocument.Feature.TableRow;
2322
import io.cucumber.plugin.ConcurrentEventListener;
2423
import io.cucumber.plugin.event.DataTableArgument;
2524
import io.cucumber.plugin.event.EmbedEvent;
@@ -154,11 +153,12 @@ private void handleTestCaseStarted(final TestCaseStarted event) {
154153
.setLabels(labelBuilder.getScenarioLabels())
155154
.setLinks(labelBuilder.getScenarioLinks());
156155

157-
final ScenarioDefinition scenarioDefinition =
156+
final Scenario scenarioDefinition =
158157
testSources.getScenarioDefinition(currentFeatureFile.get(), currentTestCase.get().getLine());
159-
if (scenarioDefinition instanceof ScenarioOutline) {
158+
159+
if (scenarioDefinition.getExamplesCount() > 0) {
160160
result.setParameters(
161-
getExamplesAsParameters((ScenarioOutline) scenarioDefinition, currentTestCase.get())
161+
getExamplesAsParameters(scenarioDefinition, currentTestCase.get())
162162
);
163163
}
164164

@@ -300,21 +300,21 @@ private Status translateTestCaseStatus(final Result testCaseResult) {
300300
}
301301

302302
private List<Parameter> getExamplesAsParameters(
303-
final ScenarioOutline scenarioOutline, final TestCase localCurrentTestCase
303+
final Scenario scenario, final TestCase localCurrentTestCase
304304
) {
305305
final Optional<Examples> examplesBlock =
306-
scenarioOutline.getExamples().stream()
307-
.filter(example -> example.getTableBody().stream()
306+
scenario.getExamplesList().stream()
307+
.filter(example -> example.getTableBodyList().stream()
308308
.anyMatch(row -> row.getLocation().getLine() == localCurrentTestCase.getLine())
309309
).findFirst();
310310

311311
if (examplesBlock.isPresent()) {
312-
final TableRow row = examplesBlock.get().getTableBody().stream()
312+
final TableRow row = examplesBlock.get().getTableBodyList().stream()
313313
.filter(example -> example.getLocation().getLine() == localCurrentTestCase.getLine())
314314
.findFirst().get();
315-
return IntStream.range(0, examplesBlock.get().getTableHeader().getCells().size()).mapToObj(index -> {
316-
final String name = examplesBlock.get().getTableHeader().getCells().get(index).getValue();
317-
final String value = row.getCells().get(index).getValue();
315+
return IntStream.range(0, examplesBlock.get().getTableHeader().getCellsList().size()).mapToObj(index -> {
316+
final String name = examplesBlock.get().getTableHeader().getCellsList().get(index).getValue();
317+
final String value = row.getCellsList().get(index).getValue();
318318
return createParameter(name, value);
319319
}).collect(Collectors.toList());
320320
} else {

allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/LabelBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
*/
1616
package io.qameta.allure.cucumber6jvm;
1717

18+
import io.cucumber.messages.Messages.GherkinDocument.Feature;
1819
import io.cucumber.plugin.event.TestCase;
19-
import gherkin.ast.Feature;
2020
import io.qameta.allure.model.Label;
2121
import io.qameta.allure.model.Link;
2222
import io.qameta.allure.util.ResultsUtils;

allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/TagParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616
package io.qameta.allure.cucumber6jvm;
1717

18-
import gherkin.ast.Feature;
18+
import io.cucumber.messages.Messages.GherkinDocument.Feature;
1919
import io.cucumber.plugin.event.TestCase;
2020
import io.qameta.allure.SeverityLevel;
2121

@@ -52,7 +52,7 @@ public boolean isKnown() {
5252
private boolean getStatusDetailByTag(final String tagName) {
5353
return scenario.getTags().stream()
5454
.anyMatch(tag -> tag.equalsIgnoreCase(tagName))
55-
|| feature.getTags().stream()
55+
|| feature.getTagsList().stream()
5656
.anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName));
5757
}
5858

allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModel.java

Lines changed: 111 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -15,31 +15,41 @@
1515
*/
1616
package io.qameta.allure.cucumber6jvm.testsourcemodel;
1717

18-
import gherkin.AstBuilder;
19-
import gherkin.Parser;
20-
import gherkin.ParserException;
21-
import gherkin.TokenMatcher;
22-
import gherkin.ast.Examples;
23-
import gherkin.ast.Feature;
24-
import gherkin.ast.GherkinDocument;
25-
import gherkin.ast.Node;
26-
import gherkin.ast.ScenarioDefinition;
27-
import gherkin.ast.ScenarioOutline;
28-
import gherkin.ast.Step;
29-
import gherkin.ast.TableRow;
18+
import io.cucumber.gherkin.Gherkin;
19+
import io.cucumber.messages.Messages;
20+
import io.cucumber.messages.Messages.GherkinDocument;
21+
import io.cucumber.messages.Messages.GherkinDocument.Feature;
22+
import io.cucumber.messages.Messages.GherkinDocument.Feature.Background;
23+
import io.cucumber.messages.Messages.GherkinDocument.Feature.FeatureChild;
24+
import io.cucumber.messages.Messages.GherkinDocument.Feature.FeatureChild.RuleChild;
25+
import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario;
26+
import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario.Examples;
27+
import io.cucumber.messages.Messages.GherkinDocument.Feature.Step;
28+
import io.cucumber.messages.Messages.GherkinDocument.Feature.TableRow;
29+
import io.cucumber.messages.internal.com.google.protobuf.GeneratedMessageV3;
3030
import io.cucumber.plugin.event.TestSourceRead;
31+
3132
import java.net.URI;
3233
import java.util.HashMap;
34+
import java.util.List;
3335
import java.util.Map;
36+
import java.util.UUID;
37+
38+
import static io.cucumber.gherkin.Gherkin.makeSourceEnvelope;
39+
import static java.util.Collections.singletonList;
40+
import static java.util.stream.Collectors.toList;
3441

35-
public final class TestSourcesModel {
42+
final class TestSourcesModel {
3643
private final Map<URI, TestSourceRead> pathToReadEventMap = new HashMap<>();
3744
private final Map<URI, GherkinDocument> pathToAstMap = new HashMap<>();
3845
private final Map<URI, Map<Integer, AstNode>> pathToNodeMap = new HashMap<>();
3946

40-
public static ScenarioDefinition getScenarioDefinition(final AstNode astNode) {
41-
return astNode.node instanceof ScenarioDefinition ? (ScenarioDefinition) astNode.node
42-
: (ScenarioDefinition) astNode.parent.parent.node;
47+
public static Scenario getScenarioDefinition(final AstNode astNode) {
48+
AstNode candidate = astNode;
49+
while (candidate != null && !(candidate.node instanceof Scenario)) {
50+
candidate = candidate.parent;
51+
}
52+
return candidate == null ? null : (Scenario) candidate.node;
4353
}
4454

4555
public void addTestSourceReadEvent(final URI path, final TestSourceRead event) {
@@ -56,102 +66,119 @@ public Feature getFeature(final URI path) {
5666
return null;
5767
}
5868

59-
public AstNode getAstNode(final URI path, final int line) {
60-
if (!pathToNodeMap.containsKey(path)) {
61-
parseGherkinSource(path);
62-
}
63-
if (pathToNodeMap.containsKey(path)) {
64-
return pathToNodeMap.get(path).get(line);
65-
}
66-
return null;
67-
}
68-
6969
private void parseGherkinSource(final URI path) {
7070
if (!pathToReadEventMap.containsKey(path)) {
7171
return;
7272
}
73-
final Parser<GherkinDocument> parser = new Parser<>(new AstBuilder());
74-
final TokenMatcher matcher = new TokenMatcher();
75-
try {
76-
final GherkinDocument gherkinDocument = parser.parse(pathToReadEventMap.get(path).getSource(),
77-
matcher);
78-
pathToAstMap.put(path, gherkinDocument);
79-
final Map<Integer, AstNode> nodeMap = new HashMap<>();
80-
final AstNode currentParent = new AstNode(gherkinDocument.getFeature(), null);
81-
for (ScenarioDefinition child : gherkinDocument.getFeature().getChildren()) {
82-
processScenarioDefinition(nodeMap, child, currentParent);
73+
final String source = pathToReadEventMap.get(path).getSource();
74+
75+
final List<Messages.Envelope> sources = singletonList(
76+
makeSourceEnvelope(source, path.toString()));
77+
78+
final List<Messages.Envelope> envelopes = Gherkin.fromSources(
79+
sources,
80+
true,
81+
true,
82+
true,
83+
() -> String.valueOf(UUID.randomUUID())).collect(toList());
84+
85+
final GherkinDocument gherkinDocument = envelopes.stream()
86+
.filter(Messages.Envelope::hasGherkinDocument)
87+
.map(Messages.Envelope::getGherkinDocument)
88+
.findFirst()
89+
.orElse(null);
90+
91+
pathToAstMap.put(path, gherkinDocument);
92+
final Map<Integer, AstNode> nodeMap = new HashMap<>();
93+
final AstNode currentParent = createAstNode(gherkinDocument.getFeature(), null);
94+
for (FeatureChild child : gherkinDocument.getFeature().getChildrenList()) {
95+
processFeatureDefinition(nodeMap, child, currentParent);
96+
}
97+
pathToNodeMap.put(path, nodeMap);
98+
99+
}
100+
101+
private void processFeatureDefinition(
102+
final Map<Integer, AstNode> nodeMap, final FeatureChild child, final AstNode currentParent) {
103+
if (child.hasBackground()) {
104+
processBackgroundDefinition(nodeMap, child.getBackground(), currentParent);
105+
} else if (child.hasScenario()) {
106+
processScenarioDefinition(nodeMap, child.getScenario(), currentParent);
107+
} else if (child.hasRule()) {
108+
final AstNode childNode = createAstNode(child.getRule(), currentParent);
109+
nodeMap.put(child.getRule().getLocation().getLine(), childNode);
110+
for (RuleChild ruleChild : child.getRule().getChildrenList()) {
111+
processRuleDefinition(nodeMap, ruleChild, childNode);
83112
}
84-
pathToNodeMap.put(path, nodeMap);
85-
} catch (ParserException e) {
86-
throw new IllegalStateException("You are using a plugin that only supports till Gherkin 5.\n"
87-
+ "Please check if the Gherkin provided follows the standard of Gherkin 5\n", e
88-
);
89113
}
90114
}
91115

92-
private void processScenarioDefinition(final Map<Integer, AstNode> nodeMap, final ScenarioDefinition child,
93-
final AstNode currentParent) {
94-
final AstNode childNode = new AstNode(child, currentParent);
116+
private void processBackgroundDefinition(
117+
final Map<Integer, AstNode> nodeMap, final Background background, final AstNode currentParent
118+
) {
119+
final AstNode childNode = createAstNode(background, currentParent);
120+
nodeMap.put(background.getLocation().getLine(), childNode);
121+
for (Step step : background.getStepsList()) {
122+
nodeMap.put(step.getLocation().getLine(), createAstNode(step, childNode));
123+
}
124+
}
125+
126+
private void processScenarioDefinition(
127+
final Map<Integer, AstNode> nodeMap, final Scenario child, final AstNode currentParent) {
128+
final AstNode childNode = createAstNode(child, currentParent);
95129
nodeMap.put(child.getLocation().getLine(), childNode);
96-
for (Step step : child.getSteps()) {
130+
for (Step step : child.getStepsList()) {
97131
nodeMap.put(step.getLocation().getLine(), createAstNode(step, childNode));
98132
}
99-
if (child instanceof ScenarioOutline) {
100-
processScenarioOutlineExamples(nodeMap, (ScenarioOutline) child, childNode);
133+
if (child.getExamplesCount() > 0) {
134+
processScenarioOutlineExamples(nodeMap, child, childNode);
101135
}
102136
}
103137

104-
private void processScenarioOutlineExamples(final Map<Integer, AstNode> nodeMap,
105-
final ScenarioOutline scenarioOutline,
106-
final AstNode childNode) {
107-
for (Examples examples : scenarioOutline.getExamples()) {
108-
final AstNode examplesNode = createAstNode(examples, childNode);
138+
private void processRuleDefinition(
139+
final Map<Integer, AstNode> nodeMap, final RuleChild child, final AstNode currentParent) {
140+
if (child.hasBackground()) {
141+
processBackgroundDefinition(nodeMap, child.getBackground(), currentParent);
142+
} else if (child.hasScenario()) {
143+
processScenarioDefinition(nodeMap, child.getScenario(), currentParent);
144+
}
145+
}
146+
147+
private void processScenarioOutlineExamples(
148+
final Map<Integer, AstNode> nodeMap, final Scenario scenarioOutline, final AstNode parent
149+
) {
150+
for (Examples examples : scenarioOutline.getExamplesList()) {
151+
final AstNode examplesNode = createAstNode(examples, parent);
109152
final TableRow headerRow = examples.getTableHeader();
110153
final AstNode headerNode = createAstNode(headerRow, examplesNode);
111154
nodeMap.put(headerRow.getLocation().getLine(), headerNode);
112-
for (int i = 0; i < examples.getTableBody().size(); ++i) {
113-
final TableRow examplesRow = examples.getTableBody().get(i);
114-
final Node rowNode = createExamplesRowWrapperNode(examplesRow, i);
115-
final AstNode expandedScenarioNode = createAstNode(rowNode, examplesNode);
155+
for (int i = 0; i < examples.getTableBodyCount(); ++i) {
156+
final TableRow examplesRow = examples.getTableBody(i);
157+
final AstNode expandedScenarioNode = createAstNode(examplesRow, examplesNode);
116158
nodeMap.put(examplesRow.getLocation().getLine(), expandedScenarioNode);
117159
}
118160
}
119161
}
120162

121-
private static ExamplesRowWrapperNode createExamplesRowWrapperNode(final Node examplesRow, final int bodyRowIndex) {
122-
return new ExamplesRowWrapperNode(examplesRow, bodyRowIndex);
123-
}
124-
125-
private static AstNode createAstNode(final Node node, final AstNode astNode) {
126-
return new AstNode(node, astNode);
127-
}
128-
129-
static class ExamplesRowWrapperNode extends Node {
130-
private final int bodyRowIndex;
131-
132-
public int getBodyRowIndex() {
133-
return bodyRowIndex;
163+
public AstNode getAstNode(final URI path, final int line) {
164+
if (!pathToNodeMap.containsKey(path)) {
165+
parseGherkinSource(path);
134166
}
135-
136-
ExamplesRowWrapperNode(final Node examplesRow, final int bodyRowIndex) {
137-
super(examplesRow.getLocation());
138-
this.bodyRowIndex = bodyRowIndex;
167+
if (pathToNodeMap.containsKey(path)) {
168+
return pathToNodeMap.get(path).get(line);
139169
}
170+
return null;
140171
}
141172

142-
static class AstNode {
143-
private final Node node;
144-
private final AstNode parent;
145-
146-
public Node getNode() {
147-
return node;
148-
}
173+
private AstNode createAstNode(final GeneratedMessageV3 node, final AstNode astNode) {
174+
return createAstNode(node, astNode);
175+
}
149176

150-
public AstNode getParent() {
151-
return parent;
152-
}
177+
private static class AstNode {
178+
private final GeneratedMessageV3 node;
179+
private final AstNode parent;
153180

154-
AstNode(final Node node, final AstNode parent) {
181+
AstNode(final GeneratedMessageV3 node, final AstNode parent) {
155182
this.node = node;
156183
this.parent = parent;
157184
}

allure-cucumber6-jvm/src/main/java/io/qameta/allure/cucumber6jvm/testsourcemodel/TestSourcesModelProxy.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@
1515
*/
1616
package io.qameta.allure.cucumber6jvm.testsourcemodel;
1717

18-
import gherkin.GherkinDialect;
19-
import gherkin.GherkinDialectProvider;
20-
import gherkin.ast.Feature;
21-
import gherkin.ast.ScenarioDefinition;
18+
import io.cucumber.gherkin.GherkinDialect;
19+
import io.cucumber.gherkin.GherkinDialectProvider;
20+
import io.cucumber.messages.Messages.GherkinDocument.Feature;
21+
import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario;
2222
import io.cucumber.plugin.event.TestSourceRead;
2323

2424
import java.net.URI;
@@ -43,8 +43,8 @@ public Feature getFeature(final URI path) {
4343
return testSources.getFeature(path);
4444
}
4545

46-
public ScenarioDefinition getScenarioDefinition(final URI path, final int line) {
47-
return testSources.getScenarioDefinition(testSources.getAstNode(path, line));
46+
public Scenario getScenarioDefinition(final URI path, final int line) {
47+
return TestSourcesModel.getScenarioDefinition(testSources.getAstNode(path, line));
4848
}
4949

5050
public String getKeywordFromSource(final URI uri, final int stepLine) {

0 commit comments

Comments
 (0)