diff --git a/docs/modules/integrations/assets/images/zephyr.png b/docs/modules/integrations/assets/images/zephyr.png new file mode 100644 index 0000000000..79a687b23b Binary files /dev/null and b/docs/modules/integrations/assets/images/zephyr.png differ diff --git a/docs/modules/integrations/pages/zephyr-exporter.adoc b/docs/modules/integrations/pages/zephyr-exporter.adoc index 549c08d9e4..7861ed321e 100644 --- a/docs/modules/integrations/pages/zephyr-exporter.adoc +++ b/docs/modules/integrations/pages/zephyr-exporter.adoc @@ -4,6 +4,8 @@ Zephyr Exporter is a tool used for exporting test execution results into Jira Ze Features: +* Create test cases +* Update test cases * Create test executions * Set test execution statuses @@ -18,6 +20,10 @@ include::partial$jira-configuration.adoc[] |Required |Description +|`zephyr.exporter.level` +|true +|Property to export stories on STORY or SCENARIO level + |`zephyr.exporter.jira-instance-key` |false |The key of the configured JIRA instance, in case of missing value it will be evaluated automatically based on issue keys being exported @@ -30,6 +36,14 @@ include::partial$jira-configuration.adoc[] |false |Property for update existing executions statuses only. +|`zephyr.exporter.update-cases-on-export` +|false +|Property to update existing test cases. User with update status permissions required in order to work. + +|`zephyr.exporter.status-for-updated-test-cases` +|false +|Property defines status which will be set for all updated tests ("Backlog" by default). + |`zephyr.exporter.statuses-of-test-cases-to-add-to-execution` |false |List of test case statuses for adding to execution. @@ -52,6 +66,30 @@ include::partial$jira-configuration.adoc[] |=== +== Jira Fields Mapping + +The Zephyr is a Jira plugin that uses custom Jira fields for it's data, one of the ways to find out custom field names for particular field used by Zephyr on Jira UI (if access to Jira configuration is prohibited) is to request description of some issue. + +=== Test Case Properties + +image::zephyr.png[Zephyr test case view] + +[cols="1,2", options="header"] +|=== + +|Property +|Description + +|`jira.fields-mapping.story-type` +|Key of a field containing story type + +|`jira.fields-mapping.test-step` +|Key of a field containing step of cucumber story/scenario + +|=== + +include::partial$authentication.adoc[] + == Zephyr Execution Status Mapping The Zephyr plugin for Jira has own configurable execution statuses. testExecutionStatus endpoint is used to get the detailed information about the statuses, like: https://jira.example.com/rest/zapi/latest/util/testExecutionStatus. The following properties are used to setup a mapping between Vividus and Zephyr execution statuses. diff --git a/gradlew b/gradlew index 5f6a2d5bf8..744e882ed5 100755 --- a/gradlew +++ b/gradlew @@ -1,3 +1,58 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + die () { echo echo "$*" @@ -5,35 +60,126 @@ die () { exit 1 } -getValue () { - local value=$(grep "$1" $file | cut -d '=' -f2) - if [ -z $value ] ; then - die "Unable to find $1 in $file" +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MSYS* | MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" else - echo $value + JAVACMD="$JAVA_HOME/bin/java" fi -} + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME -file="./gradle.properties" -if [ -f "$file" ] ; then - export buildSystemVersion=$(getValue 'buildSystemVersion') +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi else - die "$file not found." + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." fi -#If VIVIDUS_BUILD_SYSTEM_HOME is not set -> try embedded -if [ -z "$VIVIDUS_BUILD_SYSTEM_HOME" ] ; then - export VIVIDUS_BUILD_SYSTEM_HOME=$(getValue 'buildSystemRootDir') +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi fi -GRADLEW_PATH=$VIVIDUS_BUILD_SYSTEM_HOME/$buildSystemVersion/gradlew -if [ -f "$GRADLEW_PATH" ] ; then - exec "$GRADLEW_PATH" "$@" -else - die "ERROR: Neither environment variable "VIVIDUS_BUILD_SYSTEM_HOME" is set nor embedded build system is synced -Used path: $GRADLEW_PATH -Please check Build System guide: -https://github.com/vividus-framework/vividus-build-system -or -clone this repo recursively: git clone --recursive " +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index e0c5a879f0..107acd32c4 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,35 +1,89 @@ -@echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. -set GRADLE_PROPERTIES=gradle.properties -set ERROR_MSG=%GRADLE_PROPERTIES% file is missing -if exist %GRADLE_PROPERTIES% goto readproperties goto fail -:readproperties -call :setpropertyvalue buildSystemVersion , buildSystemVersion +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe -:checkpath -if not exist "%VIVIDUS_BUILD_SYSTEM_HOME%" ( - call :setpropertyvalue buildSystemRootDir , VIVIDUS_BUILD_SYSTEM_HOME -) -set GRADLEW_PATH=%VIVIDUS_BUILD_SYSTEM_HOME%\%buildSystemVersion%\gradlew.bat -if exist "%GRADLEW_PATH%" goto call -set ERROR_MSG=Couldn't find %GRADLEW_PATH%. Neither environment variable "VIVIDUS_BUILD_SYSTEM_HOME" is set nor embedded build system is synced +if exist "%JAVA_EXE%" goto execute -:fail echo. -echo ERROR: %ERROR_MSG% -echo Please check Build System guide: -echo https://github.com/vividus-framework/vividus-build-system -echo or -echo clone this repo recursively: git clone --recursive +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 -:call -@CALL "%GRADLEW_PATH%" %* -exit /b %ERRORLEVEL% +:mainEnd +if "%OS%"=="Windows_NT" endlocal -:setpropertyvalue -For /F "tokens=1* delims==" %%A IN (%GRADLE_PROPERTIES%) DO ( - IF %%A==%~1 set %~2=%%B -) +:omega diff --git a/vividus-engine/src/main/java/org/vividus/model/jbehave/IContainingMeta.java b/vividus-engine/src/main/java/org/vividus/model/jbehave/IContainingMeta.java new file mode 100644 index 0000000000..e04e0b2abc --- /dev/null +++ b/vividus-engine/src/main/java/org/vividus/model/jbehave/IContainingMeta.java @@ -0,0 +1,100 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.model.jbehave; + +import static org.vividus.model.MetaWrapper.META_VALUES_SEPARATOR; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; + +public interface IContainingMeta +{ + List getMeta(); + + /** + * Get unique meta value + * + *

If the meta does not exist or has no value, an empty {@link Optional} will be returned + * + *

Notes + *

