From 0b1bb90989ac9a29cf38d71e6f96ba9af2817502 Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Fri, 12 Sep 2025 14:50:40 +0300 Subject: [PATCH 01/12] Add a test --- .../cucumber/ScenarioOutlineTest.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/test/java/com/epam/reportportal/cucumber/ScenarioOutlineTest.java b/src/test/java/com/epam/reportportal/cucumber/ScenarioOutlineTest.java index 1de27e6..f7ebd13 100644 --- a/src/test/java/com/epam/reportportal/cucumber/ScenarioOutlineTest.java +++ b/src/test/java/com/epam/reportportal/cucumber/ScenarioOutlineTest.java @@ -60,6 +60,13 @@ public static class RunDynamicScenarioOutlineTitlesTest extends AbstractTestNGCu } + @CucumberOptions(features = "src/test/resources/features/BasicScenarioOutlineParameters.feature:11", glue = { + "com.epam.reportportal.cucumber.integration.feature" }, plugin = { + "com.epam.reportportal.cucumber.integration.TestScenarioReporter" }) + public static class RunSingleTestFromExamplesTest extends AbstractTestNGCucumberTests { + + } + private final String launchId = CommonUtils.namedId("launch_"); private final String suiteId = CommonUtils.namedId("suite_"); private final List testIds = Stream.generate(() -> CommonUtils.namedId("test_")).limit(3).collect(Collectors.toList()); @@ -120,4 +127,24 @@ items, hasItems( ) ); } + + public static final String[] SINGLE_STEP_NAMES = new String[] { "Given It is test with parameters", "When I have parameter \"second\"", + "Then I emit number 12345 on level info" }; + + @Test + public void verify_single_scenario_from_outline() { + TestUtils.runTests(RunSingleTestFromExamplesTest.class); + + verify(client, times(1)).startTestItem(any()); + ArgumentCaptor testCaptor = ArgumentCaptor.forClass(StartTestItemRQ.class); + verify(client, times(1)).startTestItem(same(suiteId), testCaptor.capture()); + + StartTestItemRQ item = testCaptor.getValue(); + assertThat(item.getName(), is("Scenario Outline: Test with different parameters")); + + ArgumentCaptor stepCaptor = ArgumentCaptor.forClass(StartTestItemRQ.class); + verify(client, times(3)).startTestItem(same(testIds.get(0)), stepCaptor.capture()); + List steps = stepCaptor.getAllValues(); + IntStream.range(0, steps.size()).forEach(i -> assertThat(steps.get(i).getName(), matchesPattern(SINGLE_STEP_NAMES[i]))); + } } From 13e407208cf2e2fcd21319b425aa7eeb1d7151d8 Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Fri, 12 Sep 2025 21:41:23 +0300 Subject: [PATCH 02/12] Add finished flag --- .../reportportal/cucumber/ScenarioContext.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/main/java/com/epam/reportportal/cucumber/ScenarioContext.java b/src/main/java/com/epam/reportportal/cucumber/ScenarioContext.java index 1151fbb..f3ba817 100644 --- a/src/main/java/com/epam/reportportal/cucumber/ScenarioContext.java +++ b/src/main/java/com/epam/reportportal/cucumber/ScenarioContext.java @@ -87,6 +87,8 @@ public class ScenarioContext { */ private HookSuite hookSuite; + private volatile boolean finished = false; + /** * Creates a new scenario context for a regular scenario. * @@ -247,4 +249,20 @@ public void setPreviousStep(@Nullable Step previousStep) { public Optional getPreviousStep() { return ofNullable(previousStep); } + + /** + * Sets the finished flag to true. + */ + public void finish() { + this.finished = true; + } + + /** + * Returns the finished flag. + * + * @return the finished flag + */ + public boolean finished(){ + return finished; + } } From f0c30f050d1a5f0abb518a1770025d45d8345a8e Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Sat, 13 Sep 2025 10:17:35 +0300 Subject: [PATCH 03/12] Refactoring --- .../cucumber/ScenarioReporter.java | 74 +++++++++++++------ 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java index bb9331c..56edbec 100644 --- a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java +++ b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java @@ -540,21 +540,11 @@ protected void afterHooksSuite(@Nonnull TestCase testCase) { * Put scenario end time in a map to check last scenario end time per feature * * @param event Cucumber's TestCaseFinished object + * @deprecated Use {@link} #handleFinishOfTestCase(TestCaseFinished)} instead */ + @Deprecated protected void afterScenario(TestCaseFinished event) { - TestCase testCase = event.getTestCase(); - afterHooksSuite(testCase); - execute( - testCase, (f, s) -> { - URI featureUri = f.getUri(); - if (mapItemStatus(event.getResult().getStatus()) == ItemStatus.FAILED) { - Optional.ofNullable(event.getResult().getError()).ifPresent(error -> errorMap.put(s.getId(), error)); - } - Date endTime = finishTestItem(s.getId(), mapItemStatus(event.getResult().getStatus()), null); - featureEndTime.put(featureUri, endTime); - removeFromTree(f.getFeature(), testCase); - } - ); + handleFinishOfTestCase(event); } /** @@ -985,7 +975,32 @@ private void addToTree(Feature feature, Maybe featureId) { } /** - * Starts a Cucumber Test Case start, also starts corresponding Feature if is not started already. + * Handles a Cucumber {@link TestSourceParsed} event by materializing feature metadata into + * internal context structures. For each node provided by the event: + *
    + *
  • If the node is a {@link Feature}, a new {@link FeatureContext} is created and stored + * in {@code featureContextMap} under the feature {@link URI}.
  • + *
  • For any other node type, a warning is logged and the node is ignored.
  • + *
+ * + * The populated {@code featureContextMap} is later used to resolve feature/rule/scenario + * contexts during execution. + * + * @param parseEvent the parsed source event containing Gherkin AST nodes for feature files + */ + protected void handleSourceEvents(TestSourceParsed parseEvent) { + parseEvent.getNodes().forEach(n -> { + if (n instanceof Feature) { + Feature feature = (Feature) n; + featureContextMap.put(feature.getUri(), new FeatureContext(feature)); + } else { + LOGGER.warn("Unknown node type: {}", n.getClass().getSimpleName()); + } + }); + } + + /** + * Start a Cucumber Test Case as a Scenario on ReportPortal, also starts corresponding Feature if is not started already. * * @param event Cucumber's Test Case started event object */ @@ -1012,15 +1027,26 @@ protected void handleStartOfTestCase(@Nonnull TestCaseStarted event) { ); } - protected void handleSourceEvents(TestSourceParsed parseEvent) { - parseEvent.getNodes().forEach(n -> { - if (n instanceof Feature) { - Feature feature = (Feature) n; - featureContextMap.put(feature.getUri(), new FeatureContext(feature)); - } else { - LOGGER.warn("Unknown node type: {}", n.getClass().getSimpleName()); - } - }); + /** + * Finish a Cucumber Test Case as a Scenario on ReportPortal, also finishes corresponding Feature if it's the last scenario. + * Put scenario end time in a map to check last scenario end time per feature. + * + * @param event Cucumber's TestCaseFinished object + */ + protected void handleFinishOfTestCase(TestCaseFinished event) { + TestCase testCase = event.getTestCase(); + afterHooksSuite(testCase); + execute( + testCase, (f, s) -> { + URI featureUri = f.getUri(); + if (mapItemStatus(event.getResult().getStatus()) == ItemStatus.FAILED) { + Optional.ofNullable(event.getResult().getError()).ifPresent(error -> errorMap.put(s.getId(), error)); + } + Date endTime = finishTestItem(s.getId(), mapItemStatus(event.getResult().getStatus()), null); + featureEndTime.put(featureUri, endTime); + removeFromTree(f.getFeature(), testCase); + } + ); } protected void handleTestStepStarted(@Nonnull TestStepStarted event) { @@ -1087,7 +1113,7 @@ protected EventHandler getTestStepFinishedHandler() { } protected EventHandler getTestCaseFinishedHandler() { - return this::afterScenario; + return this::handleFinishOfTestCase; } protected EventHandler getTestRunFinishedHandler() { From c983451f0bdae1e3da2607b3ebdcae9ffcd653b6 Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Mon, 15 Sep 2025 13:12:53 +0300 Subject: [PATCH 04/12] Refactoring --- .../cucumber/ScenarioReporter.java | 46 +++++++++++++------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java index 56edbec..858d22f 100644 --- a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java +++ b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java @@ -536,11 +536,30 @@ protected void afterHooksSuite(@Nonnull TestCase testCase) { } /** - * Finish Cucumber scenario - * Put scenario end time in a map to check last scenario end time per feature + * Finish Cucumber scenario. + * Put scenario end time in a map to check last scenario end time per feature. * + * @param testCase Cucumber's TestCase object + * @param status Cucumber's status object + * @param error the error being thrown by the Test Case (if any) + */ + protected void afterScenario(@Nonnull TestCase testCase, @Nullable Status status, @Nullable Throwable error) { + execute( + testCase, (f, s) -> { + URI featureUri = f.getUri(); + if (mapItemStatus(status) == ItemStatus.FAILED) { + Optional.ofNullable(error).ifPresent(e -> errorMap.put(s.getId(), e)); + } + Date endTime = finishTestItem(s.getId(), mapItemStatus(status), null); + featureEndTime.put(featureUri, endTime); + removeFromTree(f.getFeature(), testCase); + } + ); + } + + /** * @param event Cucumber's TestCaseFinished object - * @deprecated Use {@link} #handleFinishOfTestCase(TestCaseFinished)} instead + * @deprecated Use {@link #handleFinishOfTestCase(TestCaseFinished)} instead. */ @Deprecated protected void afterScenario(TestCaseFinished event) { @@ -974,6 +993,11 @@ private void addToTree(Feature feature, Maybe featureId) { getItemTree().getTestItems().put(createKey(feature.getUri()), TestItemTree.createTestItemLeaf(featureId)); } + protected void handleStartOfLaunch(TestRunStarted event) { + beforeLaunch(); + } + + /** * Handles a Cucumber {@link TestSourceParsed} event by materializing feature metadata into * internal context structures. For each node provided by the event: @@ -1035,18 +1059,10 @@ protected void handleStartOfTestCase(@Nonnull TestCaseStarted event) { */ protected void handleFinishOfTestCase(TestCaseFinished event) { TestCase testCase = event.getTestCase(); + Status status = event.getResult().getStatus(); + Throwable error = event.getResult().getError(); afterHooksSuite(testCase); - execute( - testCase, (f, s) -> { - URI featureUri = f.getUri(); - if (mapItemStatus(event.getResult().getStatus()) == ItemStatus.FAILED) { - Optional.ofNullable(event.getResult().getError()).ifPresent(error -> errorMap.put(s.getId(), error)); - } - Date endTime = finishTestItem(s.getId(), mapItemStatus(event.getResult().getStatus()), null); - featureEndTime.put(featureUri, endTime); - removeFromTree(f.getFeature(), testCase); - } - ); + afterScenario(testCase, status, error); } protected void handleTestStepStarted(@Nonnull TestStepStarted event) { @@ -1093,7 +1109,7 @@ protected void handleEndOfFeature() { } protected EventHandler getTestRunStartedHandler() { - return event -> beforeLaunch(); + return this::handleStartOfLaunch; } protected EventHandler getTestSourceParsedHandler() { From 03d1b513c6a5c3f9a81225512f6b9082bc9cb338 Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Mon, 15 Sep 2025 13:16:21 +0300 Subject: [PATCH 05/12] Do not create new Feature if created. --- .../java/com/epam/reportportal/cucumber/ScenarioReporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java index 858d22f..10f1597 100644 --- a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java +++ b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java @@ -1016,7 +1016,7 @@ protected void handleSourceEvents(TestSourceParsed parseEvent) { parseEvent.getNodes().forEach(n -> { if (n instanceof Feature) { Feature feature = (Feature) n; - featureContextMap.put(feature.getUri(), new FeatureContext(feature)); + featureContextMap.computeIfAbsent(feature.getUri(), u -> new FeatureContext(feature)); } else { LOGGER.warn("Unknown node type: {}", n.getClass().getSimpleName()); } From afb25aff61fc83f62a4e4a54e8e0d108332e5a44 Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Mon, 15 Sep 2025 13:25:37 +0300 Subject: [PATCH 06/12] Remove feature handler, add `finishFeature` method --- .../cucumber/ScenarioReporter.java | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java index 10f1597..b1a0a7d 100644 --- a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java +++ b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java @@ -989,6 +989,21 @@ protected Maybe startFeature(@Nonnull StartTestItemRQ startFeatureRq) { return getLaunch().startTestItem(startFeatureRq); } + private void removeFromTree(Feature feature) { + itemTree.getTestItems().remove(createKey(feature.getUri())); + } + + protected void finishFeature(FeatureContext f) { + //noinspection ReactiveStreamsUnusedPublisher + if (f.getId().equals(Maybe.empty())) { + return; + } + Date featureCompletionDateTime = featureEndTime.get(f.getUri()); + f.getCurrentRule().ifPresent(r -> finishTestItem(r.getId(), null, featureCompletionDateTime)); + finishTestItem(f.getId(), null, featureCompletionDateTime); + removeFromTree(f.getFeature()); + } + private void addToTree(Feature feature, Maybe featureId) { getItemTree().getTestItems().put(createKey(feature.getUri()), TestItemTree.createTestItemLeaf(featureId)); } @@ -997,7 +1012,6 @@ protected void handleStartOfLaunch(TestRunStarted event) { beforeLaunch(); } - /** * Handles a Cucumber {@link TestSourceParsed} event by materializing feature metadata into * internal context structures. For each node provided by the event: @@ -1006,7 +1020,7 @@ protected void handleStartOfLaunch(TestRunStarted event) { * in {@code featureContextMap} under the feature {@link URI}. *
  • For any other node type, a warning is logged and the node is ignored.
  • * - * + *

    * The populated {@code featureContextMap} is later used to resolve feature/rule/scenario * contexts during execution. * @@ -1090,24 +1104,6 @@ protected void handleTestStepFinished(@Nonnull TestStepFinished event) { } } - private void removeFromTree(Feature feature) { - itemTree.getTestItems().remove(createKey(feature.getUri())); - } - - protected void handleEndOfFeature() { - featureContextMap.values().forEach(f -> { - //noinspection ReactiveStreamsUnusedPublisher - if (f.getId().equals(Maybe.empty())) { - return; - } - Date featureCompletionDateTime = featureEndTime.get(f.getUri()); - f.getCurrentRule().ifPresent(r -> finishTestItem(r.getId(), null, featureCompletionDateTime)); - finishTestItem(f.getId(), null, featureCompletionDateTime); - removeFromTree(f.getFeature()); - }); - featureContextMap.clear(); - } - protected EventHandler getTestRunStartedHandler() { return this::handleStartOfLaunch; } @@ -1133,10 +1129,7 @@ protected EventHandler getTestCaseFinishedHandler() { } protected EventHandler getTestRunFinishedHandler() { - return event -> { - handleEndOfFeature(); - afterLaunch(); - }; + return event -> afterLaunch(); } protected EventHandler getEmbedEventHandler() { From fddbe301a8ccdc09eb66ac7f6d7d35305dc1792c Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Mon, 15 Sep 2025 13:35:43 +0300 Subject: [PATCH 07/12] Implement feature finish by scenario count --- .../reportportal/cucumber/FeatureContext.java | 9 +++++++++ .../cucumber/ScenarioReporter.java | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/main/java/com/epam/reportportal/cucumber/FeatureContext.java b/src/main/java/com/epam/reportportal/cucumber/FeatureContext.java index 3e2f4dd..8fa98f7 100644 --- a/src/main/java/com/epam/reportportal/cucumber/FeatureContext.java +++ b/src/main/java/com/epam/reportportal/cucumber/FeatureContext.java @@ -104,4 +104,13 @@ public Optional getCurrentRule() { public void setCurrentRule(@Nonnull RuleContext rule) { currentRule = rule; } + + /** + * Checks whether all scenarios in this feature have finished execution. + * + * @return true if every {@link ScenarioContext} is marked as finished, false otherwise + */ + public boolean isComplete() { + return scenarios.values().stream().allMatch(ScenarioContext::finished); + } } diff --git a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java index b1a0a7d..a991454 100644 --- a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java +++ b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java @@ -552,11 +552,28 @@ protected void afterScenario(@Nonnull TestCase testCase, @Nullable Status status } Date endTime = finishTestItem(s.getId(), mapItemStatus(status), null); featureEndTime.put(featureUri, endTime); + s.finish(); removeFromTree(f.getFeature(), testCase); } ); } + /** + * Verifies whether all scenarios for the feature are finished and + * finishes the feature when appropriate. + * + * @param testCase Cucumber's TestCase object + */ + protected void afterFeature(@Nonnull TestCase testCase) { + execute( + testCase, (f, s) -> { + if (f.isComplete()) { + finishFeature(f); + } + } + ); + } + /** * @param event Cucumber's TestCaseFinished object * @deprecated Use {@link #handleFinishOfTestCase(TestCaseFinished)} instead. @@ -1077,6 +1094,7 @@ protected void handleFinishOfTestCase(TestCaseFinished event) { Throwable error = event.getResult().getError(); afterHooksSuite(testCase); afterScenario(testCase, status, error); + afterFeature(testCase); } protected void handleTestStepStarted(@Nonnull TestStepStarted event) { From dcc95bc0c0ee6d00eb3c7297b495ac4bf97d9ea3 Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Mon, 15 Sep 2025 13:49:29 +0300 Subject: [PATCH 08/12] Refactoring --- .../cucumber/ScenarioReporter.java | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java index a991454..748012b 100644 --- a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java +++ b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java @@ -558,6 +558,21 @@ protected void afterScenario(@Nonnull TestCase testCase, @Nullable Status status ); } + private void removeFromTree(Feature feature) { + itemTree.getTestItems().remove(createKey(feature.getUri())); + } + + protected void finishFeature(FeatureContext f) { + //noinspection ReactiveStreamsUnusedPublisher + if (f.getId().equals(Maybe.empty())) { + return; + } + Date featureCompletionDateTime = featureEndTime.get(f.getUri()); + f.getCurrentRule().ifPresent(r -> finishTestItem(r.getId(), null, featureCompletionDateTime)); + finishTestItem(f.getId(), null, featureCompletionDateTime); + removeFromTree(f.getFeature()); + } + /** * Verifies whether all scenarios for the feature are finished and * finishes the feature when appropriate. @@ -1006,21 +1021,6 @@ protected Maybe startFeature(@Nonnull StartTestItemRQ startFeatureRq) { return getLaunch().startTestItem(startFeatureRq); } - private void removeFromTree(Feature feature) { - itemTree.getTestItems().remove(createKey(feature.getUri())); - } - - protected void finishFeature(FeatureContext f) { - //noinspection ReactiveStreamsUnusedPublisher - if (f.getId().equals(Maybe.empty())) { - return; - } - Date featureCompletionDateTime = featureEndTime.get(f.getUri()); - f.getCurrentRule().ifPresent(r -> finishTestItem(r.getId(), null, featureCompletionDateTime)); - finishTestItem(f.getId(), null, featureCompletionDateTime); - removeFromTree(f.getFeature()); - } - private void addToTree(Feature feature, Maybe featureId) { getItemTree().getTestItems().put(createKey(feature.getUri()), TestItemTree.createTestItemLeaf(featureId)); } From 3e8ce4b4f97cd2d59568dd7a21117dfa3234fc2b Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Mon, 15 Sep 2025 15:10:13 +0300 Subject: [PATCH 09/12] Refactoring --- .../cucumber/ScenarioReporter.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java index 748012b..09ec1f8 100644 --- a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java +++ b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java @@ -1029,6 +1029,10 @@ protected void handleStartOfLaunch(TestRunStarted event) { beforeLaunch(); } + protected void handleFinishOfLaunch(TestRunFinished event) { + afterLaunch(); + } + /** * Handles a Cucumber {@link TestSourceParsed} event by materializing feature metadata into * internal context structures. For each node provided by the event: @@ -1122,6 +1126,14 @@ protected void handleTestStepFinished(@Nonnull TestStepFinished event) { } } + protected void handleEmbedEvent(EmbedEvent event) { + embedding(event.getName(), event.getMediaType(), event.getData()); + } + + protected void handleWriteEvent(WriteEvent event) { + sendLog(event.getText()); + } + protected EventHandler getTestRunStartedHandler() { return this::handleStartOfLaunch; } @@ -1147,15 +1159,15 @@ protected EventHandler getTestCaseFinishedHandler() { } protected EventHandler getTestRunFinishedHandler() { - return event -> afterLaunch(); + return this::handleFinishOfLaunch; } protected EventHandler getEmbedEventHandler() { - return event -> embedding(event.getName(), event.getMediaType(), event.getData()); + return this::handleEmbedEvent; } protected EventHandler getWriteEventHandler() { - return event -> sendLog(event.getText()); + return this::handleWriteEvent; } /** From ef670026c33633dfd2dd6a7bfd94509bf41a841d Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Mon, 15 Sep 2025 15:15:34 +0300 Subject: [PATCH 10/12] Add Javadocs to handle* event methods in ScenarioReporter. Clarify behavior for launch start/finish, test case start/finish, step start/finish, and embed/write handlers to improve maintainability and API comprehension. --- .../cucumber/ScenarioReporter.java | 61 +++++++++++++++++-- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java index 09ec1f8..2cda094 100644 --- a/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java +++ b/src/main/java/com/epam/reportportal/cucumber/ScenarioReporter.java @@ -1025,10 +1025,24 @@ private void addToTree(Feature feature, Maybe featureId) { getItemTree().getTestItems().put(createKey(feature.getUri()), TestItemTree.createTestItemLeaf(featureId)); } + /** + * Handles the Cucumber {@link TestRunStarted} event by initializing the ReportPortal launch. + *

    + * Delegates to {@link #beforeLaunch()} to ensure the launch is created and prepared. + * + * @param event the test run started event + */ protected void handleStartOfLaunch(TestRunStarted event) { beforeLaunch(); } + /** + * Handles the Cucumber {@link TestRunFinished} event by finalizing the ReportPortal launch. + *

    + * Delegates to {@link #afterLaunch()} to properly close the launch. + * + * @param event the test run finished event + */ protected void handleFinishOfLaunch(TestRunFinished event) { afterLaunch(); } @@ -1059,9 +1073,13 @@ protected void handleSourceEvents(TestSourceParsed parseEvent) { } /** - * Start a Cucumber Test Case as a Scenario on ReportPortal, also starts corresponding Feature if is not started already. + * Handles a Cucumber {@link TestCaseStarted} event by preparing and starting + * corresponding Feature/Rule/Scenario items in ReportPortal. + *

    + * Ensures the Feature is started (if not already), then initializes Scenario + * context and starts the Scenario item. * - * @param event Cucumber's Test Case started event object + * @param event the test case started event */ protected void handleStartOfTestCase(@Nonnull TestCaseStarted event) { TestCase testCase = event.getTestCase(); @@ -1087,10 +1105,13 @@ protected void handleStartOfTestCase(@Nonnull TestCaseStarted event) { } /** - * Finish a Cucumber Test Case as a Scenario on ReportPortal, also finishes corresponding Feature if it's the last scenario. - * Put scenario end time in a map to check last scenario end time per feature. + * Handles a Cucumber {@link TestCaseFinished} event by finishing the Scenario + * and, if applicable, completing the Feature when all scenarios are done. + *

    + * Performs hooks suite finalization, reports the Scenario result, and + * triggers feature completion checks. * - * @param event Cucumber's TestCaseFinished object + * @param event the test case finished event */ protected void handleFinishOfTestCase(TestCaseFinished event) { TestCase testCase = event.getTestCase(); @@ -1101,6 +1122,14 @@ protected void handleFinishOfTestCase(TestCaseFinished event) { afterFeature(testCase); } + /** + * Handles a Cucumber {@link TestStepStarted} event. + *

    + * Starts a corresponding hook or step item depending on the runtime type of the step. + * Logs a warning for unknown step types. + * + * @param event the test step started event + */ protected void handleTestStepStarted(@Nonnull TestStepStarted event) { TestStep testStep = event.getTestStep(); TestCase testCase = event.getTestCase(); @@ -1114,6 +1143,14 @@ protected void handleTestStepStarted(@Nonnull TestStepStarted event) { } } + /** + * Handles a Cucumber {@link TestStepFinished} event. + *

    + * Finalizes the corresponding hook or step item depending on the runtime type of the step. + * Logs a warning for unknown step types. + * + * @param event the test step finished event + */ protected void handleTestStepFinished(@Nonnull TestStepFinished event) { TestStep testStep = event.getTestStep(); TestCase testCase = event.getTestCase(); @@ -1126,10 +1163,24 @@ protected void handleTestStepFinished(@Nonnull TestStepFinished event) { } } + /** + * Handles a Cucumber {@link EmbedEvent} by forwarding the attachment to ReportPortal. + *

    + * Delegates to {@link #embedding(String, String, byte[])} to send the data. + * + * @param event the embed event containing name, media type and data + */ protected void handleEmbedEvent(EmbedEvent event) { embedding(event.getName(), event.getMediaType(), event.getData()); } + /** + * Handles a Cucumber {@link WriteEvent} by sending the provided text to ReportPortal. + *

    + * Delegates to {@link #sendLog(String)}. + * + * @param event the write event carrying the text to log + */ protected void handleWriteEvent(WriteEvent event) { sendLog(event.getText()); } From 4389dca07d3724046e272c1203dd940ed4e8f82d Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Mon, 15 Sep 2025 15:17:55 +0300 Subject: [PATCH 11/12] .gitignore update --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index beed998..8163e50 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ test-output/ # IntelliJ Idea files .idea/ *.iml + +# VSCode +.vscode/ \ No newline at end of file From eb8bdd92961e5b848d628060d11a12b775e97e38 Mon Sep 17 00:00:00 2001 From: Vadzim Hushchanskou Date: Mon, 15 Sep 2025 23:42:04 +0300 Subject: [PATCH 12/12] Client version update --- CHANGELOG.md | 3 +++ build.gradle | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 82985de..c901ea0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog ## [Unreleased] +### Changed +- Features now finish on the last scenario finish, not on the end of the Launch, by @HardNorth +- Client version updated on [5.3.16](https://github.com/reportportal/client-java/releases/tag/5.3.16), by @HardNorth ## [5.3.8] ### Fixed diff --git a/build.gradle b/build.gradle index 679716a..bef3e6d 100644 --- a/build.gradle +++ b/build.gradle @@ -40,7 +40,7 @@ repositories { } dependencies { - api 'com.epam.reportportal:client-java:5.3.15' + api 'com.epam.reportportal:client-java:5.3.16' implementation "io.cucumber:cucumber-gherkin:${project.cucumber_version}" implementation 'org.slf4j:slf4j-api:2.0.7'