From 6eba0bbcc6b856661343087db768f38d6221cd65 Mon Sep 17 00:00:00 2001 From: bbischoff Date: Sat, 15 Aug 2020 16:05:09 +0200 Subject: [PATCH] fixed tag handling --- CHANGELOG.md | 7 +++ example-project/pom.xml | 2 +- .../features/testfeature/MyTest10.feature | 25 ++++++++ plugin-code/pom.xml | 2 +- .../gherkin/GherkinDocumentParser.java | 29 ++++++--- .../gherkin/GherkinToCucableConverter.java | 41 +++++-------- .../java/com/trivago/vo/SingleScenario.java | 24 +++++++- .../src/main/java/com/trivago/vo/Step.java | 9 +++ .../gherkin/GherkinDocumentParserTest.java | 59 +++++++++++++++++++ 9 files changed, 159 insertions(+), 39 deletions(-) create mode 100644 example-project/src/test/resources/features/testfeature/MyTest10.feature diff --git a/CHANGELOG.md b/CHANGELOG.md index 542025e..7123c47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. Back to [Readme](README.md). +## [1.8.0] - 2020-08-15 + +### Fixed + +* Correct handling of feature tags in combination with multiple tagged example tables (#168) + ## [1.7.2] - 2020-04-03 ### Fixed @@ -304,6 +310,7 @@ Back to [Readme](README.md). Initial project version on GitHub and Maven Central. +[1.8.0]: https://github.com/trivago/cucable-plugin/compare/1.7.2...1.8.0 [1.7.2]: https://github.com/trivago/cucable-plugin/compare/1.7.1...1.7.2 [1.7.1]: https://github.com/trivago/cucable-plugin/compare/1.7.0...1.7.1 [1.7.0]: https://github.com/trivago/cucable-plugin/compare/1.6.0...1.7.0 diff --git a/example-project/pom.xml b/example-project/pom.xml index f157d70..ca793be 100644 --- a/example-project/pom.xml +++ b/example-project/pom.xml @@ -7,7 +7,7 @@ com.trivago.rta cucable-test-project - 1.7.2 + 1.8.0 jar diff --git a/example-project/src/test/resources/features/testfeature/MyTest10.feature b/example-project/src/test/resources/features/testfeature/MyTest10.feature new file mode 100644 index 0000000..4903330 --- /dev/null +++ b/example-project/src/test/resources/features/testfeature/MyTest10.feature @@ -0,0 +1,25 @@ +@us1 +Feature: My feature with tags and tagged examples + + Scenario Outline: Tag test + Given this is a given step + When I do something + Then I am on a page with text '' + + @env + Examples: + | text | + | one | + | two | + + @env2 + Examples: + | text | + | three | + | four | + + @env3: + Examples: + | text | + | five | + | six | diff --git a/plugin-code/pom.xml b/plugin-code/pom.xml index b5c4738..f0f71fa 100644 --- a/plugin-code/pom.xml +++ b/plugin-code/pom.xml @@ -6,7 +6,7 @@ com.trivago.rta cucable-plugin - 1.7.2 + 1.8.0 https://github.com/trivago/cucable-plugin Cucable Maven Plugin diff --git a/plugin-code/src/main/java/com/trivago/gherkin/GherkinDocumentParser.java b/plugin-code/src/main/java/com/trivago/gherkin/GherkinDocumentParser.java index 60fcedc..92c0f59 100644 --- a/plugin-code/src/main/java/com/trivago/gherkin/GherkinDocumentParser.java +++ b/plugin-code/src/main/java/com/trivago/gherkin/GherkinDocumentParser.java @@ -25,7 +25,13 @@ import gherkin.AstBuilder; import gherkin.Parser; import gherkin.ParserException; -import gherkin.ast.*; +import gherkin.ast.Background; +import gherkin.ast.Examples; +import gherkin.ast.Feature; +import gherkin.ast.GherkinDocument; +import gherkin.ast.Scenario; +import gherkin.ast.ScenarioDefinition; +import gherkin.ast.ScenarioOutline; import io.cucumber.tagexpressions.Expression; import io.cucumber.tagexpressions.TagExpressionException; import io.cucumber.tagexpressions.TagExpressionParser; @@ -38,6 +44,7 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; @Singleton public class GherkinDocumentParser { @@ -143,6 +150,7 @@ public List getSingleScenariosFromFeature( featureTags, backgroundSteps ); + for (SingleScenario singleScenario : outlineScenarios) { if (scenarioShouldBeIncluded(singleScenario)) { singleScenarioFeatures.add(singleScenario); @@ -151,6 +159,7 @@ public List getSingleScenariosFromFeature( } } } + return singleScenarioFeatures; } @@ -186,7 +195,7 @@ private List getSingleScenariosFromOutline( List steps = gherkinToCucableConverter.convertGherkinStepsToCucableSteps(scenarioOutline.getSteps()); if (scenarioOutline.getExamples().isEmpty()) { - cucableLogger.warn("Scenario outline without example table!"); + cucableLogger.warn("Scenario outline '" + scenarioOutline.getName() + "' without example table!"); return outlineScenarios; } @@ -214,6 +223,7 @@ private List getSingleScenariosFromOutline( List substitutedSteps = substituteStepExamplePlaceholders(steps, exampleMap, rowIndex); singleScenario.setSteps(substitutedSteps); singleScenario.setScenarioTags(scenarioTags); + singleScenario.setExampleTags( gherkinToCucableConverter.convertGherkinTagsToCucableTags(exampleTable.getTags()) ); @@ -327,30 +337,31 @@ private GherkinDocument getGherkinDocumentFromFeatureFileContent(final String fe * scenarioNames settings. * * @param singleScenario a single scenario object. - * @return true if an include tag and no exclude tags are included in the source tag list and scenario name - * (if specified) matches. + * @return true if the combined tags match the given tag expression and the scenario name (if specified) matches. */ - private boolean scenarioShouldBeIncluded(SingleScenario singleScenario) throws CucablePluginException { + private boolean scenarioShouldBeIncluded(final SingleScenario singleScenario) throws CucablePluginException { String includeScenarioTags = propertyManager.getIncludeScenarioTags(); String language = singleScenario.getFeatureLanguage(); String scenarioName = singleScenario.getScenarioName(); boolean scenarioNameMatchExists = matchScenarioWithScenarioNames(language, scenarioName) >= 0; - List combinedScenarioTags = singleScenario.getScenarioTags(); + List combinedScenarioTags = new ArrayList<>(singleScenario.getScenarioTags()); combinedScenarioTags.addAll(singleScenario.getFeatureTags()); + combinedScenarioTags.addAll(singleScenario.getExampleTags()); + combinedScenarioTags = combinedScenarioTags.stream().distinct().collect(Collectors.toList()); if (includeScenarioTags == null || includeScenarioTags.isEmpty()) { return scenarioNameMatchExists; } - Expression tagExpression; try { - tagExpression = tagExpressionParser.parse(includeScenarioTags); + Expression tagExpression = tagExpressionParser.parse(includeScenarioTags); + return tagExpression.evaluate(combinedScenarioTags) && scenarioNameMatchExists; } catch (TagExpressionException e) { throw new CucablePluginException("The tag expression '" + includeScenarioTags + "' is invalid: " + e.getMessage()); } - return tagExpression.evaluate(combinedScenarioTags) && scenarioNameMatchExists; + } /** diff --git a/plugin-code/src/main/java/com/trivago/gherkin/GherkinToCucableConverter.java b/plugin-code/src/main/java/com/trivago/gherkin/GherkinToCucableConverter.java index dbc5ae7..055c084 100644 --- a/plugin-code/src/main/java/com/trivago/gherkin/GherkinToCucableConverter.java +++ b/plugin-code/src/main/java/com/trivago/gherkin/GherkinToCucableConverter.java @@ -30,6 +30,8 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.IntStream; @Singleton class GherkinToCucableConverter { @@ -72,14 +74,9 @@ private com.trivago.vo.DataTable convertGherkinDataTableToCucableDataTable( final DataTable gherkinDataTable) { com.trivago.vo.DataTable dataTable = new com.trivago.vo.DataTable(); - for (TableRow row : gherkinDataTable.getRows()) { - List cells = row.getCells(); - List rowValues = new ArrayList<>(); - for (TableCell cell : cells) { - rowValues.add(cell.getValue()); - } - dataTable.addRow(rowValues); - } + gherkinDataTable.getRows().stream().map(TableRow::getCells) + .map(cells -> cells.stream().map(TableCell::getValue).collect(Collectors.toList())) + .forEachOrdered(dataTable::addRow); return dataTable; } @@ -90,11 +87,7 @@ private com.trivago.vo.DataTable convertGherkinDataTableToCucableDataTable( * @return a {@link String} list of tags. */ List convertGherkinTagsToCucableTags(final List gherkinTags) { - List tags = new ArrayList<>(); - for (Tag gherkinTag : gherkinTags) { - tags.add(gherkinTag.getName()); - } - return tags; + return gherkinTags.stream().map(Tag::getName).collect(Collectors.toList()); } /** @@ -106,23 +99,21 @@ List convertGherkinTagsToCucableTags(final List gherkinTags) { Map> convertGherkinExampleTableToCucableExampleMap( final Examples exampleTable ) { - Map> exampleMap = new LinkedHashMap<>(); + Map> exampleMap; List headerCells = exampleTable.getTableHeader().getCells(); - for (TableCell headerCell : headerCells) { - exampleMap.put("<" + headerCell.getValue() + ">", new ArrayList<>()); - } + exampleMap = headerCells.stream().collect( + Collectors.toMap(headerCell -> "<" + headerCell.getValue() + ">", + headerCell -> new ArrayList<>(), (a, b) -> b, LinkedHashMap::new)); Object[] columnKeys = exampleMap.keySet().toArray(); List tableBody = exampleTable.getTableBody(); - for (TableRow tableRow : tableBody) { - List cells = tableRow.getCells(); - for (int i = 0; i < cells.size(); i++) { - String columnKey = (String) columnKeys[i]; - List values = exampleMap.get(columnKey); - values.add(cells.get(i).getValue()); - } - } + tableBody.stream().map(TableRow::getCells).forEachOrdered( + cells -> IntStream.range(0, cells.size()).forEachOrdered(i -> { + String columnKey = (String) columnKeys[i]; + List values = exampleMap.get(columnKey); + values.add(cells.get(i).getValue()); + })); return exampleMap; } } diff --git a/plugin-code/src/main/java/com/trivago/vo/SingleScenario.java b/plugin-code/src/main/java/com/trivago/vo/SingleScenario.java index 684971b..e511d73 100644 --- a/plugin-code/src/main/java/com/trivago/vo/SingleScenario.java +++ b/plugin-code/src/main/java/com/trivago/vo/SingleScenario.java @@ -16,6 +16,7 @@ package com.trivago.vo; +import java.util.ArrayList; import java.util.List; /** @@ -31,9 +32,9 @@ public final class SingleScenario { private final String scenarioDescription; private final List featureTags; private final List backgroundSteps; - private List scenarioTags; - private List exampleTags; - private List steps; + private List scenarioTags = new ArrayList<>(); + private List exampleTags = new ArrayList<>(); + private List steps = new ArrayList<>(); public SingleScenario( final String featureName, @@ -110,4 +111,21 @@ public List getExampleTags() { public void setExampleTags(final List exampleTags) { this.exampleTags = exampleTags; } + + @Override + public String toString() { + return "SingleScenario{" + + "featureName='" + featureName + '\'' + + ", featureFilePath='" + featureFilePath + '\'' + + ", featureLanguage='" + featureLanguage + '\'' + + ", featureDescription='" + featureDescription + '\'' + + ", scenarioName='" + scenarioName + '\'' + + ", scenarioDescription='" + scenarioDescription + '\'' + + ", featureTags=" + featureTags + + ", backgroundSteps=" + backgroundSteps + + ", scenarioTags=" + scenarioTags + + ", exampleTags=" + exampleTags + + ", steps=" + steps + + '}'; + } } diff --git a/plugin-code/src/main/java/com/trivago/vo/Step.java b/plugin-code/src/main/java/com/trivago/vo/Step.java index 08ba5c5..c682697 100644 --- a/plugin-code/src/main/java/com/trivago/vo/Step.java +++ b/plugin-code/src/main/java/com/trivago/vo/Step.java @@ -42,4 +42,13 @@ public DataTable getDataTable() { public String getDocString() { return docString; } + + @Override + public String toString() { + return "Step{" + + "dataTable=" + dataTable + + ", docString='" + docString + '\'' + + ", name='" + name + '\'' + + '}'; + } } \ No newline at end of file diff --git a/plugin-code/src/test/java/com/trivago/gherkin/GherkinDocumentParserTest.java b/plugin-code/src/test/java/com/trivago/gherkin/GherkinDocumentParserTest.java index b69dee0..628b598 100644 --- a/plugin-code/src/test/java/com/trivago/gherkin/GherkinDocumentParserTest.java +++ b/plugin-code/src/test/java/com/trivago/gherkin/GherkinDocumentParserTest.java @@ -441,6 +441,65 @@ public void replaceDataTableExamplePlaceholderTest() throws Exception { assertThat(firstRow.get(2), is("one")); } + @Test + public void taggedFeatureAndExamplesTest() throws Exception { + String featureContent = getScenarioWithFeatureAndExampleTags(); + List singleScenariosFromFeature = gherkinDocumentParser.getSingleScenariosFromFeature(featureContent, "", null); + assertThat(singleScenariosFromFeature.size(), is(3)); + assertThat(singleScenariosFromFeature.get(0).getSteps().size(), is(2)); + assertThat(singleScenariosFromFeature.get(1).getSteps().size(), is(2)); + assertThat(singleScenariosFromFeature.get(2).getSteps().size(), is(2)); + + } + + @Test + public void taggedFeatureAndExamplesRequestedExampleTagTest() throws Exception { + String featureContent = getScenarioWithFeatureAndExampleTags(); + when(propertyManager.getIncludeScenarioTags()).thenReturn("@exampleTag1"); + List singleScenariosFromFeature = gherkinDocumentParser.getSingleScenariosFromFeature(featureContent, "", null); + assertThat(singleScenariosFromFeature.size(), is(1)); + assertThat(singleScenariosFromFeature.get(0).getSteps().size(), is(2)); + } + + @Test + public void taggedFeatureAndExamplesRequestedFeatureAndExampleTagTest() throws Exception { + String featureContent = getScenarioWithFeatureAndExampleTags(); + when(propertyManager.getIncludeScenarioTags()).thenReturn("@featureTag and @exampleTag1"); + List singleScenariosFromFeature = gherkinDocumentParser.getSingleScenariosFromFeature(featureContent, "", null); + assertThat(singleScenariosFromFeature.size(), is(1)); + assertThat(singleScenariosFromFeature.get(0).getSteps().size(), is(2)); + } + + @Test + public void taggedFeatureAndExamplesRequestedInvalidExampleTagTest() throws Exception { + String featureContent = getScenarioWithFeatureAndExampleTags(); + when(propertyManager.getIncludeScenarioTags()).thenReturn("@exampleTag1 and @exampleTag2"); + List singleScenariosFromFeature = gherkinDocumentParser.getSingleScenariosFromFeature(featureContent, "", null); + assertThat(singleScenariosFromFeature.size(), is(0)); + } + + private String getScenarioWithFeatureAndExampleTags() { + return "@featureTag\n" + + "Feature: test feature 3\n" + + "\n" + + " Scenario Outline: This is a scenario outline\n" + + " When I search for key \n" + + " Then I get \n" + + "\n" + + "@exampleTag1\n" + + "Examples:\n" + + " | key | value |\n" + + " | 1 | one |\n" + + "@exampleTag2\n" + + "Examples:\n" + + " | key | value |\n" + + " | 2 | two |\n" + + "@exampleTag3\n" + + "Examples:\n" + + " | key | value |\n" + + " | 3 | three |\n"; + } + private String getTwoScenariosWithTags() { return "@featureTag\n" + "Feature: test feature\n" +