Skip to content

Commit

Permalink
Merge pull request #141 from josepj/scenario-name-support
Browse files Browse the repository at this point in the history
Scenario name support -  Generate only matching feature files when <scenarioNames> is specified
  • Loading branch information
Benjamin Bischoff authored Sep 4, 2019
2 parents d70941b + 7299c3a commit 4a1b0b6
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 36 deletions.
3 changes: 3 additions & 0 deletions example-project/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@
<!--<includeScenarioTags>not @skipMe</includeScenarioTags>-->
<!--<includeScenarioTags>(@scenario1Tag1 or @scenario1Tag2) and not @skipMe</includeScenarioTags>-->

<!--optional: A comma separated list of strings matching a scenario name, either completely or partially. Please see "name" option in Cucumber command-line options-->
<!--<scenarioNames>Scenario 1, Scenario 2, Mulțumesc</scenarioNames>-->

<!-- optional: change parallelization mode of Cucable (default: 'scenarios')-->
<!--<parallelizationMode>scenarios</parallelizationMode>-->
<!--<parallelizationMode>features</parallelizationMode>-->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
import com.trivago.vo.CucableFeature;
import com.trivago.vo.FeatureRunner;
import com.trivago.vo.SingleScenario;
import gherkin.AstBuilder;
import gherkin.Parser;
import gherkin.ast.GherkinDocument;

