From 1a62f99f766c795a597770e8aabb75bf92dca525 Mon Sep 17 00:00:00 2001 From: Tatiana Tochko <70381117+TatianaTochko@users.noreply.github.com> Date: Thu, 27 Jan 2022 14:49:04 +0300 Subject: [PATCH] [vividus-to-zephyr-exporter] Add ability import automated tests to zephyr --- .../integrations/assets/images/zephyr.png | Bin 0 -> 13497 bytes .../integrations/pages/zephyr-exporter.adoc | 40 +++- .../model/jbehave/IContainingMeta.java | 100 +++++++++ .../org/vividus/model/jbehave/Scenario.java | 81 +------- .../java/org/vividus/model/jbehave/Story.java | 16 +- .../org/vividus/model/jbehave/StoryTests.java | 29 ++- vividus-exporter-commons/build.gradle | 4 + vividus-exporter-commons/dependencies.gradle | 11 - vividus-exporter-commons/logging.gradle | 13 ++ .../converter/CucumberExamplesConverter.java | 52 +++++ .../databind/SerializeJsonHelper.java | 60 ++++++ .../exporter/facade/ExporterFacade.java | 48 +++++ ...vidusExporterCommonConfigurationTests.java | 3 +- .../CucumberExamplesConverterTests.java | 70 +++++++ .../databind/SerializeJsonHelperTests.java | 78 +++++++ .../config/facade/ExporterFacadeTests.java | 67 ++++++ .../java/org/vividus/jira/JiraFacade.java | 25 ++- .../org/vividus/jira/JiraFacadeTests.java | 38 +++- vividus-to-azure-devops-exporter/build.gradle | 1 + vividus-to-xray-exporter/build.gradle | 1 + .../converter/CucumberScenarioConverter.java | 24 +-- .../databind/AbstractTestCaseSerializer.java | 34 +--- .../vividus/xray/exporter/XrayExporter.java | 30 +-- .../org/vividus/xray/facade/XrayFacade.java | 19 +- .../xray/exporter/XrayExporterTests.java | 11 +- .../vividus/xray/facade/XrayFacadeTests.java | 16 +- ...VividusToXrayExporterIntegrationTests.java | 10 +- vividus-to-zephyr-exporter/build.gradle | 2 + .../ZephyrExporterProperties.java | 51 ++++- .../zephyr/databind/TestCaseDeserializer.java | 12 +- .../zephyr/databind/TestCaseSerializer.java | 81 ++++++++ .../zephyr/exporter/ZephyrExporter.java | 182 +++++++++++++++-- .../vividus/zephyr/facade/IZephyrFacade.java | 13 +- .../zephyr/facade/TestCaseParameters.java | 56 ++++++ .../vividus/zephyr/facade/ZephyrFacade.java | 119 +++++++++-- .../zephyr/model/CucumberTestStep.java | 37 ++++ .../{TestCase.java => TestCaseExecution.java} | 8 +- .../vividus/zephyr/model/TestCaseLevel.java | 35 ++++ .../org/vividus/zephyr/model/TestStep.java | 39 ++++ .../vividus/zephyr/model/ZephyrTestCase.java | 79 ++++++++ .../vividus/zephyr/parser/TestCaseParser.java | 43 ++-- .../databind/TestCaseDeserializerTests.java | 22 +- .../databind/TestCaseSerializerTests.java | 108 ++++++++++ .../zephyr/exporter/ZephyrExporterTests.java | 170 +++++++++++++++- .../zephyr/facade/ZephyrFacadeTests.java | 190 +++++++++++++++++- .../zephyr/parser/TestCaseParserTests.java | 17 +- .../org.mockito.plugins.MockMaker | 1 + .../org/vividus/zephyr/databind/report.json | 26 +++ .../exporter/createreports/test-report.json | 36 ++++ .../test-report-with-testcaseid.json | 74 +++++++ 50 files changed, 1980 insertions(+), 302 deletions(-) create mode 100644 docs/modules/integrations/assets/images/zephyr.png create mode 100644 vividus-engine/src/main/java/org/vividus/model/jbehave/IContainingMeta.java create mode 100644 vividus-exporter-commons/logging.gradle create mode 100644 vividus-exporter-commons/src/main/java/org/vividus/exporter/converter/CucumberExamplesConverter.java create mode 100644 vividus-exporter-commons/src/main/java/org/vividus/exporter/databind/SerializeJsonHelper.java create mode 100644 vividus-exporter-commons/src/main/java/org/vividus/exporter/facade/ExporterFacade.java rename vividus-exporter-commons/src/test/java/org/vividus/exporter/config/{ => congig}/VividusExporterCommonConfigurationTests.java (94%) create mode 100644 vividus-exporter-commons/src/test/java/org/vividus/exporter/config/converter/CucumberExamplesConverterTests.java create mode 100644 vividus-exporter-commons/src/test/java/org/vividus/exporter/config/databind/SerializeJsonHelperTests.java create mode 100644 vividus-exporter-commons/src/test/java/org/vividus/exporter/config/facade/ExporterFacadeTests.java create mode 100644 vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/TestCaseSerializer.java create mode 100644 vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/TestCaseParameters.java create mode 100644 vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/CucumberTestStep.java rename vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/{TestCase.java => TestCaseExecution.java} (85%) create mode 100644 vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestCaseLevel.java create mode 100644 vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestStep.java create mode 100644 vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/ZephyrTestCase.java create mode 100644 vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/databind/TestCaseSerializerTests.java create mode 100644 vividus-to-zephyr-exporter/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker create mode 100644 vividus-to-zephyr-exporter/src/test/resources/org/vividus/zephyr/databind/report.json create mode 100644 vividus-to-zephyr-exporter/src/test/resources/org/vividus/zephyr/exporter/createreports/test-report.json create mode 100644 vividus-to-zephyr-exporter/src/test/resources/org/vividus/zephyr/exporter/updatereports/test-report-with-testcaseid.json diff --git a/docs/modules/integrations/assets/images/zephyr.png b/docs/modules/integrations/assets/images/zephyr.png new file mode 100644 index 0000000000000000000000000000000000000000..0e284648e434bf9a7167af4e92f60b606b83129a GIT binary patch literal 13497 zcmeHuXH-*L*KVvF8|bm1(vA&Ox+p01C<=OzVu64_A|g#nL}~&dDgr793QC8FD2S8* zAs{3n0@4f+kP?a^QbK?LfrJD|y_@&@?ikI^~`6^ zys)#e+_nAib`S`(>)O@JH$k9H3=n9;!#_3xS8lws-VA(g2)b!`2~^glI0Kw)_PS_& z5d^A8+97h=0-SGqc-1Kg1o|_0{n#+-sS*wXowd1k`Qq(x$UIGmg@BQ2-jaCNEg~KB zpD9H*ZkU6;8~%Q;cdfkFdb4tzv&Q?qyBl8KGeVmGy?5L96C2}w<8@9P*U~lbH9T^$ zW!v?mZgKH{Y~TH+?T_R$ReBHh4DU*R_N?WjGnP8cqD7PvS0VyT(~w>8+&c`!ivPU$ z%dEJJg6}eHP5QbO1iIcI4v{vPn?)|-=|T)WYLOJ>+ryuiVfD_Y^m>3m-!c!kvqB^b zOwOmwHdzpe<#rqsm%+Pu6iVn%`xw6g^cbg?OihDq)$^lA>9`VR>ZQCS)Hn-8C)7ti zSSd5<4=L0-mjwcq_a`uTkONSNmAsrw9TI9EH&t!IcQWJa)eu+twxS_=N$a1PuzE4G z$u)PmTG*6IkOh4^P;(g~{UyIp>RewKAf3U~2d)&>+er=R8CuF$;o?9FjklK~H-C+S zxW9eDKs+9&5`HoV_nBmfnj9EW4x13B?!im>$7*QHZLOor<8P&=p_MUgIH{n0k@sd{^D4c$u3JI!enQisyIJMGTmDR=cIIp56V) zqoWdiK@s72WiFMv0W>&%fXqtMLd_RZ&cC+g>z7dHLtt=dgeUE1^QI<(GLjiB%yng> zCP`m?XWp?nh9Hnp|4)|Na9h-PqR~jXghQ=YH!BU88GmvCE*{41V6W0=#drv>p-Oei zXobM-zLopX{rp-W1u5ELn~mTj4~z2*UXJ>r9F~xh>Z?z(wp15b06tQq?^mxlAkWPq|wBAQ~h#A9MpXbyRFV0l* z>vi>tQmqRt(#uU6RP1UX!Wa-{uhi5pT+t3+%4eC|hWDx%FL%ezW>=={6skt;ulA(~-t0EaC?JV}lU3;?Xn;*^70c*K@6^WMx z!#IqxAfpcL#U>f~b#$^iS?CLay2?ecKpuva0@(=qc3V>&LoH35BL$DJB76t9saneE zSyWQQRK^huUBEAj;sCn#F6=>WNPYk`*a^EzRs$N01q3rckS;1IhSaI+m@b2w8P7&gXY; z^0JAXEAC{eVrr}?>B`#>l89k8lS)9X?Kg8m4~V{~10z$f0*1lUS*=T- z?jWz#h5yCi@ujd#$sZHBmSz(M?3tfy;3Q)cEpuBGkKrM0O`>;q#v4=^Nqo|nv_*cB zi+CQrYXfo3N?Ti~W+wTCT3*%4r*yJHez3*YGEjm#zcZ z9vWvUS+!0=@mWbZ;ud=0gdSZgfYGYW#GGk&G|$~EY~ovPCUYdrQ275)wJn#~O+7prg+_4;$5MW$=eFJ=NyZ?wphHv@A( zuX%=;R#OI~V(oqta4}v|S;s2wr-6&ZB$B<-c?GDnYvtqmh%~{E8s3NbYCqn{by#>| z-2@Je`tFM8$C?)wLLcwN{@mX$_)CHWt-d&zh_^~IFs&HO!q`IZQ3DZ=`Js7 zhGZ+r70K1lO;OCor!cOBP=DG}zp!{00*#4|3LCz@Ao9D-X)Pb+*H*)@YfRVY z7z4j3@%U17t1Rt;bYxq{9*zJ{iS^+bmQde_IcB#}0`cr#a50@&bLXq0IF15lOM`i- z^rEPF>wasJgSb`O0mUcIMxznqWcp7EE5oXW?Q6EFcrW7X!dvv7#?(-0j+(5ZamB+E z{s7>IjCucHjdk~6S6Zo7+f#0-MLc;jk4f{b3ABQJWp<36P3|H$=#ovRh;jqkRM_|h zzUWgLQqy1mQe!Ty4ma!NcpS4LaN*V*Ix3U|@piy-isIp7xu(PE+E8Fo&3SPvV75p+ zo0{LhOS?3ezoS0|eZz!*s$VMUdqB~)uR)iYa_48sQajoa%qFWles`0pPB*ptx`v(2 zM6ZGyJBYATlv+laOy(x=0>ZbRc(kDeny%Yy{#EdSyssp zfUR9JOnoLRWcFz)Y5oeEtm7B@Af0E~v==XhX4r1eqG==0f)Wqn}rJo8()$%z_KEy*S9>X03 zQz4}48eH5s!?ApQ@pD+k`GUqY%&9r~&hC-BrDOdu`AvF5oJ$ZCh1Rbo30igiiCRH{ zDS~&iEolzgs|NiTLbU)lVt(Chv2GR&0V|2^cth;lY0`)`7Tfs>#H^;qxd)>7QjH?p ze~3tutyg#amU_mkZyMumn{V%8+)FYAF6d102VlN=jCKhQ{=Ek-2W4pl~bErKqFmkYiOdcBu*k&f8hzpV8-H8QCacKx}L zjJZHKk~|wT>_n~%Ar5_K`6&~f79KImY6?QM{V&_-jRT9~Ji^q;Nn-GN$~^TJ-1AT` z4%lk-ZSq(vJbZ7wLp3j=9XHr9fTLDc(XuweHU_5oGP{E0mkf-r&3z32IJY5lC(Xn; z@-d&Yy3+u4O1kidR)d;PhUrE8UJbo@654L`ilkQV!*W9~H0X2v4e!xYYYL^~tj`99utT_4(a z*1@;8ymPIe$R#5OZlb~UH!MWmEgV5On3ge9e97?0d{WEaNW;N)d5oIJBUg?3IGsBs zDKm|}fuC*03{Z?2gPNepu_|K=ugcXd>tZjW2PMVYl`?6okRQaK#5o}r;&{VH;l#FI zua>-=XLDLUS!UHlnZ*C*rSvl@)6cNAH~VR;IMh8Vd+N_@uTg?`M3~+XccH%#67z6B5p-USK%a5U4^R5;{8YG;jr+gOEN_N1UwBByl29J(lT z7OK2sgBT2f66ciqI`NQg7{jzvgo%)4-=Ng2Ku|(rS@0f^Ao?d9* zSVxc@vF#Z0(V1oP&I&qihU+q_J`j|jaVLJ;%f|cZuG`dABQfjLE(6r|0_lq5;m*jl zd%m}aO}!e3S@%@hSre{|`1=+%-U9eRxXF%u;yz~<|NBY0=1hR|k8b~tQt0;%LozNe z?w&(^Hy_%vqenqM-_jHMNcqXoC@Lk}%qbvFky21VSK5?*h-0 zy7c9#O_3i2V&Rt+TRqhv@zx9>{V5W@oq0q>cH4Jj{()L zSTWIued$MQ9(S&)&-ssE*v&3m1^1U)eYIXNm*qUXDoQj{BJ#hXp(kE^#MPMNqZ3H)JSB>h>%s6ZXj zHa?z>8AYw^g|1Ztz!|_J|2gh8byei6!!h);q=i?7Lz48SIucMd$q%61TWz6wHdiNIbIgWZlvzcMHS^C1 zQL`?q(yUx*=&Fzf6WN9M<=t8J)kZfY1|OQPgf zJ@^!MC!x)wEkL;v?@lPExoBVtsl*Nwz?*6ojz&z^UR@+d6n2{NRGGY}14)@LUV8FB z_=tCS;VO+!V_>#NFz5s+sy9L!5i%pwkB-C$#_9H`rRZ4-$)3o}osET-K~^hg7mH>? z!fr=MLsm`tj7*(#&I`YTiO4N*qC?p|7&byQlITy?eu3q<752$G-@5SjnZr(t!0X$D zSoDH^VgDv;f_^+z_kG*|WjU{(7CJ*R2@3su1OvNBw8Ou0Ml{vV$#m5obd1*+on{Q{ zTNsoYn@!CaWBJi+#U|^CSVu8uZ?Xbu%2p%3CPA;Y|cKt_A^1oydU)3b2 z$d~Bb*nwf`wvK`+o`kY@FEF%KqGW0p2HwiE9EFP&Y?eo01fjs7Uacx$4I_P(LM0W! z#S1JbeC`S+iW`9v<`?Y%Ir^f-DON;pyr#2QzgI!|xs}BgJ}+6y4FK{@%{c|vVl_;P za3VgfD{88-bz!ZeRXQ!=jHqcxE@{_G!0RK(grpLIENmoohx@F7JqdTE(NMt$O9hfd z5cl7bBwDLtpy!dle^=Bgk-Gh$uICTy%PE>fps<@j)#dT6a>_=*%>Jq^3R9dS8lFFA zl2Sd;lF`&(hP2cw)EpSuI>l4>lItYXkCdPtzb04TbTotO`uo2Q?_I*kIVIfT?!_zSZ7aG9q-IwX;%AV6c1iH862QtO@x> zRNQ_qfH~B%S0}8`?~ZF7nMPmnt4gQJv1w3Ce$x&gs5wN&0d_vFQVV;!y{VmbJ(bX9 zG5DEanzc!}^x`dG$FsV3DJCc2FIlOByV>4BNG73vx4Mnf0Y=DOp2g2KL&3U;tO;G59!iRBpDV+8wOm_}bR91PXW;hkfNBW+Cj0`2vmYsTotl8v zsvTJAyXFvTzP}88+T=dhrZvDoQGp{%HZP*gJ$vIaT0C!_)yMvgM}_vT|Ppe!ttG z2A-s^{R#(eF;sG)#(zh7^AA++tvhn}4ZZJPNNNhTmv#QxIrHoKuVZvvVJ%}CtkXud zb7q?vRV2kPH|2^Ln_RM}txtuG5|hA(3FF&};zGS{%D&+UT87gJ%3oSavOE8|=~=YK ztE~U7_)FRUw0kwYZW~UV>D;owRg2%4Gei9@8oH;5c0H(4`I%s88%!}~hA#PxIJd66 zv*!+?BJ5A&=9~6~*>X^W7ny2Vt%+j4G(zs|4e;e31m$8!Fg3GI&&aSCiJd)=#J34B zFc<$hVaXFfZHo*)9hWU5zCp^Ub(Cq|;xtOevo-QtjZ#k;n|7=o2M@r79Sm=0 z**94BPGFuxuL;Vht-vG!ucF8szBbL^oe>VyafR*mz3QRS!v^3r9j6zHztkg+0VH-W z1@F2vN=Bug#8AbimZN?{=HVQX$p1K*Mf~z6EqAf5SoKv&RkO|3ErAXRFSo_sg>2he zo#dF()N%E}m2H|EgS!umEq*xoF4^NY&nZBQ8&)nND0gXKG`Id(EtuMwSwe(IUm1i> z@{CV}Z9wdf@m=frthBi){@&+*8h1N^t6f*;_ZXQVYHr$jGQ36=!xY-Na&?$>{Vtoei`wc zD*JM^y1Q6==)1WC^peM+fyA<8S+dK3Kg3ZX`o|VOBe-?IKi=RF%S&+QXHD^A)7b1U zrs_RnMEz5Osl-3e$nBy3lD?BKxs%1%o#J{fvnwKxkQ4DhwOZ)jTQ0o=i_}mqK>pTx zo|KR`pW!a{>UHyb%%iY$BjXl_eVxuZoyD~NIe^!%vO@^uJ!7}1M0U z8E-~YcP6QB9QjAPt;Nu6#E$g#d? zL{LiTBExbqZH9Obb z{B)0(YGC9ni*=XkXBvBs_ys^<`JsXNOS)CM10+Iy#efk;e#^;QmnqF$!MzyI$jl=L z50qU0D$EX7I!7CI3N@8NtU?BrI_cy=;SXtTEOJWfZAFY>ygKXLd=+A=w_6w*7IQ`& zRa){MNPU_6T<$t642*p(Je3P?cfGFAn|7<;OG$QX>-j}o#3eA7Bbbi%IM(-jxJSml zGa=EA@f=%8sGmFeE7VB;8RHN!jXJT0hndMsM`SbI-M!EJMColNvlKjYxQNexO;ntr ztaIA}{rzgeqy!aA=TjfyiW_37ZUpMA0XG;|!u~we%*jk6sCy+*u2HxF-~*jeaZALi zEU{_)VI{$wJYAGDdf(tB`&0NaXNvbZzn504Ph6cZkAI8+2i%K!U?AVgiuN_vJv_Q+ z(4pn2A6RdqhagLlEI5EgzD^zx3wu~l=ny=}&7oL0aXZl8IMcu7B6b=c07)`9ox~|` zZaAp|9eT(#9d3__CgC;Ym!3C+9SN8yG@K|4-gR{KD5ct(D<~PCgbK1U&-scuRaYUHchspN zrNPwLbF~Be7H>xZ8bx&#He&}Gr)6Ln59Wy`=QQ z;mDk`+wlIx4Zfe?CMUrag|fY$)9$dpr1yB09&FqSH+dDtcng31H2gvHiM2dIqiAF! z3noU<-=35IvHTNy0R$QZ@TFAkdV9JOP{ukm83F>OYr&NOUEB&H1A07@3{+Ad{sUawlUMJ4ol}}PJ4xR2Q_KClsMXSdQ;|hm?N?X{1(IeV))ZTwG?ctv(eI$4ZvbbE-A)_&JRVX`iE(uhfV5K z90J@qh^wE7FYTz$bCRj%H1w?!^B3TgX{j8+)v*-nlm4tM2XH1e%WYw@xvIam?qP7} zCBES#Z!vr-K{@v=wb(UI{%7tXcn%J`0dC?@{N{AEdk2M;x4JNi%J2Ym)5TG7)6}Eyg1rArQ`HbL0Lcx!BwBnV?3QnS8Cd!SU9bNp>*~*zH zpK^XhZIgB$ja_@ zbc2FCrP%R!ogW=Lxq^%)?2&?iD_^05Dn>8T&Z@mlxm8pX>>ni`8XMBZa&6Dj9;nx9 z(=Q5h2)o(&qqx6eZ(L$EGWCVN!{<5gIs(m43uZ+b>?t{%dgX$0PCtJ2^}L>Tl$*1G zl}Fyrv|who*(r?tFv0ZXb3kbPR9+lt9g?N=DyQ#Vz=7`*J)`aOo+opT8u^6BHl#PU6FaH=_D@S4RarEHa69!h`lv8=w?Up^ z6LNM=>3N>eMk&SA@}pqi_;2>AqHdY3)tQtK;Q2N*n+G9{ zCQ*@MnCMkVJL^hpfTnh6oIFGEx}O#FU;?$X0iW1>7TABn=lqC--2LB6>^x(%u>^o$ zK5)^zRb7eLIMPwcMGRvHA07x%nlcGy5dFB7A*C7Li=6fSLXMFam!1OB{aCrZuVvi{ zx@tzlqzyjw1evvV8{w4!HwiuO#}On}j>3u8%gpmb9&~aMb8g-CRRn@*)O^QL`K{4~ zcd86h6pw9%6E&Z+3`nwmsOCSC)qDMKWymR&+CsjZr)X(J$R4Bq1)(G)MZbFJHe#z^ znGpI_M5v$UuENQ)Lhkk@?MkgPU${utb3|92!Ff9V3B+fZ^?E0b!uU8{UvQx8*9 zxF3gf(VNbm2Lx_~7qJ5O%cqPoX~>c$QAQln(^6)WFFjf9^&utGak(d$5o+lf%GP{` zv^IGPD?eYss6)Wy4G*UA>J~m${+Sed#Ax7cXpr5x)GijhT(;j|;z_7BHZ%xr9Zyf= z4PhwM1H^I%)1YvYcx+5!WE&QP69(+o%61+POYR}$ZofwxJ}j?B!+$uy)F1A=!WH}; zwo{J_+g$zb-sW~57M4X5x_8^nVGsH~9la_a`yz7kw9SI-J6p#Djm~iDuHB4q-D$lD zdAIWlgcv{sDy!4pM7)u#8&nW6p8h9FHaqle&~^X&Nt^6yYJoC^O&98qNctOb^TU?v znj>%%Z%)8y<)1@+IZj#cn*Z7t|Bk_{tNdR1?oMc$x5G8dM6Lb!iK#)H6=ZO&@6V87}@YWG-V1?4Qo+flI3m?r{r*%xVzmZoIRU5H+^^nF!TFQKn zJ5Y>^ekB^#c)!9y4EovnBA&~aEs#N3stn%pJ^CB6kJJ&UdKr{u``l4pXu?r*>fWNe z4< zWpQ7(`CPzTxVU5NXnyP3D&KqV_>OVnkl;~}+x_`npl_<_0L_Zrb$w4m%Ilu2NR_0i z-g(`p21_R7X?UDppc90>g=LJ1qpm$e?EW({GfAbRv(?TtUb4nI*@0V$*aRAUn3Y3q z>X9VB>-kKJ zWk7?+vjBp977lYFKjI+jKb!bA5g_=whi7wMQ(Cc%We(zww{l}}>KAO$atDWlz&`RE z1bpEx*53h=bWH%z7<*?MNK)_YF)k|y0f z5ZVwxH_s}UYXJ(N9Y2^*xxt$jzytPUVPQ27KE+Svj0$+QaN4Xq=J zD0|~tnx8}M{T;oSY?ItGh*T;*2N!@6-P6%t(7TZjHz|GD4lYd)Bhy2JdI+Xp^&?dd zj_v{7&G)|!k#jPJ>86FnlbZyoZ+JRK?o+g8q{H5|FNr31CV5jvfAV&c;)4FcFZrF-2l>W5-_A#q9l_Q zn`A6UX1KgDKwMKY4i#MrS}?2)7yYg<4}(}ctM^4n>{ptaLZ%+#d~HYgx!3T{JA@v1K54!9Qm+dP*;@KOmXf8uL!a zk}`^}MojXo52*HyTvkr~v2X8e^MBrv*@Z}d>x(}zVeL}?u+G(V)QT9m-B>A8YW@Bz zY+5wgJXLSya*%D*F(lr<=F`vtRIG=(oVrzv;H&K2nE3;v&-M;mWy(BfFMn@*7oL4S zdi2vs#Tu`6sK!wST^gK}^HIDWqI~`FXUK>up{foH)$6>vy`t@lV9rjw=m9~myas(s zJX=Z_RqdPV&dXODo*N;T=x(PB6rWW~Qe$6)VAwxxvMJjbuO#g`-ClwAHC4m|^s5;p zY)U-y1&07pCO)jIwS!P%J~~^>rX#sLR?wM%3w!CMFOt^6Q&-)$vjuY3>IVHD!Bg1y z1W2yFl{848h`*9WWttoZmEW?~z=Y}6ydcA`b|Ow{?VQp!&|Yx@>-{8J@l%8f|apULMkWwsImZX;u|$$?THPXb*@vN&htIFjs44l>33dh`)Gjj-p-U z0X~O`XLj5Q4|g@aA1cX?+YAaVYcGxS*>yCWYJTW>{_YaXlz>3`YbrTdnV-<{J`E^R zsuV~+zdEae9ww|$O~7RZ)rY@6i&9awYGT|=c5?`5hA&rSyK0~)h8htI&%{I9vn^7E zambd!+4@b(xkpGn)8-pNMuVvxERQn*BXtbkU#c@2_HxQNR&6BbUvE#mAo+TTej{qf zqqPrdWBXR`S>3xApj9xI6>02!#Kh|eUZ)#A^_qB2h} z2z59Ji7_KztKxj&O8q58=vjm;(vsQy%5r_<_imLUe;xe+;FOB-$-odK|Kr_YE^V?oE{BU zV&9otn?dFK;I6&;d7o_efWF$EVpfE<IH$&k8T~lq)w^_gI5Yjr# zZE`gNI+2tH|B$!ws?i!=+A*_Fb*}yxb-pO{6^>=&`VUhh3FwRSyU0RQ-Cep;mVgYM z5XG3wd)2wXv{g2GxI8EJi7!>%ykHVR`B=I?1%Nh9Zz5L!piHZ5HX$8Atq_NGs0_^o zvR}6o9eGBWg7sdV{cnIWNnIM2Jw-AN z9#>*3#o;M?E##QgiF+#x9jcj>)ZXtqk>hp>Uz^&S8UDk$ zh1pP*vi_Ozs5&s!nY$(bmqzaKE1__t5|0Cb7N1mrCzYQ8?EW<0jM4ZOI?7j97 zF5Ky0tZ>3skbjwBbtCl$MuF=6AiXrAC{zjZhcL5Qr;~L(g-Us5qkq1H>aYY%{IkC| zM6UAFG4V5#^r}RJ@9iHul@M(QK4_e4GRfXT;XhN@Pwoc7W%a1!>w6$=GF0ax((Tv- z-_B@8%>d6ytQmuwKyj5X*MMGev~@^az(@uM9t$VpeE?~C z%K{uI@H9V+%6avg$!lzWQ|XTHKXnae zz4jNTFx$0Y9uDvoL~cCstI9&Zkkg$-Hpr@90@|=Up9${nx{keDkP&SQCr076y+PvE zUAxekO@LS~9;L>9|22PT^YU&y)X^RZ+GFQhN)7VJ!zU&vARK+}C; zmU$;oCc=E`tu$fzz=79&T>lCxhviQ(Ry}sWiSjEdp=i7GF2L%8cd9Q!nZL=ke{xzDJH34{@uYOA;-|y_vrep$wQ5B6IGtO+_92E94|kJyZ%e> zx^WKRlm;+O%WDJ>sQ4JB$zl<300&D*+^gTfSS5)BS%rrGEk+Ff@h@O#W0miTgn%CR zzoEJvTmA13G@ya;&8!yvpWy0$gKbVo03|>JS^v&9-2ay?zW;yh|3|&k|9@fE7uKFy cCYF|b-*P=b$s72a5eRhcip}M+OLw3C4_O&aq5uE@ literal 0 HcmV?d00001 diff --git a/docs/modules/integrations/pages/zephyr-exporter.adoc b/docs/modules/integrations/pages/zephyr-exporter.adoc index 549c08d9e4..99a1f1a595 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,18 +20,34 @@ include::partial$jira-configuration.adoc[] |Required |Description +|`zephyr.exporter.export-results` +|true +|Property to export tests + +|`zephyr.exporter.level` +|false +|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 |`zephyr.exporter.source-directory` |true -|Path to directory with test execution JSON results. +|Path to directory with test JSON results. |`zephyr.exporter.update-execution-statuses-only` |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 +70,26 @@ 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[] +[cols="1,2", options="header"] +|=== + +|Property +|Description + +|`jira..fields-mapping.story-type` +|Key of a field containing story type + +|=== + +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/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 + *

    + *
  • 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 + */ + 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 504d72b525..68b9df51a5 100644 --- a/vividus-exporter-commons/build.gradle +++ b/vividus-exporter-commons/build.gradle @@ -1,11 +1,15 @@ project.description = 'Common module for all VIVIDUS exporters' +apply from: "logging.gradle" + ext { springBootVersion = '2.6.3' } dependencies { + implementation project(':vividus-facade-jira') implementation project(':vividus-util') + implementation project(':vividus-engine') implementation(group: 'org.springframework.boot', name: 'spring-boot-starter', version: "${springBootVersion}") testImplementation (group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: "${springBootVersion}") diff --git a/vividus-exporter-commons/dependencies.gradle b/vividus-exporter-commons/dependencies.gradle index d0f8571d3c..23d374dd97 100644 --- a/vividus-exporter-commons/dependencies.gradle +++ b/vividus-exporter-commons/dependencies.gradle @@ -19,21 +19,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/converter/CucumberExamplesConverter.java b/vividus-exporter-commons/src/main/java/org/vividus/exporter/converter/CucumberExamplesConverter.java new file mode 100644 index 0000000000..39665b5a3d --- /dev/null +++ b/vividus-exporter-commons/src/main/java/org/vividus/exporter/converter/CucumberExamplesConverter.java @@ -0,0 +1,52 @@ +/* + * 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.converter; + +import static java.lang.System.lineSeparator; + +import java.util.List; +import java.util.stream.Collectors; + +import org.vividus.model.jbehave.Parameters; + +public final class CucumberExamplesConverter +{ + private static final String CUCUMBER_SEPARATOR = "|"; + + private CucumberExamplesConverter() + { + } + + public static String buildScenarioExamplesTable(Parameters parameters) + { + return "Examples:" + lineSeparator() + buildScenarioExamplesTableWithoutName(parameters); + } + + public static String buildScenarioExamplesTableWithoutName(Parameters parameters) + { + return joinTableRow(parameters.getNames()) + + parameters.getValues().stream() + .map(CucumberExamplesConverter::joinTableRow) + .collect(Collectors.joining()); + } + + private static String joinTableRow(List values) + { + return values.stream().collect( + Collectors.joining(CUCUMBER_SEPARATOR, CUCUMBER_SEPARATOR, CUCUMBER_SEPARATOR + lineSeparator())); + } +} 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..be0baec49f --- /dev/null +++ b/vividus-exporter-commons/src/main/java/org/vividus/exporter/facade/ExporterFacade.java @@ -0,0 +1,48 @@ +/* + * 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 final class ExporterFacade +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ExporterFacade.class); + + private ExporterFacade() + { + } + + public static 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/converter/CucumberExamplesConverterTests.java b/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/converter/CucumberExamplesConverterTests.java new file mode 100644 index 0000000000..4746ec587a --- /dev/null +++ b/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/converter/CucumberExamplesConverterTests.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.exporter.config.converter; + +import static java.lang.System.lineSeparator; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.vividus.exporter.converter.CucumberExamplesConverter; +import org.vividus.model.jbehave.Parameters; + +public class CucumberExamplesConverterTests +{ + private static final String KEY_1_KEY_2 = "|key 1|key 2|"; + private static final String KEY = "key "; + private static final String VALUE = "value "; + private static final String VALUES_FIRST_LINE = "|value 11|value 12|"; + private static final String VALUES_SECOND_LINE = "|value 21|value 22|"; + + @Test + void shouldConvertExamplesWithExampleName() + { + Parameters parameters = new Parameters(); + parameters.setNames(List.of(KEY + 1, KEY + 2)); + parameters.setValues(List.of( + List.of(VALUE + 11, VALUE + 12), + List.of(VALUE + 21, VALUE + 22) + )); + + String examplesAsString = + "Examples:" + lineSeparator() + + KEY_1_KEY_2 + lineSeparator() + + VALUES_FIRST_LINE + lineSeparator() + + VALUES_SECOND_LINE + lineSeparator(); + assertEquals(examplesAsString, CucumberExamplesConverter.buildScenarioExamplesTable(parameters)); + } + + @Test + void shouldConvertExamplesWithoutExampleName() + { + Parameters parameters = new Parameters(); + parameters.setNames(List.of(KEY + 1, KEY + 2)); + parameters.setValues(List.of( + List.of(VALUE + 11, VALUE + 12), + List.of(VALUE + 21, VALUE + 22) + )); + + String examplesAsString = + KEY_1_KEY_2 + lineSeparator() + + VALUES_FIRST_LINE + lineSeparator() + + VALUES_SECOND_LINE + lineSeparator(); + assertEquals(examplesAsString, CucumberExamplesConverter.buildScenarioExamplesTableWithoutName(parameters)); + } +} 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..5da9db97b5 --- /dev/null +++ b/vividus-exporter-commons/src/test/java/org/vividus/exporter/config/facade/ExporterFacadeTests.java @@ -0,0 +1,67 @@ +/* + * 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); + + @Mock private JiraFacade jiraFacade; + + @Test + void createLinkIfRequirementIdPresents() throws IOException, JiraConfigurationException + { + ExporterFacade.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 + { + ExporterFacade.createTestsLink(ISSUE_ID, Optional.empty(), jiraFacade); + verifyNoMoreInteractions(jiraFacade); + } +} 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..07e94e3796 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. @@ -16,7 +16,10 @@ package org.vividus.jira; +import static org.apache.commons.lang3.Validate.notEmpty; + import java.io.IOException; +import java.util.List; import java.util.Optional; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -38,7 +41,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 +78,23 @@ 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 transitionId = getTransitionIdByName(issueKey, status); + return jiraClientProvider.getByIssueKey(issueKey).executePost(String.format(TRANSITIONS_ENDPOINT, issueKey), + String.format(UPDATE_TRANSITION_BODY, transitionId)); + } + + public String getTransitionIdByName(String issueKey, String status) throws JiraConfigurationException, IOException + { + String statuses = jiraClientProvider.getByIssueKey(issueKey) + .executeGet(String.format(TRANSITIONS_ENDPOINT, issueKey)); + List transitionIds = JsonPathUtils.getData(statuses, + String.format("$.transitions[?(@.to.name=='%s')].id", status)); + notEmpty(transitionIds, "Issue status cannot be set to '%s'. No such transition Id's found.", status); + return transitionIds.get(0); + } + public Project getProject(String projectKey) throws IOException, JiraConfigurationException { return getJiraEntity("project/", projectKey, () -> jiraClientProvider.getByProjectKey(projectKey), diff --git a/vividus-facade-jira/src/test/java/org/vividus/jira/JiraFacadeTests.java b/vividus-facade-jira/src/test/java/org/vividus/jira/JiraFacadeTests.java index fa8321c243..eadbde19cb 100644 --- a/vividus-facade-jira/src/test/java/org/vividus/jira/JiraFacadeTests.java +++ b/vividus-facade-jira/src/test/java/org/vividus/jira/JiraFacadeTests.java @@ -18,6 +18,7 @@ import static java.util.Optional.empty; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -39,6 +40,8 @@ class JiraFacadeTests private static final String ISSUE_ID = "issue id"; private static final String ISSUE_BODY = "issue body"; private static final String ISSUE_ENDPOINT = "/rest/api/latest/issue/"; + private static final String TRANSITIONS_ENDPOINT = ISSUE_ENDPOINT + "%s/transitions/"; + private static final String BACKLOG = "Backlog"; @Mock private JiraClient jiraClient; @Mock private JiraClientProvider jiraClientProvider; @@ -80,7 +83,7 @@ void shouldCreateIssueLink() throws IOException, JiraConfigurationException } @Test - void shouldReturnIssueStatue() throws IOException, JiraConfigurationException + void shouldReturnIssueStatus() throws IOException, JiraConfigurationException { when(jiraClientProvider.getByIssueKey(ISSUE_ID)).thenReturn(jiraClient); when(jiraClient.executeGet(ISSUE_ENDPOINT + ISSUE_ID)) @@ -88,6 +91,36 @@ void shouldReturnIssueStatue() throws IOException, JiraConfigurationException assertEquals("Open", jiraFacade.getIssueStatus(ISSUE_ID)); } + @Test + void shouldSetIssueStatus() throws JiraConfigurationException, IOException + { + String transitionsEndpoint = String.format(TRANSITIONS_ENDPOINT, ISSUE_ID); + when(jiraClientProvider.getByIssueKey(ISSUE_ID)).thenReturn(jiraClient); + when(jiraClient.executePost(eq(transitionsEndpoint), eq("{'transition':{'id':'1'}}"))) + .thenReturn(ISSUE_ID); + when(jiraClient.executeGet(eq(transitionsEndpoint))) + .thenReturn("{\"expand\": \"transitions\",\"transitions\": [{\"id\": \"1" + + "\",\"name\": \"Move to Backlog\",\"to\":{\"name\": \"Backlog\",\"id\":\"10000\"}}]}"); + + String issueId = jiraFacade.setIssueStatus(ISSUE_ID, BACKLOG); + assertEquals(ISSUE_ID, issueId); + } + + @Test + void shouldGetTransitionId() throws IOException, JiraConfigurationException + { + String expectedTransitionId = "11"; + when(jiraClientProvider.getByIssueKey(ISSUE_ID)).thenReturn(jiraClient); + when(jiraClient.executeGet(String.format(TRANSITIONS_ENDPOINT, ISSUE_ID))) + .thenReturn("{\"expand\": \"transitions\",\"transitions\": [{\"id\": \"" + expectedTransitionId + + "\",\"name\": \"To Do\",\"to\":{\"self\": \"https://jira.atlassian.net/rest/api/2/status/10000\"," + + "\"name\": \"Backlog\",\"id\":\"10000\"}},{\"id\": \"12\",\"name\": \"To Do\",\"to\": {\"self\": " + + "\"https://jira.atlassian.net/rest/api/2/status/10001\",\"name\": \"Open\",\"id\": \"10001\"}}]}"); + + String transitionId = jiraFacade.getTransitionIdByName(ISSUE_ID, BACKLOG); + assertEquals(expectedTransitionId, transitionId); + } + @Test void shouldGetIssue() throws IOException, JiraConfigurationException { @@ -102,7 +135,8 @@ void shouldGetProject() throws IOException, JiraConfigurationException { String projectKey = "TEST"; when(jiraClientProvider.getByProjectKey(projectKey)).thenReturn(jiraClient); - when(jiraClient.executeGet("/rest/api/latest/project/TEST")).thenReturn("{\"id\": \"002\", \"key\": \"TEST\", " + when(jiraClient.executeGet("/rest/api/latest/project/TEST")) + .thenReturn("{\"id\": \"002\", \"key\": \"TEST\", " + "\"versions\": [{\"id\": \"0021\", \"name\": \"Release 1.0\"}, " + "{\"id\": \"0022\", \"name\": \"Release 2.0\"}]}"); Project project = jiraFacade.getProject(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/converter/CucumberScenarioConverter.java b/vividus-to-xray-exporter/src/main/java/org/vividus/xray/converter/CucumberScenarioConverter.java index 4811ff65d4..a8f08bf4de 100644 --- a/vividus-to-xray-exporter/src/main/java/org/vividus/xray/converter/CucumberScenarioConverter.java +++ b/vividus-to-xray-exporter/src/main/java/org/vividus/xray/converter/CucumberScenarioConverter.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,17 +18,15 @@ import static java.lang.System.lineSeparator; -import java.util.List; import java.util.stream.Collectors; -import org.vividus.model.jbehave.Parameters; +import org.vividus.exporter.converter.CucumberExamplesConverter; import org.vividus.model.jbehave.Scenario; import org.vividus.model.jbehave.Step; public final class CucumberScenarioConverter { private static final String COMMENT_KEYWORD = "!-- "; - private static final String CUCUMBER_SEPARATOR = "|"; private CucumberScenarioConverter() { @@ -42,7 +40,7 @@ public static CucumberScenario convert(Scenario scenario) return new CucumberScenario("Scenario", cucumberScenario); } String cucumberScenarioOutline = cucumberScenario + lineSeparator() - + buildScenarioExamplesTable(scenario.getExamples().getParameters()); + + CucumberExamplesConverter.buildScenarioExamplesTable(scenario.getExamples().getParameters()); return new CucumberScenario("Scenario Outline", cucumberScenarioOutline); } @@ -60,22 +58,6 @@ private static String replaceCommentSign(String step) return step.startsWith(COMMENT_KEYWORD) ? step.replace(COMMENT_KEYWORD, "# ") : step; } - private static String buildScenarioExamplesTable(Parameters parameters) - { - return new StringBuilder("Examples:").append(lineSeparator()) - .append(joinTableRow(parameters.getNames())) - .append(parameters.getValues().stream() - .map(CucumberScenarioConverter::joinTableRow) - .collect(Collectors.joining())) - .toString(); - } - - private static String joinTableRow(List values) - { - return values.stream().collect( - Collectors.joining(CUCUMBER_SEPARATOR, CUCUMBER_SEPARATOR, CUCUMBER_SEPARATOR + lineSeparator())); - } - public static class CucumberScenario { private final String type; 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 94492552c5..fa443f03d7 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 @@ -17,9 +17,10 @@ package org.vividus.xray.databind; import static org.apache.commons.lang3.Validate.isTrue; +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 java.util.Map; import com.fasterxml.jackson.core.JsonGenerator; @@ -88,37 +89,6 @@ protected void writeObjectWithValueField(JsonGenerator generator, String objectK protected abstract void serializeCustomFields(T testCase, Map mapping, 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 String getSafely(String key, Map mapping) { String value = mapping.get(key); 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..a09a247e6e 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; @@ -43,7 +41,6 @@ import org.vividus.output.SyntaxException; import org.vividus.xray.configuration.XrayExporterOptions; import org.vividus.xray.converter.CucumberScenarioConverter; -import org.vividus.xray.converter.CucumberScenarioConverter.CucumberScenario; import org.vividus.xray.facade.AbstractTestCaseParameters; import org.vividus.xray.facade.CucumberTestCaseParameters; import org.vividus.xray.facade.ManualTestCaseParameters; @@ -55,15 +52,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 +73,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<>(); @@ -187,7 +192,7 @@ private CucumberTestCaseParameters createCucumberTestCaseParameters(Scenario sce { CucumberTestCaseParameters parameters = new CucumberTestCaseParameters(); fillTestCaseParameters(parameters, TestCaseType.CUCUMBER, scenario); - CucumberScenario cucumberScenario = CucumberScenarioConverter.convert(scenario); + CucumberScenarioConverter.CucumberScenario cucumberScenario = CucumberScenarioConverter.convert(scenario); parameters.setScenarioType(cucumberScenario.getType()); parameters.setScenario(cucumberScenario.getScenario()); return parameters; @@ -226,10 +231,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..0215971ccb 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; @@ -118,6 +119,12 @@ public void updateTestSet(String testSetKey, List testCaseKeys) requestBody); } + public void createTestsLink(String testCaseId, Optional requirementId) + throws IOException, JiraConfigurationException + { + ExporterFacade.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..b823a44bf7 100644 --- a/vividus-to-zephyr-exporter/build.gradle +++ b/vividus-to-zephyr-exporter/build.gradle @@ -1,9 +1,11 @@ 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-facade-jira') implementation project(':vividus-exporter-commons') 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/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/TestCaseSerializer.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/TestCaseSerializer.java new file mode 100644 index 0000000000..dceccf78fa --- /dev/null +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/databind/TestCaseSerializer.java @@ -0,0 +1,81 @@ +/* + * 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.Map; +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.springframework.stereotype.Component; +import org.vividus.jira.JiraConfigurationException; +import org.vividus.jira.JiraConfigurationProvider; +import org.vividus.zephyr.model.ZephyrTestCase; + +@Component +public class TestCaseSerializer extends JsonSerializer +{ + private static final String NAME = "name"; + private static final String STORY_TYPE_FIELD_KEY = "story-type"; + + @Autowired private JiraConfigurationProvider jiraConfigurationProvider; + + @Override + public void serialize(ZephyrTestCase zephyrTestCase, JsonGenerator generator, SerializerProvider serializers) + throws IOException + { + generator.writeStartObject(); + + generator.writeObjectFieldStart("fields"); + + String projectKey = zephyrTestCase.getProjectKey(); + + 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()); + + try + { + Map mapping = jiraConfigurationProvider.getFieldsMappingByProjectKey(projectKey); + String storyType = mapping.get(STORY_TYPE_FIELD_KEY); + if (Objects.nonNull(storyType)) + { + writeObjectWithField(generator, mapping.get(STORY_TYPE_FIELD_KEY), "value", "Task"); + } + } + catch (JiraConfigurationException e) + { + throw new IllegalStateException(e); + } + + generator.writeEndObject(); + 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..c2c467a872 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,48 @@ 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.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 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 +84,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 +147,112 @@ 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); + fillTestCase(parameters, zephyrTest); + + if (testCaseId != null && zephyrExporterProperties.isUpdateCasesOnExport()) + { + zephyrFacade.updateTestCase(testCaseId, zephyrTest); + } + else + { + testCaseId = zephyrFacade.createTestCase(zephyrTest); + zephyrFacade.createTestSteps(story, getIssueId(testCaseId)); + } + Optional requirementId = story.getUniqueMetaValue(REQUIREMENT_ID); + + ExporterFacade.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); + fillTestCase(parameters, zephyrTest); + + if (testCaseId != null && zephyrExporterProperties.isUpdateCasesOnExport()) + { + zephyrFacade.updateTestCase(testCaseId, zephyrTest); + } + else + { + testCaseId = zephyrFacade.createTestCase(zephyrTest); + zephyrFacade.createTestSteps(scenario, getIssueId(testCaseId)); + } + Optional requirementId = scenario.getUniqueMetaValue(REQUIREMENT_ID); + ExporterFacade.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; + } + + @SuppressWarnings("MagicNumber") + private TestCaseParameters createTestCaseStoryParameters(Story story) + { + String summary = story.getPath(); + File summaryFile = new File(summary); + TestCaseParameters parameters = createTestCaseParameters(story); + String storyName = summaryFile.getName(); + storyName = storyName.substring(0, storyName.length() - 6); + parameters.setSummary(storyName); + 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()); + } + + private String getIssueId(String testCaseId) throws IOException, JiraConfigurationException + { + JiraEntity issue = jiraFacade.getIssue(testCaseId); + return issue.getId(); + } } 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..3d09baa551 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. @@ -20,7 +20,10 @@ import java.util.OptionalInt; import org.vividus.jira.JiraConfigurationException; +import org.vividus.model.jbehave.Scenario; +import org.vividus.model.jbehave.Story; import org.vividus.zephyr.configuration.ZephyrConfiguration; +import org.vividus.zephyr.model.ZephyrTestCase; public interface IZephyrFacade { @@ -28,6 +31,14 @@ 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 createTestSteps(Scenario scenario, String issueId) throws IOException, JiraConfigurationException; + + void createTestSteps(Story story, String issueId) 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..24ab4cbae2 --- /dev/null +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/facade/TestCaseParameters.java @@ -0,0 +1,56 @@ +/* + * 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.Set; + +public class TestCaseParameters +{ + private Set labels; + private Set components; + 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 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..61224e4b3c 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. @@ -24,38 +24,63 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; 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.exporter.converter.CucumberExamplesConverter; import org.vividus.jira.JiraClient; import org.vividus.jira.JiraClientProvider; import org.vividus.jira.JiraConfigurationException; import org.vividus.jira.JiraFacade; import org.vividus.jira.model.Project; import org.vividus.jira.model.Version; +import org.vividus.model.jbehave.Examples; +import org.vividus.model.jbehave.Scenario; +import org.vividus.model.jbehave.Step; +import org.vividus.model.jbehave.Story; import org.vividus.util.json.JsonPathUtils; import org.vividus.zephyr.configuration.ZephyrConfiguration; import org.vividus.zephyr.configuration.ZephyrExporterConfiguration; import org.vividus.zephyr.configuration.ZephyrExporterProperties; +import org.vividus.zephyr.databind.TestCaseSerializer; import org.vividus.zephyr.model.TestCaseStatus; +import org.vividus.zephyr.model.TestStep; +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 static final String TEST_STEP_CREATED = "{} test step has been created"; + private static final String TESTSTEP = "teststep/%s"; 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, TestCaseSerializer testCaseSerializer) { 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, testCaseSerializer)); } @Override @@ -66,6 +91,67 @@ 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 createTestSteps(Scenario scenario, String issueId) throws IOException, JiraConfigurationException + { + List steps = scenario.collectSteps(); + for (int i = 0; i < steps.size(); i++) + { + LOGGER.atInfo().addArgument(i + 1).log(TEST_STEP_CREATED); + String executionBody = objectMapper.writeValueAsString(new TestStep(steps.get(i).getValue(), null)); + getJiraClient().executePost(String.format(ZAPI_ENDPOINT + TESTSTEP, issueId), executionBody); + } + Examples examples = scenario.getExamples(); + + if (Objects.nonNull(examples)) + { + String examplesStr = CucumberExamplesConverter + .buildScenarioExamplesTableWithoutName(examples.getParameters()); + String executionBody = objectMapper.writeValueAsString(new TestStep("Examples:", examplesStr)); + getJiraClient().executePost(String.format(ZAPI_ENDPOINT + TESTSTEP, issueId), executionBody); + } + } + + @Override + public void createTestSteps(Story story, String issueId) throws IOException, JiraConfigurationException + { + List scenarios = story.getScenarios(); + for (int i = 0; i < scenarios.size(); i++) + { + LOGGER.atInfo().addArgument(i + 1).log(TEST_STEP_CREATED); + String executionBody = objectMapper + .writeValueAsString(new TestStep(scenarios.get(i).getTitle(), null)); + getJiraClient().executePost(String.format(ZAPI_ENDPOINT + TESTSTEP, issueId), executionBody); + } + } + @Override public void updateExecutionStatus(int executionId, String executionBody) throws IOException, JiraConfigurationException @@ -144,19 +230,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 { @@ -165,8 +238,8 @@ public OptionalInt findExecutionId(String issueId) throws IOException, JiraConfi if (StringUtils.isNotBlank(zephyrExporterConfiguration.getFolderName())) { jsonpath = String.format("$..[?(@.versionName=='%s' && @.cycleName=='%s' && @.folderName=='%s')].id", - zephyrExporterConfiguration.getVersionName(), zephyrExporterConfiguration.getCycleName(), - zephyrExporterConfiguration.getFolderName()); + zephyrExporterConfiguration.getVersionName(), zephyrExporterConfiguration.getCycleName(), + zephyrExporterConfiguration.getFolderName()); } else { @@ -177,6 +250,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..bdad689515 --- /dev/null +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestCaseLevel.java @@ -0,0 +1,35 @@ +/* + * 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("story"), + SCENARIO("scenario"); + + private final String level; + + TestCaseLevel(String level) + { + this.level = level; + } + + public String getLevel() + { + return level; + } +} diff --git a/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestStep.java b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestStep.java new file mode 100644 index 0000000000..21d4838651 --- /dev/null +++ b/vividus-to-zephyr-exporter/src/main/java/org/vividus/zephyr/model/TestStep.java @@ -0,0 +1,39 @@ +/* + * 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 TestStep +{ + private String step; + private String data; + + public TestStep(String step, String data) + { + this.step = step; + this.data = data; + } + + public String getStep() + { + return step; + } + + public String getData() + { + return data; + } +} 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/TestCaseSerializerTests.java b/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/databind/TestCaseSerializerTests.java new file mode 100644 index 0000000000..fb950a08fe --- /dev/null +++ b/vividus-to-zephyr-exporter/src/test/java/org/vividus/zephyr/databind/TestCaseSerializerTests.java @@ -0,0 +1,108 @@ +/* + * 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 static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +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.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.vividus.jira.JiraConfigurationException; +import org.vividus.jira.JiraConfigurationProvider; +import org.vividus.util.ResourceUtils; +import org.vividus.zephyr.model.ZephyrTestCase; + +@ExtendWith(MockitoExtension.class) +public class TestCaseSerializerTests +{ + private static final String PROJECT_KEY = "project key"; + private static final String STORY_TYPE = "story-type"; + + @InjectMocks private TestCaseSerializer serializer; + @Mock private JiraConfigurationProvider jiraConfigurationProvider; + + @Test + void shouldSerializeTest() throws IOException, JiraConfigurationException + { + ZephyrTestCase test = createBaseTest(); + when(jiraConfigurationProvider.getFieldsMappingByProjectKey(PROJECT_KEY)).thenReturn(Map.of( + STORY_TYPE, STORY_TYPE)); + + verifySerialization(test); + } + + @Test + void shouldRethrowJiraConfigurationException() throws JiraConfigurationException + { + JiraConfigurationException jiraConfigurationException = mock(JiraConfigurationException.class); + doThrow(jiraConfigurationException).when(jiraConfigurationProvider).getFieldsMappingByProjectKey(PROJECT_KEY); + + IllegalStateException thrown = assertThrows(IllegalStateException.class, this::verifySerialization); + assertEquals(jiraConfigurationException, thrown.getCause()); + } + + private void verifySerialization() throws IOException + { + ZephyrTestCase test = createBaseTest(); + verifySerialization(test); + } + + private static ZephyrTestCase createBaseTest() + { + ZephyrTestCase test = new ZephyrTestCase(); + test.setProjectKey(PROJECT_KEY); + test.setSummary("summary"); + test.setLabels(new LinkedHashSet<>(List.of("label 1", "label 2"))); + test.setComponents(new LinkedHashSet<>(List.of("component 1", "component 2"))); + return test; + } + + private void verifySerialization(ZephyrTestCase test) throws IOException + { + 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); + } + } +} 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..a884ebf53e 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. @@ -16,63 +16,93 @@ package org.vividus.zephyr.exporter; +import static com.github.valfirst.slf4jtest.LoggingEvent.error; 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.junit.jupiter.params.provider.Arguments.arguments; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; 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.ArrayList; +import java.util.Collections; import java.util.EnumMap; import java.util.List; import java.util.Map; import java.util.OptionalInt; +import java.util.stream.Stream; +import com.github.valfirst.slf4jtest.LoggingEvent; 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.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; +import org.vividus.jira.JiraClientProvider; 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 JiraClientProvider jiraClientProvider; @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 +110,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\"}"); @@ -88,22 +121,134 @@ void testExportResults() throws IOException, URISyntaxException, JiraConfigurati @Test void testExportResultsWithOnlyStatusUpdate() throws IOException, URISyntaxException, JiraConfigurationException { + when(jiraFacade.getIssue(any())).thenReturn(new JiraEntity()); 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); + when(jiraFacade.getIssue(any())).thenReturn(new JiraEntity()); + + 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); + when(jiraFacade.getIssue(any())).thenReturn(new JiraEntity()); + + 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)))); + } + + static Stream levels() + { + return Stream.of( + arguments(TestCaseLevel.STORY, List.of(info(EXPORTING_STORY, STORY_TITLE))), + arguments(TestCaseLevel.SCENARIO, List.of(info(EXPORTING_SCENARIO_FROM_STORY, STORY_TITLE), + info(EXPORTING_SCENARIO, SCENARIO_TITLE))) + ); + } + + @ParameterizedTest + @MethodSource("levels") + void shouldNotExportTestWithExceptionDifferentLevel(TestCaseLevel level, List events) + throws URISyntaxException, IOException, JiraConfigurationException + { + URI jsonResultsUri = getJsonResultsUri(CREATEREPORTS); + zephyrExporterProperties.setSourceDirectory(Paths.get(jsonResultsUri)); + zephyrExporterProperties.setExportResults(true); + zephyrExporterProperties.setLevel(level); + zephyrExporterProperties.setUpdateCasesOnExport(false); + + IOException exception = mock(IOException.class); + doThrow(exception).when(zephyrFacade).createTestCase(any()); + + zephyrExporter.exportResults(); + ArrayList allEvents = new ArrayList<>(); + allEvents.addAll(events); + allEvents.add(error(exception, "Got an error while exporting " + level.getLevel())); + + assertThat(logger.getLoggingEvents(), is(allEvents)); + } + + @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 +269,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..b5a38ea7b0 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,36 @@ package org.vividus.zephyr.facade; +import static com.github.valfirst.slf4jtest.LoggingEvent.info; +import static java.lang.System.lineSeparator; +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.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import java.io.IOException; +import java.util.Arrays; 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.ObjectMapper; +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; @@ -41,12 +57,20 @@ import org.vividus.jira.JiraFacade; import org.vividus.jira.model.Project; import org.vividus.jira.model.Version; +import org.vividus.model.jbehave.Examples; +import org.vividus.model.jbehave.Parameters; +import org.vividus.model.jbehave.Scenario; +import org.vividus.model.jbehave.Step; +import org.vividus.model.jbehave.Story; import org.vividus.zephyr.configuration.ZephyrConfiguration; import org.vividus.zephyr.configuration.ZephyrExporterConfiguration; import org.vividus.zephyr.configuration.ZephyrExporterProperties; +import org.vividus.zephyr.databind.TestCaseSerializer; +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/"; @@ -54,6 +78,7 @@ class ZephyrFacadeTests private static final String GET_FOLDER_ID_ENDPOINT = ZAPI_ENDPOINT + "cycle/%s/folders?projectId=%s&versionId=%s"; private static final String GET_STATUSES_ENDPOINT = ZAPI_ENDPOINT + "util/testExecutionStatus"; private static final String GET_EXECUTION_ID_ENDPOINT = ZAPI_ENDPOINT + "execution?issueId=111"; + private static final String TEST_STEP_ENDPOINT = ZAPI_ENDPOINT + "teststep/%s"; private static final String GET_CYCLE_ID_RESPONSE = "{\"11113\":{\"name\":\"test\"},\"recordsCount\":1}"; private static final String GET_FOLDER_ID_RESPONSE = "[{\"folderId\":11114,\"folderName\":\"test\"}]"; private static final String GET_EXECUTION_ID_RESPONSE = "{\"issueId\": 111,\"executions\": [{\"id\": 1001," @@ -66,19 +91,22 @@ 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 + "\"}"; + private static final String STEP_VALUE = "When I perform action "; + private static final String NAME = "name "; + private static final String VALUE = "value "; + private static final String TEST_STEP_CREATED = "{} test step has been created"; @Mock private JiraFacade jiraFacade; @Mock private JiraClient client; @Mock private JiraClientProvider jiraClientProvider; @Mock private ZephyrExporterConfiguration zephyrExporterConfiguration; + @Mock private TestCaseSerializer testCaseSerializer; + @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 +272,116 @@ 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)))); + } + + @Test + void testCreateTestStepsScenario() throws JiraConfigurationException, IOException + { + Scenario scenario = new Scenario(); + List steps = List.of( + createStep(STEP_VALUE + 1), + createStep(STEP_VALUE + 2), + createStep(STEP_VALUE + 3) + ); + scenario.setSteps(steps); + + when(jiraClientProvider.getByJiraConfigurationKey(Optional.empty())).thenReturn(client); + + zephyrFacade.createTestSteps(scenario, ISSUE_ID); + + verify(client).executePost(String.format(TEST_STEP_ENDPOINT, ISSUE_ID), + "{\"step\":\"When I perform action 1\"}"); + verify(client).executePost(String.format(TEST_STEP_ENDPOINT, ISSUE_ID), + "{\"step\":\"When I perform action 2\"}"); + verify(client).executePost(String.format(TEST_STEP_ENDPOINT, ISSUE_ID), + "{\"step\":\"When I perform action 3\"}"); + assertThat(logger.getLoggingEvents(), is(List.of( + info(TEST_STEP_CREATED, 1), + info(TEST_STEP_CREATED, 2), + info(TEST_STEP_CREATED, 3)))); + verifyNoMoreInteractions(client); + } + + @Test + void testCreateTestStepsWithExamples() throws JiraConfigurationException, IOException + { + Scenario scenario = new Scenario(); + Examples examples = new Examples(); + Parameters parameters = new Parameters(); + parameters.setNames(Arrays.asList(NAME + 1, NAME + 2)); + parameters.setValues(Arrays.asList(Arrays.asList(VALUE + 1, VALUE + 2), Arrays.asList(VALUE + 3, VALUE + 4))); + examples.setParameters(parameters); + scenario.setExamples(examples); + + when(jiraClientProvider.getByJiraConfigurationKey(Optional.empty())).thenReturn(client); + + zephyrFacade.createTestSteps(scenario, ISSUE_ID); + + String table = "|name 1|name 2|" + lineSeparator() + + "|value 1|value 2|" + lineSeparator() + + "|value 3|value 4|" + lineSeparator(); + + ObjectMapper objectMapper = new ObjectMapper(); + table = objectMapper.writeValueAsString(table); + + String body = "{\"step\":\"Examples:\",\"data\":" + table + "}"; + + verify(client).executePost(String.format(TEST_STEP_ENDPOINT, ISSUE_ID), body); + } + + @Test + void testCreateTestStepsStory() throws JiraConfigurationException, IOException + { + Story story = new Story(); + List scenarios = List.of( + createScenario(NAME + 1), + createScenario(NAME + 2), + createScenario(NAME + 3) + ); + story.setScenarios(scenarios); + + when(jiraClientProvider.getByJiraConfigurationKey(Optional.empty())).thenReturn(client); + + zephyrFacade.createTestSteps(story, ISSUE_ID); + + verify(client).executePost(String.format(TEST_STEP_ENDPOINT, ISSUE_ID), + "{\"step\":\"name 1\"}"); + verify(client).executePost(String.format(TEST_STEP_ENDPOINT, ISSUE_ID), + "{\"step\":\"name 2\"}"); + verify(client).executePost(String.format(TEST_STEP_ENDPOINT, ISSUE_ID), + "{\"step\":\"name 3\"}"); + assertThat(logger.getLoggingEvents(), is(List.of( + info(TEST_STEP_CREATED, 1), + info(TEST_STEP_CREATED, 2), + info(TEST_STEP_CREATED, 3)))); + verifyNoMoreInteractions(client); + } + private void mockJiraProjectRetrieve() throws IOException, JiraConfigurationException { Version version = new Version(); @@ -266,4 +404,38 @@ 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(testCaseSerializer).serialize(eq(test), any(JsonGenerator.class), any(SerializerProvider.class)); + } + + private Step createStep(String value) + { + Step step = new Step(); + step.setValue(value); + return step; + } + + private Scenario createScenario(String title) + { + Scenario scenario = new Scenario(); + scenario.setTitle(title); + return scenario; + } } 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/mockito-extensions/org.mockito.plugins.MockMaker b/vividus-to-zephyr-exporter/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..1f0955d450 --- /dev/null +++ b/vividus-to-zephyr-exporter/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1 @@ +mock-maker-inline 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..5ea6847063 --- /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" + }, + "labels": [ + "label 1", + "label 2" + ], + "components": [ + { + "name": "component 1" + }, + { + "name": "component 2" + } + ], + "summary": "summary", + "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": [] +}