From 22648c5672f3565d8e4bb53eb92b99b823d1d605 Mon Sep 17 00:00:00 2001 From: savkk Date: Wed, 4 Oct 2023 22:46:23 +0300 Subject: [PATCH] cucumber 6 and 7 support --- pom.xml | 6 +- qase-api/README.md | 2 +- qase-api/pom.xml | 2 +- qase-cucumber3-jvm/README.md | 2 +- qase-cucumber3-jvm/pom.xml | 2 +- qase-cucumber4-jvm/README.md | 2 +- qase-cucumber4-jvm/pom.xml | 2 +- qase-cucumber5-jvm/README.md | 2 +- qase-cucumber5-jvm/pom.xml | 2 +- qase-cucumber6-jvm/README.md | 69 +++++ qase-cucumber6-jvm/pom.xml | 42 +++ .../io/qase/cucumber6/QaseEventListener.java | 146 +++++++++ .../configuration/Cucumber6ApiConfigurer.java | 26 ++ .../guice/module/Cucumber6Module.java | 24 ++ .../cucumber6/QaseEventListenerTests.java | 280 ++++++++++++++++++ .../test/java/io/qase/cucumber6/Steps.java | 22 ++ .../test/resources/features/failed.feature | 5 + .../features/failed_with_time.feature | 6 + .../test/resources/features/new_case.feature | 5 + .../test/resources/features/success.feature | 5 + .../features/success_with_time.feature | 6 + qase-cucumber7-jvm/README.md | 69 +++++ qase-cucumber7-jvm/pom.xml | 42 +++ .../io/qase/cucumber7/QaseEventListener.java | 146 +++++++++ .../configuration/Cucumber7ApiConfigurer.java | 26 ++ .../guice/module/Cucumber7Module.java | 24 ++ .../cucumber7/QaseEventListenerTests.java | 280 ++++++++++++++++++ .../test/java/io/qase/cucumber7/Steps.java | 22 ++ .../test/resources/features/failed.feature | 5 + .../features/failed_with_time.feature | 6 + .../test/resources/features/new_case.feature | 5 + .../test/resources/features/success.feature | 5 + .../features/success_with_time.feature | 6 + qase-junit4-aspect/pom.xml | 2 +- qase-junit4/README.md | 8 +- qase-junit4/pom.xml | 2 +- qase-junit5/README.md | 4 +- qase-junit5/pom.xml | 2 +- .../java/io/qase/junit5/samples/Multiple.java | 4 + qase-testng/README.md | 4 +- qase-testng/pom.xml | 2 +- 41 files changed, 1301 insertions(+), 21 deletions(-) create mode 100644 qase-cucumber6-jvm/README.md create mode 100644 qase-cucumber6-jvm/pom.xml create mode 100644 qase-cucumber6-jvm/src/main/java/io/qase/cucumber6/QaseEventListener.java create mode 100644 qase-cucumber6-jvm/src/main/java/io/qase/cucumber6/configuration/Cucumber6ApiConfigurer.java create mode 100644 qase-cucumber6-jvm/src/main/java/io/qase/cucumber6/guice/module/Cucumber6Module.java create mode 100644 qase-cucumber6-jvm/src/test/java/io/qase/cucumber6/QaseEventListenerTests.java create mode 100644 qase-cucumber6-jvm/src/test/java/io/qase/cucumber6/Steps.java create mode 100644 qase-cucumber6-jvm/src/test/resources/features/failed.feature create mode 100644 qase-cucumber6-jvm/src/test/resources/features/failed_with_time.feature create mode 100644 qase-cucumber6-jvm/src/test/resources/features/new_case.feature create mode 100644 qase-cucumber6-jvm/src/test/resources/features/success.feature create mode 100644 qase-cucumber6-jvm/src/test/resources/features/success_with_time.feature create mode 100644 qase-cucumber7-jvm/README.md create mode 100644 qase-cucumber7-jvm/pom.xml create mode 100644 qase-cucumber7-jvm/src/main/java/io/qase/cucumber7/QaseEventListener.java create mode 100644 qase-cucumber7-jvm/src/main/java/io/qase/cucumber7/configuration/Cucumber7ApiConfigurer.java create mode 100644 qase-cucumber7-jvm/src/main/java/io/qase/cucumber7/guice/module/Cucumber7Module.java create mode 100644 qase-cucumber7-jvm/src/test/java/io/qase/cucumber7/QaseEventListenerTests.java create mode 100644 qase-cucumber7-jvm/src/test/java/io/qase/cucumber7/Steps.java create mode 100644 qase-cucumber7-jvm/src/test/resources/features/failed.feature create mode 100644 qase-cucumber7-jvm/src/test/resources/features/failed_with_time.feature create mode 100644 qase-cucumber7-jvm/src/test/resources/features/new_case.feature create mode 100644 qase-cucumber7-jvm/src/test/resources/features/success.feature create mode 100644 qase-cucumber7-jvm/src/test/resources/features/success_with_time.feature diff --git a/pom.xml b/pom.xml index 0523fb74..ce4b76a5 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ io.qase qase-java pom - 3.1.0 + 3.1.1 qase-api qase-testng @@ -16,6 +16,8 @@ qase-cucumber3-jvm qase-cucumber4-jvm qase-cucumber5-jvm + qase-cucumber6-jvm + qase-cucumber7-jvm qase-junit4-aspect @@ -38,6 +40,8 @@ 3.0.2 4.8.1 5.7.0 + 6.11.0 + 7.14.0 2.0.0 1.9.8 2.13.4 diff --git a/qase-api/README.md b/qase-api/README.md index e751d6fe..1ae0a41a 100644 --- a/qase-api/README.md +++ b/qase-api/README.md @@ -8,7 +8,7 @@ Add the following dependency and repository to your pom.xml: io.qase qase-api - 3.1.0 + 3.1.1 ``` diff --git a/qase-api/pom.xml b/qase-api/pom.xml index c69c293a..a58f31c3 100644 --- a/qase-api/pom.xml +++ b/qase-api/pom.xml @@ -5,7 +5,7 @@ qase-java io.qase - 3.1.0 + 3.1.1 4.0.0 diff --git a/qase-cucumber3-jvm/README.md b/qase-cucumber3-jvm/README.md index 3bd9fa82..3eb5d017 100644 --- a/qase-cucumber3-jvm/README.md +++ b/qase-cucumber3-jvm/README.md @@ -42,7 +42,7 @@ Add the following dependency and repository to your pom.xml: io.qase qase-cucumber3-jvm - 3.1.0 + 3.1.1 diff --git a/qase-cucumber3-jvm/pom.xml b/qase-cucumber3-jvm/pom.xml index 6c2b9c36..c27395a9 100644 --- a/qase-cucumber3-jvm/pom.xml +++ b/qase-cucumber3-jvm/pom.xml @@ -5,7 +5,7 @@ qase-java io.qase - 3.1.0 + 3.1.1 4.0.0 diff --git a/qase-cucumber4-jvm/README.md b/qase-cucumber4-jvm/README.md index c963d5b9..33805594 100644 --- a/qase-cucumber4-jvm/README.md +++ b/qase-cucumber4-jvm/README.md @@ -42,7 +42,7 @@ Add the following dependency and repository to your pom.xml: io.qase qase-cucumber4-jvm - 3.1.0 + 3.1.1 diff --git a/qase-cucumber4-jvm/pom.xml b/qase-cucumber4-jvm/pom.xml index 3c32597b..01233292 100644 --- a/qase-cucumber4-jvm/pom.xml +++ b/qase-cucumber4-jvm/pom.xml @@ -5,7 +5,7 @@ qase-java io.qase - 3.1.0 + 3.1.1 4.0.0 diff --git a/qase-cucumber5-jvm/README.md b/qase-cucumber5-jvm/README.md index c9b0c949..7a3c1368 100644 --- a/qase-cucumber5-jvm/README.md +++ b/qase-cucumber5-jvm/README.md @@ -42,7 +42,7 @@ Add the following dependency and repository to your pom.xml: io.qase qase-cucumber5-jvm - 3.1.0 + 3.1.1 diff --git a/qase-cucumber5-jvm/pom.xml b/qase-cucumber5-jvm/pom.xml index d49337b8..21c6cbd4 100644 --- a/qase-cucumber5-jvm/pom.xml +++ b/qase-cucumber5-jvm/pom.xml @@ -5,7 +5,7 @@ qase-java io.qase - 3.1.0 + 3.1.1 4.0.0 diff --git a/qase-cucumber6-jvm/README.md b/qase-cucumber6-jvm/README.md new file mode 100644 index 00000000..786c3568 --- /dev/null +++ b/qase-cucumber6-jvm/README.md @@ -0,0 +1,69 @@ +# Qase TMS Cucumber 5 JVM Integration # +[![License](https://lxgaming.github.io/badges/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +## Description ## +This integration uploads test run results to Qase TMS via API. + +To link autotest to test case in Qase TMS use tag `@caseId` or `@tmsLink` + +For example: +``` +Feature: example features + + @caseId=59 + Scenario: example scenario + Given example step + When example step + Then example step + + + @tmsLink=55 + Scenario: example scenario2 + Given example step2 + When example step2 + Then example step2 +``` + +### Required params ### +All required params are passed through system properties or environment variables: + +| Key | Description | +| :---------------: | :----------: | +| QASE_ENABLE | Enable Integration | +| QASE_PROJECT_CODE | Project Code | +| QASE_RUN_ID | Run Id | +| QASE_API_TOKEN | Qase API Token | + +## Maven ## + +Add the following dependency and repository to your pom.xml: +```xml + + + io.qase + qase-cucumber6-jvm + 3.1.1 + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" -Dcucumber.plugin="io.qase.cucumber5.QaseEventListener" + + + + + org.aspectj + aspectjweaver + ${aspectj.version} + + + + + +``` diff --git a/qase-cucumber6-jvm/pom.xml b/qase-cucumber6-jvm/pom.xml new file mode 100644 index 00000000..7e2a2344 --- /dev/null +++ b/qase-cucumber6-jvm/pom.xml @@ -0,0 +1,42 @@ + + + + qase-java + io.qase + 3.1.1 + + 4.0.0 + + qase-cucumber6-jvm + + Cucumber 6 Integration with Qase TMS + + + + io.qase + qase-api + ${project.parent.version} + + + org.junit.jupiter + junit-jupiter-engine + test + + + com.github.tomakehurst + wiremock-jre8 + test + + + io.cucumber + cucumber-java + ${cucumber6-java.version} + + + org.projectlombok + lombok + + + diff --git a/qase-cucumber6-jvm/src/main/java/io/qase/cucumber6/QaseEventListener.java b/qase-cucumber6-jvm/src/main/java/io/qase/cucumber6/QaseEventListener.java new file mode 100644 index 00000000..e9a933dd --- /dev/null +++ b/qase-cucumber6-jvm/src/main/java/io/qase/cucumber6/QaseEventListener.java @@ -0,0 +1,146 @@ +package io.qase.cucumber6; + +import io.cucumber.plugin.ConcurrentEventListener; +import io.cucumber.plugin.event.*; +import io.qase.api.QaseClient; +import io.qase.api.StepStorage; +import io.qase.api.config.QaseConfig; +import io.qase.api.services.QaseTestCaseListener; +import io.qase.api.utils.CucumberUtils; +import io.qase.api.utils.IntegrationUtils; +import io.qase.client.model.ResultCreate; +import io.qase.client.model.ResultCreate.StatusEnum; +import io.qase.client.model.ResultCreateCase; +import io.qase.client.model.ResultCreateStepsInner; +import io.qase.cucumber6.guice.module.Cucumber6Module; +import lombok.AccessLevel; +import lombok.Getter; + +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +import static io.qase.api.utils.IntegrationUtils.getStacktrace; + +public class QaseEventListener implements ConcurrentEventListener { + + private static final String REPORTER_NAME = "Cucumber 6-JVM"; + + @Getter(lazy = true, value = AccessLevel.PRIVATE) + private final QaseTestCaseListener qaseTestCaseListener = createQaseListener(); + + static { + System.setProperty(QaseConfig.QASE_CLIENT_REPORTER_NAME_KEY, REPORTER_NAME); + } + + @Override + public void setEventPublisher(EventPublisher publisher) { + if (QaseClient.isEnabled()) { + publisher.registerHandlerFor(TestCaseStarted.class, this::testCaseStarted); + publisher.registerHandlerFor(TestCaseFinished.class, this::testCaseFinished); + publisher.registerHandlerFor(TestRunFinished.class, this::testRunFinished); + publisher.registerHandlerFor(TestStepFinished.class, this::testStepFinished); + publisher.registerHandlerFor(TestStepStarted.class, this::testCaseStarted); + } + } + + private void testCaseStarted(TestStepStarted testStepStarted) { + if (testStepStarted.getTestStep() instanceof PickleStepTestStep) { + StepStorage.startStep(); + } + } + + private void testStepFinished(TestStepFinished testStepFinished) { + if (testStepFinished.getTestStep() instanceof PickleStepTestStep) { + PickleStepTestStep step = (PickleStepTestStep) testStepFinished.getTestStep(); + String stepText = step.getStep().getKeyWord() + step.getStep().getText(); + Result result = testStepFinished.getResult(); + switch (result.getStatus()) { + case PASSED: + StepStorage.getCurrentStep() + .action(stepText) + .status(ResultCreateStepsInner.StatusEnum.PASSED); + StepStorage.stopStep(); + break; + case SKIPPED: + break; + case PENDING: + break; + case UNDEFINED: + break; + case AMBIGUOUS: + break; + case FAILED: + StepStorage.getCurrentStep() + .action(stepText) + .status(ResultCreateStepsInner.StatusEnum.FAILED) + .addAttachmentsItem(IntegrationUtils.getStacktrace(result.getError())); + StepStorage.stopStep(); + break; + case UNUSED: + break; + } + } + } + + private void testRunFinished(TestRunFinished testRunFinished) { + getQaseTestCaseListener().onTestCasesSetFinished(); + } + + private void testCaseStarted(TestCaseStarted event) { + getQaseTestCaseListener().onTestCaseStarted(); + } + + private void testCaseFinished(TestCaseFinished event) { + getQaseTestCaseListener().onTestCaseFinished(resultCreate -> setupResultItem(resultCreate, event)); + } + + private void setupResultItem(ResultCreate resultCreate, TestCaseFinished event) { + List tags = event.getTestCase().getTags(); + Long caseId = CucumberUtils.getCaseId(tags); + + String caseTitle = null; + if (caseId == null) { + caseTitle = event.getTestCase().getName(); + } + + StatusEnum status = convertStatus(event.getResult().getStatus()); + Optional optionalThrowable = Optional.ofNullable(event.getResult().getError()); + String comment = optionalThrowable + .flatMap(throwable -> Optional.of(throwable.toString())).orElse(null); + Boolean isDefect = optionalThrowable + .flatMap(throwable -> Optional.of(throwable instanceof AssertionError)) + .orElse(false); + String stacktrace = optionalThrowable + .flatMap(throwable -> Optional.of(getStacktrace(throwable))).orElse(null); + LinkedList steps = StepStorage.stopSteps(); + resultCreate + ._case(caseTitle == null ? null : new ResultCreateCase().title(caseTitle)) + .caseId(caseId) + .status(status) + .comment(comment) + .stacktrace(stacktrace) + .steps(steps.isEmpty() ? null : steps) + .defect(isDefect); + } + + private StatusEnum convertStatus(Status status) { + switch (status) { + case FAILED: + return StatusEnum.FAILED; + case PASSED: + return StatusEnum.PASSED; + case PENDING: + case SKIPPED: + case AMBIGUOUS: + case UNDEFINED: + case UNUSED: + default: + return StatusEnum.SKIPPED; + } + } + + private static QaseTestCaseListener createQaseListener() { + return Cucumber6Module.getInjector().getInstance(QaseTestCaseListener.class); + } +} diff --git a/qase-cucumber6-jvm/src/main/java/io/qase/cucumber6/configuration/Cucumber6ApiConfigurer.java b/qase-cucumber6-jvm/src/main/java/io/qase/cucumber6/configuration/Cucumber6ApiConfigurer.java new file mode 100644 index 00000000..a4efd2d0 --- /dev/null +++ b/qase-cucumber6-jvm/src/main/java/io/qase/cucumber6/configuration/Cucumber6ApiConfigurer.java @@ -0,0 +1,26 @@ +package io.qase.cucumber6.configuration; + +import com.google.inject.Singleton; +import io.cucumber.java.After; +import io.qase.api.config.apiclient.DefaultHeadersApiConfigurer; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Singleton +@NoArgsConstructor(access = AccessLevel.PUBLIC) +public class Cucumber6ApiConfigurer extends DefaultHeadersApiConfigurer { + + private static final Class CUCUMBER_FRAMEWORK_CLASS = After.class; + + private static final String CUCUMBER_FRAMEWORK_NAME = "cucumber6"; + + @Override + protected String getFrameworkName() { + return CUCUMBER_FRAMEWORK_NAME; + } + + @Override + protected String getFrameworkVersion() { + return getFrameworkVersionByClassOrUnknown(CUCUMBER_FRAMEWORK_CLASS); + } +} diff --git a/qase-cucumber6-jvm/src/main/java/io/qase/cucumber6/guice/module/Cucumber6Module.java b/qase-cucumber6-jvm/src/main/java/io/qase/cucumber6/guice/module/Cucumber6Module.java new file mode 100644 index 00000000..fe8f2aeb --- /dev/null +++ b/qase-cucumber6-jvm/src/main/java/io/qase/cucumber6/guice/module/Cucumber6Module.java @@ -0,0 +1,24 @@ +package io.qase.cucumber6.guice.module; + +import com.google.inject.AbstractModule; +import com.google.inject.Injector; +import com.google.inject.Provides; +import io.qase.api.config.apiclient.ApiClientConfigurer; +import io.qase.cucumber6.configuration.Cucumber6ApiConfigurer; +import io.qase.guice.Injectors; +import lombok.Getter; + +public class Cucumber6Module extends AbstractModule { + + @Getter + private static final Injector injector = initializeInjector(); + + @Provides + public ApiClientConfigurer apiClientConfigurer() { + return new Cucumber6ApiConfigurer(); + } + + private static Injector initializeInjector() { + return Injectors.createDefaultInjectorWithOverridingModules(new Cucumber6Module()); + } +} diff --git a/qase-cucumber6-jvm/src/test/java/io/qase/cucumber6/QaseEventListenerTests.java b/qase-cucumber6-jvm/src/test/java/io/qase/cucumber6/QaseEventListenerTests.java new file mode 100644 index 00000000..e73c83b7 --- /dev/null +++ b/qase-cucumber6-jvm/src/test/java/io/qase/cucumber6/QaseEventListenerTests.java @@ -0,0 +1,280 @@ +package io.qase.cucumber6; + + +import com.github.tomakehurst.wiremock.WireMockServer; +import io.cucumber.core.cli.Main; +import io.qase.api.utils.TestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static io.qase.api.utils.TestUtils.useBulk; + +class QaseEventListenerTests { + static final WireMockServer wireMockServer = new WireMockServer(options().port(8088)); + + @BeforeAll + static void setUp() { + configureFor(8088); + wireMockServer.start(); + System.setProperty("cucumber.publish.quiet", "true"); + TestUtils.setupQaseTestEnvironmentVariables(wireMockServer.port()); + } + + @AfterAll + static void tearDown() { + wireMockServer.stop(); + } + + @AfterEach + void resetRequests() { + wireMockServer.resetRequests(); + } + + @Test + void newCase() { + useBulk(false); + String[] args = new String[]{ + "-g", "io.qase.cucumber6", + "--plugin", "io.qase.cucumber6.QaseEventListener", + "classpath:features/new_case.feature" + }; + Main.run(args, Thread.currentThread().getContextClassLoader()); + verify(postRequestedFor(urlPathEqualTo("/v1/result/PRJ/777")) + .withHeader("Token", equalTo("secret-token")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(equalToJson("{\n" + + " \"case\" : {\n" + + " \"title\" : \"Failed scenario\"\n" + + " },\n" + + " \"status\" : \"failed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : true,\n" + + " \"stacktrace\" : \"${json-unit.ignore}\",\n" + + " \"comment\" : \"java.lang.AssertionError\",\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given success step\"\n" + + " }, {\n" + + " \"position\" : 2,\n" + + " \"status\" : \"failed\",\n" + + " \"attachments\" : \"${json-unit.ignore}\"," + + " \"action\" : \"Given failed step\"\n" + + " } ]\n" + + "}"))); + } + + @Test + void bulk() { + useBulk(true); + String[] args = new String[]{ + "-g", "io.qase.cucumber6", + "--plugin", "io.qase.cucumber6.QaseEventListener", + "classpath:features/" + }; + Main.run(args, Thread.currentThread().getContextClassLoader()); + + verify(postRequestedFor(urlPathEqualTo("/v1/result/PRJ/777/bulk")) + .withHeader("Token", equalTo("secret-token")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(equalToJson("{\n" + + " \"results\" : [ {\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"failed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : true,\n" + + " \"stacktrace\" : \"${json-unit.ignore}\",\n" + + " \"comment\" : \"java.lang.AssertionError\",\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"failed\",\n" + + " \"attachments\" : \"${json-unit.ignore}\"," + + " \"action\" : \"Given failed step\"\n" + + " } ]\n" + + " }, {\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"failed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : true,\n" + + " \"stacktrace\" : \"${json-unit.ignore}\",\n" + + " \"comment\" : \"java.lang.AssertionError\",\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"When timeout 3 seconds\"\n" + + " }, {\n" + + " \"position\" : 2,\n" + + " \"status\" : \"failed\",\n" + + " \"attachments\" : \"${json-unit.ignore}\"," + + " \"action\" : \"Given failed step\"\n" + + " } ]\n" + + " }, {\n" + + " \"case\" : {\n" + + " \"title\" : \"Failed scenario\"\n" + + " },\n" + + " \"status\" : \"failed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : true,\n" + + " \"stacktrace\" : \"${json-unit.ignore}\",\n" + + " \"comment\" : \"java.lang.AssertionError\",\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given success step\"\n" + + " }, {\n" + + " \"position\" : 2,\n" + + " \"status\" : \"failed\",\n" + + " \"attachments\" : \"${json-unit.ignore}\"," + + " \"action\" : \"Given failed step\"\n" + + " } ]\n" + + " }, {\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"passed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : false,\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given success step\"\n" + + " } ]\n" + + " }, {\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"passed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : false,\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given timeout 5 seconds\"\n" + + " }, {\n" + + " \"position\" : 2,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given success step\"\n" + + " } ]\n" + + " } ]\n" + + "}"))); + } + + @Test + void success() { + useBulk(false); + String[] args = new String[]{ + "-g", "io.qase.cucumber6", + "--plugin", "io.qase.cucumber6.QaseEventListener", + "classpath:features/success.feature" + }; + Main.run(args, Thread.currentThread().getContextClassLoader()); + + verify(postRequestedFor(urlPathEqualTo("/v1/result/PRJ/777")) + .withHeader("Token", equalTo("secret-token")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(equalToJson("{\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"passed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : false,\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given success step\"\n" + + " } ]" + + "}"))); + + } + + @Test + void successWithTime() { + useBulk(false); + String[] args = new String[]{ + "-g", "io.qase.cucumber6", + "--plugin", "io.qase.cucumber6.QaseEventListener", + "classpath:features/success_with_time.feature" + }; + Main.run(args, Thread.currentThread().getContextClassLoader()); + + verify(postRequestedFor(urlPathEqualTo("/v1/result/PRJ/777")) + .withHeader("Token", equalTo("secret-token")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(equalToJson("{\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"passed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : false,\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given timeout 5 seconds\"\n" + + " }, {\n" + + " \"position\" : 2,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given success step\"\n" + + " } ]\n" + + "}"))); + } + + @Test + void failed() { + useBulk(false); + String[] args = new String[]{ + "-g", "io.qase.cucumber6", + "--plugin", "io.qase.cucumber6.QaseEventListener", + "classpath:features/failed.feature" + }; + Main.run(args, Thread.currentThread().getContextClassLoader()); + + verify(postRequestedFor(urlPathEqualTo("/v1/result/PRJ/777")) + .withHeader("Token", equalTo("secret-token")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(equalToJson("{\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"failed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : true,\n" + + " \"stacktrace\" : \"${json-unit.ignore}\",\n" + + " \"comment\" : \"java.lang.AssertionError\",\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"failed\",\n" + + " \"attachments\" : \"${json-unit.ignore}\"," + + " \"action\" : \"Given failed step\"\n" + + " } ]\n" + + "}"))); + } + + @Test + void failedWithTime() { + useBulk(false); + String[] args = new String[]{ + "-g", "io.qase.cucumber6", + "--plugin", "io.qase.cucumber6.QaseEventListener", + "classpath:features/failed_with_time.feature" + }; + Main.run(args, Thread.currentThread().getContextClassLoader()); + + verify(postRequestedFor(urlPathEqualTo("/v1/result/PRJ/777")) + .withHeader("Token", equalTo("secret-token")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(equalToJson("{\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"failed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : true,\n" + + " \"stacktrace\" : \"${json-unit.ignore}\",\n" + + " \"comment\" : \"java.lang.AssertionError\",\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"When timeout 3 seconds\"\n" + + " }, {\n" + + " \"position\" : 2,\n" + + " \"status\" : \"failed\",\n" + + " \"attachments\" : \"${json-unit.ignore}\"," + + " \"action\" : \"Given failed step\"\n" + + " } ]\n" + + "}"))); + } +} diff --git a/qase-cucumber6-jvm/src/test/java/io/qase/cucumber6/Steps.java b/qase-cucumber6-jvm/src/test/java/io/qase/cucumber6/Steps.java new file mode 100644 index 00000000..668e45e5 --- /dev/null +++ b/qase-cucumber6-jvm/src/test/java/io/qase/cucumber6/Steps.java @@ -0,0 +1,22 @@ +package io.qase.cucumber6; + + +import io.cucumber.java.en.Given; + +import java.util.concurrent.TimeUnit; + +public class Steps { + @Given("success step") + public void success() { + } + + @Given("timeout {int} seconds") + public void success(int integer) throws InterruptedException { + TimeUnit.SECONDS.sleep(integer); + } + + @Given("failed step") + public void failed() { + throw new AssertionError(); + } +} diff --git a/qase-cucumber6-jvm/src/test/resources/features/failed.feature b/qase-cucumber6-jvm/src/test/resources/features/failed.feature new file mode 100644 index 00000000..5ca08c08 --- /dev/null +++ b/qase-cucumber6-jvm/src/test/resources/features/failed.feature @@ -0,0 +1,5 @@ +Feature: Failed feature + + @caseId=123 + Scenario: Failed scenario + Given failed step \ No newline at end of file diff --git a/qase-cucumber6-jvm/src/test/resources/features/failed_with_time.feature b/qase-cucumber6-jvm/src/test/resources/features/failed_with_time.feature new file mode 100644 index 00000000..85139e7d --- /dev/null +++ b/qase-cucumber6-jvm/src/test/resources/features/failed_with_time.feature @@ -0,0 +1,6 @@ +Feature: Failed feature + + @caseId=123 + Scenario: Failed scenario + When timeout 3 seconds + Given failed step diff --git a/qase-cucumber6-jvm/src/test/resources/features/new_case.feature b/qase-cucumber6-jvm/src/test/resources/features/new_case.feature new file mode 100644 index 00000000..5e671422 --- /dev/null +++ b/qase-cucumber6-jvm/src/test/resources/features/new_case.feature @@ -0,0 +1,5 @@ +Feature: Failed feature + + Scenario: Failed scenario + Given success step + Given failed step diff --git a/qase-cucumber6-jvm/src/test/resources/features/success.feature b/qase-cucumber6-jvm/src/test/resources/features/success.feature new file mode 100644 index 00000000..06c196c8 --- /dev/null +++ b/qase-cucumber6-jvm/src/test/resources/features/success.feature @@ -0,0 +1,5 @@ +Feature: Success feature + + @caseId=123 + Scenario: Success scenario + Given success step \ No newline at end of file diff --git a/qase-cucumber6-jvm/src/test/resources/features/success_with_time.feature b/qase-cucumber6-jvm/src/test/resources/features/success_with_time.feature new file mode 100644 index 00000000..ee3c3d03 --- /dev/null +++ b/qase-cucumber6-jvm/src/test/resources/features/success_with_time.feature @@ -0,0 +1,6 @@ +Feature: Success feature + + @caseId=123 + Scenario: Success scenario + Given timeout 5 seconds + Given success step diff --git a/qase-cucumber7-jvm/README.md b/qase-cucumber7-jvm/README.md new file mode 100644 index 00000000..8cfd7e22 --- /dev/null +++ b/qase-cucumber7-jvm/README.md @@ -0,0 +1,69 @@ +# Qase TMS Cucumber 5 JVM Integration # +[![License](https://lxgaming.github.io/badges/License-Apache%202.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0) + +## Description ## +This integration uploads test run results to Qase TMS via API. + +To link autotest to test case in Qase TMS use tag `@caseId` or `@tmsLink` + +For example: +``` +Feature: example features + + @caseId=59 + Scenario: example scenario + Given example step + When example step + Then example step + + + @tmsLink=55 + Scenario: example scenario2 + Given example step2 + When example step2 + Then example step2 +``` + +### Required params ### +All required params are passed through system properties or environment variables: + +| Key | Description | +| :---------------: | :----------: | +| QASE_ENABLE | Enable Integration | +| QASE_PROJECT_CODE | Project Code | +| QASE_RUN_ID | Run Id | +| QASE_API_TOKEN | Qase API Token | + +## Maven ## + +Add the following dependency and repository to your pom.xml: +```xml + + + io.qase + qase-cucumber7-jvm + 3.1.1 + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" -Dcucumber.plugin="io.qase.cucumber5.QaseEventListener" + + + + + org.aspectj + aspectjweaver + ${aspectj.version} + + + + + +``` diff --git a/qase-cucumber7-jvm/pom.xml b/qase-cucumber7-jvm/pom.xml new file mode 100644 index 00000000..c81443b1 --- /dev/null +++ b/qase-cucumber7-jvm/pom.xml @@ -0,0 +1,42 @@ + + + + qase-java + io.qase + 3.1.1 + + 4.0.0 + + qase-cucumber7-jvm + + Cucumber 7 Integration with Qase TMS + + + + io.qase + qase-api + ${project.parent.version} + + + org.junit.jupiter + junit-jupiter-engine + test + + + com.github.tomakehurst + wiremock-jre8 + test + + + io.cucumber + cucumber-java + ${cucumber7-java.version} + + + org.projectlombok + lombok + + + diff --git a/qase-cucumber7-jvm/src/main/java/io/qase/cucumber7/QaseEventListener.java b/qase-cucumber7-jvm/src/main/java/io/qase/cucumber7/QaseEventListener.java new file mode 100644 index 00000000..b401a024 --- /dev/null +++ b/qase-cucumber7-jvm/src/main/java/io/qase/cucumber7/QaseEventListener.java @@ -0,0 +1,146 @@ +package io.qase.cucumber7; + +import io.cucumber.plugin.ConcurrentEventListener; +import io.cucumber.plugin.event.*; +import io.qase.api.QaseClient; +import io.qase.api.StepStorage; +import io.qase.api.config.QaseConfig; +import io.qase.api.services.QaseTestCaseListener; +import io.qase.api.utils.CucumberUtils; +import io.qase.api.utils.IntegrationUtils; +import io.qase.client.model.ResultCreate; +import io.qase.client.model.ResultCreate.StatusEnum; +import io.qase.client.model.ResultCreateCase; +import io.qase.client.model.ResultCreateStepsInner; +import io.qase.cucumber7.guice.module.Cucumber7Module; +import lombok.AccessLevel; +import lombok.Getter; + +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; + +import static io.qase.api.utils.IntegrationUtils.getStacktrace; + +public class QaseEventListener implements ConcurrentEventListener { + + private static final String REPORTER_NAME = "Cucumber 7-JVM"; + + @Getter(lazy = true, value = AccessLevel.PRIVATE) + private final QaseTestCaseListener qaseTestCaseListener = createQaseListener(); + + static { + System.setProperty(QaseConfig.QASE_CLIENT_REPORTER_NAME_KEY, REPORTER_NAME); + } + + @Override + public void setEventPublisher(EventPublisher publisher) { + if (QaseClient.isEnabled()) { + publisher.registerHandlerFor(TestCaseStarted.class, this::testCaseStarted); + publisher.registerHandlerFor(TestCaseFinished.class, this::testCaseFinished); + publisher.registerHandlerFor(TestRunFinished.class, this::testRunFinished); + publisher.registerHandlerFor(TestStepFinished.class, this::testStepFinished); + publisher.registerHandlerFor(TestStepStarted.class, this::testCaseStarted); + } + } + + private void testCaseStarted(TestStepStarted testStepStarted) { + if (testStepStarted.getTestStep() instanceof PickleStepTestStep) { + StepStorage.startStep(); + } + } + + private void testStepFinished(TestStepFinished testStepFinished) { + if (testStepFinished.getTestStep() instanceof PickleStepTestStep) { + PickleStepTestStep step = (PickleStepTestStep) testStepFinished.getTestStep(); + String stepText = step.getStep().getKeyWord() + step.getStep().getText(); + Result result = testStepFinished.getResult(); + switch (result.getStatus()) { + case PASSED: + StepStorage.getCurrentStep() + .action(stepText) + .status(ResultCreateStepsInner.StatusEnum.PASSED); + StepStorage.stopStep(); + break; + case SKIPPED: + break; + case PENDING: + break; + case UNDEFINED: + break; + case AMBIGUOUS: + break; + case FAILED: + StepStorage.getCurrentStep() + .action(stepText) + .status(ResultCreateStepsInner.StatusEnum.FAILED) + .addAttachmentsItem(IntegrationUtils.getStacktrace(result.getError())); + StepStorage.stopStep(); + break; + case UNUSED: + break; + } + } + } + + private void testRunFinished(TestRunFinished testRunFinished) { + getQaseTestCaseListener().onTestCasesSetFinished(); + } + + private void testCaseStarted(TestCaseStarted event) { + getQaseTestCaseListener().onTestCaseStarted(); + } + + private void testCaseFinished(TestCaseFinished event) { + getQaseTestCaseListener().onTestCaseFinished(resultCreate -> setupResultItem(resultCreate, event)); + } + + private void setupResultItem(ResultCreate resultCreate, TestCaseFinished event) { + List tags = event.getTestCase().getTags(); + Long caseId = CucumberUtils.getCaseId(tags); + + String caseTitle = null; + if (caseId == null) { + caseTitle = event.getTestCase().getName(); + } + + StatusEnum status = convertStatus(event.getResult().getStatus()); + Optional optionalThrowable = Optional.ofNullable(event.getResult().getError()); + String comment = optionalThrowable + .flatMap(throwable -> Optional.of(throwable.toString())).orElse(null); + Boolean isDefect = optionalThrowable + .flatMap(throwable -> Optional.of(throwable instanceof AssertionError)) + .orElse(false); + String stacktrace = optionalThrowable + .flatMap(throwable -> Optional.of(getStacktrace(throwable))).orElse(null); + LinkedList steps = StepStorage.stopSteps(); + resultCreate + ._case(caseTitle == null ? null : new ResultCreateCase().title(caseTitle)) + .caseId(caseId) + .status(status) + .comment(comment) + .stacktrace(stacktrace) + .steps(steps.isEmpty() ? null : steps) + .defect(isDefect); + } + + private StatusEnum convertStatus(Status status) { + switch (status) { + case FAILED: + return StatusEnum.FAILED; + case PASSED: + return StatusEnum.PASSED; + case PENDING: + case SKIPPED: + case AMBIGUOUS: + case UNDEFINED: + case UNUSED: + default: + return StatusEnum.SKIPPED; + } + } + + private static QaseTestCaseListener createQaseListener() { + return Cucumber7Module.getInjector().getInstance(QaseTestCaseListener.class); + } +} diff --git a/qase-cucumber7-jvm/src/main/java/io/qase/cucumber7/configuration/Cucumber7ApiConfigurer.java b/qase-cucumber7-jvm/src/main/java/io/qase/cucumber7/configuration/Cucumber7ApiConfigurer.java new file mode 100644 index 00000000..d65d260e --- /dev/null +++ b/qase-cucumber7-jvm/src/main/java/io/qase/cucumber7/configuration/Cucumber7ApiConfigurer.java @@ -0,0 +1,26 @@ +package io.qase.cucumber7.configuration; + +import com.google.inject.Singleton; +import io.cucumber.java.After; +import io.qase.api.config.apiclient.DefaultHeadersApiConfigurer; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +@Singleton +@NoArgsConstructor(access = AccessLevel.PUBLIC) +public class Cucumber7ApiConfigurer extends DefaultHeadersApiConfigurer { + + private static final Class CUCUMBER_FRAMEWORK_CLASS = After.class; + + private static final String CUCUMBER_FRAMEWORK_NAME = "cucumber7"; + + @Override + protected String getFrameworkName() { + return CUCUMBER_FRAMEWORK_NAME; + } + + @Override + protected String getFrameworkVersion() { + return getFrameworkVersionByClassOrUnknown(CUCUMBER_FRAMEWORK_CLASS); + } +} diff --git a/qase-cucumber7-jvm/src/main/java/io/qase/cucumber7/guice/module/Cucumber7Module.java b/qase-cucumber7-jvm/src/main/java/io/qase/cucumber7/guice/module/Cucumber7Module.java new file mode 100644 index 00000000..f901fb40 --- /dev/null +++ b/qase-cucumber7-jvm/src/main/java/io/qase/cucumber7/guice/module/Cucumber7Module.java @@ -0,0 +1,24 @@ +package io.qase.cucumber7.guice.module; + +import com.google.inject.AbstractModule; +import com.google.inject.Injector; +import com.google.inject.Provides; +import io.qase.api.config.apiclient.ApiClientConfigurer; +import io.qase.cucumber7.configuration.Cucumber7ApiConfigurer; +import io.qase.guice.Injectors; +import lombok.Getter; + +public class Cucumber7Module extends AbstractModule { + + @Getter + private static final Injector injector = initializeInjector(); + + @Provides + public ApiClientConfigurer apiClientConfigurer() { + return new Cucumber7ApiConfigurer(); + } + + private static Injector initializeInjector() { + return Injectors.createDefaultInjectorWithOverridingModules(new Cucumber7Module()); + } +} diff --git a/qase-cucumber7-jvm/src/test/java/io/qase/cucumber7/QaseEventListenerTests.java b/qase-cucumber7-jvm/src/test/java/io/qase/cucumber7/QaseEventListenerTests.java new file mode 100644 index 00000000..243738a1 --- /dev/null +++ b/qase-cucumber7-jvm/src/test/java/io/qase/cucumber7/QaseEventListenerTests.java @@ -0,0 +1,280 @@ +package io.qase.cucumber7; + + +import com.github.tomakehurst.wiremock.WireMockServer; +import io.cucumber.core.cli.Main; +import io.qase.api.utils.TestUtils; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; +import static io.qase.api.utils.TestUtils.useBulk; + +class QaseEventListenerTests { + static final WireMockServer wireMockServer = new WireMockServer(options().port(8088)); + + @BeforeAll + static void setUp() { + configureFor(8088); + wireMockServer.start(); + System.setProperty("cucumber.publish.quiet", "true"); + TestUtils.setupQaseTestEnvironmentVariables(wireMockServer.port()); + } + + @AfterAll + static void tearDown() { + wireMockServer.stop(); + } + + @AfterEach + void resetRequests() { + wireMockServer.resetRequests(); + } + + @Test + void newCase() { + useBulk(false); + String[] args = new String[]{ + "-g", "io.qase.cucumber7", + "--plugin", "io.qase.cucumber7.QaseEventListener", + "classpath:features/new_case.feature" + }; + Main.run(args, Thread.currentThread().getContextClassLoader()); + verify(postRequestedFor(urlPathEqualTo("/v1/result/PRJ/777")) + .withHeader("Token", equalTo("secret-token")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(equalToJson("{\n" + + " \"case\" : {\n" + + " \"title\" : \"Failed scenario\"\n" + + " },\n" + + " \"status\" : \"failed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : true,\n" + + " \"stacktrace\" : \"${json-unit.ignore}\",\n" + + " \"comment\" : \"java.lang.AssertionError\",\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given success step\"\n" + + " }, {\n" + + " \"position\" : 2,\n" + + " \"status\" : \"failed\",\n" + + " \"attachments\" : \"${json-unit.ignore}\"," + + " \"action\" : \"Given failed step\"\n" + + " } ]\n" + + "}"))); + } + + @Test + void bulk() { + useBulk(true); + String[] args = new String[]{ + "-g", "io.qase.cucumber7", + "--plugin", "io.qase.cucumber7.QaseEventListener", + "classpath:features/" + }; + Main.run(args, Thread.currentThread().getContextClassLoader()); + + verify(postRequestedFor(urlPathEqualTo("/v1/result/PRJ/777/bulk")) + .withHeader("Token", equalTo("secret-token")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(equalToJson("{\n" + + " \"results\" : [ {\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"failed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : true,\n" + + " \"stacktrace\" : \"${json-unit.ignore}\",\n" + + " \"comment\" : \"java.lang.AssertionError\",\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"failed\",\n" + + " \"attachments\" : \"${json-unit.ignore}\"," + + " \"action\" : \"Given failed step\"\n" + + " } ]\n" + + " }, {\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"failed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : true,\n" + + " \"stacktrace\" : \"${json-unit.ignore}\",\n" + + " \"comment\" : \"java.lang.AssertionError\",\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"When timeout 3 seconds\"\n" + + " }, {\n" + + " \"position\" : 2,\n" + + " \"status\" : \"failed\",\n" + + " \"attachments\" : \"${json-unit.ignore}\"," + + " \"action\" : \"Given failed step\"\n" + + " } ]\n" + + " }, {\n" + + " \"case\" : {\n" + + " \"title\" : \"Failed scenario\"\n" + + " },\n" + + " \"status\" : \"failed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : true,\n" + + " \"stacktrace\" : \"${json-unit.ignore}\",\n" + + " \"comment\" : \"java.lang.AssertionError\",\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given success step\"\n" + + " }, {\n" + + " \"position\" : 2,\n" + + " \"status\" : \"failed\",\n" + + " \"attachments\" : \"${json-unit.ignore}\"," + + " \"action\" : \"Given failed step\"\n" + + " } ]\n" + + " }, {\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"passed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : false,\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given success step\"\n" + + " } ]\n" + + " }, {\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"passed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : false,\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given timeout 5 seconds\"\n" + + " }, {\n" + + " \"position\" : 2,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given success step\"\n" + + " } ]\n" + + " } ]\n" + + "}"))); + } + + @Test + void success() { + useBulk(false); + String[] args = new String[]{ + "-g", "io.qase.cucumber7", + "--plugin", "io.qase.cucumber7.QaseEventListener", + "classpath:features/success.feature" + }; + Main.run(args, Thread.currentThread().getContextClassLoader()); + + verify(postRequestedFor(urlPathEqualTo("/v1/result/PRJ/777")) + .withHeader("Token", equalTo("secret-token")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(equalToJson("{\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"passed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : false,\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given success step\"\n" + + " } ]" + + "}"))); + + } + + @Test + void successWithTime() { + useBulk(false); + String[] args = new String[]{ + "-g", "io.qase.cucumber7", + "--plugin", "io.qase.cucumber7.QaseEventListener", + "classpath:features/success_with_time.feature" + }; + Main.run(args, Thread.currentThread().getContextClassLoader()); + + verify(postRequestedFor(urlPathEqualTo("/v1/result/PRJ/777")) + .withHeader("Token", equalTo("secret-token")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(equalToJson("{\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"passed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : false,\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given timeout 5 seconds\"\n" + + " }, {\n" + + " \"position\" : 2,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"Given success step\"\n" + + " } ]\n" + + "}"))); + } + + @Test + void failed() { + useBulk(false); + String[] args = new String[]{ + "-g", "io.qase.cucumber7", + "--plugin", "io.qase.cucumber7.QaseEventListener", + "classpath:features/failed.feature" + }; + Main.run(args, Thread.currentThread().getContextClassLoader()); + + verify(postRequestedFor(urlPathEqualTo("/v1/result/PRJ/777")) + .withHeader("Token", equalTo("secret-token")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(equalToJson("{\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"failed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : true,\n" + + " \"stacktrace\" : \"${json-unit.ignore}\",\n" + + " \"comment\" : \"java.lang.AssertionError\",\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"failed\",\n" + + " \"attachments\" : \"${json-unit.ignore}\"," + + " \"action\" : \"Given failed step\"\n" + + " } ]\n" + + "}"))); + } + + @Test + void failedWithTime() { + useBulk(false); + String[] args = new String[]{ + "-g", "io.qase.cucumber7", + "--plugin", "io.qase.cucumber7.QaseEventListener", + "classpath:features/failed_with_time.feature" + }; + Main.run(args, Thread.currentThread().getContextClassLoader()); + + verify(postRequestedFor(urlPathEqualTo("/v1/result/PRJ/777")) + .withHeader("Token", equalTo("secret-token")) + .withHeader("Content-Type", equalTo("application/json; charset=UTF-8")) + .withRequestBody(equalToJson("{\n" + + " \"case_id\" : 123,\n" + + " \"status\" : \"failed\",\n" + + " \"time_ms\" : \"${json-unit.ignore}\",\n" + + " \"defect\" : true,\n" + + " \"stacktrace\" : \"${json-unit.ignore}\",\n" + + " \"comment\" : \"java.lang.AssertionError\",\n" + + " \"steps\" : [ {\n" + + " \"position\" : 1,\n" + + " \"status\" : \"passed\",\n" + + " \"action\" : \"When timeout 3 seconds\"\n" + + " }, {\n" + + " \"position\" : 2,\n" + + " \"status\" : \"failed\",\n" + + " \"attachments\" : \"${json-unit.ignore}\"," + + " \"action\" : \"Given failed step\"\n" + + " } ]\n" + + "}"))); + } +} diff --git a/qase-cucumber7-jvm/src/test/java/io/qase/cucumber7/Steps.java b/qase-cucumber7-jvm/src/test/java/io/qase/cucumber7/Steps.java new file mode 100644 index 00000000..b60c543b --- /dev/null +++ b/qase-cucumber7-jvm/src/test/java/io/qase/cucumber7/Steps.java @@ -0,0 +1,22 @@ +package io.qase.cucumber7; + + +import io.cucumber.java.en.Given; + +import java.util.concurrent.TimeUnit; + +public class Steps { + @Given("success step") + public void success() { + } + + @Given("timeout {int} seconds") + public void success(int integer) throws InterruptedException { + TimeUnit.SECONDS.sleep(integer); + } + + @Given("failed step") + public void failed() { + throw new AssertionError(); + } +} diff --git a/qase-cucumber7-jvm/src/test/resources/features/failed.feature b/qase-cucumber7-jvm/src/test/resources/features/failed.feature new file mode 100644 index 00000000..5ca08c08 --- /dev/null +++ b/qase-cucumber7-jvm/src/test/resources/features/failed.feature @@ -0,0 +1,5 @@ +Feature: Failed feature + + @caseId=123 + Scenario: Failed scenario + Given failed step \ No newline at end of file diff --git a/qase-cucumber7-jvm/src/test/resources/features/failed_with_time.feature b/qase-cucumber7-jvm/src/test/resources/features/failed_with_time.feature new file mode 100644 index 00000000..85139e7d --- /dev/null +++ b/qase-cucumber7-jvm/src/test/resources/features/failed_with_time.feature @@ -0,0 +1,6 @@ +Feature: Failed feature + + @caseId=123 + Scenario: Failed scenario + When timeout 3 seconds + Given failed step diff --git a/qase-cucumber7-jvm/src/test/resources/features/new_case.feature b/qase-cucumber7-jvm/src/test/resources/features/new_case.feature new file mode 100644 index 00000000..5e671422 --- /dev/null +++ b/qase-cucumber7-jvm/src/test/resources/features/new_case.feature @@ -0,0 +1,5 @@ +Feature: Failed feature + + Scenario: Failed scenario + Given success step + Given failed step diff --git a/qase-cucumber7-jvm/src/test/resources/features/success.feature b/qase-cucumber7-jvm/src/test/resources/features/success.feature new file mode 100644 index 00000000..06c196c8 --- /dev/null +++ b/qase-cucumber7-jvm/src/test/resources/features/success.feature @@ -0,0 +1,5 @@ +Feature: Success feature + + @caseId=123 + Scenario: Success scenario + Given success step \ No newline at end of file diff --git a/qase-cucumber7-jvm/src/test/resources/features/success_with_time.feature b/qase-cucumber7-jvm/src/test/resources/features/success_with_time.feature new file mode 100644 index 00000000..ee3c3d03 --- /dev/null +++ b/qase-cucumber7-jvm/src/test/resources/features/success_with_time.feature @@ -0,0 +1,6 @@ +Feature: Success feature + + @caseId=123 + Scenario: Success scenario + Given timeout 5 seconds + Given success step diff --git a/qase-junit4-aspect/pom.xml b/qase-junit4-aspect/pom.xml index 214a06ef..8f8fcfd0 100644 --- a/qase-junit4-aspect/pom.xml +++ b/qase-junit4-aspect/pom.xml @@ -6,7 +6,7 @@ io.qase qase-java - 3.1.0 + 3.1.1 qase-junit4-aspect diff --git a/qase-junit4/README.md b/qase-junit4/README.md index b817fdf9..6e3bfb91 100644 --- a/qase-junit4/README.md +++ b/qase-junit4/README.md @@ -12,7 +12,7 @@ Add the following dependency and repository to your pom.xml: io.qase qase-junit4 - 3.1.0 + 3.1.1 test @@ -63,9 +63,9 @@ configurations { dependencies { aspectjweaver "org.aspectj:aspectjweaver:1.9.8" - testImplementation 'io.qase:qase-api:3.1.0' - testImplementation 'io.qase:qase-junit4:3.1.0' - testImplementation 'io.qase:qase-junit4-aspect:3.1.0' + testImplementation 'io.qase:qase-api:3.1.1' + testImplementation 'io.qase:qase-junit4:3.1.1' + testImplementation 'io.qase:qase-junit4-aspect:3.1.1' testImplementation 'junit:junit:4.13.2' } diff --git a/qase-junit4/pom.xml b/qase-junit4/pom.xml index 6e485d4f..6c9ec320 100644 --- a/qase-junit4/pom.xml +++ b/qase-junit4/pom.xml @@ -5,7 +5,7 @@ qase-java io.qase - 3.1.0 + 3.1.1 4.0.0 diff --git a/qase-junit5/README.md b/qase-junit5/README.md index e323263b..6cc19052 100644 --- a/qase-junit5/README.md +++ b/qase-junit5/README.md @@ -12,7 +12,7 @@ Add the following dependency and repository to your pom.xml: io.qase qase-junit5 - 3.1.0 + 3.1.1 @@ -55,7 +55,7 @@ add the below code to build.gradle: ``` dependencies { ... - testImplementation 'io.qase:qase-junit5:3.1.0' + testImplementation 'io.qase:qase-junit5:3.1.1' } test { diff --git a/qase-junit5/pom.xml b/qase-junit5/pom.xml index 6266e6a5..00119d59 100644 --- a/qase-junit5/pom.xml +++ b/qase-junit5/pom.xml @@ -5,7 +5,7 @@ qase-java io.qase - 3.1.0 + 3.1.1 4.0.0 diff --git a/qase-junit5/src/test/java/io/qase/junit5/samples/Multiple.java b/qase-junit5/src/test/java/io/qase/junit5/samples/Multiple.java index 8e43619e..b4c28eec 100644 --- a/qase-junit5/src/test/java/io/qase/junit5/samples/Multiple.java +++ b/qase-junit5/src/test/java/io/qase/junit5/samples/Multiple.java @@ -11,6 +11,7 @@ public class Multiple { @Test @QaseId(123) public void failedWithStepsTest() { + System.out.println(Thread.currentThread().getId()); Steps steps = new Steps(); steps.successStep(); steps.failureStep(); @@ -19,6 +20,8 @@ public void failedWithStepsTest() { @Test @QaseId(456) public void passedWithStepsTest() { + System.out.println(Thread.currentThread().getId()); + Steps steps = new Steps(); steps.successStep(); } @@ -26,6 +29,7 @@ public void passedWithStepsTest() { @Test @QaseId(321) public void failedTest() throws InterruptedException { + System.out.println(Thread.currentThread().getId()); Thread.sleep(TimeUnit.SECONDS.toMillis(2)); throw new AssertionError("Error message"); } diff --git a/qase-testng/README.md b/qase-testng/README.md index 1ba270e7..69ea7d81 100644 --- a/qase-testng/README.md +++ b/qase-testng/README.md @@ -22,7 +22,7 @@ Add the following dependency to your pom.xml: io.qase qase-testng - 3.1.0 + 3.1.1 test @@ -67,7 +67,7 @@ configurations { dependencies { aspectjweaver "org.aspectj:aspectjweaver:1.9.8" - testImplementation 'io.qase:qase-testng:3.1.0' + testImplementation 'io.qase:qase-testng:3.1.1' testImplementation 'org.testng:testng:7.1.0' } diff --git a/qase-testng/pom.xml b/qase-testng/pom.xml index 91270dc2..ba2053d9 100644 --- a/qase-testng/pom.xml +++ b/qase-testng/pom.xml @@ -5,7 +5,7 @@ qase-java io.qase - 3.1.0 + 3.1.1 4.0.0