+ * + * @param metaName the meta name + * @return the meta value + * @throws NotUniqueMetaValueException if the meta has more than one value + */ + default Optional getUniqueMetaValue(String metaName) throws NotUniqueMetaValueException + { + Set values = getMetaValues(metaName); + if (values.size() > 1) + { + throw new NotUniqueMetaValueException(metaName, values); + } + return values.isEmpty() ? Optional.empty() : Optional.of(values.iterator().next()); + } + + /** + * Get all meta values + * + *

Notes + *

    + *
  • metas without value are ignored
  • + *
  • meta values are trimmed upon returning
  • + *
  • ; char is used as a separator for meta with multiple values
  • + *
+ * + * @param metaName the meta name + * @return the meta values + */ + default Set getMetaValues(String metaName) + { + return getMetaStream().filter(m -> metaName.equals(m.getName())) + .map(Meta::getValue) + .filter(StringUtils::isNotEmpty) + .map(String::trim) + .map(value -> StringUtils.splitPreserveAllTokens(value, META_VALUES_SEPARATOR)) + .flatMap(Stream::of) + .map(String::trim) + .collect(Collectors.toCollection(LinkedHashSet::new)); + } + + /** + * Determine if scenario has meta with the name + * + * @param metaName the meta name + * @return {@code true} if scenario has meta with the name + */ + default boolean hasMetaWithName(String metaName) + { + return getMetaStream().anyMatch(m -> metaName.equals(m.getName())); + } + + private Stream getMetaStream() + { + return Optional.ofNullable(getMeta()).stream().flatMap(Collection::stream); + } +} diff --git a/vividus-engine/src/main/java/org/vividus/model/jbehave/Scenario.java b/vividus-engine/src/main/java/org/vividus/model/jbehave/Scenario.java index 45cd3f33c2..495d8554ca 100644 --- a/vividus-engine/src/main/java/org/vividus/model/jbehave/Scenario.java +++ b/vividus-engine/src/main/java/org/vividus/model/jbehave/Scenario.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,19 +16,11 @@ package org.vividus.model.jbehave; -import static org.vividus.model.MetaWrapper.META_VALUES_SEPARATOR; - -import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import org.apache.commons.lang3.StringUtils; -public class Scenario extends AbstractStepsContainer +public class Scenario extends AbstractStepsContainer implements IContainingMeta { private String title; private List meta; @@ -46,6 +38,7 @@ public void setTitle(String title) this.title = title; } + @Override public List getMeta() { return meta; @@ -101,76 +94,8 @@ public List collectSteps() .orElse(List.of()); } - /** - * Get unique meta value - * - *

If the meta does not exist or has no value, an empty {@link Optional} will be returned - * - *

Notes - *

    - *
  • meta value is trimmed upon returning
  • - *
  • ; char is used as a separator for meta with multiple values
  • - *
- * - * @param metaName the meta name - * @return the meta value - * @throws NotUniqueMetaValueException if the meta has more than one value - */ - public Optional getUniqueMetaValue(String metaName) throws NotUniqueMetaValueException - { - Set values = getMetaValues(metaName); - if (values.size() > 1) - { - throw new NotUniqueMetaValueException(metaName, values); - } - return values.isEmpty() ? Optional.empty() : Optional.of(values.iterator().next()); - } - - /** - * Get all meta values - * - *

Notes - *

    - *
  • metas without value are ignored
  • - *
  • meta values are trimmed upon returning
  • - *
  • ; char is used as a separator for meta with multiple values
  • - *
- * - * @param metaName the meta name - * @return the meta values - */ - public Set getMetaValues(String metaName) - { - return getMetaStream().filter(m -> metaName.equals(m.getName())) - .map(Meta::getValue) - .filter(StringUtils::isNotEmpty) - .map(String::trim) - .map(value -> StringUtils.splitPreserveAllTokens(value, META_VALUES_SEPARATOR)) - .flatMap(Stream::of) - .map(String::trim) - .collect(Collectors.toCollection(LinkedHashSet::new)); - } - - /** - * Determine if scenario has meta with the name - * - * @param metaName the meta name - * @return {@code true} if scenario has meta with the name - */ - public boolean hasMetaWithName(String metaName) - { - return getMetaStream().anyMatch(m -> metaName.equals(m.getName())); - } - public boolean isManual() { return collectSteps().stream().allMatch(Step::isManual); } - - private Stream getMetaStream() - { - return Optional.ofNullable(getMeta()) - .map(List::stream) - .orElseGet(Stream::empty); - } } diff --git a/vividus-engine/src/main/java/org/vividus/model/jbehave/Story.java b/vividus-engine/src/main/java/org/vividus/model/jbehave/Story.java index 71167925b9..dbe9a0625c 100644 --- a/vividus-engine/src/main/java/org/vividus/model/jbehave/Story.java +++ b/vividus-engine/src/main/java/org/vividus/model/jbehave/Story.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,11 +24,12 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -public class Story +public class Story implements IContainingMeta { private String path; private Lifecycle lifecycle; private List scenarios; + private List meta; public String getPath() { @@ -60,6 +61,17 @@ public void setScenarios(List scenarios) this.scenarios = scenarios; } + @Override + public List getMeta() + { + return meta; + } + + public void setMeta(List meta) + { + this.meta = meta; + } + /** * Get unique scenarios. * diff --git a/vividus-engine/src/test/java/org/vividus/model/jbehave/StoryTests.java b/vividus-engine/src/test/java/org/vividus/model/jbehave/StoryTests.java index 503ab374c3..375d4ec6b6 100644 --- a/vividus-engine/src/test/java/org/vividus/model/jbehave/StoryTests.java +++ b/vividus-engine/src/test/java/org/vividus/model/jbehave/StoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verifyNoInteractions; @@ -67,6 +68,8 @@ class StoryTests private static final String SCENARIO_KEY = "scenario-key-"; private static final String SCENARIO_VALUE = "scenario-val-"; private static final String STORY = "story.json"; + private static final String META = "meta"; + private static final String VALUE = "value"; private static final org.jbehave.core.model.Story TEST_STORY = new org.jbehave.core.model.Story(STORY_PATH); private static final org.jbehave.core.model.Scenario TEST_SCENARIO = new org.jbehave.core.model.Scenario( @@ -219,6 +222,22 @@ void shouldHandleStoryWithOneScenario() ), params.getValues()); } + @Test + void shouldCheckIfStoryHasMetaWithEmptyValues() + { + Story story = new Story(); + story.setMeta(List.of(createMeta(""))); + assertThat(story.getMetaValues(META), hasSize(0)); + } + + @Test + void shouldCheckIfStoryHasMetaWithName() + { + Story story = new Story(); + story.setMeta(List.of(createMeta(VALUE))); + assertTrue(story.hasMetaWithName(META)); + } + private static void reportStep(StoryReporter reporter, Stage stage, ExecutionType type) { reporter.beforeScenarioSteps(stage, type); @@ -290,4 +309,12 @@ private static Story readStory(String resource) throw new UncheckedIOException(e); } } + + private static Meta createMeta(String value) + { + Meta meta = new Meta(); + meta.setName(META); + meta.setValue(value); + return meta; + } } diff --git a/vividus-exporter-commons/build.gradle b/vividus-exporter-commons/build.gradle index 9f9979a5a0..6a29254e13 100644 --- a/vividus-exporter-commons/build.gradle +++ b/vividus-exporter-commons/build.gradle @@ -1,6 +1,9 @@ project.description = 'Common module for all VIVIDUS exporters' +apply from: "logging.gradle" + dependencies { + implementation project(':vividus-facade-jira') implementation project(':vividus-util') implementation(group: 'org.springframework.boot', name: 'spring-boot-starter', version: versions.springBoot) diff --git a/vividus-exporter-commons/dependencies.gradle b/vividus-exporter-commons/dependencies.gradle index 6be51948aa..2373e257c0 100644 --- a/vividus-exporter-commons/dependencies.gradle +++ b/vividus-exporter-commons/dependencies.gradle @@ -16,21 +16,10 @@ dependencies { implementation(group: 'org.springframework.boot', name: 'spring-boot-starter') implementation(group: 'org.springframework.boot', name: 'spring-boot-starter-validation') - implementation(group: 'org.slf4j', name: 'slf4j-api', version: versions.slf4j) - implementation platform(group: 'org.apache.logging.log4j', name: 'log4j-bom', version: '2.17.1') - implementation(group: 'org.apache.logging.log4j', name: 'log4j-api') - implementation(group: 'org.apache.logging.log4j', name: 'log4j-core') - implementation(group: 'org.apache.logging.log4j', name: 'log4j-slf4j18-impl') - testImplementation(group: 'org.springframework.boot', name: 'spring-boot-starter-test') testImplementation platform(group: 'org.junit', name: 'junit-bom', version: versions.junit) testImplementation(group: 'org.junit.jupiter', name: 'junit-jupiter') testImplementation(group: 'org.mockito', name: 'mockito-junit-jupiter', version: versions.mockito) - testImplementation(group: 'com.github.valfirst', name: 'slf4j-test', version: versions.slf4jTest) -} - -configurations.all { - exclude group: 'org.apache.logging.log4j', module: 'log4j-to-slf4j' } configurations.testImplementation { diff --git a/vividus-exporter-commons/logging.gradle b/vividus-exporter-commons/logging.gradle new file mode 100644 index 0000000000..0dba2f4e81 --- /dev/null +++ b/vividus-exporter-commons/logging.gradle @@ -0,0 +1,13 @@ +configurations.all { + exclude group: 'org.apache.logging.log4j', module: 'log4j-to-slf4j' +} + +dependencies { + implementation(group: 'org.slf4j', name: 'slf4j-api', version: versions.slf4j) + implementation platform(group: 'org.apache.logging.log4j', name: 'log4j-bom', version: '2.17.1') + implementation(group: 'org.apache.logging.log4j', name: 'log4j-api') + implementation(group: 'org.apache.logging.log4j', name: 'log4j-core') + implementation(group: 'org.apache.logging.log4j', name: 'log4j-slf4j18-impl') + + testImplementation(group: 'com.github.valfirst', name: 'slf4j-test', version: versions.slf4jTest) +} diff --git a/vividus-exporter-commons/src/main/java/org/vividus/exporter/databind/SerializeJsonHelper.java b/vividus-exporter-commons/src/main/java/org/vividus/exporter/databind/SerializeJsonHelper.java new file mode 100644 index 0000000000..5074d47cab --- /dev/null +++ b/vividus-exporter-commons/src/main/java/org/vividus/exporter/databind/SerializeJsonHelper.java @@ -0,0 +1,60 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.exporter.databind; + +import java.io.IOException; +import java.util.Collection; + +import com.fasterxml.jackson.core.JsonGenerator; + +public final class SerializeJsonHelper +{ + private SerializeJsonHelper() + { + } + + public static void writeJsonArray(JsonGenerator generator, String startField, Collection values, + boolean wrapValuesAsObjects) throws IOException + { + if (values != null) + { + generator.writeArrayFieldStart(startField); + for (String value : values) + { + if (wrapValuesAsObjects) + { + generator.writeStartObject(); + generator.writeStringField("name", value); + generator.writeEndObject(); + } + else + { + generator.writeString(value); + } + } + generator.writeEndArray(); + } + } + + public static void writeObjectWithField(JsonGenerator generator, String objectKey, String fieldName, + String fieldValue) throws IOException + { + generator.writeObjectFieldStart(objectKey); + generator.writeStringField(fieldName, fieldValue); + generator.writeEndObject(); + } +} diff --git a/vividus-exporter-commons/src/main/java/org/vividus/exporter/facade/ExporterFacade.java b/vividus-exporter-commons/src/main/java/org/vividus/exporter/facade/ExporterFacade.java new file mode 100644 index 0000000000..af73491d97 --- /dev/null +++ b/vividus-exporter-commons/src/main/java/org/vividus/exporter/facade/ExporterFacade.java @@ -0,0 +1,44 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.exporter.facade; + +import java.io.IOException; +import java.util.Optional; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.vividus.jira.JiraConfigurationException; +import org.vividus.jira.JiraFacade; + +public abstract class ExporterFacade +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ExporterFacade.class); + + public void createTestsLink(String testCaseId, Optional requirementId, JiraFacade jiraFacade) + throws IOException, JiraConfigurationException + { + if (requirementId.isPresent()) + { + String linkType = "Tests"; + LOGGER.atInfo().addArgument(linkType) + .addArgument(testCaseId) + .addArgument(requirementId.get()) + .log("Create '{}' link from {} to {}"); + jiraFacade.createIssueLink(testCaseId, requirementId.get(), linkType); + } + } +} diff --git a/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/VividusExporterCommonConfigurationTests.java b/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/congig/VividusExporterCommonConfigurationTests.java similarity index 94% rename from vividus-exporter-commons/src/test/java/org/vividus/exporter/config/VividusExporterCommonConfigurationTests.java rename to vividus-exporter-commons/src/test/java/org/vividus/exporter/config/congig/VividusExporterCommonConfigurationTests.java index ed1e7a7824..0126869088 100644 --- a/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/VividusExporterCommonConfigurationTests.java +++ b/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/congig/VividusExporterCommonConfigurationTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.vividus.exporter.config; +package org.vividus.exporter.config.congig; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -22,6 +22,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.mock.env.MockEnvironment; +import org.vividus.exporter.config.VividusExporterCommonConfiguration; import org.vividus.util.property.PropertyParser; @ExtendWith(MockitoExtension.class) diff --git a/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/databind/SerializeJsonHelperTests.java b/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/databind/SerializeJsonHelperTests.java new file mode 100644 index 0000000000..64e7de9849 --- /dev/null +++ b/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/databind/SerializeJsonHelperTests.java @@ -0,0 +1,78 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.exporter.config.databind; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Collection; + +import com.fasterxml.jackson.core.JsonGenerator; + +import org.junit.jupiter.api.Test; +import org.vividus.exporter.databind.SerializeJsonHelper; + +class SerializeJsonHelperTests +{ + private static final String NAME = "name"; + private static final String VALUE = "value"; + private static final String START_FIELD = "startField"; + private static final String FIRST = "first"; + private static final String SECOND = "second"; + private static final Collection VALUES = Arrays.asList(FIRST, SECOND); + + private final JsonGenerator generator = mock(JsonGenerator.class); + + @Test + void testSerializeJsonValueWithObjTrue() throws IOException + { + SerializeJsonHelper.writeJsonArray(generator, START_FIELD, VALUES, true); + verify(generator).writeArrayFieldStart(START_FIELD); + verify(generator).writeStringField(NAME, FIRST); + verify(generator).writeStringField(NAME, SECOND); + verify(generator).writeEndArray(); + } + + @Test + void testSerializeJsonValueWithObjFalse() throws IOException + { + SerializeJsonHelper.writeJsonArray(generator, START_FIELD, VALUES, false); + verify(generator).writeArrayFieldStart(START_FIELD); + verify(generator).writeString(FIRST); + verify(generator).writeString(SECOND); + verify(generator).writeEndArray(); + } + + @Test + void testSerializeJsonNullValues() throws IOException + { + SerializeJsonHelper.writeJsonArray(generator, START_FIELD, null, true); + verifyNoMoreInteractions(generator); + } + + @Test + void testWriteObjectWithField() throws IOException + { + SerializeJsonHelper.writeObjectWithField(generator, START_FIELD, NAME, VALUE); + verify(generator).writeObjectFieldStart(START_FIELD); + verify(generator).writeStringField(NAME, VALUE); + verify(generator).writeEndObject(); + } +} diff --git a/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/facade/ExporterFacadeTests.java b/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/facade/ExporterFacadeTests.java new file mode 100644 index 0000000000..23bc21cf52 --- /dev/null +++ b/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/facade/ExporterFacadeTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.exporter.config.facade; + +import static com.github.valfirst.slf4jtest.LoggingEvent.info; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import java.io.IOException; +import java.util.Collections; +import java.util.Optional; + +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.github.valfirst.slf4jtest.TestLoggerFactoryExtension; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.vividus.exporter.facade.ExporterFacade; +import org.vividus.jira.JiraConfigurationException; +import org.vividus.jira.JiraFacade; + +@ExtendWith({ MockitoExtension.class, TestLoggerFactoryExtension.class }) +class ExporterFacadeTests +{ + private static final String REQUIREMENT_ID = "STUB-REQ-0"; + private static final String ISSUE_ID = "ISSUE_ID"; + private static final String LINK_NAME = "Tests"; + + private final TestLogger testLogger = TestLoggerFactory.getTestLogger(ExporterFacade.class); + + private final TestExporter facade = new TestExporter(); + @Mock private JiraFacade jiraFacade; + + @Test + void createLinkIfRequirementIdPresents() throws IOException, JiraConfigurationException + { + facade.createTestsLink(ISSUE_ID, Optional.of(REQUIREMENT_ID), jiraFacade); + assertThat(testLogger.getLoggingEvents(), is(Collections.singletonList( + info("Create '{}' link from {} to {}", LINK_NAME, ISSUE_ID, REQUIREMENT_ID)))); + verify(jiraFacade).createIssueLink(ISSUE_ID, REQUIREMENT_ID, LINK_NAME); + } + + @Test + void createLinkIfRequirementIdAbsent() throws IOException, JiraConfigurationException + { + facade.createTestsLink(ISSUE_ID, Optional.empty(), jiraFacade); + verifyNoMoreInteractions(jiraFacade); + } + + private static final class TestExporter extends ExporterFacade + { + } +} diff --git a/vividus-facade-jira/src/main/java/org/vividus/jira/JiraFacade.java b/vividus-facade-jira/src/main/java/org/vividus/jira/JiraFacade.java index aeaffd5be1..b5a63e659d 100644 --- a/vividus-facade-jira/src/main/java/org/vividus/jira/JiraFacade.java +++ b/vividus-facade-jira/src/main/java/org/vividus/jira/JiraFacade.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,7 +38,10 @@ public class JiraFacade private static final String REST_API_ENDPOINT = "/rest/api/latest/"; private static final String ISSUE = "issue/"; + private static final String TRANSITIONS = ISSUE + "%s/transitions/"; private static final String ISSUE_ENDPOINT = REST_API_ENDPOINT + ISSUE; + private static final String TRANSITIONS_ENDPOINT = REST_API_ENDPOINT + TRANSITIONS; + private static final String UPDATE_TRANSITION_BODY = "{'transition':{'id':%s}}"; private final JiraClientProvider jiraClientProvider; @@ -72,6 +75,20 @@ public String getIssueStatus(String issueKey) throws IOException, JiraConfigurat return JsonPathUtils.getData(issue, "$.fields.status.name"); } + public String setIssueStatus(String issueKey, String status) throws JiraConfigurationException, IOException + { + String statusId = getStatusIdByName(issueKey, status); + return jiraClientProvider.getByIssueKey(issueKey).executePost(String.format(TRANSITIONS_ENDPOINT, issueKey), + String.format(UPDATE_TRANSITION_BODY, statusId)); + } + + private String getStatusIdByName(String issueKey, String name) throws JiraConfigurationException, IOException + { + String statuses = jiraClientProvider.getByIssueKey(issueKey) + .executeGet(String.format(TRANSITIONS_ENDPOINT, issueKey)); + return JsonPathUtils.getData(statuses, String.format("$.transitions[?(@.to.name=='%s')].id", name)); + } + public Project getProject(String projectKey) throws IOException, JiraConfigurationException { return getJiraEntity("project/", projectKey, () -> jiraClientProvider.getByProjectKey(projectKey), diff --git a/vividus-to-azure-devops-exporter/build.gradle b/vividus-to-azure-devops-exporter/build.gradle index 11d69a4f45..69d898f504 100644 --- a/vividus-to-azure-devops-exporter/build.gradle +++ b/vividus-to-azure-devops-exporter/build.gradle @@ -1,6 +1,7 @@ project.description = 'Vividus to Azure DevOps exporter' apply from: "$rootDir/vividus-exporter-commons/dependencies.gradle" +apply from: "$rootDir/vividus-exporter-commons/logging.gradle" dependencies { implementation project(':vividus-http-client') diff --git a/vividus-to-xray-exporter/build.gradle b/vividus-to-xray-exporter/build.gradle index 2198e8d061..4a2a86777f 100644 --- a/vividus-to-xray-exporter/build.gradle +++ b/vividus-to-xray-exporter/build.gradle @@ -1,6 +1,7 @@ project.description = 'Vividus to Xray exporter' apply from: "$rootDir/vividus-exporter-commons/dependencies.gradle" +apply from: "$rootDir/vividus-exporter-commons/logging.gradle" dependencies { implementation project(':vividus-util') diff --git a/vividus-to-xray-exporter/src/main/java/org/vividus/xray/databind/AbstractTestCaseSerializer.java b/vividus-to-xray-exporter/src/main/java/org/vividus/xray/databind/AbstractTestCaseSerializer.java index 58f4f4c417..22055205ec 100644 --- a/vividus-to-xray-exporter/src/main/java/org/vividus/xray/databind/AbstractTestCaseSerializer.java +++ b/vividus-to-xray-exporter/src/main/java/org/vividus/xray/databind/AbstractTestCaseSerializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,10 @@ package org.vividus.xray.databind; +import static org.vividus.exporter.databind.SerializeJsonHelper.writeJsonArray; +import static org.vividus.exporter.databind.SerializeJsonHelper.writeObjectWithField; + import java.io.IOException; -import java.util.Collection; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; @@ -71,37 +73,6 @@ protected void writeObjectWithValueField(JsonGenerator generator, String objectK protected abstract void serializeCustomFields(T testCase, JsonGenerator generator) throws IOException; - private static void writeJsonArray(JsonGenerator generator, String startField, Collection values, - boolean wrapValuesAsObjects) throws IOException - { - if (values != null) - { - generator.writeArrayFieldStart(startField); - for (String value : values) - { - if (wrapValuesAsObjects) - { - generator.writeStartObject(); - generator.writeStringField(NAME, value); - generator.writeEndObject(); - } - else - { - generator.writeString(value); - } - } - generator.writeEndArray(); - } - } - - private static void writeObjectWithField(JsonGenerator generator, String objectKey, String fieldName, - String fieldValue) throws IOException - { - generator.writeObjectFieldStart(objectKey); - generator.writeStringField(fieldName, fieldValue); - generator.writeEndObject(); - } - protected JiraFieldsMapping getJiraFieldsMapping() { return jiraFieldsMapping; diff --git a/vividus-to-xray-exporter/src/main/java/org/vividus/xray/exporter/XrayExporter.java b/vividus-to-xray-exporter/src/main/java/org/vividus/xray/exporter/XrayExporter.java index 9e9abf0fd5..500b33ccc3 100644 --- a/vividus-to-xray-exporter/src/main/java/org/vividus/xray/exporter/XrayExporter.java +++ b/vividus-to-xray-exporter/src/main/java/org/vividus/xray/exporter/XrayExporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,8 +32,6 @@ import org.apache.commons.lang3.function.FailableBiFunction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; import org.vividus.jira.JiraConfigurationException; import org.vividus.model.jbehave.NotUniqueMetaValueException; import org.vividus.model.jbehave.Scenario; @@ -55,15 +53,14 @@ import org.vividus.xray.model.TestCaseType; import org.vividus.xray.model.TestExecution; -@Component public class XrayExporter { private static final Logger LOGGER = LoggerFactory.getLogger(XrayExporter.class); - @Autowired private XrayExporterOptions xrayExporterOptions; - @Autowired private XrayFacade xrayFacade; - @Autowired private TestCaseFactory testCaseFactory; - @Autowired private TestExecutionFactory testExecutionFactory; + private final XrayExporterOptions xrayExporterOptions; + private final XrayFacade xrayFacade; + private TestCaseFactory testCaseFactory; + private final TestExecutionFactory testExecutionFactory; private final List errors = new ArrayList<>(); @@ -77,6 +74,15 @@ public class XrayExporter TestCaseType.CUCUMBER, (title, scenario) -> createCucumberTestCaseParameters(scenario) ); + public XrayExporter(XrayExporterOptions xrayExporterOptions, XrayFacade xrayFacade, TestCaseFactory testCaseFactory, + TestExecutionFactory testExecutionFactory) + { + this.xrayExporterOptions = xrayExporterOptions; + this.xrayFacade = xrayFacade; + this.testCaseFactory = testCaseFactory; + this.testExecutionFactory = testExecutionFactory; + } + public void exportResults() throws IOException { List> testCases = new ArrayList<>(); @@ -226,10 +232,7 @@ private void createTestsLink(String testCaseId, Scenario scenario) throws IOException, NotUniqueMetaValueException, JiraConfigurationException { Optional requirementId = scenario.getUniqueMetaValue("requirementId"); - if (requirementId.isPresent()) - { - xrayFacade.createTestsLink(testCaseId, requirementId.get()); - } + xrayFacade.createTestsLink(testCaseId, requirementId); } @FunctionalInterface diff --git a/vividus-to-xray-exporter/src/main/java/org/vividus/xray/facade/XrayFacade.java b/vividus-to-xray-exporter/src/main/java/org/vividus/xray/facade/XrayFacade.java index 62a1665fd7..00fa8964c9 100644 --- a/vividus-to-xray-exporter/src/main/java/org/vividus/xray/facade/XrayFacade.java +++ b/vividus-to-xray-exporter/src/main/java/org/vividus/xray/facade/XrayFacade.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.vividus.exporter.facade.ExporterFacade; import org.vividus.jira.JiraClientProvider; import org.vividus.jira.JiraConfigurationException; import org.vividus.jira.JiraFacade; @@ -40,7 +41,7 @@ import org.vividus.xray.model.ManualTestCase; import org.vividus.xray.model.TestExecution; -public class XrayFacade +public class XrayFacade extends ExporterFacade { private static final Logger LOGGER = LoggerFactory.getLogger(XrayFacade.class); @@ -118,6 +119,12 @@ public void updateTestSet(String testSetKey, List testCaseKeys) requestBody); } + public void createTestsLink(String testCaseId, Optional requirementId) + throws IOException, JiraConfigurationException + { + createTestsLink(testCaseId, requirementId, jiraFacade); + } + private void checkIfIssueEditable(String issueKey) throws IOException, NonEditableIssueStatusException, JiraConfigurationException { @@ -129,16 +136,6 @@ private void checkIfIssueEditable(String issueKey) } } - public void createTestsLink(String testCaseId, String requirementId) throws IOException, JiraConfigurationException - { - String linkType = "Tests"; - LOGGER.atInfo().addArgument(linkType) - .addArgument(testCaseId) - .addArgument(requirementId) - .log("Create '{}' link from {} to {}"); - jiraFacade.createIssueLink(testCaseId, requirementId, linkType); - } - public static final class NonEditableIssueStatusException extends Exception { private static final long serialVersionUID = -5547086076322794984L; diff --git a/vividus-to-xray-exporter/src/test/java/org/vividus/xray/exporter/XrayExporterTests.java b/vividus-to-xray-exporter/src/test/java/org/vividus/xray/exporter/XrayExporterTests.java index d6e51b755c..ec44df8ecd 100644 --- a/vividus-to-xray-exporter/src/test/java/org/vividus/xray/exporter/XrayExporterTests.java +++ b/vividus-to-xray-exporter/src/test/java/org/vividus/xray/exporter/XrayExporterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -134,6 +134,7 @@ void shouldExportCucumberTestCaseWithoutTestCaseId() throws URISyntaxException, + "|parameter-value-3|" + lineSeparator(); verifyCucumberTestCaseParameters("Scenario Outline", scenario); validateLogs(jsonResultsUri, getExportingScenarioEvent(), getExportSuccessfulEvent()); + verify(xrayFacade).createTestsLink(any(), any()); } @Test @@ -152,6 +153,7 @@ void shouldUpdateExistingCucumberTestCase() throws URISyntaxException, IOExcepti String scenario = GIVEN_STEP + lineSeparator() + WHEN_STEP + lineSeparator() + THEN_STEP; verifyCucumberTestCaseParameters("Scenario", scenario); validateLogs(jsonResultsUri, getExportingScenarioEvent(), getExportSuccessfulEvent()); + verify(xrayFacade).createTestsLink(any(), any()); } @Test @@ -182,6 +184,7 @@ void shouldExportTestWithLabelsAndComponentsAndUpdatableTestCaseId() verify(xrayFacade).updateTestSet(TEST_SET_KEY, List.of(ISSUE_ID)); validateLogs(jsonResultsUri, getExportingScenarioEvent(), getExportSuccessfulEvent()); + verify(xrayFacade).createTestsLink(any(), any()); } @Test @@ -215,13 +218,14 @@ void shouldCompleteExportIfExportAttemptThrownIOException() throws URISyntaxExce verify(xrayFacade).updateTestCase(ISSUE_ID, testCase); verify(xrayFacade).updateTestSet(TEST_SET_KEY, List.of(ISSUE_ID)); verify(xrayFacade).updateTestExecution(any()); + verify(xrayFacade).createTestsLink(any(), any()); verifyManualTestCaseParameters(Set.of(), Set.of()); validateLogs(jsonResultsUri, getExportingScenarioEvent(), error(exception, ERROR_MESSAGE), getExportingScenarioEvent(), getExportFailedErrorEvent(errorLogMessage)); } @Test - void shouldNotExportSkippedTest() throws URISyntaxException, IOException, JiraConfigurationException + void shouldNotExportSkippedTest() throws URISyntaxException, IOException { URI jsonResultsUri = getJsonResultsUri("skipped"); xrayExporterOptions.setJsonResultsDirectory(Paths.get(jsonResultsUri)); @@ -252,10 +256,9 @@ void shouldExportNewTestAndLinkToRequirements() throws URISyntaxException, IOExc when(xrayFacade.createTestCase(testCase)).thenReturn(ISSUE_ID); when(testCaseFactory.createManualTestCase(manualTestCaseParametersCaptor.capture())).thenReturn(testCase); - xrayExporter.exportResults(); - verify(xrayFacade).createTestsLink(ISSUE_ID, "STUB-REQ-0"); + verify(xrayFacade).createTestsLink(ISSUE_ID, Optional.of("STUB-REQ-0")); verifyManualTestCaseParameters(Set.of(), Set.of()); validateLogs(jsonResultsUri, getExportingScenarioEvent(), getExportSuccessfulEvent()); diff --git a/vividus-to-xray-exporter/src/test/java/org/vividus/xray/facade/XrayFacadeTests.java b/vividus-to-xray-exporter/src/test/java/org/vividus/xray/facade/XrayFacadeTests.java index c9722db2e5..c6a9155835 100644 --- a/vividus-to-xray-exporter/src/test/java/org/vividus/xray/facade/XrayFacadeTests.java +++ b/vividus-to-xray-exporter/src/test/java/org/vividus/xray/facade/XrayFacadeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -89,20 +89,6 @@ void afterEach() verifyNoMoreInteractions(jiraFacade, jiraClient); } - @Test - void shouldCreateTestsLink() throws IOException, JiraConfigurationException - { - initializeFacade(List.of()); - String requirementId = "requirement id"; - String linkType = "Tests"; - - xrayFacade.createTestsLink(ISSUE_ID, requirementId); - - verify(jiraFacade).createIssueLink(ISSUE_ID, requirementId, linkType); - assertThat(logger.getLoggingEvents(), - is(List.of(info("Create '{}' link from {} to {}", linkType, ISSUE_ID, requirementId)))); - } - @Test void shouldUpdateManualTestCase() throws IOException, NonEditableIssueStatusException, JiraConfigurationException { diff --git a/vividus-to-xray-exporter/src/test/java/org/vividus/xray/integration/VividusToXrayExporterIntegrationTests.java b/vividus-to-xray-exporter/src/test/java/org/vividus/xray/integration/VividusToXrayExporterIntegrationTests.java index b1c020a0ef..737c079b15 100644 --- a/vividus-to-xray-exporter/src/test/java/org/vividus/xray/integration/VividusToXrayExporterIntegrationTests.java +++ b/vividus-to-xray-exporter/src/test/java/org/vividus/xray/integration/VividusToXrayExporterIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,9 +26,9 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; -import org.springframework.beans.factory.annotation.Autowired; +import org.mockito.InjectMocks; +import org.mockito.Mock; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.mock.mockito.SpyBean; import org.vividus.xray.VividusToXrayExporterApplication; import org.vividus.xray.configuration.XrayExporterOptions; @@ -42,10 +42,10 @@ }) class VividusToXrayExporterIntegrationTests { - @MockBean private XrayExporterOptions xrayExporterOptions; + @Mock private XrayExporterOptions xrayExporterOptions; @SpyBean private XrayFacade xrayFacade; @SpyBean private TestCaseFactory testCaseFactory; - @Autowired private XrayExporter xrayExporter; + @InjectMocks private XrayExporter xrayExporter; @Test void shouldStartContext(@TempDir Path tempDirectory) throws IOException diff --git a/vividus-to-zephyr-exporter/build.gradle b/vividus-to-zephyr-exporter/build.gradle index 57a83f509d..2faa192906 100644 --- a/vividus-to-zephyr-exporter/build.gradle +++ b/vividus-to-zephyr-exporter/build.gradle @@ -1,12 +1,22 @@ project.description = 'Vividus to Zephyr exporter' apply from: "$rootDir/vividus-exporter-commons/dependencies.gradle" +apply from: "$rootDir/vividus-exporter-commons/logging.gradle" dependencies { implementation project(':vividus-util') + implementation project(':vividus-engine') + implementation project(':vividus-engine') implementation project(':vividus-facade-jira') implementation project(':vividus-exporter-commons') implementation(group: 'com.google.guava', name: 'guava', version: versions.guava) implementation(group: 'org.apache.commons', name: 'commons-lang3', version: versions.commonsLang3) + + testImplementation(group: 'org.springframework.boot', name: 'spring-boot-starter-test') + testImplementation platform(group: 'org.junit', name: 'junit-bom', version: versions.junit) + testImplementation(group: 'org.junit.jupiter', name: 'junit-jupiter') + testImplementation(group: 'org.hamcrest', name: 'hamcrest', version: versions.hamcrest) + testImplementation(group: 'org.mockito', name: 'mockito-junit-jupiter', version: versions.mockito) + testImplementation(group: 'com.github.valfirst', name: 'slf4j-test', version: versions.slf4jTest) } diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/VividusToZephyrExporterApplication.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/VividusToZephyrExporterApplication.java index 7d19dde21e..dffb89aac6 100644 --- a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/VividusToZephyrExporterApplication.java +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/VividusToZephyrExporterApplication.java @@ -26,6 +26,7 @@ import org.springframework.context.annotation.ImportResource; import org.vividus.exporter.config.VividusExporterCommonConfiguration; import org.vividus.jira.JiraConfigurationException; +import org.vividus.zephyr.configuration.JiraFieldsMapping; import org.vividus.zephyr.configuration.ZephyrExporterConfiguration; import org.vividus.zephyr.configuration.ZephyrExporterProperties; import org.vividus.zephyr.exporter.ZephyrExporter; @@ -33,7 +34,8 @@ @SpringBootApplication @Import(VividusExporterCommonConfiguration.class) @ImportResource(locations = { "org/vividus/zephyr/spring.xml", "org/vividus/jira/spring.xml" }) -@EnableConfigurationProperties({ ZephyrExporterConfiguration.class, ZephyrExporterProperties.class }) +@EnableConfigurationProperties({ ZephyrExporterConfiguration.class, ZephyrExporterProperties.class, + JiraFieldsMapping.class }) @SuppressWarnings("checkstyle:hideutilityclassconstructor") public class VividusToZephyrExporterApplication { diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/configuration/JiraFieldsMapping.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/configuration/JiraFieldsMapping.java new file mode 100644 index 0000000000..66a78c82c3 --- /dev/null +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/configuration/JiraFieldsMapping.java @@ -0,0 +1,47 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.zephyr.configuration; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("jira.fields-mapping") +public class JiraFieldsMapping +{ + private String storyType; + + private String testSteps; + + public String getStoryType() + { + return storyType; + } + + public void setStoryType(String storyType) + { + this.storyType = storyType; + } + + public String getTestSteps() + { + return testSteps; + } + + public void setTestSteps(String testSteps) + { + this.testSteps = testSteps; + } +} diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/configuration/ZephyrExporterProperties.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/configuration/ZephyrExporterProperties.java index 82b14bd3ec..e00a5cf003 100644 --- a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/configuration/ZephyrExporterProperties.java +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/configuration/ZephyrExporterProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import javax.validation.constraints.NotBlank; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.vividus.zephyr.model.TestCaseLevel; import org.vividus.zephyr.model.TestCaseStatus; @ConfigurationProperties("zephyr.exporter") @@ -29,6 +30,8 @@ public class ZephyrExporterProperties { private String jiraInstanceKey; + private boolean exportResults; + @NotBlank(message = "Property 'zephyr.exporter.source-directory' must not be blank") private Path sourceDirectory; @@ -36,6 +39,12 @@ public class ZephyrExporterProperties private List statusesOfTestCasesToAddToExecution; + private TestCaseLevel level; + + private boolean updateCasesOnExport; + + private String statusForUpdatedTestCases = "Backlog"; + public String getJiraInstanceKey() { return jiraInstanceKey; @@ -46,6 +55,16 @@ public void setJiraInstanceKey(String jiraInstanceKey) this.jiraInstanceKey = jiraInstanceKey; } + public boolean getExportResults() + { + return exportResults; + } + + public void setExportResults(boolean exportResults) + { + this.exportResults = exportResults; + } + public Path getSourceDirectory() { return sourceDirectory; @@ -75,4 +94,34 @@ public void setStatusesOfTestCasesToAddToExecution(List statuses { this.statusesOfTestCasesToAddToExecution = statusesOfTestCasesToAddToExecution; } + + public TestCaseLevel getLevel() + { + return level; + } + + public void setLevel(TestCaseLevel level) + { + this.level = level; + } + + public boolean isUpdateCasesOnExport() + { + return updateCasesOnExport; + } + + public void setUpdateCasesOnExport(boolean updateCasesOnExport) + { + this.updateCasesOnExport = updateCasesOnExport; + } + + public String getStatusForUpdatedTestCases() + { + return statusForUpdatedTestCases; + } + + public void setStatusForUpdatedTestCases(String statusForUpdatedTestCases) + { + this.statusForUpdatedTestCases = statusForUpdatedTestCases; + } } diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/convertor/CucumberStoryScenarioConverter.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/convertor/CucumberStoryScenarioConverter.java new file mode 100644 index 0000000000..1f2406055c --- /dev/null +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/convertor/CucumberStoryScenarioConverter.java @@ -0,0 +1,43 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.zephyr.convertor; + +import java.util.ArrayList; +import java.util.List; + +import org.vividus.model.jbehave.Step; +import org.vividus.model.jbehave.Story; +import org.vividus.zephyr.model.CucumberTestStep; + +public final class CucumberStoryScenarioConverter +{ + private CucumberStoryScenarioConverter() + { + } + + public static List convert(String scenarioTitle, List steps) + { + List testSteps = new ArrayList<>(); + return testSteps; + } + + public static List convert(Story steps) + { + List testSteps = new ArrayList<>(); + return testSteps; + } +} diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/AbstractTestCaseSerializer.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/AbstractTestCaseSerializer.java new file mode 100644 index 0000000000..0439dabd43 --- /dev/null +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/AbstractTestCaseSerializer.java @@ -0,0 +1,73 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.zephyr.databind; + +import static org.vividus.exporter.databind.SerializeJsonHelper.writeJsonArray; +import static org.vividus.exporter.databind.SerializeJsonHelper.writeObjectWithField; + +import java.io.IOException; +import java.util.Objects; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import org.springframework.beans.factory.annotation.Autowired; +import org.vividus.zephyr.configuration.JiraFieldsMapping; +import org.vividus.zephyr.model.ZephyrTestCase; + +public abstract class AbstractTestCaseSerializer extends JsonSerializer +{ + private static final String NAME = "name"; + + @Autowired + private JiraFieldsMapping jiraFieldsMapping; + + @Override + public void serialize(ZephyrTestCase zephyrTestCase, JsonGenerator generator, SerializerProvider serializers) + throws IOException + { + generator.writeStartObject(); + + generator.writeObjectFieldStart("fields"); + + writeObjectWithField(generator, "project", "key", zephyrTestCase.getProjectKey()); + + writeObjectWithField(generator, "issuetype", NAME, "Test"); + + writeJsonArray(generator, "labels", zephyrTestCase.getLabels(), false); + + writeJsonArray(generator, "components", zephyrTestCase.getComponents(), true); + + generator.writeStringField("summary", zephyrTestCase.getSummary()); + + if (Objects.nonNull(jiraFieldsMapping.getStoryType()) && !jiraFieldsMapping.getStoryType().isEmpty()) + { + writeObjectWithField(generator, jiraFieldsMapping.getStoryType(), "value", "Task"); + } + + generator.writeEndObject(); + generator.writeEndObject(); + } + + protected abstract void serializeCustomFields(ZephyrTestCase testCase, JsonGenerator generator) throws IOException; + + protected JiraFieldsMapping getJiraFieldsMapping() + { + return jiraFieldsMapping; + } +} diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/TestCaseDeserializer.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/TestCaseDeserializer.java index 99261d7f7e..89837f6813 100644 --- a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/TestCaseDeserializer.java +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/TestCaseDeserializer.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,23 +24,23 @@ import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import org.vividus.util.json.JsonPathUtils; -import org.vividus.zephyr.model.TestCase; +import org.vividus.zephyr.model.TestCaseExecution; -public class TestCaseDeserializer extends StdDeserializer +public class TestCaseDeserializer extends StdDeserializer { private static final long serialVersionUID = 7820826665413256040L; public TestCaseDeserializer() { - super(TestCase.class); + super(TestCaseExecution.class); } @Override - public TestCase deserialize(JsonParser parser, DeserializationContext deserializer) throws IOException + public TestCaseExecution deserialize(JsonParser parser, DeserializationContext deserializer) throws IOException { String node = parser.getCodec().readTree(parser).toString(); String status = JsonPathUtils.getData(node, "$.status"); List testCaseIds = JsonPathUtils.getData(node, "$..[?(@.name=='testCaseId')].value"); - return new TestCase(testCaseIds, status); + return new TestCaseExecution(testCaseIds, status); } } diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/TestStepsSerializer.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/TestStepsSerializer.java new file mode 100644 index 0000000000..628e81a0fc --- /dev/null +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/TestStepsSerializer.java @@ -0,0 +1,49 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.zephyr.databind; + +import java.io.IOException; +import java.util.List; + +import com.fasterxml.jackson.core.JsonGenerator; + +import org.springframework.stereotype.Component; +import org.vividus.zephyr.model.CucumberTestStep; +import org.vividus.zephyr.model.ZephyrTestCase; + +@Component +public class TestStepsSerializer extends AbstractTestCaseSerializer +{ + @Override + protected void serializeCustomFields(ZephyrTestCase testCase, JsonGenerator generator) throws IOException + { + List steps = testCase.getTestSteps(); + + generator.writeObjectFieldStart(getJiraFieldsMapping().getTestSteps()); + generator.writeArrayFieldStart("tests"); + + for (int i = 0; i < steps.size(); i++) + { + CucumberTestStep testStep = steps.get(i); + generator.writeStartObject(); + generator.writeStringField("Test Step", testStep.getTestStep()); + generator.writeEndObject(); + } + generator.writeEndArray(); + generator.writeEndObject(); + } +} diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/exporter/ZephyrExporter.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/exporter/ZephyrExporter.java index dc61e50e8d..9645edaafb 100644 --- a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/exporter/ZephyrExporter.java +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/exporter/ZephyrExporter.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,8 +16,13 @@ package org.vividus.zephyr.exporter; +import static java.lang.System.lineSeparator; + +import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.OptionalInt; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -28,31 +33,49 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.vividus.exporter.facade.ExporterFacade; import org.vividus.jira.JiraConfigurationException; import org.vividus.jira.JiraFacade; import org.vividus.jira.model.JiraEntity; +import org.vividus.model.jbehave.IContainingMeta; +import org.vividus.model.jbehave.NotUniqueMetaValueException; +import org.vividus.model.jbehave.Scenario; +import org.vividus.model.jbehave.Story; +import org.vividus.output.OutputReader; import org.vividus.zephyr.configuration.ZephyrConfiguration; import org.vividus.zephyr.configuration.ZephyrExporterProperties; +import org.vividus.zephyr.convertor.CucumberStoryScenarioConverter; import org.vividus.zephyr.databind.TestCaseDeserializer; import org.vividus.zephyr.facade.IZephyrFacade; +import org.vividus.zephyr.facade.TestCaseParameters; import org.vividus.zephyr.facade.ZephyrFacade; import org.vividus.zephyr.model.ExecutionStatus; -import org.vividus.zephyr.model.TestCase; +import org.vividus.zephyr.model.TestCaseExecution; +import org.vividus.zephyr.model.TestCaseLevel; import org.vividus.zephyr.model.ZephyrExecution; +import org.vividus.zephyr.model.ZephyrTestCase; import org.vividus.zephyr.parser.TestCaseParser; -public class ZephyrExporter +public class ZephyrExporter extends ExporterFacade { + public static final String ZEPHYR_COMPONENTS = "zephyr.components"; + public static final String ZEPHYR_LABELS = "zephyr.labels"; private static final Logger LOGGER = LoggerFactory.getLogger(ZephyrExporter.class); + private static final String TEST_CASE_ID = "testCaseId"; + private static final String STORY = "Story: "; + private static final String ERROR = "Error: "; + private static final String REQUIREMENT_ID = "requirementId"; + + private final List errors = new ArrayList<>(); private final JiraFacade jiraFacade; - private IZephyrFacade zephyrFacade; - private TestCaseParser testCaseParser; - private ZephyrExporterProperties zephyrExporterProperties; + private final IZephyrFacade zephyrFacade; + private final TestCaseParser testCaseParser; + private final ZephyrExporterProperties zephyrExporterProperties; private final ObjectMapper objectMapper; - public ZephyrExporter(JiraFacade jiraFacade, ZephyrFacade zephyrFacade, TestCaseParser testCaseParser, - ZephyrExporterProperties zephyrExporterProperties) + public ZephyrExporter(JiraFacade jiraFacade, ZephyrFacade zephyrFacade, + TestCaseParser testCaseParser, ZephyrExporterProperties zephyrExporterProperties) { this.jiraFacade = jiraFacade; this.zephyrFacade = zephyrFacade; @@ -62,24 +85,50 @@ public ZephyrExporter(JiraFacade jiraFacade, ZephyrFacade zephyrFacade, TestCase .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) .build() - .registerModule(new SimpleModule().addDeserializer(TestCase.class, new TestCaseDeserializer())); + .registerModule(new SimpleModule() + .addDeserializer(TestCaseExecution.class, new TestCaseDeserializer())); } public void exportResults() throws IOException, JiraConfigurationException { - List testCasesForImporting = testCaseParser.createTestCases(objectMapper); + if (zephyrExporterProperties.getExportResults()) + { + for (Story story : OutputReader.readStoriesFromJsons(zephyrExporterProperties.getSourceDirectory())) + { + TestCaseLevel testCaseLevel = zephyrExporterProperties.getLevel(); + if (testCaseLevel.equals(TestCaseLevel.STORY)) + { + LOGGER.atInfo().addArgument(story::getPath).log("Exporting {} story"); + exportStory(story); + } + if (zephyrExporterProperties.getLevel().equals(TestCaseLevel.SCENARIO)) + { + LOGGER.atInfo().addArgument(story::getPath).log("Exporting scenarios from {} story"); + for (Scenario scenario : story.getFoldedScenarios()) + { + exportScenario(story.getPath(), scenario); + } + } + } + } + exportTestExecutions(); + } + + private void exportTestExecutions() throws IOException, JiraConfigurationException + { ZephyrConfiguration configuration = zephyrFacade.prepareConfiguration(); - for (TestCase testCase : testCasesForImporting) + List testCasesForImportingExecution = testCaseParser.createTestCases(objectMapper); + for (TestCaseExecution testCaseExecution : testCasesForImportingExecution) { - exportTestExecution(testCase, configuration); + exportTestExecution(testCaseExecution, configuration); } } - private void exportTestExecution(TestCase testCase, ZephyrConfiguration configuration) + private void exportTestExecution(TestCaseExecution testCaseExecution, ZephyrConfiguration configuration) throws IOException, JiraConfigurationException { - JiraEntity issue = jiraFacade.getIssue(testCase.getKey()); - ZephyrExecution execution = new ZephyrExecution(configuration, issue.getId(), testCase.getStatus()); + JiraEntity issue = jiraFacade.getIssue(testCaseExecution.getKey()); + ZephyrExecution execution = new ZephyrExecution(configuration, issue.getId(), testCaseExecution.getStatus()); OptionalInt executionId; if (zephyrExporterProperties.getUpdateExecutionStatusesOnly()) @@ -99,8 +148,105 @@ private void exportTestExecution(TestCase testCase, ZephyrConfiguration configur } else { - LOGGER.atInfo().addArgument(testCase::getKey).log("Test case result for {} was not exported, " + LOGGER.atInfo().addArgument(testCaseExecution::getKey).log("Test case result for {} was not exported, " + "because execution does not exist"); } } + + private void exportStory(Story story) + { + try + { + String testCaseId = story.getUniqueMetaValue(TEST_CASE_ID).orElse(null); + ZephyrTestCase zephyrTest = new ZephyrTestCase(); + TestCaseParameters parameters = createTestCaseStoryParameters(story); + parameters.setCucumberTestSteps(CucumberStoryScenarioConverter.convert(story)); + fillTestCase(parameters, zephyrTest); + + if (testCaseId != null && zephyrExporterProperties.isUpdateCasesOnExport()) + { + zephyrFacade.updateTestCase(testCaseId, zephyrTest); + } + else + { + testCaseId = zephyrFacade.createTestCase(zephyrTest); + } + Optional requirementId = story.getUniqueMetaValue(REQUIREMENT_ID); + + createTestsLink(testCaseId, requirementId, jiraFacade); + } + catch (IOException | NotUniqueMetaValueException | JiraConfigurationException e) + { + String errorMessage = STORY + story.getPath() + lineSeparator() + ERROR + e.getMessage(); + errors.add(errorMessage); + LOGGER.atError().setCause(e).log("Got an error while exporting story"); + } + } + + private void exportScenario(String storyTitle, Scenario scenario) + { + String scenarioTitle = scenario.getTitle(); + LOGGER.atInfo().addArgument(scenarioTitle).log("Exporting {} scenario"); + + try + { + String testCaseId = scenario.getUniqueMetaValue(TEST_CASE_ID).orElse(null); + + ZephyrTestCase zephyrTest = new ZephyrTestCase(); + TestCaseParameters parameters = createTestCaseScenarioParameters(scenario); + parameters.setCucumberTestSteps(CucumberStoryScenarioConverter + .convert(scenario.getTitle(), scenario.collectSteps())); + fillTestCase(parameters, zephyrTest); + + if (testCaseId != null && zephyrExporterProperties.isUpdateCasesOnExport()) + { + zephyrFacade.updateTestCase(testCaseId, zephyrTest); + } + else + { + testCaseId = zephyrFacade.createTestCase(zephyrTest); + } + Optional requirementId = scenario.getUniqueMetaValue(REQUIREMENT_ID); + createTestsLink(testCaseId, requirementId, jiraFacade); + } + catch (IOException | NotUniqueMetaValueException | JiraConfigurationException e) + { + String errorMessage = STORY + storyTitle + lineSeparator() + "Scenario: " + scenarioTitle + + lineSeparator() + ERROR + e.getMessage(); + errors.add(errorMessage); + LOGGER.atError().setCause(e).log("Got an error while exporting scenario"); + } + } + + private TestCaseParameters createTestCaseScenarioParameters(Scenario scenario) + { + TestCaseParameters parameters = createTestCaseParameters(scenario); + parameters.setSummary(scenario.getTitle()); + return parameters; + } + + private TestCaseParameters createTestCaseStoryParameters(Story story) + { + String summary = story.getPath(); + File summaryFile = new File(summary); + TestCaseParameters parameters = createTestCaseParameters(story); + parameters.setSummary(summaryFile.getName()); + return parameters; + } + + private TestCaseParameters createTestCaseParameters(IContainingMeta entity) + { + TestCaseParameters parameters = new TestCaseParameters(); + parameters.setLabels(entity.getMetaValues(ZEPHYR_LABELS)); + parameters.setComponents(entity.getMetaValues(ZEPHYR_COMPONENTS)); + return parameters; + } + + private void fillTestCase(TestCaseParameters parameters, ZephyrTestCase zephyrTest) + { + zephyrTest.setLabels(parameters.getLabels()); + zephyrTest.setComponents(parameters.getComponents()); + zephyrTest.setSummary(parameters.getSummary()); + parameters.setCucumberTestSteps(parameters.getCucumberTestSteps()); + } } diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/IZephyrFacade.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/IZephyrFacade.java index 3a1c797e88..140bf84d8d 100644 --- a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/IZephyrFacade.java +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/IZephyrFacade.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import org.vividus.jira.JiraConfigurationException; import org.vividus.zephyr.configuration.ZephyrConfiguration; +import org.vividus.zephyr.model.ZephyrTestCase; public interface IZephyrFacade { @@ -28,6 +29,10 @@ public interface IZephyrFacade Integer createExecution(String execution) throws IOException, JiraConfigurationException; + void updateTestCase(String testCaseId, ZephyrTestCase zephyrTest) throws IOException, JiraConfigurationException; + + String createTestCase(ZephyrTestCase zephyrTest) throws IOException, JiraConfigurationException; + void updateExecutionStatus(int executionId, String executionBody) throws IOException, JiraConfigurationException; OptionalInt findExecutionId(String issueId) throws IOException, JiraConfigurationException; diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/TestCaseParameters.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/TestCaseParameters.java new file mode 100644 index 0000000000..a3981f5751 --- /dev/null +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/TestCaseParameters.java @@ -0,0 +1,70 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.zephyr.facade; + +import java.util.List; +import java.util.Set; + +import org.vividus.zephyr.model.CucumberTestStep; + +public class TestCaseParameters +{ + private Set labels; + private Set components; + private List cucumberTestSteps; + private String summary; + + public Set getLabels() + { + return labels; + } + + public void setLabels(Set labels) + { + this.labels = labels; + } + + public Set getComponents() + { + return components; + } + + public void setComponents(Set components) + { + this.components = components; + } + + public List getCucumberTestSteps() + { + return cucumberTestSteps; + } + + public void setCucumberTestSteps(List cucumberTestSteps) + { + this.cucumberTestSteps = cucumberTestSteps; + } + + public String getSummary() + { + return summary; + } + + public void setSummary(String summary) + { + this.summary = summary; + } +} diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/ZephyrFacade.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/ZephyrFacade.java index 37fc0e267b..bbc1394e60 100644 --- a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/ZephyrFacade.java +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/ZephyrFacade.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,14 @@ import java.util.Optional; import java.util.OptionalInt; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.vividus.jira.JiraClient; import org.vividus.jira.JiraClientProvider; import org.vividus.jira.JiraConfigurationException; @@ -38,24 +45,33 @@ import org.vividus.zephyr.configuration.ZephyrConfiguration; import org.vividus.zephyr.configuration.ZephyrExporterConfiguration; import org.vividus.zephyr.configuration.ZephyrExporterProperties; +import org.vividus.zephyr.databind.TestStepsSerializer; import org.vividus.zephyr.model.TestCaseStatus; +import org.vividus.zephyr.model.ZephyrTestCase; public class ZephyrFacade implements IZephyrFacade { + private static final Logger LOGGER = LoggerFactory.getLogger(ZephyrFacade.class); private static final String ZAPI_ENDPOINT = "/rest/zapi/latest/"; private final JiraFacade jiraFacade; private final JiraClientProvider jiraClientProvider; private final ZephyrExporterConfiguration zephyrExporterConfiguration; private final ZephyrExporterProperties zephyrExporterProperties; + private final ObjectMapper objectMapper; public ZephyrFacade(JiraFacade jiraFacade, JiraClientProvider jiraClientProvider, - ZephyrExporterConfiguration zephyrExporterConfiguration, ZephyrExporterProperties zephyrExporterProperties) + ZephyrExporterConfiguration zephyrExporterConfiguration, + ZephyrExporterProperties zephyrExporterProperties, TestStepsSerializer testStepsSerializer) { this.jiraFacade = jiraFacade; this.jiraClientProvider = jiraClientProvider; this.zephyrExporterConfiguration = zephyrExporterConfiguration; this.zephyrExporterProperties = zephyrExporterProperties; + this.objectMapper = new ObjectMapper() + .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .setSerializationInclusion(JsonInclude.Include.NON_NULL) + .registerModule(new SimpleModule().addSerializer(ZephyrTestCase.class, testStepsSerializer)); } @Override @@ -66,6 +82,33 @@ public Integer createExecution(String execution) throws IOException, JiraConfigu return executionId.get(0); } + @Override + public void updateTestCase(String testCaseId, ZephyrTestCase zephyrTest) + throws IOException, JiraConfigurationException + { + String updateTestRequest = objectMapper.writeValueAsString(zephyrTest); + LOGGER.atInfo().addArgument(testCaseId) + .addArgument(updateTestRequest) + .log("Updating Test Case with ID {}: {}"); + jiraFacade.updateIssue(testCaseId, updateTestRequest); + jiraFacade.setIssueStatus(testCaseId, zephyrExporterProperties.getStatusForUpdatedTestCases()); + LOGGER.atInfo().addArgument(testCaseId) + .log("Test with key {} has been updated"); + } + + @Override + public String createTestCase(ZephyrTestCase zephyrTest) throws IOException, JiraConfigurationException + { + zephyrTest.setProjectKey(zephyrExporterConfiguration.getProjectKey()); + String createTestRequest = objectMapper.writeValueAsString(zephyrTest); + LOGGER.atInfo().addArgument(createTestRequest).log("Creating Test Case: {}"); + String response = jiraFacade + .createIssue(createTestRequest, Optional.ofNullable(zephyrExporterProperties.getJiraInstanceKey())); + String issueKey = JsonPathUtils.getData(response, "$.key"); + LOGGER.atInfo().addArgument(issueKey).log("Test with key {} has been created"); + return issueKey; + } + @Override public void updateExecutionStatus(int executionId, String executionBody) throws IOException, JiraConfigurationException @@ -144,19 +187,6 @@ private String findFolderId(String cycleId, String projectAndVersionUrlQuery) return folderId.get(0).toString(); } - private Map getExecutionStatuses() throws IOException, JiraConfigurationException - { - String json = getJiraClient().executeGet(ZAPI_ENDPOINT + "util/testExecutionStatus"); - Map testStatusPerZephyrIdMapping = new EnumMap<>(TestCaseStatus.class); - zephyrExporterConfiguration.getStatuses().entrySet().forEach(s -> - { - List statusId = JsonPathUtils.getData(json, String.format("$.[?(@.name=='%s')].id", s.getValue())); - notEmpty(statusId, "Status '%s' does not exist", s.getValue()); - testStatusPerZephyrIdMapping.put(s.getKey(), statusId.get(0)); - }); - return testStatusPerZephyrIdMapping; - } - @Override public OptionalInt findExecutionId(String issueId) throws IOException, JiraConfigurationException { @@ -177,6 +207,18 @@ public OptionalInt findExecutionId(String issueId) throws IOException, JiraConfi return executionId.size() != 0 ? OptionalInt.of(executionId.get(0)) : OptionalInt.empty(); } + private Map getExecutionStatuses() throws IOException, JiraConfigurationException + { + String json = getJiraClient().executeGet(ZAPI_ENDPOINT + "util/testExecutionStatus"); + Map testStatusPerZephyrIdMapping = new EnumMap<>(TestCaseStatus.class); + zephyrExporterConfiguration.getStatuses().forEach((key, value) -> { + List statusId = JsonPathUtils.getData(json, String.format("$.[?(@.name=='%s')].id", value)); + notEmpty(statusId, "Status '%s' does not exist", value); + testStatusPerZephyrIdMapping.put(key, statusId.get(0)); + }); + return testStatusPerZephyrIdMapping; + } + private JiraClient getJiraClient() throws JiraConfigurationException { return jiraClientProvider diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/CucumberTestStep.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/CucumberTestStep.java new file mode 100644 index 0000000000..03ef6a528f --- /dev/null +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/CucumberTestStep.java @@ -0,0 +1,37 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.zephyr.model; + +public class CucumberTestStep +{ + private String testStep; + + public CucumberTestStep(String testStep) + { + this.testStep = testStep; + } + + public String getTestStep() + { + return testStep; + } + + public void setTestStep(String testStep) + { + this.testStep = testStep; + } +} diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestCase.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestCaseExecution.java similarity index 85% rename from vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestCase.java rename to vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestCaseExecution.java index eb5a8ff277..4febe0f4e6 100644 --- a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestCase.java +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestCaseExecution.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,18 +18,18 @@ import java.util.List; -public class TestCase +public class TestCaseExecution { private List keys; private TestCaseStatus status; - public TestCase(List keys, String status) + public TestCaseExecution(List keys, String status) { this.keys = keys; this.status = TestCaseStatus.valueOf(status.toUpperCase()); } - public TestCase(String key, TestCaseStatus status) + public TestCaseExecution(String key, TestCaseStatus status) { this.keys = List.of(key); this.status = status; diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestCaseLevel.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestCaseLevel.java new file mode 100644 index 0000000000..a50902dd3c --- /dev/null +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestCaseLevel.java @@ -0,0 +1,23 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.zephyr.model; + +public enum TestCaseLevel +{ + STORY, + SCENARIO; +} diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/ZephyrTestCase.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/ZephyrTestCase.java new file mode 100644 index 0000000000..99c318b620 --- /dev/null +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/ZephyrTestCase.java @@ -0,0 +1,79 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.zephyr.model; + +import java.util.List; +import java.util.Set; + +public class ZephyrTestCase +{ + private String projectKey; + private Set labels; + private Set components; + private List testSteps; + private String summary; + + public String getProjectKey() + { + return projectKey; + } + + public void setProjectKey(String projectKey) + { + this.projectKey = projectKey; + } + + public Set getLabels() + { + return labels; + } + + public void setLabels(Set labels) + { + this.labels = labels; + } + + public Set getComponents() + { + return components; + } + + public void setComponents(Set components) + { + this.components = components; + } + + public List getTestSteps() + { + return testSteps; + } + + public void setTestSteps(List testSteps) + { + this.testSteps = testSteps; + } + + public String getSummary() + { + return summary; + } + + public void setSummary(String summary) + { + this.summary = summary; + } +} diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/parser/TestCaseParser.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/parser/TestCaseParser.java index d555f2d9a4..8553da2e55 100644 --- a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/parser/TestCaseParser.java +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/parser/TestCaseParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ import org.slf4j.LoggerFactory; import org.vividus.zephyr.configuration.ZephyrExporterProperties; import org.vividus.zephyr.configuration.ZephyrFileVisitor; -import org.vividus.zephyr.model.TestCase; +import org.vividus.zephyr.model.TestCaseExecution; import org.vividus.zephyr.model.TestCaseStatus; public class TestCaseParser @@ -47,27 +47,27 @@ public TestCaseParser(ZephyrExporterProperties zephyrExporterProperties) this.zephyrExporterProperties = zephyrExporterProperties; } - public List createTestCases(ObjectMapper objectMapper) throws IOException + public List createTestCases(ObjectMapper objectMapper) throws IOException { - List testCases = parseJsonResultsFile(getJsonResultsFiles(), objectMapper); - notEmpty(testCases, "There are not any test cases for exporting", + List testCaseExecutions = parseJsonResultsFile(getJsonResultsFiles(), objectMapper); + notEmpty(testCaseExecutions, "There are not any test cases for exporting", zephyrExporterProperties.getSourceDirectory()); - LOGGER.info("Test cases: {}", testCases); - Map> testCasesMap = testCases.stream() - .collect(Collectors.groupingBy(TestCase::getKey, - Collectors.mapping(TestCase::getStatus, Collectors.toCollection(TreeSet::new)))); + LOGGER.info("Test cases: {}", testCaseExecutions); + Map> testCasesMap = testCaseExecutions.stream() + .collect(Collectors.groupingBy(TestCaseExecution::getKey, + Collectors.mapping(TestCaseExecution::getStatus, Collectors.toCollection(TreeSet::new)))); - List testCasesForExporting = new ArrayList<>(); + List testCasesForExportingExecution = new ArrayList<>(); for (Map.Entry> entry : testCasesMap.entrySet()) { TestCaseStatus testCaseStatus = entry.getValue().first(); if (zephyrExporterProperties.getStatusesOfTestCasesToAddToExecution().contains(testCaseStatus)) { - testCasesForExporting.add(new TestCase(entry.getKey(), testCaseStatus)); + testCasesForExportingExecution.add(new TestCaseExecution(entry.getKey(), testCaseStatus)); } } - LOGGER.info("Test cases for exporting to JIRA: {}", testCasesForExporting); - return testCasesForExporting; + LOGGER.info("Test cases for exporting to JIRA: {}", testCasesForExportingExecution); + return testCasesForExportingExecution; } private List getJsonResultsFiles() throws IOException @@ -81,15 +81,15 @@ private List getJsonResultsFiles() throws IOException return jsonFiles; } - private List parseJsonResultsFile(List jsonFiles, ObjectMapper objectMapper) + private List parseJsonResultsFile(List jsonFiles, ObjectMapper objectMapper) { - List testCases = new ArrayList<>(); + List testCaseExecutions = new ArrayList<>(); for (File jsonFile : jsonFiles) { try { - TestCase testCase = objectMapper.readValue(jsonFile, TestCase.class); - List testCaseKeys = testCase.getKeys(); + TestCaseExecution testCaseExecution = objectMapper.readValue(jsonFile, TestCaseExecution.class); + List testCaseKeys = testCaseExecution.getKeys(); if (testCaseKeys.isEmpty()) { continue; @@ -98,13 +98,14 @@ private List parseJsonResultsFile(List jsonFiles, ObjectMapper o { for (String key : testCaseKeys) { - TestCase additionalTestCase = new TestCase(key, testCase.getStatus()); - testCases.add(additionalTestCase); + TestCaseExecution additionalTestCaseExecution = new TestCaseExecution(key, + testCaseExecution.getStatus()); + testCaseExecutions.add(additionalTestCaseExecution); } } else { - testCases.add(testCase); + testCaseExecutions.add(testCaseExecution); } } catch (IOException e) @@ -112,6 +113,6 @@ private List parseJsonResultsFile(List jsonFiles, ObjectMapper o throw new IllegalArgumentException("Problem with reading values from json file " + jsonFile, e); } } - return testCases; + return testCaseExecutions; } } diff --git a/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/databind/TestCaseDeserializerTests.java b/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/databind/TestCaseDeserializerTests.java index 936ba44c24..dc15c93ed6 100644 --- a/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/databind/TestCaseDeserializerTests.java +++ b/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/databind/TestCaseDeserializerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2020 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import org.vividus.zephyr.model.TestCase; +import org.vividus.zephyr.model.TestCaseExecution; import org.vividus.zephyr.model.TestCaseStatus; @ExtendWith(MockitoExtension.class) @@ -60,10 +60,10 @@ void testDeserialize() throws IOException JsonNode root = MAPPER.readTree("{\"status\" : \"failed\", \"labels\" : [{\"name\" : \"testCaseId\"," + "\"value\" : \"TEST-001\"}, {\"name\" : \"framework\", \"value\" : \"Vividus\"}]}"); when(objectCodec.readTree(parser)).thenReturn(root); - TestCase testCase = deserializer.deserialize(parser, null); + TestCaseExecution testCaseExecution = deserializer.deserialize(parser, null); - assertEquals(List.of("TEST-001"), testCase.getKeys()); - assertEquals(TestCaseStatus.FAILED, testCase.getStatus()); + assertEquals(List.of("TEST-001"), testCaseExecution.getKeys()); + assertEquals(TestCaseStatus.FAILED, testCaseExecution.getStatus()); } @Test @@ -72,10 +72,10 @@ void testDeserializeWithoutTestCaseId() throws IOException JsonNode root = MAPPER.readTree("{\"status\" : \"passed\"," + "\"labels\" : [{\"name\" : \"framework\", \"value\" : \"Vividus\"}]}"); when(objectCodec.readTree(parser)).thenReturn(root); - TestCase testCase = deserializer.deserialize(parser, null); + TestCaseExecution testCaseExecution = deserializer.deserialize(parser, null); - assertEquals(List.of(), testCase.getKeys()); - assertEquals(TestCaseStatus.PASSED, testCase.getStatus()); + assertEquals(List.of(), testCaseExecution.getKeys()); + assertEquals(TestCaseStatus.PASSED, testCaseExecution.getStatus()); } @Test @@ -85,9 +85,9 @@ void testDeserializeWithTwoTestCaseIds() throws IOException + "\"value\" : \"TEST-002\"}, {\"name\" : \"testCaseId\",\"value\" : \"TEST-003\"}," + "{\"name\" : \"framework\", \"value\" : \"Vividus\"}]}"); when(objectCodec.readTree(parser)).thenReturn(root); - TestCase testCase = deserializer.deserialize(parser, null); + TestCaseExecution testCaseExecution = deserializer.deserialize(parser, null); - assertEquals(List.of("TEST-002", "TEST-003"), testCase.getKeys()); - assertEquals(TestCaseStatus.BROKEN, testCase.getStatus()); + assertEquals(List.of("TEST-002", "TEST-003"), testCaseExecution.getKeys()); + assertEquals(TestCaseStatus.BROKEN, testCaseExecution.getStatus()); } } diff --git a/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/databind/TestStepsSerializerTests.java b/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/databind/TestStepsSerializerTests.java new file mode 100644 index 0000000000..63c923c189 --- /dev/null +++ b/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/databind/TestStepsSerializerTests.java @@ -0,0 +1,88 @@ +/* + * Copyright 2019-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.vividus.zephyr.databind; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.LinkedHashSet; +import java.util.List; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Spy; +import org.mockito.junit.jupiter.MockitoExtension; +import org.vividus.util.ResourceUtils; +import org.vividus.zephyr.configuration.JiraFieldsMapping; +import org.vividus.zephyr.model.CucumberTestStep; +import org.vividus.zephyr.model.ZephyrTestCase; + +@ExtendWith(MockitoExtension.class) +public class TestStepsSerializerTests +{ + @Spy + private JiraFieldsMapping fields; + @InjectMocks + private TestStepsSerializer serializer; + + @BeforeEach + void init() + { + fields.setTestSteps("step 1, step 2"); + fields.setStoryType("story-type"); + } + + @Test + void shouldSerializeTest() throws IOException + { + ZephyrTestCase test = createBaseTest(); + test.setLabels(new LinkedHashSet<>(List.of("label 1", "label 2"))); + test.setComponents(new LinkedHashSet<>(List.of("component 1", "component 2"))); + + try (StringWriter writer = new StringWriter()) + { + JsonFactory factory = new JsonFactory(); + JsonGenerator generator = factory.createGenerator(writer); + + serializer.serialize(test, generator, null); + + generator.close(); + + ObjectMapper mapper = new ObjectMapper(); + JsonNode actual = mapper.readTree(writer.toString()); + JsonNode expected = mapper.readTree(ResourceUtils.loadResource(getClass(), "report.json")); + assertEquals(expected, actual); + } + } + + private static ZephyrTestCase createBaseTest() + { + ZephyrTestCase test = new ZephyrTestCase(); + test.setProjectKey("project key"); + test.setSummary("summary"); + test.setTestSteps(List.of(new CucumberTestStep("testStep 1"), new CucumberTestStep("testStep 2"))); + return test; + } +} diff --git a/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/exporter/ZephyrExporterTests.java b/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/exporter/ZephyrExporterTests.java index 3f2d3800c3..4e3efd72cb 100644 --- a/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/exporter/ZephyrExporterTests.java +++ b/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/exporter/ZephyrExporterTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,20 @@ import static com.github.valfirst.slf4jtest.LoggingEvent.info; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.io.IOException; +import java.net.URI; import java.net.URISyntaxException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Collections; import java.util.EnumMap; import java.util.List; import java.util.Map; @@ -37,42 +43,55 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.io.TempDir; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.vividus.jira.JiraConfigurationException; import org.vividus.jira.JiraFacade; import org.vividus.jira.model.JiraEntity; +import org.vividus.util.ResourceUtils; import org.vividus.zephyr.configuration.ZephyrConfiguration; import org.vividus.zephyr.configuration.ZephyrExporterProperties; import org.vividus.zephyr.facade.ZephyrFacade; -import org.vividus.zephyr.model.TestCase; +import org.vividus.zephyr.model.TestCaseExecution; +import org.vividus.zephyr.model.TestCaseLevel; import org.vividus.zephyr.model.TestCaseStatus; import org.vividus.zephyr.parser.TestCaseParser; @ExtendWith({MockitoExtension.class, TestLoggerFactoryExtension.class}) class ZephyrExporterTests { + public static final String CREATEREPORTS = "createreports"; + public static final String UPDATEREPORTS = "updatereports"; private static final String TEST_CASE_KEY1 = "TEST-1"; private static final String TEST_CASE_KEY2 = "TEST-2"; private static final String ISSUE_ID1 = "1"; private static final String ISSUE_ID2 = "2"; private static final String STATUS_UPDATE_JSON = "{\"status\":\"-1\"}"; + private static final String SCENARIO_TITLE = "scenarioTitle"; + private static final String STORY_TITLE = "storyPath"; + private static final String EXPORTING_SCENARIO = "Exporting {} scenario"; + private static final String EXPORTING_SCENARIO_FROM_STORY = "Exporting scenarios from {} story"; + private static final String EXPORTING_STORY = "Exporting {} story"; private final TestLogger testLogger = TestLoggerFactory.getTestLogger(ZephyrExporter.class); @Mock private JiraFacade jiraFacade; @Mock private ZephyrFacade zephyrFacade; @Mock private TestCaseParser testCaseParser; - @Mock private ZephyrExporterProperties zephyrExporterProperties; + @Spy private ZephyrExporterProperties zephyrExporterProperties; @InjectMocks private ZephyrExporter zephyrExporter; + private final TestLogger logger = TestLoggerFactory.getTestLogger(ZephyrExporter.class); + @Test void testExportResults() throws IOException, URISyntaxException, JiraConfigurationException { when(testCaseParser.createTestCases(any())).thenReturn(List.of( - new TestCase(TEST_CASE_KEY1, TestCaseStatus.SKIPPED), - new TestCase(TEST_CASE_KEY2, TestCaseStatus.PASSED))); + new TestCaseExecution(TEST_CASE_KEY1, TestCaseStatus.SKIPPED), + new TestCaseExecution(TEST_CASE_KEY2, TestCaseStatus.PASSED))); when(zephyrFacade.prepareConfiguration()).thenReturn(prepareTestConfiguration()); mockJiraIssueRetrieve(TEST_CASE_KEY1, ISSUE_ID1); mockJiraIssueRetrieve(TEST_CASE_KEY2, ISSUE_ID2); @@ -80,6 +99,9 @@ void testExportResults() throws IOException, URISyntaxException, JiraConfigurati + "\"projectId\":\"11111\",\"versionId\":\"11112\"}"; when(zephyrFacade.createExecution(String.format(executionBody, ISSUE_ID1))).thenReturn(111); when(zephyrFacade.createExecution(String.format(executionBody, ISSUE_ID2))).thenReturn(222); + URI jsonResultsUri = getJsonResultsUri(CREATEREPORTS); + zephyrExporterProperties.setLevel(TestCaseLevel.SCENARIO); + zephyrExporterProperties.setSourceDirectory(Paths.get(jsonResultsUri)); zephyrExporter.exportResults(); verify(zephyrFacade).updateExecutionStatus(111, STATUS_UPDATE_JSON); verify(zephyrFacade).updateExecutionStatus(222, "{\"status\":\"1\"}"); @@ -90,20 +112,99 @@ void testExportResultsWithOnlyStatusUpdate() throws IOException, URISyntaxExcept { when(zephyrExporterProperties.getUpdateExecutionStatusesOnly()).thenReturn(true); when(testCaseParser.createTestCases(any())).thenReturn(List.of( - new TestCase(TEST_CASE_KEY1, TestCaseStatus.SKIPPED), - new TestCase(TEST_CASE_KEY2, TestCaseStatus.PASSED))); + new TestCaseExecution(TEST_CASE_KEY1, TestCaseStatus.SKIPPED), + new TestCaseExecution(TEST_CASE_KEY2, TestCaseStatus.PASSED))); when(zephyrFacade.prepareConfiguration()).thenReturn(prepareTestConfiguration()); mockJiraIssueRetrieve(TEST_CASE_KEY1, ISSUE_ID1); mockJiraIssueRetrieve(TEST_CASE_KEY2, ISSUE_ID2); when(zephyrFacade.findExecutionId(ISSUE_ID1)).thenReturn(OptionalInt.of(111)); when(zephyrFacade.findExecutionId(ISSUE_ID2)).thenReturn(OptionalInt.empty()); + URI jsonResultsUri = getJsonResultsUri(UPDATEREPORTS); + zephyrExporterProperties.setLevel(TestCaseLevel.SCENARIO); + zephyrExporterProperties.setSourceDirectory(Paths.get(jsonResultsUri)); + zephyrExporterProperties.setExportResults(true); + zephyrExporter.exportResults(); verify(zephyrFacade).updateExecutionStatus(111, STATUS_UPDATE_JSON); - verifyNoMoreInteractions(zephyrFacade); - assertThat(testLogger.getLoggingEvents(), is(List.of(info("Test case result for {} was not exported, " + assertThat(testLogger.getLoggingEvents(), is(List.of(info(EXPORTING_SCENARIO_FROM_STORY, STORY_TITLE), + info(EXPORTING_SCENARIO, SCENARIO_TITLE), info("Test case result for {} was not exported, " + "because execution does not exist", TEST_CASE_KEY2)))); } + @Test + void shouldFailIfResultsDirectoryIsEmpty(@TempDir Path sourceDirectory) + { + zephyrExporterProperties.setSourceDirectory(sourceDirectory); + zephyrExporterProperties.setExportResults(true); + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, + zephyrExporter::exportResults); + + assertEquals(String.format("The directory '%s' does not contain needed JSON files", sourceDirectory), + exception.getMessage()); + assertThat(logger.getLoggingEvents(), empty()); + } + + @Test + void shouldExportNewTestWithStoryLevel() throws URISyntaxException, IOException, JiraConfigurationException + { + URI jsonResultsUri = getJsonResultsUri(CREATEREPORTS); + zephyrExporterProperties.setLevel(TestCaseLevel.STORY); + zephyrExporterProperties.setSourceDirectory(Paths.get(jsonResultsUri)); + zephyrExporterProperties.setExportResults(true); + + zephyrExporter.exportResults(); + + verify(zephyrFacade).createTestCase(any()); + assertThat(logger.getLoggingEvents(), is(Collections.singletonList(info(EXPORTING_STORY, STORY_TITLE)))); + } + + @Test + void shouldExportNewTestWithScenarioLevel() throws URISyntaxException, IOException, JiraConfigurationException + { + URI jsonResultsUri = getJsonResultsUri(CREATEREPORTS); + zephyrExporterProperties.setLevel(TestCaseLevel.SCENARIO); + zephyrExporterProperties.setSourceDirectory(Paths.get(jsonResultsUri)); + zephyrExporterProperties.setExportResults(true); + + + zephyrExporter.exportResults(); + + verify(zephyrFacade).createTestCase(any()); + assertThat(logger.getLoggingEvents(), is(List.of(info(EXPORTING_SCENARIO_FROM_STORY, STORY_TITLE), + info(EXPORTING_SCENARIO, SCENARIO_TITLE)))); + } + + @Test + void shouldUpdateTestWithStoryLevel() throws URISyntaxException, IOException, JiraConfigurationException + { + URI jsonResultsUri = getJsonResultsUri(UPDATEREPORTS); + zephyrExporterProperties.setLevel(TestCaseLevel.STORY); + zephyrExporterProperties.setSourceDirectory(Paths.get(jsonResultsUri)); + zephyrExporterProperties.setExportResults(true); + zephyrExporterProperties.setUpdateCasesOnExport(true); + + zephyrExporter.exportResults(); + + verify(zephyrFacade).updateTestCase(any(), any()); + assertThat(logger.getLoggingEvents(), is(List.of(info(EXPORTING_STORY, STORY_TITLE)))); + } + + @Test + void shouldUpdateTestWithScenarioLevel() throws URISyntaxException, IOException, JiraConfigurationException + { + URI jsonResultsUri = getJsonResultsUri(UPDATEREPORTS); + zephyrExporterProperties.setLevel(TestCaseLevel.SCENARIO); + zephyrExporterProperties.setSourceDirectory(Paths.get(jsonResultsUri)); + zephyrExporterProperties.setUpdateCasesOnExport(true); + zephyrExporterProperties.setExportResults(true); + + zephyrExporter.exportResults(); + + verify(zephyrFacade).updateTestCase(any(), any()); + assertThat(logger.getLoggingEvents(), is(List.of(info(EXPORTING_SCENARIO_FROM_STORY, STORY_TITLE), + info(EXPORTING_SCENARIO, SCENARIO_TITLE)))); + } + private ZephyrConfiguration prepareTestConfiguration() { ZephyrConfiguration configuration = new ZephyrConfiguration(); @@ -124,4 +225,9 @@ private void mockJiraIssueRetrieve(String issueKey, String issueId) throws IOExc issue.setId(issueId); when(jiraFacade.getIssue(issueKey)).thenReturn(issue); } + + public URI getJsonResultsUri(String resource) throws URISyntaxException + { + return ResourceUtils.findResource(getClass(), resource).toURI(); + } } diff --git a/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/facade/ZephyrFacadeTests.java b/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/facade/ZephyrFacadeTests.java index 517c306b74..b299a134d2 100644 --- a/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/facade/ZephyrFacadeTests.java +++ b/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/facade/ZephyrFacadeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +16,32 @@ package org.vividus.zephyr.facade; +import static com.github.valfirst.slf4jtest.LoggingEvent.info; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.io.IOException; import java.util.EnumMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.OptionalInt; -import org.junit.jupiter.api.BeforeEach; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.github.valfirst.slf4jtest.TestLogger; +import com.github.valfirst.slf4jtest.TestLoggerFactory; +import com.github.valfirst.slf4jtest.TestLoggerFactoryExtension; + import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -44,9 +56,12 @@ import org.vividus.zephyr.configuration.ZephyrConfiguration; import org.vividus.zephyr.configuration.ZephyrExporterConfiguration; import org.vividus.zephyr.configuration.ZephyrExporterProperties; +import org.vividus.zephyr.databind.TestStepsSerializer; +import org.vividus.zephyr.model.CucumberTestStep; import org.vividus.zephyr.model.TestCaseStatus; +import org.vividus.zephyr.model.ZephyrTestCase; -@ExtendWith(MockitoExtension.class) +@ExtendWith({MockitoExtension.class, TestLoggerFactoryExtension.class}) class ZephyrFacadeTests { private static final String ZAPI_ENDPOINT = "/rest/zapi/latest/"; @@ -66,19 +81,18 @@ class ZephyrFacadeTests private static final String FOLDER_ID = "11114"; private static final String ISSUE_ID = "111"; private static final String TEST = "test"; + private static final String BODY = "{}"; + private static final String CREATE_RESPONSE = "{\"key\" : \"" + ISSUE_ID + "\"}"; @Mock private JiraFacade jiraFacade; @Mock private JiraClient client; @Mock private JiraClientProvider jiraClientProvider; @Mock private ZephyrExporterConfiguration zephyrExporterConfiguration; + @Mock private TestStepsSerializer testStepsSerializer; + @Mock private ZephyrExporterProperties zephyrExporterProperties; @InjectMocks private ZephyrFacade zephyrFacade; - @BeforeEach - void init() - { - zephyrFacade = new ZephyrFacade(jiraFacade, jiraClientProvider, zephyrExporterConfiguration, - new ZephyrExporterProperties()); - } + private final TestLogger logger = TestLoggerFactory.getTestLogger(ZephyrFacade.class); @Test void testCreateExecution() throws IOException, JiraConfigurationException @@ -244,6 +258,33 @@ void testExecutionIdNotFound() throws IOException, JiraConfigurationException assertEquals(OptionalInt.empty(), zephyrFacade.findExecutionId(ISSUE_ID)); } + @Test + void testUpdateTestCase() throws IOException, JiraConfigurationException + { + ZephyrTestCase test = createZephyrTestCase(); + mockSerialization(test); + when(jiraFacade.updateIssue(ISSUE_ID, BODY)).thenReturn(BODY); + + zephyrFacade.updateTestCase(ISSUE_ID, test); + + assertThat(logger.getLoggingEvents(), is(List.of( + info("Updating Test Case with ID {}: {}", ISSUE_ID, BODY), + info("Test with key {} has been updated", ISSUE_ID)))); + } + + @Test + void testCreateNewTestCase() throws IOException, JiraConfigurationException + { + ZephyrTestCase test = createZephyrTestCase(); + mockSerialization(test); + when(jiraFacade.createIssue(BODY, Optional.empty())).thenReturn(CREATE_RESPONSE); + zephyrFacade.createTestCase(test); + + assertThat(logger.getLoggingEvents(), is(List.of( + info("Creating Test Case: {}", BODY), + info("Test with key {} has been created", ISSUE_ID)))); + } + private void mockJiraProjectRetrieve() throws IOException, JiraConfigurationException { Version version = new Version(); @@ -266,4 +307,24 @@ private void setConfiguration() statuses.put(TestCaseStatus.PASSED, TEST); when(zephyrExporterConfiguration.getStatuses()).thenReturn(statuses); } + + private ZephyrTestCase createZephyrTestCase() + { + ZephyrTestCase test = new ZephyrTestCase(); + test.setLabels(new LinkedHashSet<>(List.of("label"))); + test.setComponents(new LinkedHashSet<>(List.of("component"))); + test.setTestSteps(List.of(new CucumberTestStep("testStep 1"), new CucumberTestStep("testStep 2"))); + return test; + } + + private void mockSerialization(ZephyrTestCase test) throws IOException + { + doAnswer(a -> + { + JsonGenerator generator = a.getArgument(1, JsonGenerator.class); + generator.writeStartObject(); + generator.writeEndObject(); + return null; + }).when(testStepsSerializer).serialize(eq(test), any(JsonGenerator.class), any(SerializerProvider.class)); + } } diff --git a/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/parser/TestCaseParserTests.java b/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/parser/TestCaseParserTests.java index 5c3e06875c..8ec0dce1d6 100644 --- a/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/parser/TestCaseParserTests.java +++ b/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/parser/TestCaseParserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,7 +52,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.vividus.zephyr.configuration.ZephyrExporterProperties; import org.vividus.zephyr.databind.TestCaseDeserializer; -import org.vividus.zephyr.model.TestCase; +import org.vividus.zephyr.model.TestCaseExecution; import org.vividus.zephyr.model.TestCaseStatus; import uk.org.lidalia.slf4jext.Level; @@ -118,8 +118,8 @@ void testCreateTestCases() throws URISyntaxException, IOException when(zephyrExporterProperties.getSourceDirectory()).thenReturn(sourceDirectory); when(zephyrExporterProperties.getStatusesOfTestCasesToAddToExecution()) .thenReturn(List.of(TestCaseStatus.SKIPPED, TestCaseStatus.PASSED)); - List testCases = testCaseParser.createTestCases(objectMapper); - assertEquals(testCases.size(), 2); + List testCaseExecutions = testCaseParser.createTestCases(objectMapper); + assertEquals(testCaseExecutions.size(), 2); List events = testLogger.getLoggingEvents(); assertThat(events.get(0).getMessage(), is(JSON_FILES_STRING)); assertThat(events.get(0).getLevel(), is(Level.INFO)); @@ -138,14 +138,14 @@ void testCreateTestCasesWithStatusFilter() throws URISyntaxException, IOExceptio when(zephyrExporterProperties.getSourceDirectory()).thenReturn(sourceDirectory); when(zephyrExporterProperties.getStatusesOfTestCasesToAddToExecution()) .thenReturn(List.of(TestCaseStatus.PASSED)); - List testCases = testCaseParser.createTestCases(objectMapper); - assertEquals(testCases.size(), 1); + List testCaseExecutions = testCaseParser.createTestCases(objectMapper); + assertEquals(testCaseExecutions.size(), 1); List events = testLogger.getLoggingEvents(); assertThat(events.get(0).getMessage(), is(JSON_FILES_STRING)); assertThat(events.get(0).getLevel(), is(Level.INFO)); assertThat(events.get(1).getMessage(), is(TEST_CASES_STRING)); assertThat(events.get(1).getLevel(), is(Level.INFO)); - assertThat(events.get(2), is(info(FOR_EXPORTING_STRING, testCases))); + assertThat(events.get(2), is(info(FOR_EXPORTING_STRING, testCaseExecutions))); assertThat(events.size(), equalTo(3)); } @@ -155,6 +155,7 @@ private ObjectMapper configureObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) .build() - .registerModule(new SimpleModule().addDeserializer(TestCase.class, new TestCaseDeserializer())); + .registerModule(new SimpleModule() + .addDeserializer(TestCaseExecution.class, new TestCaseDeserializer())); } } diff --git a/vividus-to-zephyr-exporter/src/test/resources/org/vividus/zephyr/databind/report.json b/vividus-to-zephyr-exporter/src/test/resources/org/vividus/zephyr/databind/report.json new file mode 100644 index 0000000000..0fb356e32b --- /dev/null +++ b/vividus-to-zephyr-exporter/src/test/resources/org/vividus/zephyr/databind/report.json @@ -0,0 +1,26 @@ +{ + "fields": { + "project": { + "key": "project key" + }, + "issuetype": { + "name": "Test" + }, + "summary": "summary", + "labels": [ + "label 1", + "label 2" + ], + "components": [ + { + "name": "component 1" + }, + { + "name": "component 2" + } + ], + "story-type": { + "value": "Task" + } + } +} diff --git a/vividus-to-zephyr-exporter/src/test/resources/org/vividus/zephyr/exporter/createreports/test-report.json b/vividus-to-zephyr-exporter/src/test/resources/org/vividus/zephyr/exporter/createreports/test-report.json new file mode 100644 index 0000000000..ff48446167 --- /dev/null +++ b/vividus-to-zephyr-exporter/src/test/resources/org/vividus/zephyr/exporter/createreports/test-report.json @@ -0,0 +1,36 @@ +{ + "path": "storyPath", + "title": "", + "lifecycle": { + "keyword": "Lifecycle:" + }, + "beforeStorySteps": [], + "scenarios": [ + { + "keyword": "Scenario:", + "title": "scenarioTitle", + "meta": [ + { + "keyword": "@", + "name": "requirementId", + "value": "STUB-REQ-0" + } + ], + "steps": [ + { + "outcome": "comment", + "value": "!-- Step: Step" + }, + { + "outcome": "comment", + "value": "!-- Data: Data" + }, + { + "outcome": "comment", + "value": "!-- Result: Result" + } + ] + } + ], + "afterStorySteps": [] +} diff --git a/vividus-to-zephyr-exporter/src/test/resources/org/vividus/zephyr/exporter/updatereports/test-report-with-testcaseid.json b/vividus-to-zephyr-exporter/src/test/resources/org/vividus/zephyr/exporter/updatereports/test-report-with-testcaseid.json new file mode 100644 index 0000000000..495ca4b790 --- /dev/null +++ b/vividus-to-zephyr-exporter/src/test/resources/org/vividus/zephyr/exporter/updatereports/test-report-with-testcaseid.json @@ -0,0 +1,74 @@ +{ + "path": "storyPath", + "title": "", + "lifecycle": { + "keyword": "Lifecycle:" + }, + "meta": [ + { + "keyword": "@", + "name": "testCaseId", + "value": "STUB-1" + } + ], + "beforeStorySteps": [], + "scenarios": [ + { + "keyword": "Scenario:", + "title": "scenarioTitle", + "meta": [ + { + "keyword": "@", + "name": "testCaseId", + "value": "STUB-0" + }, + { + "keyword": "@", + "name": "requirementId", + "value": "" + }, + { + "keyword": "@", + "name": "zephyr.components", + "value": "dummy-component-1; dummy-component-2" + }, + { + "keyword": "@", + "name": "zephyr.labels", + "value": "dummy-label-1; dummy-label-2" + } + ], + "examples": { + "keyword": "Examples:", + "steps": [ + "!-- Comment" + ], + "parameters": { + "names": [], + "values": [] + }, + "examples": [ + { + "keyword": "Example:", + "value": "{zephyr.labels=dummy-label-1; dummy-label-2, zephyr.components=dummy-label-1; dummy-label-2, key=stub, testCaseId=STUB-1}", + "steps": [ + { + "outcome": "comment", + "value": "!-- Step: Step" + }, + { + "outcome": "comment", + "value": "!-- Data: Data" + }, + { + "outcome": "comment", + "value": "!-- Result: Result" + } + ] + } + ] + } + } + ], + "afterStorySteps": [] +}