Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions allure-cucumber6-jvm/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ description = "Allure CucumberJVM 6.0"

val agent: Configuration by configurations.creating

val cucumberVersion = "6.1.1"
val cucumberGherkinVersion = "5.1.0"
val cucumberVersion = "6.9.0"
val cucumberGherkinVersion = "15.0.2"

dependencies {
agent("org.aspectj:aspectjweaver")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@
*/
package io.qameta.allure.cucumber6jvm;

import gherkin.ast.Examples;
import gherkin.ast.Feature;
import gherkin.ast.ScenarioDefinition;
import gherkin.ast.ScenarioOutline;
import gherkin.ast.TableRow;
import io.cucumber.messages.Messages.GherkinDocument.Feature;
import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario;
import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario.Examples;
import io.cucumber.messages.Messages.GherkinDocument.Feature.TableRow;
import io.cucumber.plugin.ConcurrentEventListener;
import io.cucumber.plugin.event.DataTableArgument;
import io.cucumber.plugin.event.EmbedEvent;
Expand Down Expand Up @@ -154,11 +153,12 @@ private void handleTestCaseStarted(final TestCaseStarted event) {
.setLabels(labelBuilder.getScenarioLabels())
.setLinks(labelBuilder.getScenarioLinks());

final ScenarioDefinition scenarioDefinition =
final Scenario scenarioDefinition =
testSources.getScenarioDefinition(currentFeatureFile.get(), currentTestCase.get().getLine());
if (scenarioDefinition instanceof ScenarioOutline) {

if (scenarioDefinition.getExamplesCount() > 0) {
result.setParameters(
getExamplesAsParameters((ScenarioOutline) scenarioDefinition, currentTestCase.get())
getExamplesAsParameters(scenarioDefinition, currentTestCase.get())
);
}

Expand Down Expand Up @@ -300,21 +300,21 @@ private Status translateTestCaseStatus(final Result testCaseResult) {
}

private List<Parameter> getExamplesAsParameters(
final ScenarioOutline scenarioOutline, final TestCase localCurrentTestCase
final Scenario scenario, final TestCase localCurrentTestCase
) {
final Optional<Examples> examplesBlock =
scenarioOutline.getExamples().stream()
.filter(example -> example.getTableBody().stream()
scenario.getExamplesList().stream()
.filter(example -> example.getTableBodyList().stream()
.anyMatch(row -> row.getLocation().getLine() == localCurrentTestCase.getLine())
).findFirst();

if (examplesBlock.isPresent()) {
final TableRow row = examplesBlock.get().getTableBody().stream()
final TableRow row = examplesBlock.get().getTableBodyList().stream()
.filter(example -> example.getLocation().getLine() == localCurrentTestCase.getLine())
.findFirst().get();
return IntStream.range(0, examplesBlock.get().getTableHeader().getCells().size()).mapToObj(index -> {
final String name = examplesBlock.get().getTableHeader().getCells().get(index).getValue();
final String value = row.getCells().get(index).getValue();
return IntStream.range(0, examplesBlock.get().getTableHeader().getCellsList().size()).mapToObj(index -> {
final String name = examplesBlock.get().getTableHeader().getCellsList().get(index).getValue();
final String value = row.getCellsList().get(index).getValue();
return createParameter(name, value);
}).collect(Collectors.toList());
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
*/
package io.qameta.allure.cucumber6jvm;

import io.cucumber.messages.Messages.GherkinDocument.Feature;
import io.cucumber.plugin.event.TestCase;
import gherkin.ast.Feature;
import io.qameta.allure.model.Label;
import io.qameta.allure.model.Link;
import io.qameta.allure.util.ResultsUtils;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package io.qameta.allure.cucumber6jvm;

import gherkin.ast.Feature;
import io.cucumber.messages.Messages.GherkinDocument.Feature;
import io.cucumber.plugin.event.TestCase;
import io.qameta.allure.SeverityLevel;

Expand Down Expand Up @@ -52,7 +52,7 @@ public boolean isKnown() {
private boolean getStatusDetailByTag(final String tagName) {
return scenario.getTags().stream()
.anyMatch(tag -> tag.equalsIgnoreCase(tagName))
|| feature.getTags().stream()
|| feature.getTagsList().stream()
.anyMatch(tag -> tag.getName().equalsIgnoreCase(tagName));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,41 @@
*/
package io.qameta.allure.cucumber6jvm.testsourcemodel;

import gherkin.AstBuilder;
import gherkin.Parser;
import gherkin.ParserException;
import gherkin.TokenMatcher;
import gherkin.ast.Examples;
import gherkin.ast.Feature;
import gherkin.ast.GherkinDocument;
import gherkin.ast.Node;
import gherkin.ast.ScenarioDefinition;
import gherkin.ast.ScenarioOutline;
import gherkin.ast.Step;
import gherkin.ast.TableRow;
import io.cucumber.gherkin.Gherkin;
import io.cucumber.messages.Messages;
import io.cucumber.messages.Messages.GherkinDocument;
import io.cucumber.messages.Messages.GherkinDocument.Feature;
import io.cucumber.messages.Messages.GherkinDocument.Feature.Background;
import io.cucumber.messages.Messages.GherkinDocument.Feature.FeatureChild;
import io.cucumber.messages.Messages.GherkinDocument.Feature.FeatureChild.RuleChild;
import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario;
import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario.Examples;
import io.cucumber.messages.Messages.GherkinDocument.Feature.Step;
import io.cucumber.messages.Messages.GherkinDocument.Feature.TableRow;
import io.cucumber.messages.internal.com.google.protobuf.GeneratedMessageV3;
import io.cucumber.plugin.event.TestSourceRead;

import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static io.cucumber.gherkin.Gherkin.makeSourceEnvelope;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;

public final class TestSourcesModel {
final class TestSourcesModel {
private final Map<URI, TestSourceRead> pathToReadEventMap = new HashMap<>();
private final Map<URI, GherkinDocument> pathToAstMap = new HashMap<>();
private final Map<URI, Map<Integer, AstNode>> pathToNodeMap = new HashMap<>();

public static ScenarioDefinition getScenarioDefinition(final AstNode astNode) {
return astNode.node instanceof ScenarioDefinition ? (ScenarioDefinition) astNode.node
: (ScenarioDefinition) astNode.parent.parent.node;
public static Scenario getScenarioDefinition(final AstNode astNode) {
AstNode candidate = astNode;
while (candidate != null && !(candidate.node instanceof Scenario)) {
candidate = candidate.parent;
}
return candidate == null ? null : (Scenario) candidate.node;
}

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

public AstNode getAstNode(final URI path, final int line) {
if (!pathToNodeMap.containsKey(path)) {
parseGherkinSource(path);
}
if (pathToNodeMap.containsKey(path)) {
return pathToNodeMap.get(path).get(line);
}
return null;
}

private void parseGherkinSource(final URI path) {
if (!pathToReadEventMap.containsKey(path)) {
return;
}
final Parser<GherkinDocument> parser = new Parser<>(new AstBuilder());
final TokenMatcher matcher = new TokenMatcher();
try {
final GherkinDocument gherkinDocument = parser.parse(pathToReadEventMap.get(path).getSource(),
matcher);
pathToAstMap.put(path, gherkinDocument);
final Map<Integer, AstNode> nodeMap = new HashMap<>();
final AstNode currentParent = new AstNode(gherkinDocument.getFeature(), null);
for (ScenarioDefinition child : gherkinDocument.getFeature().getChildren()) {
processScenarioDefinition(nodeMap, child, currentParent);
final String source = pathToReadEventMap.get(path).getSource();

final List<Messages.Envelope> sources = singletonList(
makeSourceEnvelope(source, path.toString()));

final List<Messages.Envelope> envelopes = Gherkin.fromSources(
sources,
true,
true,
true,
() -> String.valueOf(UUID.randomUUID())).collect(toList());

final GherkinDocument gherkinDocument = envelopes.stream()
.filter(Messages.Envelope::hasGherkinDocument)
.map(Messages.Envelope::getGherkinDocument)
.findFirst()
.orElse(null);

pathToAstMap.put(path, gherkinDocument);
final Map<Integer, AstNode> nodeMap = new HashMap<>();
final AstNode currentParent = createAstNode(gherkinDocument.getFeature(), null);
for (FeatureChild child : gherkinDocument.getFeature().getChildrenList()) {
processFeatureDefinition(nodeMap, child, currentParent);
}
pathToNodeMap.put(path, nodeMap);

}

private void processFeatureDefinition(
final Map<Integer, AstNode> nodeMap, final FeatureChild child, final AstNode currentParent) {
if (child.hasBackground()) {
processBackgroundDefinition(nodeMap, child.getBackground(), currentParent);
} else if (child.hasScenario()) {
processScenarioDefinition(nodeMap, child.getScenario(), currentParent);
} else if (child.hasRule()) {
final AstNode childNode = createAstNode(child.getRule(), currentParent);
nodeMap.put(child.getRule().getLocation().getLine(), childNode);
for (RuleChild ruleChild : child.getRule().getChildrenList()) {
processRuleDefinition(nodeMap, ruleChild, childNode);
}
pathToNodeMap.put(path, nodeMap);
} catch (ParserException e) {
throw new IllegalStateException("You are using a plugin that only supports till Gherkin 5.\n"
+ "Please check if the Gherkin provided follows the standard of Gherkin 5\n", e
);
}
}

private void processScenarioDefinition(final Map<Integer, AstNode> nodeMap, final ScenarioDefinition child,
final AstNode currentParent) {
final AstNode childNode = new AstNode(child, currentParent);
private void processBackgroundDefinition(
final Map<Integer, AstNode> nodeMap, final Background background, final AstNode currentParent
) {
final AstNode childNode = createAstNode(background, currentParent);
nodeMap.put(background.getLocation().getLine(), childNode);
for (Step step : background.getStepsList()) {
nodeMap.put(step.getLocation().getLine(), createAstNode(step, childNode));
}
}

private void processScenarioDefinition(
final Map<Integer, AstNode> nodeMap, final Scenario child, final AstNode currentParent) {
final AstNode childNode = createAstNode(child, currentParent);
nodeMap.put(child.getLocation().getLine(), childNode);
for (Step step : child.getSteps()) {
for (Step step : child.getStepsList()) {
nodeMap.put(step.getLocation().getLine(), createAstNode(step, childNode));
}
if (child instanceof ScenarioOutline) {
processScenarioOutlineExamples(nodeMap, (ScenarioOutline) child, childNode);
if (child.getExamplesCount() > 0) {
processScenarioOutlineExamples(nodeMap, child, childNode);
}
}

private void processScenarioOutlineExamples(final Map<Integer, AstNode> nodeMap,
final ScenarioOutline scenarioOutline,
final AstNode childNode) {
for (Examples examples : scenarioOutline.getExamples()) {
final AstNode examplesNode = createAstNode(examples, childNode);
private void processRuleDefinition(
final Map<Integer, AstNode> nodeMap, final RuleChild child, final AstNode currentParent) {
if (child.hasBackground()) {
processBackgroundDefinition(nodeMap, child.getBackground(), currentParent);
} else if (child.hasScenario()) {
processScenarioDefinition(nodeMap, child.getScenario(), currentParent);
}
}

private void processScenarioOutlineExamples(
final Map<Integer, AstNode> nodeMap, final Scenario scenarioOutline, final AstNode parent
) {
for (Examples examples : scenarioOutline.getExamplesList()) {
final AstNode examplesNode = createAstNode(examples, parent);
final TableRow headerRow = examples.getTableHeader();
final AstNode headerNode = createAstNode(headerRow, examplesNode);
nodeMap.put(headerRow.getLocation().getLine(), headerNode);
for (int i = 0; i < examples.getTableBody().size(); ++i) {
final TableRow examplesRow = examples.getTableBody().get(i);
final Node rowNode = createExamplesRowWrapperNode(examplesRow, i);
final AstNode expandedScenarioNode = createAstNode(rowNode, examplesNode);
for (int i = 0; i < examples.getTableBodyCount(); ++i) {
final TableRow examplesRow = examples.getTableBody(i);
final AstNode expandedScenarioNode = createAstNode(examplesRow, examplesNode);
nodeMap.put(examplesRow.getLocation().getLine(), expandedScenarioNode);
}
}
}

private static ExamplesRowWrapperNode createExamplesRowWrapperNode(final Node examplesRow, final int bodyRowIndex) {
return new ExamplesRowWrapperNode(examplesRow, bodyRowIndex);
}

private static AstNode createAstNode(final Node node, final AstNode astNode) {
return new AstNode(node, astNode);
}

static class ExamplesRowWrapperNode extends Node {
private final int bodyRowIndex;

public int getBodyRowIndex() {
return bodyRowIndex;
public AstNode getAstNode(final URI path, final int line) {
if (!pathToNodeMap.containsKey(path)) {
parseGherkinSource(path);
}

ExamplesRowWrapperNode(final Node examplesRow, final int bodyRowIndex) {
super(examplesRow.getLocation());
this.bodyRowIndex = bodyRowIndex;
if (pathToNodeMap.containsKey(path)) {
return pathToNodeMap.get(path).get(line);
}
return null;
}

static class AstNode {
private final Node node;
private final AstNode parent;

public Node getNode() {
return node;
}
private AstNode createAstNode(final GeneratedMessageV3 node, final AstNode astNode) {
return createAstNode(node, astNode);
}

public AstNode getParent() {
return parent;
}
private static class AstNode {
private final GeneratedMessageV3 node;
private final AstNode parent;

AstNode(final Node node, final AstNode parent) {
AstNode(final GeneratedMessageV3 node, final AstNode parent) {
this.node = node;
this.parent = parent;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
*/
package io.qameta.allure.cucumber6jvm.testsourcemodel;

import gherkin.GherkinDialect;
import gherkin.GherkinDialectProvider;
import gherkin.ast.Feature;
import gherkin.ast.ScenarioDefinition;
import io.cucumber.gherkin.GherkinDialect;
import io.cucumber.gherkin.GherkinDialectProvider;
import io.cucumber.messages.Messages.GherkinDocument.Feature;
import io.cucumber.messages.Messages.GherkinDocument.Feature.Scenario;
import io.cucumber.plugin.event.TestSourceRead;

import java.net.URI;
Expand All @@ -43,8 +43,8 @@ public Feature getFeature(final URI path) {
return testSources.getFeature(path);
}

public ScenarioDefinition getScenarioDefinition(final URI path, final int line) {
return testSources.getScenarioDefinition(testSources.getAstNode(path, line));
public Scenario getScenarioDefinition(final URI path, final int line) {
return TestSourcesModel.getScenarioDefinition(testSources.getAstNode(path, line));
}

public String getKeywordFromSource(final URI uri, final int stepLine) {
Expand Down
Loading