From 1f7fd0811044820cc003e346c9126e8b512d1a37 Mon Sep 17 00:00:00 2001 From: Liviu Carausu Date: Sun, 8 Mar 2020 22:55:04 +0100 Subject: [PATCH 1/3] upgrade to cucumber 5.4.1 --- build.gradle | 8 +- gradle.properties | 2 +- .../net/serenitybdd/core/pages/PageUrls.java | 2 - .../core/model/WhenReportingExceptions.groovy | 50 ++++----- .../core/model/failures/FailureAnalysis.java | 2 +- .../FailureAnalysisConfiguration.java | 2 +- .../thucydides/core/reports/AsciiColors.java | 1 - .../reports/html/CucumberTagConverter.java | 3 +- .../model/cucumber/AnnotatedFeature.java | 6 +- .../model/cucumber/CucumberParser.java | 104 ++++++++---------- .../DescriptionWithScenarioReferences.java | 2 +- .../cucumber/ExampleTableInMarkdown.java | 7 +- .../cucumber/IdentifiedExampleTable.java | 12 +- .../model/cucumber/IdentifiedScenario.java | 6 +- .../model/cucumber/NamedExampleTable.java | 6 +- .../model/cucumber/NamedScenario.java | 4 +- .../NarrativeFromCucumberComments.java | 3 +- .../cucumber/ReferencedExampleTable.java | 7 +- .../model/cucumber/ReferencedScenario.java | 3 +- .../model/cucumber/RenderedExampleTable.java | 9 +- .../model/cucumber/ScenarioReport.java | 3 +- .../model/cucumber/URIResource.java | 31 ++++++ .../cucumber/FeatureFileScenarioOutcomes.java | 6 +- .../reports/cucumber/RenderCucumber.java | 3 +- .../WhenLoadingIncorrectFeatureFiles.groovy | 3 +- ...rencingScenariosInAFeatureNarrative.groovy | 37 +++++-- 26 files changed, 187 insertions(+), 135 deletions(-) create mode 100644 serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/URIResource.java diff --git a/build.gradle b/build.gradle index 21bc460089..645d492b3c 100644 --- a/build.gradle +++ b/build.gradle @@ -246,7 +246,13 @@ subprojects { compile "org.objenesis:objenesis:${objenesisVersion}" compile "org.slf4j:slf4j-api:${slf4jVersion}" compile "xml-apis:xml-apis:${xmlApisVersion}" - compile "io.cucumber:cucumber-core:${cucumberVersion}" + compile ("io.cucumber:cucumber-core:${cucumberVersion}") { + exclude group: "org.apiguardian", module: 'apiguardian-api' + } + compile ("io.cucumber:cucumber-java:${cucumberVersion}") { + exclude group: "org.apiguardian", module: 'apiguardian-api' + } + compile("commons-logging:commons-logging:${commonsLoggingVersion}") testCompile "junit:junit:${junitVersion}" diff --git a/gradle.properties b/gradle.properties index 3ae85073c5..549a11d3e6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -29,7 +29,7 @@ gsonVersion = 2.8.4 typesafeConfigVersion = 1.3.1 freemarkerVersion = 2.3.29 wiremockCoreVersion = 1.58 -cucumberVersion = 2.4.0 +cucumberVersion = 5.4.1 commonsLoggingVersion = 1.2 xmlApisVersion = 1.4.01 cglibVersion = 3.3.0 diff --git a/serenity-core/src/main/java/net/serenitybdd/core/pages/PageUrls.java b/serenity-core/src/main/java/net/serenitybdd/core/pages/PageUrls.java index 716098f08a..de93a1b176 100644 --- a/serenity-core/src/main/java/net/serenitybdd/core/pages/PageUrls.java +++ b/serenity-core/src/main/java/net/serenitybdd/core/pages/PageUrls.java @@ -1,12 +1,10 @@ package net.serenitybdd.core.pages; -import cucumber.runtime.Env; import net.serenitybdd.core.environment.ConfiguredEnvironment; import net.serenitybdd.core.environment.EnvironmentSpecificConfiguration; import net.thucydides.core.annotations.DefaultUrl; import net.thucydides.core.annotations.NamedUrl; import net.thucydides.core.annotations.NamedUrls; -import net.thucydides.core.configuration.SystemPropertiesConfiguration; import net.thucydides.core.guice.Injectors; import net.thucydides.core.util.EnvironmentVariables; import net.thucydides.core.webdriver.Configuration; diff --git a/serenity-core/src/test/groovy/net/serenitybdd/core/model/WhenReportingExceptions.groovy b/serenity-core/src/test/groovy/net/serenitybdd/core/model/WhenReportingExceptions.groovy index c8437dd811..f7778d3e6e 100644 --- a/serenity-core/src/test/groovy/net/serenitybdd/core/model/WhenReportingExceptions.groovy +++ b/serenity-core/src/test/groovy/net/serenitybdd/core/model/WhenReportingExceptions.groovy @@ -1,8 +1,10 @@ package net.serenitybdd.core.model -import cucumber.api.PendingException + +import io.cucumber.java.PendingException import net.serenitybdd.core.PendingStepException import net.serenitybdd.core.exceptions.TestCompromisedException import net.serenitybdd.core.model.sampleexceptions.MyFailureException +import net.thucydides.core.model.TestResult import net.thucydides.core.model.failures.FailureAnalysis import net.thucydides.core.steps.StepFailureException import net.thucydides.core.util.MockEnvironmentVariables @@ -13,8 +15,6 @@ import org.openqa.selenium.WebDriverException import spock.lang.Specification import spock.lang.Unroll -import static net.thucydides.core.model.TestResult.* - class WhenReportingExceptions extends Specification { def failureAnalysisOf = new FailureAnalysis() @@ -29,21 +29,21 @@ class WhenReportingExceptions extends Specification { where: exception | expectedResult - new WebdriverAssertionError(new NullPointerException()) | ERROR - new WebdriverAssertionError(new NoSuchElementException()) | ERROR - new StepFailureException("bother", new NoSuchElementException()) | ERROR - new AssertionError("test message") | FAILURE - new SoftAssertionError(["test message"]) | FAILURE + new WebdriverAssertionError(new NullPointerException()) | TestResult.ERROR + new WebdriverAssertionError(new NoSuchElementException()) | TestResult.ERROR + new StepFailureException("bother", new NoSuchElementException()) | TestResult.ERROR + new AssertionError("test message") | TestResult.FAILURE + new SoftAssertionError(["test message"]) | TestResult.FAILURE new ArrayComparisonFailure("test message", - new AssertionError("wrapped exception"), 1) | FAILURE - new WebdriverAssertionError(new AssertionError("wrapped assertion error")) | FAILURE - new StepFailureException("bother", new AssertionError("test message")) | FAILURE - new RuntimeException("message") | ERROR - new NullPointerException() | ERROR - new WebDriverException() | ERROR - new PendingStepException("step is pending") | PENDING - new PendingException("step is pending") | PENDING - new TestCompromisedException("test is compromised") | COMPROMISED + new AssertionError("wrapped exception"), 1) | TestResult.FAILURE + new WebdriverAssertionError(new AssertionError("wrapped assertion error")) | TestResult.FAILURE + new StepFailureException("bother", new AssertionError("test message")) | TestResult.FAILURE + new RuntimeException("message") | TestResult.ERROR + new NullPointerException() | TestResult.ERROR + new WebDriverException() | TestResult.ERROR + new PendingStepException("step is pending") | TestResult.PENDING + new PendingException("step is pending") | TestResult.PENDING + new TestCompromisedException("test is compromised") | TestResult.COMPROMISED } def "non-assertion exceptions should be reported as Errors by default"() { @@ -51,7 +51,7 @@ class WhenReportingExceptions extends Specification { def failureAnalysisOf = new FailureAnalysis() def result = failureAnalysisOf.resultFor(new MyFailureException()) then: - result == ERROR + result == TestResult.ERROR } def "should be able to define what exceptions cause failures using serenity.fail.on"() { @@ -63,7 +63,7 @@ class WhenReportingExceptions extends Specification { def failureAnalysisOf = new FailureAnalysis(environmentVariables) def result = failureAnalysisOf.resultFor(new MyFailureException()) then: - result == FAILURE + result == TestResult.FAILURE } def "should be able to override failures as errors using serenity.error.on"() { @@ -75,7 +75,7 @@ class WhenReportingExceptions extends Specification { def failureAnalysisOf = new FailureAnalysis(environmentVariables) def result = failureAnalysisOf.resultFor(new AssertionError("oh crap")) then: - result == ERROR + result == TestResult.ERROR } def "should be able to override errors as compromised using serenity.compromised.on"() { @@ -87,7 +87,7 @@ class WhenReportingExceptions extends Specification { def failureAnalysisOf = new FailureAnalysis(environmentVariables) def result = failureAnalysisOf.resultFor(new AssertionError("oh crap")) then: - result == COMPROMISED + result == TestResult.COMPROMISED } def "should be able to override errors as skipped using serenity.skip.on"() { @@ -99,7 +99,7 @@ class WhenReportingExceptions extends Specification { def failureAnalysisOf = new FailureAnalysis(environmentVariables) def result = failureAnalysisOf.resultFor(new AssertionError("oh crap")) then: - result == SKIPPED + result == TestResult.SKIPPED } def "should be able to override errors as failures"() { @@ -112,7 +112,7 @@ class WhenReportingExceptions extends Specification { def failureAnalysisOf = new FailureAnalysis(environmentVariables) def result = failureAnalysisOf.resultFor(new NoSuchElementException()) then: - result == FAILURE + result == TestResult.FAILURE } def "should be able to override errors as pending"() { @@ -124,7 +124,7 @@ class WhenReportingExceptions extends Specification { def failureAnalysisOf = new FailureAnalysis(environmentVariables) def result = failureAnalysisOf.resultFor(new MyFailureException()) then: - result == PENDING + result == TestResult.PENDING } def "should be able to override errors as failures even with nested errors"() { @@ -137,7 +137,7 @@ class WhenReportingExceptions extends Specification { def failureAnalysisOf = new FailureAnalysis(environmentVariables) def result = failureAnalysisOf.resultFor(new StepFailureException("oh bother!",new NoSuchElementException())) then: - result == FAILURE + result == TestResult.FAILURE } } diff --git a/serenity-model/src/main/java/net/thucydides/core/model/failures/FailureAnalysis.java b/serenity-model/src/main/java/net/thucydides/core/model/failures/FailureAnalysis.java index 8642d21a9c..a96d850d3c 100644 --- a/serenity-model/src/main/java/net/thucydides/core/model/failures/FailureAnalysis.java +++ b/serenity-model/src/main/java/net/thucydides/core/model/failures/FailureAnalysis.java @@ -1,6 +1,6 @@ package net.thucydides.core.model.failures; -import cucumber.api.PendingException; +import io.cucumber.java.PendingException; import net.serenitybdd.core.PendingStepException; import net.serenitybdd.core.environment.ConfiguredEnvironment; import net.serenitybdd.core.exceptions.CausesAssertionFailure; diff --git a/serenity-model/src/main/java/net/thucydides/core/model/failures/FailureAnalysisConfiguration.java b/serenity-model/src/main/java/net/thucydides/core/model/failures/FailureAnalysisConfiguration.java index eb0ea9f635..40eac47545 100644 --- a/serenity-model/src/main/java/net/thucydides/core/model/failures/FailureAnalysisConfiguration.java +++ b/serenity-model/src/main/java/net/thucydides/core/model/failures/FailureAnalysisConfiguration.java @@ -1,7 +1,7 @@ package net.thucydides.core.model.failures; import com.google.common.base.Splitter; -import cucumber.api.PendingException; +import io.cucumber.java.PendingException; import net.serenitybdd.core.PendingStepException; import net.serenitybdd.core.SkipStepException; import net.serenitybdd.core.exceptions.CausesAssertionFailure; diff --git a/serenity-model/src/main/java/net/thucydides/core/reports/AsciiColors.java b/serenity-model/src/main/java/net/thucydides/core/reports/AsciiColors.java index 7d731a3760..ed6880fdb0 100644 --- a/serenity-model/src/main/java/net/thucydides/core/reports/AsciiColors.java +++ b/serenity-model/src/main/java/net/thucydides/core/reports/AsciiColors.java @@ -1,6 +1,5 @@ package net.thucydides.core.reports; -import cucumber.api.formatter.AnsiEscapes; import net.serenitybdd.core.CurrentOS; import net.thucydides.core.ThucydidesSystemProperty; import net.thucydides.core.util.EnvironmentVariables; diff --git a/serenity-model/src/main/java/net/thucydides/core/reports/html/CucumberTagConverter.java b/serenity-model/src/main/java/net/thucydides/core/reports/html/CucumberTagConverter.java index b1683603fd..1d52f9e66c 100644 --- a/serenity-model/src/main/java/net/thucydides/core/reports/html/CucumberTagConverter.java +++ b/serenity-model/src/main/java/net/thucydides/core/reports/html/CucumberTagConverter.java @@ -1,6 +1,7 @@ package net.thucydides.core.reports.html; -import gherkin.ast.Tag; + +import io.cucumber.core.internal.gherkin.ast.Tag; import net.thucydides.core.model.TestTag; import java.util.Collection; diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/AnnotatedFeature.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/AnnotatedFeature.java index a0144f4614..4a9e4bd82c 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/AnnotatedFeature.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/AnnotatedFeature.java @@ -1,7 +1,9 @@ package net.thucydides.core.requirements.model.cucumber; -import gherkin.ast.Feature; -import gherkin.ast.ScenarioDefinition; + + +import io.cucumber.core.internal.gherkin.ast.Feature; +import io.cucumber.core.internal.gherkin.ast.ScenarioDefinition; import java.util.List; diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/CucumberParser.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/CucumberParser.java index 213ed4bf19..6546bdc500 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/CucumberParser.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/CucumberParser.java @@ -1,29 +1,28 @@ package net.thucydides.core.requirements.model.cucumber; import com.google.common.base.Splitter; -import cucumber.runtime.CucumberException; -import cucumber.runtime.io.MultiLoader; -import cucumber.runtime.io.ResourceLoader; -import cucumber.runtime.model.CucumberFeature; -import gherkin.ParserException; -import gherkin.ast.*; +import io.cucumber.core.feature.FeatureParser; +import io.cucumber.core.gherkin.FeatureParserException; +import io.cucumber.core.gherkin.vintage.internal.gherkin.ParserException; +import io.cucumber.core.internal.gherkin.ast.*; +import io.cucumber.core.internal.gherkin.events.CucumberEvent; +import io.cucumber.core.internal.gherkin.events.GherkinDocumentEvent; +import io.cucumber.core.internal.gherkin.events.SourceEvent; +import io.cucumber.core.internal.gherkin.stream.GherkinEvents; +import io.cucumber.core.internal.gherkin.stream.SourceEvents; +import io.cucumber.core.resource.Resource; import net.serenitybdd.core.environment.ConfiguredEnvironment; -import net.serenitybdd.core.exceptions.SerenityManagedException; import net.thucydides.core.ThucydidesSystemProperty; import net.thucydides.core.model.TestTag; -import net.thucydides.core.model.TestType; import net.thucydides.core.reports.html.CucumberTagConverter; import net.thucydides.core.requirements.model.Narrative; -import net.thucydides.core.requirements.reports.ScenarioOutcome; import net.thucydides.core.util.EnvironmentVariables; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.URI; import java.nio.charset.Charset; +import java.nio.file.Path; import java.util.*; import java.util.stream.Collectors; @@ -69,18 +68,14 @@ public Optional loadFeature(File narrativeFile) { List listOfFiles = new ArrayList<>(); listOfFiles.add(narrativeFile.getAbsolutePath()); - MultiLoader multiLoader = new MultiLoader(CucumberParser.class.getClassLoader()); - List cucumberFeatures = loadCucumberFeatures(multiLoader, listOfFiles); + + List gherkinDocuments = loadCucumberFeatures(listOfFiles); try { - if (cucumberFeatures.size() == 0) { + if (gherkinDocuments.size() == 0) { return Optional.empty(); } - CucumberFeature cucumberFeature = cucumberFeatures.get(0); - - List scenarios = cucumberFeature.getGherkinFeature().getFeature().getChildren(); - - GherkinDocument gherkinDocument = cucumberFeature.getGherkinFeature(); - + GherkinDocument gherkinDocument = gherkinDocuments.get(0); + String descriptionInComments = NarrativeFromCucumberComments.in(gherkinDocument.getComments()); if (featureFileCouldNotBeReadFor(gherkinDocument.getFeature())) { @@ -95,54 +90,47 @@ public Optional loadFeature(File narrativeFile) { } } - - private List loadCucumberFeatures(MultiLoader multiLoader, List listOfFiles) { - try { //try to load cucumber-core 4.2.6 - Class featureLoaderClass = CucumberParser.class.getClassLoader().loadClass(CUCUMBER_4_FEATURE_LOADER); - Method load = featureLoaderClass.getMethod("load", List.class); - Object featureLoader = featureLoaderClass.getConstructor(ResourceLoader.class).newInstance(multiLoader); - List uriList = listOfFiles.stream().map(filePath->new File(filePath).toURI()).collect(Collectors.toList()); - return (List)load.invoke(featureLoader,uriList); - } catch(ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException cucumber426Exception) { - reportAnyCucumberSyntaxErrorsIn(cucumber426Exception); - - LOGGER.debug("Found no Cucumber 4.2.x class " + CUCUMBER_4_FEATURE_LOADER + " trying Cucumber 4.8.0 "); - try { //try to load cucumber-core 4.8.0 - Class featureLoaderClass = CucumberParser.class.getClassLoader().loadClass(CUCUMBER_4_FEATURE_LOADER); - Method load = featureLoaderClass.getMethod("load", List.class); - Object featureLoader = featureLoaderClass.getConstructor(ResourceLoader.class).newInstance(multiLoader); - return (List) load.invoke(featureLoader, listOfFiles); - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException cucumber420Exception) { - reportAnyCucumberSyntaxErrorsIn(cucumber420Exception); - - LOGGER.debug("Found no Cucumber 4.8.x class " + CUCUMBER_4_FEATURE_LOADER + " try Cucumber 2.x.x "); - try { - Class featureLoaderClass = CucumberParser.class.getClassLoader().loadClass(CUCUMBER_2_FEATURE_LOADER); - Method load = featureLoaderClass.getMethod("load", ResourceLoader.class, List.class); - return (List) load.invoke(null, multiLoader, listOfFiles); - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException cucumber2Exception) { - reportAnyCucumberSyntaxErrorsIn(cucumber2Exception); - - LOGGER.error("Found no Cucumber 2.x.x class " + CUCUMBER_2_FEATURE_LOADER + " failed loading CucumberFeatures ", cucumber2Exception); - LOGGER.error("Found neither Cucumber 2.x.x nor Cucumber 4.x runtime in classpath"); - throw new RuntimeException("Found neither Cucumber 2.x.x nor Cucumber 4.x runtime in classpath", cucumber2Exception); + private List loadCucumberFeatures(List listOfFiles) { + for(String cucumberFile : listOfFiles) { + searchForCucumberSyntaxErrorsIn(cucumberFile); + } + List loadedFeatures = new ArrayList<>(); + SourceEvents sourceEvents = new SourceEvents(listOfFiles); + GherkinEvents gherkinEvents = new GherkinEvents(true,true,true); + for (SourceEvent sourceEventEvent : sourceEvents) { + for (CucumberEvent cucumberEvent : gherkinEvents.iterable(sourceEventEvent)) { + if(cucumberEvent instanceof GherkinDocumentEvent) { + GherkinDocumentEvent gherkinDocumentEvent = (GherkinDocumentEvent)cucumberEvent; + GherkinDocument gherkinDocument = gherkinDocumentEvent.document; + loadedFeatures.add(gherkinDocument); + LOGGER.debug("Added feature " + gherkinDocument.getFeature().getName()); } } - } catch(ParserException gherkinParsingException) { - LOGGER.error("Syntax error in feature file from " + listOfFiles,gherkinParsingException); - throw new RuntimeException("Syntax error in feature file from " + listOfFiles,gherkinParsingException); + } + return loadedFeatures; + } + + private void searchForCucumberSyntaxErrorsIn(String cucumberFile) { + FeatureParser featureParser = new FeatureParser(UUID::randomUUID); + Path cucumberFilePath = new File(cucumberFile).toPath(); + Resource cucumberResource = new URIResource(cucumberFilePath); + try { + featureParser.parseResource(cucumberResource); + } catch(Throwable throwable) { + reportAnyCucumberSyntaxErrorsIn(throwable); } } private void reportAnyCucumberSyntaxErrorsIn(Throwable possibleGherkinSyntaxError) { - if (possibleGherkinSyntaxError instanceof InvocationTargetException) { - Throwable gherkinError = ((InvocationTargetException) possibleGherkinSyntaxError).getTargetException(); - if (gherkinError instanceof CucumberException) { + if (possibleGherkinSyntaxError instanceof FeatureParserException) { + Throwable gherkinError = possibleGherkinSyntaxError.getCause(); + if (gherkinError instanceof ParserException) { throw new InvalidFeatureFileException(gherkinError.getMessage(), gherkinError); } } } + public Optional loadFeatureNarrative(File narrativeFile) { Optional loadedFeature = loadFeature(narrativeFile); diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/DescriptionWithScenarioReferences.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/DescriptionWithScenarioReferences.java index 82e04daa29..34d5f56869 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/DescriptionWithScenarioReferences.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/DescriptionWithScenarioReferences.java @@ -1,6 +1,6 @@ package net.thucydides.core.requirements.model.cucumber; -import gherkin.ast.Feature; +import io.cucumber.core.internal.gherkin.ast.Feature; import static net.thucydides.core.requirements.model.cucumber.ScenarioDisplayOption.WithTitle; diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ExampleTableInMarkdown.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ExampleTableInMarkdown.java index 925475b1ba..5595e434c7 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ExampleTableInMarkdown.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ExampleTableInMarkdown.java @@ -1,8 +1,9 @@ package net.thucydides.core.requirements.model.cucumber; -import gherkin.ast.Examples; -import gherkin.ast.Feature; -import gherkin.ast.ScenarioDefinition; + +import io.cucumber.core.internal.gherkin.ast.Examples; +import io.cucumber.core.internal.gherkin.ast.Feature; +import io.cucumber.core.internal.gherkin.ast.ScenarioDefinition; import net.thucydides.core.digest.Digest; import static net.thucydides.core.requirements.model.cucumber.ScenarioDisplayOption.WithTitle; diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/IdentifiedExampleTable.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/IdentifiedExampleTable.java index 1b5976e0b6..2290211e06 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/IdentifiedExampleTable.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/IdentifiedExampleTable.java @@ -1,17 +1,13 @@ package net.thucydides.core.requirements.model.cucumber; -import gherkin.ast.Examples; -import gherkin.ast.Feature; -import gherkin.ast.ScenarioDefinition; -import gherkin.ast.ScenarioOutline; -import net.thucydides.core.requirements.reports.cucumber.RenderCucumber; +import io.cucumber.core.internal.gherkin.ast.Examples; +import io.cucumber.core.internal.gherkin.ast.Feature; +import io.cucumber.core.internal.gherkin.ast.ScenarioDefinition; +import io.cucumber.core.internal.gherkin.ast.ScenarioOutline; import java.util.Optional; -import java.util.stream.Collectors; -import static java.lang.System.lineSeparator; import static net.thucydides.core.requirements.model.cucumber.ScenarioDisplayOption.WithNoTitle; -import static net.thucydides.core.requirements.model.cucumber.ScenarioDisplayOption.WithTitle; public class IdentifiedExampleTable extends NamedExampleTable { private Feature feature; diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/IdentifiedScenario.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/IdentifiedScenario.java index a95f95256b..89e5978be3 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/IdentifiedScenario.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/IdentifiedScenario.java @@ -1,9 +1,11 @@ package net.thucydides.core.requirements.model.cucumber; -import gherkin.ast.*; +import io.cucumber.core.internal.gherkin.ast.Examples; +import io.cucumber.core.internal.gherkin.ast.Feature; +import io.cucumber.core.internal.gherkin.ast.ScenarioDefinition; +import io.cucumber.core.internal.gherkin.ast.ScenarioOutline; import net.thucydides.core.digest.Digest; import net.thucydides.core.requirements.reports.cucumber.RenderCucumber; -import org.apache.commons.lang3.StringUtils; import java.util.Optional; import java.util.stream.Collectors; diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/NamedExampleTable.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/NamedExampleTable.java index 8dde4460ea..f6c6846f57 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/NamedExampleTable.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/NamedExampleTable.java @@ -1,7 +1,9 @@ package net.thucydides.core.requirements.model.cucumber; -import gherkin.ast.Feature; -import gherkin.ast.ScenarioDefinition; + + +import io.cucumber.core.internal.gherkin.ast.Feature; +import io.cucumber.core.internal.gherkin.ast.ScenarioDefinition; import java.util.Optional; diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/NamedScenario.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/NamedScenario.java index 4fe3eb9842..9dfda819d1 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/NamedScenario.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/NamedScenario.java @@ -1,7 +1,7 @@ package net.thucydides.core.requirements.model.cucumber; -import gherkin.ast.Feature; -import gherkin.ast.ScenarioDefinition; +import io.cucumber.core.internal.gherkin.ast.Feature; +import io.cucumber.core.internal.gherkin.ast.ScenarioDefinition; import java.util.Optional; diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/NarrativeFromCucumberComments.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/NarrativeFromCucumberComments.java index 69710d9128..64a18e3694 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/NarrativeFromCucumberComments.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/NarrativeFromCucumberComments.java @@ -1,6 +1,7 @@ package net.thucydides.core.requirements.model.cucumber; -import gherkin.ast.Comment; + +import io.cucumber.core.internal.gherkin.ast.Comment; import java.util.List; diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ReferencedExampleTable.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ReferencedExampleTable.java index 8f8b5e04d3..935cdc7309 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ReferencedExampleTable.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ReferencedExampleTable.java @@ -1,8 +1,9 @@ package net.thucydides.core.requirements.model.cucumber; -import gherkin.ast.Feature; -import gherkin.ast.ScenarioDefinition; -import gherkin.ast.ScenarioOutline; + +import io.cucumber.core.internal.gherkin.ast.Feature; +import io.cucumber.core.internal.gherkin.ast.ScenarioDefinition; +import io.cucumber.core.internal.gherkin.ast.ScenarioOutline; /** * An example table that is mentioned by name in a feature narrative. diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ReferencedScenario.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ReferencedScenario.java index 1d49775a67..c4e8c7b3a5 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ReferencedScenario.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ReferencedScenario.java @@ -1,6 +1,7 @@ package net.thucydides.core.requirements.model.cucumber; -import gherkin.ast.Feature; + +import io.cucumber.core.internal.gherkin.ast.Feature; /** * A scenario that is mentioned by name in a feature narrative. diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/RenderedExampleTable.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/RenderedExampleTable.java index ace16c7677..cd52ba4fa1 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/RenderedExampleTable.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/RenderedExampleTable.java @@ -1,7 +1,8 @@ package net.thucydides.core.requirements.model.cucumber; -import gherkin.ast.Examples; -import gherkin.ast.TableCell; + +import io.cucumber.core.internal.gherkin.ast.Examples; +import io.cucumber.core.internal.gherkin.ast.TableCell; import org.apache.commons.lang3.StringUtils; import java.util.HashMap; @@ -32,7 +33,7 @@ private static String emptyStringOrValueOf(String description) { public static String renderedTable(Examples exampleTable, ExampleRowResultIcon exampleRowIcon) { - final Map maxColumnWidths = maxColumnWidthForColumnsIn(exampleTable); + final Map maxColumnWidths = maxColumnWidthForColumnsIn(exampleTable); String headings = cellRow(exampleTable.getTableHeader().getCells(), maxColumnWidths, @@ -56,7 +57,7 @@ private static String headerSeparator(Map maxColumnWidths) { } public static String cellRow(List cells, - Map maxColumnWidths, + Map maxColumnWidths, int lineNumber, RowResultIcon exampleRowResultIcons) { diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ScenarioReport.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ScenarioReport.java index 8ee0c948b0..9e98c3d841 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ScenarioReport.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/ScenarioReport.java @@ -1,6 +1,7 @@ package net.thucydides.core.requirements.model.cucumber; -import gherkin.ast.Feature; + +import io.cucumber.core.internal.gherkin.ast.Feature; import net.thucydides.core.model.ReportNamer; import net.thucydides.core.model.ReportType; diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/URIResource.java b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/URIResource.java new file mode 100644 index 0000000000..c4ad6ea81d --- /dev/null +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/model/cucumber/URIResource.java @@ -0,0 +1,31 @@ +package net.thucydides.core.requirements.model.cucumber; + +import io.cucumber.core.resource.Resource; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.Files; +import java.nio.file.Path; + +public class URIResource implements Resource +{ + private final Path resource; + + public URIResource(Path resource) + { + this.resource = resource; + } + + @Override + public URI getUri() + { + return resource.toUri(); + } + + @Override + public InputStream getInputStream() throws IOException + { + return Files.newInputStream(resource); + } +} diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/reports/cucumber/FeatureFileScenarioOutcomes.java b/serenity-model/src/main/java/net/thucydides/core/requirements/reports/cucumber/FeatureFileScenarioOutcomes.java index 86bfabaf4d..1c1ef1d97f 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/reports/cucumber/FeatureFileScenarioOutcomes.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/reports/cucumber/FeatureFileScenarioOutcomes.java @@ -1,6 +1,8 @@ package net.thucydides.core.requirements.reports.cucumber; -import gherkin.ast.*; + + +import io.cucumber.core.internal.gherkin.ast.*; import net.thucydides.core.ThucydidesSystemProperty; import net.thucydides.core.guice.Injectors; import net.thucydides.core.model.TestOutcome; @@ -100,7 +102,7 @@ private ScenarioOutcome scenarioOutcomeFrom(Feature feature, .forEach(filteredExamples::add); examples.stream().forEach( - example -> exampleTags.put(example.getName() + ":" + example.getLocation(),CucumberTagConverter.toSerenityTags(example.getTags())) + example -> exampleTags.put(example.getName() + ":" + example.getLocation(), CucumberTagConverter.toSerenityTags(example.getTags())) ); } diff --git a/serenity-model/src/main/java/net/thucydides/core/requirements/reports/cucumber/RenderCucumber.java b/serenity-model/src/main/java/net/thucydides/core/requirements/reports/cucumber/RenderCucumber.java index a4dc8e9b57..bdd1431744 100644 --- a/serenity-model/src/main/java/net/thucydides/core/requirements/reports/cucumber/RenderCucumber.java +++ b/serenity-model/src/main/java/net/thucydides/core/requirements/reports/cucumber/RenderCucumber.java @@ -1,6 +1,7 @@ package net.thucydides.core.requirements.reports.cucumber; -import gherkin.ast.*; + +import io.cucumber.core.internal.gherkin.ast.*; import net.thucydides.core.requirements.model.cucumber.ExampleRowResultIcon; import java.util.ArrayList; diff --git a/serenity-model/src/test/groovy/net/thucydides/core/requirements/model/cucumber/WhenLoadingIncorrectFeatureFiles.groovy b/serenity-model/src/test/groovy/net/thucydides/core/requirements/model/cucumber/WhenLoadingIncorrectFeatureFiles.groovy index 3819000435..3606369e9e 100644 --- a/serenity-model/src/test/groovy/net/thucydides/core/requirements/model/cucumber/WhenLoadingIncorrectFeatureFiles.groovy +++ b/serenity-model/src/test/groovy/net/thucydides/core/requirements/model/cucumber/WhenLoadingIncorrectFeatureFiles.groovy @@ -1,7 +1,6 @@ package net.thucydides.core.requirements.model.cucumber -import cucumber.runtime.io.MultiLoader -import cucumber.runtime.model.CucumberFeature + import spock.lang.Specification import static net.thucydides.core.requirements.model.cucumber.ScenarioDisplayOption.WithTitle diff --git a/serenity-model/src/test/groovy/net/thucydides/core/requirements/model/cucumber/WhenReferencingScenariosInAFeatureNarrative.groovy b/serenity-model/src/test/groovy/net/thucydides/core/requirements/model/cucumber/WhenReferencingScenariosInAFeatureNarrative.groovy index ebc26c88ed..8914b33c34 100644 --- a/serenity-model/src/test/groovy/net/thucydides/core/requirements/model/cucumber/WhenReferencingScenariosInAFeatureNarrative.groovy +++ b/serenity-model/src/test/groovy/net/thucydides/core/requirements/model/cucumber/WhenReferencingScenariosInAFeatureNarrative.groovy @@ -1,16 +1,35 @@ package net.thucydides.core.requirements.model.cucumber -import cucumber.runtime.io.MultiLoader -import cucumber.runtime.model.CucumberFeature +import io.cucumber.core.internal.gherkin.ast.Feature +import io.cucumber.core.internal.gherkin.ast.GherkinDocument +import io.cucumber.core.internal.gherkin.events.CucumberEvent +import io.cucumber.core.internal.gherkin.events.GherkinDocumentEvent +import io.cucumber.core.internal.gherkin.events.SourceEvent +import io.cucumber.core.internal.gherkin.stream.GherkinEvents +import io.cucumber.core.internal.gherkin.stream.SourceEvents import spock.lang.Specification -import static net.thucydides.core.requirements.model.cucumber.ScenarioDisplayOption.WithTitle - class WhenReferencingScenariosInAFeatureNarrative extends Specification { def featureFile = "src/test/resources/serenity-cucumber/features/maintain_my_todo_list/filtering_todos.feature" - def features = CucumberFeature.load(new MultiLoader(CucumberParser.class.getClassLoader()), [featureFile]) - def filteringTodoFeature = features[0].getGherkinFeature().feature + def features = loadCucumberFeatures([featureFile]) + def filteringTodoFeature = features[0] + + private static List loadCucumberFeatures(List listOfFiles) { + List loadedFeatures = new ArrayList<>(); + SourceEvents sourceEvents = new SourceEvents(listOfFiles); + GherkinEvents gherkinEvents = new GherkinEvents(true,true,true); + for (SourceEvent sourceEventEvent : sourceEvents) { + for (CucumberEvent cucumberEvent : gherkinEvents.iterable(sourceEventEvent)) { + if(cucumberEvent instanceof GherkinDocumentEvent) { + GherkinDocumentEvent gherkinDocumentEvent = (GherkinDocumentEvent)cucumberEvent; + GherkinDocument gherkinDocument = gherkinDocumentEvent.document; + loadedFeatures.add(gherkinDocument.getFeature()); + } + } + } + return loadedFeatures; + } def "Should be able to identify scenarios in a feature file by name"() { when: @@ -46,7 +65,7 @@ class WhenReferencingScenariosInAFeatureNarrative extends Specification { def "Should return the examples table alone for scenario outline if requested"() { when: - def examples = ReferencedScenario.in(filteringTodoFeature).withName("Do many things").asExampleTable(WithTitle) + def examples = ReferencedScenario.in(filteringTodoFeature).withName("Do many things").asExampleTable(ScenarioDisplayOption.WithTitle) then: examples.isPresent() and: @@ -72,7 +91,7 @@ class WhenReferencingScenariosInAFeatureNarrative extends Specification { def "Should not change lines with no scenario references"() { expect: - DescriptionWithScenarioReferences.from(filteringTodoFeature).forText("No scenario reference") == "No scenario reference" + DescriptionWithScenarioReferences.from(filteringTodoFeature).forText("No scenario reference") == "No scenario reference" } def "Should replace scenario references with the Given-When-Then text"() { @@ -124,7 +143,7 @@ Then her todo list should contain Walk the dog {result:Filtering things I nee def "should render narratives with example tables"() { given: - CucumberParser parser = new CucumberParser() + CucumberParser parser = new CucumberParser() when: def narrative = parser.loadFeatureNarrative(new File(featureFile)) then: From 35eb793ed6add20cbee0be7d8a343432d015b645 Mon Sep 17 00:00:00 2001 From: Liviu Carausu Date: Mon, 9 Mar 2020 23:15:52 +0100 Subject: [PATCH 2/3] upgrade to cucumber 5.4.2 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 549a11d3e6..d8b0f0f434 100644 --- a/gradle.properties +++ b/gradle.properties @@ -29,7 +29,7 @@ gsonVersion = 2.8.4 typesafeConfigVersion = 1.3.1 freemarkerVersion = 2.3.29 wiremockCoreVersion = 1.58 -cucumberVersion = 5.4.1 +cucumberVersion = 5.4.2 commonsLoggingVersion = 1.2 xmlApisVersion = 1.4.01 cglibVersion = 3.3.0 From 0be94f8befb03ec39c0044ccb9e9e98a20bab913 Mon Sep 17 00:00:00 2001 From: Pavlo Vlasenko Date: Thu, 12 Mar 2020 15:41:30 +0200 Subject: [PATCH 3/3] fix: issue 1997 - report correct number of junit retries --- .../java/net/thucydides/core/steps/BaseStepListener.java | 4 ++-- .../java/net/serenitybdd/junit/runners/SerenityRunner.java | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/serenity-core/src/main/java/net/thucydides/core/steps/BaseStepListener.java b/serenity-core/src/main/java/net/thucydides/core/steps/BaseStepListener.java index d6a5b681d4..5bc6520627 100644 --- a/serenity-core/src/main/java/net/thucydides/core/steps/BaseStepListener.java +++ b/serenity-core/src/main/java/net/thucydides/core/steps/BaseStepListener.java @@ -218,14 +218,14 @@ public void cancelPreviousTest() { } } - public void lastTestPassedAfterRetries(int remainingTries, List failureMessages, TestFailureCause testfailureCause) { + public void lastTestPassedAfterRetries(int attemptNum, List failureMessages, TestFailureCause testfailureCause) { if (latestTestOutcome().isPresent()) { latestTestOutcome().get().recordStep( TestStep.forStepCalled("UNSTABLE TEST:\n" + failureHistoryFor(failureMessages)) .withResult(UNDEFINED)); // .withResult(TestResult.IGNORED)); - latestTestOutcome().get().addTag(TestTag.withName("Retries: " + (remainingTries - 1)).andType("unstable test")); + latestTestOutcome().get().addTag(TestTag.withName("Retries: " + attemptNum).andType("unstable test")); latestTestOutcome().get().setFlakyTestFailureCause(testfailureCause); } } diff --git a/serenity-junit/src/main/java/net/serenitybdd/junit/runners/SerenityRunner.java b/serenity-junit/src/main/java/net/serenitybdd/junit/runners/SerenityRunner.java index cb2784513a..7b3f0ef209 100644 --- a/serenity-junit/src/main/java/net/serenitybdd/junit/runners/SerenityRunner.java +++ b/serenity-junit/src/main/java/net/serenitybdd/junit/runners/SerenityRunner.java @@ -446,14 +446,15 @@ private void retryAtMost(int remainingTries, RerunTest rerunTest) { if (remainingTries <= 0) { return; } - logger.info(rerunTest.toString() + ": attempt " + (maxRetries() - remainingTries)); + int attemptNum = maxRetries() - remainingTries + 1; + logger.info(rerunTest.toString() + ": attempt " + attemptNum); StepEventBus.getEventBus().cancelPreviousTest(); rerunTest.perform(); if (failureDetectingStepListener.lastTestFailed()) { retryAtMost(remainingTries - 1, rerunTest); } else { - StepEventBus.getEventBus().lastTestPassedAfterRetries(remainingTries, + StepEventBus.getEventBus().lastTestPassedAfterRetries(attemptNum, failureDetectingStepListener.getFailureMessages(),failureDetectingStepListener.getTestFailureCause()); } }