import javax.inject.Inject;
import javax.inject.Singleton;
Expand All @@ -39,8 +42,6 @@
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.trivago.logging.CucableLogger.CucableLogLevel.COMPACT;
import static com.trivago.logging.CucableLogger.CucableLogLevel.DEFAULT;
Expand Down Expand Up @@ -333,17 +334,14 @@ private int generateRunnerClassesWithDesiredNumberOfRunners(final List<String> g
} else {
// Move all scenarios matching a scenario name into its own group.
String scenarioText = fileIO.readContentFromFile(propertyManager.getGeneratedFeatureDirectory() + "/" + generatedFeatureName + ".feature");

if (scenarioText != null) {
for (String scenarioName : scenarioNames) {
String regex = "Scenario:.+" + scenarioName;
Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(scenarioText);
if (matcher.find()) {
generatedFeatureNamesPerRunner.get(scenarioNames.indexOf(scenarioName)).add(generatedFeatureName);
matchCount++;
break;
}
Parser<GherkinDocument> parser = new Parser<>(new AstBuilder());
String language = parser.parse(scenarioText).getFeature().getLanguage();

int listIndex = gherkinDocumentParser.matchScenarioWithScenarioNames(language, scenarioText);
if (listIndex >= 0) {
generatedFeatureNamesPerRunner.get(listIndex).add(generatedFeatureName);
matchCount++;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,7 @@ public List<SingleScenario> getSingleScenariosFromFeature(
backgroundSteps
);
addGherkinScenarioInformationToSingleScenario(scenario, singleScenario);
if (scenarioShouldBeIncluded(
singleScenario.getScenarioTags(),
singleScenario.getFeatureTags()
)) {
if (scenarioShouldBeIncluded(singleScenario)) {
singleScenarioFeatures.add(singleScenario);
}
}
Expand All @@ -145,11 +142,7 @@ public List<SingleScenario> getSingleScenariosFromFeature(
backgroundSteps
);
for (SingleScenario singleScenario : outlineScenarios) {
if (scenarioShouldBeIncluded(
singleScenario.getScenarioTags(),
singleScenario.getFeatureTags(),
singleScenario.getExampleTags()
)) {
if (scenarioShouldBeIncluded(singleScenario)) {
singleScenarioFeatures.add(singleScenario);
}
}
Expand Down Expand Up @@ -328,23 +321,25 @@ private GherkinDocument getGherkinDocumentFromFeatureFileContent(final String fe
}

/**
* Checks if a scenario should be included in the runner and feature generation based on the tag settings.
* Checks if a scenario should be included in the runner and feature generation based on the tag settings and
* scenarioNames settings.
*
* @param sourceTagsList any number of source tag lists to be considered
* @return true if an include tag and no exclude tags are included in the source tag list.
* @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.
*/
@SafeVarargs
private final boolean scenarioShouldBeIncluded(final List<String>... sourceTagsList) throws CucablePluginException {
private boolean scenarioShouldBeIncluded(SingleScenario singleScenario) throws CucablePluginException {

String includeScenarioTags = propertyManager.getIncludeScenarioTags();
String language = singleScenario.getFeatureLanguage();
String scenarioName = singleScenario.getScenarioName();
boolean scenarioNameMatchExists = matchScenarioWithScenarioNames(language, scenarioName) >= 0;

List<String> combinedScenarioTags = new ArrayList<>();
for (List<String> sourceTags : sourceTagsList) {
combinedScenarioTags.addAll(sourceTags);
}
List<String> combinedScenarioTags = singleScenario.getScenarioTags();
combinedScenarioTags.addAll(singleScenario.getFeatureTags());

if (includeScenarioTags == null || includeScenarioTags.isEmpty()) {
return true;
return scenarioNameMatchExists;
}

Expression tagExpression;
Expand All @@ -353,7 +348,37 @@ private final boolean scenarioShouldBeIncluded(final List<String>... sourceTagsL
} catch (TagExpressionException e) {
throw new CucablePluginException("The tag expression '" + includeScenarioTags + "' is invalid: " + e.getMessage());
}
return tagExpression.evaluate(combinedScenarioTags);
return tagExpression.evaluate(combinedScenarioTags) && scenarioNameMatchExists;
}

/**
* Checks if a scenarioName value matches with the scenario name.
*
* @param language Feature file language ("en", "ro" etc).
* @param stringToMatch the string that will be matched with the scenarioName value.
* @return index of the scenarioName value in the scenarioNames list if a match exists.
* -1 if no match exists.
*/
public int matchScenarioWithScenarioNames(String language, String stringToMatch) {
List<String> scenarioNames = propertyManager.getScenarioNames();
String scenarioKeyword = gherkinTranslations.getScenarioKeyword(language);
int matchIndex = -1;

if (scenarioNames == null || scenarioNames.isEmpty()) {
return 0;
}

for (String scenarioName : scenarioNames) {
String regex = scenarioKeyword + ":.+" + scenarioName;
Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(stringToMatch);
if (matcher.find()) {
matchIndex = scenarioNames.indexOf(scenarioName);
break;
}
}

return matchIndex;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.Arrays;
import java.util.List;

@Singleton
class GherkinTranslations {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ public void convertToSingleScenariosAndRunnersTest() throws Exception {
public void convertToSingleScenariosAndRunnersWithScenarioNameTest() throws Exception {
String generatedFeatureDir = testFolder.getRoot().getPath().concat("/features/");
String generatedRunnerDir = testFolder.getRoot().getPath().concat("/runners/");
String scenarioMatchText = "Feature: feature1\n Scenario: scenarioName1";

final String FEATURE_FILE_NAME = "FEATURE_FILE.feature";
final String GENERATED_FEATURE_FILE_NAME = "FEATURE_FILE_scenario001_run001_IT.feature";
Expand All @@ -141,7 +142,7 @@ public void convertToSingleScenariosAndRunnersWithScenarioNameTest() throws Exce
propertyManager.setScenarioNames("scenarioName1");

when(fileIO.readContentFromFile(FEATURE_FILE_NAME)).thenReturn("TEST_CONTENT");
when(fileIO.readContentFromFile(generatedFeatureDir + "/" + GENERATED_FEATURE_FILE_NAME)).thenReturn("Scenario: scenarioName1");
when(fileIO.readContentFromFile(generatedFeatureDir + "/" + GENERATED_FEATURE_FILE_NAME)).thenReturn(scenarioMatchText);

List<CucableFeature> cucableFeatures = new ArrayList<>();
CucableFeature cucableFeature = new CucableFeature(FEATURE_FILE_NAME, null);
Expand All @@ -153,6 +154,7 @@ public void convertToSingleScenariosAndRunnersWithScenarioNameTest() throws Exce
SingleScenario singleScenario = getSingleScenario();
scenarioList.add(singleScenario);
when(gherkinDocumentParser.getSingleScenariosFromFeature("TEST_CONTENT", FEATURE_FILE_NAME, null)).thenReturn(scenarioList);
when(gherkinDocumentParser.matchScenarioWithScenarioNames("en", scenarioMatchText)).thenReturn(0);

String featureFileContent = "test";
when(featureFileContentRenderer.getRenderedFeatureFileContent(singleScenario)).thenReturn(featureFileContent);
Expand Down Expand Up @@ -243,6 +245,8 @@ public void convertToSingleScenariosAndMultiRunnersTest() throws Exception {
public void convertToSingleScenariosAndMultiRunnersWithScenarioNamesTest() throws Exception {
String generatedFeatureDir = testFolder.getRoot().getPath().concat("/features/");
String generatedRunnerDir = testFolder.getRoot().getPath().concat("/runners/");
final String scenarioMatch1Text = "Feature: feature1\n Scenario: scenarioName1";
final String scenarioMatch2Text = "Feature: feature2\n Scenario: scenarioName2";

final String FEATURE_FILE_NAME = "FEATURE_FILE.feature";
final String GENERATED_FEATURE_FILE_NAME1 = "FEATURE_FILE_scenario001_run001_IT.feature";
Expand All @@ -255,8 +259,8 @@ public void convertToSingleScenariosAndMultiRunnersWithScenarioNamesTest() throw
propertyManager.setScenarioNames("scenarioName1, scenarioName2");

when(fileIO.readContentFromFile(FEATURE_FILE_NAME)).thenReturn("TEST_CONTENT");
when(fileIO.readContentFromFile(generatedFeatureDir + "/" + GENERATED_FEATURE_FILE_NAME1)).thenReturn("Scenario: scenarioName1");
when(fileIO.readContentFromFile(generatedFeatureDir + "/" + GENERATED_FEATURE_FILE_NAME2)).thenReturn("Scenario: scenarioName2");
when(fileIO.readContentFromFile(generatedFeatureDir + "/" + GENERATED_FEATURE_FILE_NAME1)).thenReturn(scenarioMatch1Text);
when(fileIO.readContentFromFile(generatedFeatureDir + "/" + GENERATED_FEATURE_FILE_NAME2)).thenReturn(scenarioMatch2Text);

when(fileIO.readContentFromFile(generatedFeatureDir + "/" + FEATURE_FILE_NAME)).thenReturn("TEST_CONTENT");

Expand All @@ -271,6 +275,58 @@ public void convertToSingleScenariosAndMultiRunnersWithScenarioNamesTest() throw
scenarioList.add(singleScenario);
scenarioList.add(singleScenario);
when(gherkinDocumentParser.getSingleScenariosFromFeature("TEST_CONTENT", FEATURE_FILE_NAME, null)).thenReturn(scenarioList);
when(gherkinDocumentParser.matchScenarioWithScenarioNames("en", scenarioMatch1Text)).thenReturn(0);
when(gherkinDocumentParser.matchScenarioWithScenarioNames("en", scenarioMatch2Text)).thenReturn(1);

String featureFileContent = "test";
when(featureFileContentRenderer.getRenderedFeatureFileContent(singleScenario)).thenReturn(featureFileContent);

when(runnerFileContentRenderer.getRenderedRunnerFileContent(any(FeatureRunner.class))).thenReturn("RUNNER_CONTENT");

featureFileConverter.generateParallelizableFeatures(cucableFeatures);

ArgumentCaptor<String> logCaptor = ArgumentCaptor.forClass(String.class);
verify(logger, times(1)).info(logCaptor.capture(), any(CucableLogger.CucableLogLevel.class), any(CucableLogger.CucableLogLevel.class), any(CucableLogger.CucableLogLevel.class));
assertThat(logCaptor.getAllValues().get(0), is("Cucable created 2 separate feature files and 2 runners."));
verify(fileIO, times(4)).writeContentToFile(anyString(), anyString());
}

@Test
public void convertToSingleScenariosAndMultiRunnersWithScenarioNamesAndExampleKeywordTest() throws Exception {
String generatedFeatureDir = testFolder.getRoot().getPath().concat("/features/");
String generatedRunnerDir = testFolder.getRoot().getPath().concat("/runners/");
final String scenarioMatch1Text = "Feature: feature1\n Scenario: scenarioName1";
final String scenarioMatch2Text = "Feature: feature2\n Example: scenarioName2";

final String FEATURE_FILE_NAME = "FEATURE_FILE.feature";
final String GENERATED_FEATURE_FILE_NAME1 = "FEATURE_FILE_scenario001_run001_IT.feature";
final String GENERATED_FEATURE_FILE_NAME2 = "FEATURE_FILE_scenario002_run001_IT.feature";

propertyManager.setNumberOfTestRuns(1);
propertyManager.setGeneratedFeatureDirectory(generatedFeatureDir);
propertyManager.setGeneratedRunnerDirectory(generatedRunnerDir);
propertyManager.setParallelizationMode("scenarios");
propertyManager.setScenarioNames("scenarioName1, scenarioName2");

when(fileIO.readContentFromFile(FEATURE_FILE_NAME)).thenReturn("TEST_CONTENT");
when(fileIO.readContentFromFile(generatedFeatureDir + "/" + GENERATED_FEATURE_FILE_NAME1)).thenReturn(scenarioMatch1Text);
when(fileIO.readContentFromFile(generatedFeatureDir + "/" + GENERATED_FEATURE_FILE_NAME2)).thenReturn(scenarioMatch2Text);

when(fileIO.readContentFromFile(generatedFeatureDir + "/" + FEATURE_FILE_NAME)).thenReturn("TEST_CONTENT");

List<CucableFeature> cucableFeatures = new ArrayList<>();
CucableFeature cucableFeature = new CucableFeature(FEATURE_FILE_NAME, null);
cucableFeatures.add(cucableFeature);

when(fileSystemManager.getPathsFromCucableFeature(cucableFeature)).thenReturn(Collections.singletonList(Paths.get(cucableFeature.getName())));

List<SingleScenario> scenarioList = new ArrayList<>();
SingleScenario singleScenario = getSingleScenario();
scenarioList.add(singleScenario);
scenarioList.add(singleScenario);
when(gherkinDocumentParser.getSingleScenariosFromFeature("TEST_CONTENT", FEATURE_FILE_NAME, null)).thenReturn(scenarioList);
when(gherkinDocumentParser.matchScenarioWithScenarioNames("en", scenarioMatch1Text)).thenReturn(0);
when(gherkinDocumentParser.matchScenarioWithScenarioNames("en", scenarioMatch2Text)).thenReturn(1);

String featureFileContent = "test";
when(featureFileContentRenderer.getRenderedFeatureFileContent(singleScenario)).thenReturn(featureFileContent);
Expand Down Expand Up @@ -320,14 +376,15 @@ public void noScenariosMatchingScenarioNamesTest() throws Exception {

final String FEATURE_FILE_NAME = "FEATURE_FILE.feature";
final String GENERATED_FEATURE_FILE_NAME = "FEATURE_FILE_scenario001_run001_IT.feature";
final String scenarioNoMatchText = "Feature: feature1\n Scenario: noMatch";

propertyManager.setNumberOfTestRuns(1);
propertyManager.setGeneratedFeatureDirectory(generatedFeatureDir);
propertyManager.setGeneratedRunnerDirectory(generatedRunnerDir);
propertyManager.setScenarioNames("scenarioName1");

when(fileIO.readContentFromFile(FEATURE_FILE_NAME)).thenReturn("TEST_CONTENT");
when(fileIO.readContentFromFile(generatedFeatureDir + "/" + GENERATED_FEATURE_FILE_NAME)).thenReturn("Scenario: noMatch");
when(fileIO.readContentFromFile(generatedFeatureDir + "/" + GENERATED_FEATURE_FILE_NAME)).thenReturn(scenarioNoMatchText);

List<CucableFeature> cucableFeatures = new ArrayList<>();
CucableFeature cucableFeature = new CucableFeature(FEATURE_FILE_NAME, null);
Expand All @@ -339,6 +396,7 @@ public void noScenariosMatchingScenarioNamesTest() throws Exception {
SingleScenario singleScenario = getSingleScenario();
scenarioList.add(singleScenario);
when(gherkinDocumentParser.getSingleScenariosFromFeature("TEST_CONTENT", FEATURE_FILE_NAME, null)).thenReturn(scenarioList);
when(gherkinDocumentParser.matchScenarioWithScenarioNames("en", scenarioNoMatchText)).thenReturn(-1);

String featureFileContent = "test";
when(featureFileContentRenderer.getRenderedFeatureFileContent(singleScenario)).thenReturn(featureFileContent);
Expand Down
Loading

0 comments on commit 4a1b0b6

Please sign in to comment.