From a53181322d1e7f68c96ad8109112f6827e2a18f7 Mon Sep 17 00:00:00 2001 From: ivakoleva Date: Mon, 13 Feb 2023 18:43:56 +0200 Subject: [PATCH 1/5] frontend: e2e tests This is a stacked PR #6: data-pipelines e2e tests. End-to-end tests sources added. Testing done: fetched the `frontend/shared-components` changes (from person/mdilyan/os-shared-components feature branch in review), and verified `frontend/data-pipelines` manual start + unit + e2e tests. Signed-off-by: ivakoleva --- .../e2e/fixtures/e2e-cy-dp-failing.job.json | 19 + .../e2e/fixtures/e2e-cy-dp-failing.job.zip | Bin 0 -> 6688 bytes .../lib/explore/additional-test-job.json | 27 + .../e2e/fixtures/lib/explore/test-jobs.json | 56 ++ .../lib/manage/e2e-cypress-dp-test.json | 19 + .../lib/manage/e2e-cypress-dp-test.zip | Bin 0 -> 4853 bytes .../gui/e2e/integration/app/app.spec.js | 38 ++ .../getting-started/getting-started.spec.js | 144 ++++ .../lib/explore/data-job-details.spec.js | 158 +++++ .../lib/explore/data-job-executions.spec.js | 84 +++ .../integration/lib/explore/data-jobs.spec.js | 306 +++++++++ .../lib/manage/data-job-details.int.spec.js | 220 ++++++ .../manage/data-job-executions.int.spec.js | 644 ++++++++++++++++++ .../lib/manage/data-jobs.int.spec.js | 494 ++++++++++++++ .../data-pipelines/gui/e2e/plugins/index.js | 48 ++ .../support/application/data-job-base.po.js | 164 +++++ .../application/data-job-details-base.po.js | 74 ++ .../support/application/data-jobs-base.po.js | 180 +++++ .../application/data-pipelines-base.po.js | 194 ++++++ .../gui/e2e/support/commands.js | 252 +++++++ .../e2e/support/helpers/commands.helpers.js | 433 ++++++++++++ .../data-pipelines/gui/e2e/support/index.js | 18 + .../gui/e2e/support/pages/app/app.po.js | 20 + .../data-jobs-health-panel-component.po.js | 159 +++++ .../app/getting-started/getting-started.po.js | 50 ++ .../app/lib/explore/data-job-details.po.js | 16 + .../pages/app/lib/explore/data-jobs.po.js | 134 ++++ .../app/lib/manage/data-job-details.po.js | 123 ++++ .../app/lib/manage/data-job-executions.po.js | 360 ++++++++++ .../pages/app/lib/manage/data-jobs.po.js | 210 ++++++ 30 files changed, 4644 insertions(+) create mode 100644 projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.json create mode 100644 projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.zip create mode 100644 projects/frontend/data-pipelines/gui/e2e/fixtures/lib/explore/additional-test-job.json create mode 100644 projects/frontend/data-pipelines/gui/e2e/fixtures/lib/explore/test-jobs.json create mode 100644 projects/frontend/data-pipelines/gui/e2e/fixtures/lib/manage/e2e-cypress-dp-test.json create mode 100644 projects/frontend/data-pipelines/gui/e2e/fixtures/lib/manage/e2e-cypress-dp-test.zip create mode 100644 projects/frontend/data-pipelines/gui/e2e/integration/app/app.spec.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/integration/app/getting-started/getting-started.spec.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-details.spec.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-executions.spec.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-jobs.spec.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-details.int.spec.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-executions.int.spec.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-jobs.int.spec.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/plugins/index.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/application/data-job-base.po.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/application/data-job-details-base.po.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/application/data-jobs-base.po.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/application/data-pipelines-base.po.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/commands.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/helpers/commands.helpers.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/index.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/pages/app/app.po.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/data-jobs-health-panel-component.po.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/getting-started.po.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-job-details.po.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-jobs.po.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-details.po.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-executions.po.js create mode 100644 projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-jobs.po.js diff --git a/projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.json b/projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.json new file mode 100644 index 0000000000..6a4c95e6b8 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.json @@ -0,0 +1,19 @@ +{ + "job_name": "e2e-cp-$env-placeholder$-failing-job", + "description": "Testing failing job, that is used in data-pipelines CI/CD for UI e2e tests. It is intended to be always available in order to speed up deployment and if deleted will be automatically re-created by the e2e tests.", + "config": { + "db_default_type": "IMPALA", + "contacts": { + "notified_on_job_failure_user_error": [], + "notified_on_job_failure_platform_error": [], + "notified_on_job_success": [], + "notified_on_job_deploy": [] + }, + "schedule": { + "schedule_cron": "0 */12 * * *" + }, + "generate_keytab": true, + "enable_execution_notifications": true + }, + "team": "supercollider" +} diff --git a/projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.zip b/projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.zip new file mode 100644 index 0000000000000000000000000000000000000000..38f4fc48dd2cfd556e73ac273d1fde4d1531e4d5 GIT binary patch literal 6688 zcmd5=c{tQ-`ybobmufKfJ!BtFk?i}W4gN@ z6^IHniuAJBTaO$H0&x&sPg2v|!92hR<+i)8&_L<~{qH$~cO5&ro0hH``f~9Uy_6s`FGi`n`c^W^*?PNJ9_)x; zRio7<5nM`Sw>H34scU{VkR62jh%8+fJzis{l3MOoQ>my;>rw99Q@PfWA1+n-sMsO# z(f4yd5%?EV&fN-xdxI(K5U9wlP>J`d)TauE_bA zD=OBf^9+2N#syB>w_~2<%Rc>bvR*RR<#diYzK_lB@@l?Y@TPQq#e3=&!rKZJrSjZu zj`ezY^-fT@%8poEc~g3ge?P*-kJq|!q0$Qf)&#W1NHXMXKVS7BCI2a~-Vh^+)(B+W z)PRyIRye6j7l;@kEW`sAOo-fZLckmxjMOwt%&nzWytq@s;^A^#g; zE5LSsZZy;PCsS7mZ}^Ea3+3h)h(Nh{yCDPoB?E#3b_wRj{}*eC5G=G?2H7Odr1|7{hT3OO>L}MlZJMIc3|Vl8uE5T4d<3 zQ_?6_npX7k#x;%`fx~}CHU_ow-+d2zZ|ofSdHsk>8aLQ{`EI@&Z6E({nFX~lOI=^L^eSw~k|H*}TnHJa->&iYC(np)aD zBuRwZum_4YARc&1C9JxT%H~v$Bf{@77R41iUMciuOuw`p<0N!Fg^cDgwn*QF(kY-= z%fsK&+ULYXF?JrcbNcjMI)%1DcJA6gj~HH$=*gqL>da}{3w@XeI=Zc}W~jrQV3I_& z-d2)(O*T9u^>zkFhTtDOV_m-LjD3g~$BJZfki{Rq&reZqs_Nv)xQm`~mX~5MKhpby z+A-cHx`y|LH6=#k8paATm>?ITpY z>+_^f78}ykDS_>r^7m+iUUya2kQ;qCRrZBurM3vAo2(UAn!Uge^={hUb+!L~?~MGL zp_)W1yU+)&B3;Xq{ip3E`JNBWBk;UNnwu#2OvOOaOOrPuk4;7!C z%$7@KzE{%5*HdQT$T}Fb#AK*0-w%}x5#he6lgP>P1`{Cw~zoZ~e@ z=7uoz*ebTRm2|43qe7^#8vpwf*yZ!#8eY`gBGz_gD&kp^e0S}r8G1idEy{}ifUMA* zC{O)f&Qa`#itQVjM(16)#p8jp`%VU2o`mRz_%#v`2;dTNmI3^eJnZruOt}C5+jDWS z*pT!!&mD?DHM!l}!`U9`{Oj%6uaI#R|CD>-T56g`T9V$be@kOC;l=>Zf7}{3iw@+d zG*kV&7|W87DEk^Wq(M-mR|iy0jdy9twPHE;45!2SJv2yfB=G!;C?}MBoucd{c#%c7 z;u5X==@)rCPZwEaaaNQtyjf(dbee9&%T0~Q7S=ZIC-hO`wRG0y)#-@qrU@hq>vzi6 z8G3I@J6g#m;Gnr@?;QQSVOKqJapE{kR_%TvNsau~fv7$_&f|y5obHLOnh15u_PT^~ z>8$DGUs>G4AF{^b_?1>pg&26JvC0fr+8W&C_28p`U+$w}sId)t%Tcql9;oE|y1>Jq z`mAji^ks)}JLISowTV}&xM~?DPTDzgTHS2bY0Te8v`Ltw*RiL0RH#S1`L?3C$H4qM zg)7!+76W|4i%ytWqX_Hte0j^jQggSf$;sQXTkudOE*qQ!{zh^mtPDf>~Gtd6kRM|d6jYt zYg`^T85&0(+qwT#(3wwGtFf$!LrD{&pdeW7Dl$A#dQ^X1o3VSk-S!xZDAb}dkd`ug z+NzDm)?h?b|5O+TyXCh<0jF-%u?l|4nkZoXsvpm0{%m!*bCZQFYFS%KmC$^ zBq)Xd6pc`(^hZ^jiqYa;vg6vNexPV8_c860o0i7gHs0nn&XjRkzUQ-t*r@tZ7czR6 zKU;JzcZyF-2%92fRL^adD#aQtL|T5gx2;?g?1VIYV&f4x*JW&5Q5$5N;`U)#6NS}V zf+?e$>mH?1@J5z4p84uIa?DaTrZg$#ybVum)b!LAvU)TS$7dGClO*i=VrOGpgW}}$ z8=L1@(smo>&Vrta><%-2N8->vZWQ|5uYZWm zLsf7?zoN!7FSd|H`XOEY_{8 zye#VGI67ulN5`apOs!Fwjimfg=%nf#>R3wria)xq<#J1*Lg6c+jISYTu_3dCrj;d3 zl`zi>9k6)W6ck)(6%H4JLK$W)kE1P~S9$i5zm5X$ib@j4$?|l990G`co`r7ijlWZJ zH5CZNNf66l%R(0)q&vbx5`jeQs>)2Xp)-!4D2>0WN~Pt(1lM8G5#! z{l7&;8t~;~c;-#xsN=2tgLEpj;X+-B(+&=KWy8X+YBOyVjj5ix)Cr$T`Rj{h1qQp4TvXPs3EkucU5lRCxAI zKCIi~ic77&0>f#>+h>bupDr5mZ%-w@QdBgXQ~@8OV1De%Df-RIO>2D1?&hY0b>+ZT z+Z`o(?5Ff{l*G;K_GbN{NsW{5%zE1MD^=?Q{K?9TE`_zpzL}9v9#4}JzCLJV6J~zG zWzfJaPWv}%Jl{9Iv1t8Q5o3?n79&tccr%jc%-9o$#J*w-NXa-WtA`$1>s__fW&dDn z=32aRRhgliE$E>YZy06s9Qc8c@1!CRE3=5qIBg@AyQ`FXjNS51l24cA7p!y#W?5?4 zE+#8EB8N-)gd#-KckA1?xbj(>^w3)2lz50WlU%@J@Evsw>}YDfhM*$53TEr3vCziG zgK#&IDZ{y>kPla*624lXH51z>$y>@F-|AoDn$IysYi=lDa=lDS(dRpnZH7Y{A<8gA zl(q_5jt^sMq(r`=lXwJ@Hre2Za$ODZ^y35)T_59`4OxJG*e*r%{j10qYmOB76PkEBW64-fq@7=NJ&TS}u^ zR9;52cuDP3s(rqClJ_i@J+$6PZ5D<)xuZ?JqZ`@M!I=71fHQ^8RpVpor4&yt zKJeyC`g70fuZ*`Mi_$o{f{GYZhh3*c1q19eV!JJ;}V}e zaY;}5PQtHRr7k{j8qvIr`_l5}+hp1Hw8y91riqm(@rAC1nNRv{m@8`^E5Br5@;*tl zOv*Gm@+lcE!%iy;20i+ST4Mo9D~jn}+Gavo1!w|swE%QPn4lI1c9Z`g9RJJGSv3zx zrw7uB6}%^%(dBP`|5ZBorx7?I$?y@CPMEZVzu(2bS{cHRzpJupf_7H@s-H%11E4B< zy9MzXf&9VHi;d)shk)ngMmSvj1drs%xBEhLM9RBJE;rv7)KHLY&zApu%47_=?E72; z@td)gYP*QGLQ>QYB|SH#{gBLalp+VEsfi40lk)ArT!FAf>;{!<00!6PUe_YEXzdg^ z8%f>QE#M&#pZxwTv_;DW+rL&mVlj0yA&+bdN70v^2Q~4+`7lpJf3vR*f-jyuA839uCfdkloL&w0+4f6MKdkv)R14^@H;Hj?p_S{ErnI z-#AfAM5fUNJ&F7$sw3yUl|25u(236-zGh`nYnZHI*H=3It;g9WXuUZwuZ{;*-_d_D z-Qu{_s(1ZH^yu4#t~{~MktGTTKZ+HG+Ba$=ff@>D&(vhjpqA45>MqFo>Z9aiA1#Hh zk^pRH``RvyBJddq<4?Bl4=})X@x$1DP^|x_DL(^Ho+nI2k6e?39XJ9?DkJP9HskMd zfrJ!71|j@Me4@Dfg7C4R2K@T9eAp%Zf%C<`*58{j?jL0V6UIHmpiPOcPkd%YWE^B_ z@b8ZHkD`DoaL+Jk5&{zrR5s#1i9~t7Pk>STCqBTCYtJy~B7)Cf5AVRy&LJ29fBQ!< zz#M7MFz9N6z8{G3K-Y&r1!g__`*UE{vu7A|A~`Xr2RGtGCO`Yn7I3(~aR%D{J;R{C zfDgfXuniO z2z!P>^U)Cl_YZlWNW-un9pGtyt_8}AJ;R_)m<~br4^9t(2OQ~s{Rh(co?+0oM~J~Y tNa%?q_5BzDcl)&!NGf}VK`R|Q1mi)Ti2|h{5FZ4hC;UAHn&4d#|38emtv>(& literal 0 HcmV?d00001 diff --git a/projects/frontend/data-pipelines/gui/e2e/fixtures/lib/explore/additional-test-job.json b/projects/frontend/data-pipelines/gui/e2e/fixtures/lib/explore/additional-test-job.json new file mode 100644 index 0000000000..198165b0ee --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/fixtures/lib/explore/additional-test-job.json @@ -0,0 +1,27 @@ +{ + "config": { + "contacts": { + "notified_on_job_deploy": [ + "auserov3@vmware.com" + ], + "notified_on_job_failure_platform_error": [ + "buserov3@vmware.com" + ], + "notified_on_job_failure_user_error": [ + "cuserov3@vmware.com" + ], + "notified_on_job_success": [ + "duserov3@vmware.com" + ] + }, + "db_default_type": "IMPALA", + "generate_keytab": false, + "schedule": { + "schedule_cron": "0 0 03 * 5" + } + }, + "description": "Test description 3 with a long text on the line", + "job_name": "e2e-cp-$env-placeholder$-a-test-job-3", + "status": "disabled", + "team": "e2e-cp-$env-placeholder$-a-test-team-3" +} diff --git a/projects/frontend/data-pipelines/gui/e2e/fixtures/lib/explore/test-jobs.json b/projects/frontend/data-pipelines/gui/e2e/fixtures/lib/explore/test-jobs.json new file mode 100644 index 0000000000..5ce9ba5d2b --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/fixtures/lib/explore/test-jobs.json @@ -0,0 +1,56 @@ +[ + { + "config": { + "contacts": { + "notified_on_job_deploy": [ + "auserov1@vmware.com" + ], + "notified_on_job_failure_platform_error": [ + "buserov1@vmware.com" + ], + "notified_on_job_failure_user_error": [ + "cuserov1@vmware.com" + ], + "notified_on_job_success": [ + "duserov1@vmware.com" + ] + }, + "db_default_type": "IMPALA", + "generate_keytab": false, + "schedule": { + "schedule_cron": "0 0 01 * 5" + } + }, + "description": "Test description 1 with a long text on the line", + "job_name": "e2e-cp-$env-placeholder$-a-test-job-1", + "status": "disabled", + "team": "e2e-cp-$env-placeholder$-a-test-team-1" + }, + { + "config": { + "contacts": { + "notified_on_job_deploy": [ + "auserov2@vmware.com" + ], + "notified_on_job_failure_platform_error": [ + "buserov2@vmware.com" + ], + "notified_on_job_failure_user_error": [ + "cuserov2@vmware.com" + ], + "notified_on_job_success": [ + "duserov2@vmware.com" + ] + }, + "db_default_type": "IMPALA", + "generate_keytab": false, + "schedule": { + "schedule_cron": "0 0 02 * 5" + } + }, + "description": "Test description 2 with a long text on the line", + "job_name": "e2e-cp-$env-placeholder$-a-test-job-2", + "status": "disabled", + "team": "e2e-cp-$env-placeholder$-a-test-team-2" + } +] diff --git a/projects/frontend/data-pipelines/gui/e2e/fixtures/lib/manage/e2e-cypress-dp-test.json b/projects/frontend/data-pipelines/gui/e2e/fixtures/lib/manage/e2e-cypress-dp-test.json new file mode 100644 index 0000000000..a9c8bebeb9 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/fixtures/lib/manage/e2e-cypress-dp-test.json @@ -0,0 +1,19 @@ +{ + "job_name": "e2e-cp-$env-placeholder$-test-job", + "description": "Testing job, that is used in data-pipelines CI/CD for UI e2e tests. It is intended to be always available in order to speed up deployment and if deleted will be automatically re-created by the e2e tests." , + "config": { + "db_default_type": "IMPALA" , + "contacts": { + "notified_on_job_failure_user_error": [] , + "notified_on_job_failure_platform_error": [] , + "notified_on_job_success": [] , + "notified_on_job_deploy": [] + } , + "schedule": { + "schedule_cron": "11 23 5 8 1" + } , + "generate_keytab": true , + "enable_execution_notifications": true + } , + "team": "supercollider" +} diff --git a/projects/frontend/data-pipelines/gui/e2e/fixtures/lib/manage/e2e-cypress-dp-test.zip b/projects/frontend/data-pipelines/gui/e2e/fixtures/lib/manage/e2e-cypress-dp-test.zip new file mode 100644 index 0000000000000000000000000000000000000000..4f820842482abd451fe10aec10d3e8ea8f87936f GIT binary patch literal 4853 zcmb_g2{e@d*B>zkWy@H`&e*0bgJM)dVzO^z-^MazADXe0B?^&BN@QtaC>4cd&lqFL zo`^vz5s^Jhh;3od)%*MZ&Ux=S&phAf%sKb-z2AH9=eeK9)PSA=44_&5xq9=T zi~s%s14IEBC5)U~7&ZuV=8T*>Rt|?bgHy0Xu>wF4nHOk&>P5TbL-zvc7*^;2fS;aB ze|lsBF#VfH20j2l=no#{Pg*!z-~xg$|KpnGb{E$(X$Hm(aInr(?Th9|MBCFvF+ffX z@V@tTK6o_n{5qZZGRTh;-kR0;jBBNV>CUIU-or}hUY3#TPuwaiw^}R(>ERt=PwB`2 zaRlh_6A4#a@q6WY=_S{s+p(7wbt@d+rIol0)DCIW$CtR>Xx%c(8%cZ7`{FKJ)4NXe zaP-DwK?6Mv{Z^;oK-3DnQHU|smol2?T#IvDuSQzyb}ookg1gK!Q5N-?YwluYQ0Aj= ze8N_Al`4Ceyqg3=p?%n^FOuGpZX+5w)D;#+C0%dZI=_XdZTb1hU?d)A>Yt56w%2S{ zr&)t_C?}IFP|kCQs4LB{8)EmBtFS7Y>h~IA4GpDny1bonT|nF^B<`<=a4(3{@qI{@ zn)~VL#*<&I4XkcPHQciB?b=@HT8+{%+k#8^IzG=>d&wc|(lvOpwa*&R$4>VmHvFw{ z~r$@wH zWYD_J4R@TRZ4Jb<_Yx6tn7SF-hW@qHjL?4-@)GqQi9eKG5GD}s6@>A__~XvV<3e#i zs}Cdo0=;wnR@f1pivA67j+3uP9`I?Fy*mB?{^1*)&ebQZ>5>hObUibL75d}r^z2cj znfWr<%OT$uJ(q0TTlB*b5~$lqeh|yiGSkv-(Zu@qMvPGp{p&-T#Zx~jeKdB(&#iE} zr3o`yETraP_V$P$&UjF%e_BxHC2kcRIk|`3w$&mB)tt z-FaB1Xa!`!Ib!EF6FF<2S@utb<*B0*ALTn1To=b*yIKswdLJXJ40S)+uD#$wY2r=( z?lF?7IuLm;nP@g-_nt?G>tX!mo~ydhUUZ{!U(dRWA)5Ew*1^8jB^@&?x8T#VoJQQJ z^}f`bPRlamMKUZGSTDY|w!hm@*f47fxLYE-w6rvdFBx+wQkf@XOwdqjy@oOpKr8HpKWIs$l^Bh1Bb<~}15@*d5E zD`xyO%%Z@TZ{z~o^XRUb^gx5$O?85?7gs92EPu`p&J7*%AjWj}k%>wUf2z(x3LzM@xND)pbm zn^}Yy8N^PG2)}$3iLKZK*k_67s0Jn2DoORDTC5>iUH7Drt%6tj3Q9iPL~2bib%L{C zAtEZBV~8bGN^WqdZ1wG%bxL9Mht-3a8GjO1z2M z^0e~Uy>JqNf2MD99j%i_I`V$*e^)RVRGa@0oTFy?AkVxcjDJ}eTq_~djRcf454zRn zSn+VZBq0TVM1<&3@b<(wmmoy-QROklS)Nm+!Fv%$h`AzFvpmWqE0EHlSxf>v?bP|E zHSL&2-WK8KtZ}lH`)o?e)4eX5Ceh8T+$>pP>%IakWy0C zx>G<`fe7!7>lq|@ZfQ)NI|1bH=W@-k4Mx!-b9kP`l4!>fnOT0Vt<)~ z!4*+YF`)U`9u~F=hmdyy727L#b?oa0o@bb?9Xj}$+KgMl`xTf?e41r7pAr(`t}zoQ z%$8h+&ji34qy@UsUF0FjE?M&Rqq3g8(<5rD@V*Rr&gao7WHtBf?OMIaPCOjo& zGhxF#fDu%IVcF}O8Nd#n@1p8C1r=_^4Ti=sT-$f5BwWRmb?Tos2&kU~tEowp zyUX-{DBQ2VqRZYn(dxj(1LH%L;rD{FC#+gT91JKhebq=p(MI3~(44s*X%*VUdlhQ) z>dhd;;^opp`x*}>t|8n3QEkv+GoFoFue)#zTLerhiYGFmz|K4Hub-xX}3|Ro?tW+WBJ3$p=?>A z9b7vO5s|U$Fm@=d3~{)D8C^ITRHQerbUd0|ou392jVY`@_Wd-4%ToDLVe$=oTakpg ziO(DUqI&MUkBdK5jam1;ALARqPs0JMhLy_H@t0RX~( z;9$1^e-AHDc`tvjzw4zkOBWxgD#wpmXGN^DT}x4>pr1v&rdS>}&*2t5G|)Qydt#!Lh?YZ*M{5VYs-$PAnF!s$%kpNlv(+}#;(oX1@TTMjI9 zP;qLmx@$h|IMWdMJ>089@lkzIr;18CTT6Ql0>l?5*fO_x4N4l7SE%buEuJDM>EvcG_=#+G-#wA%uIW!X zTaK(`N;WjZ%djG+W5at6N7Jzl+WVM3k$Cw^g4Lx+=9#6}aS_Ieq#+9^M-;#{FutoF zu~^Hl159j}78~fxtyEQ4)x}7T^)E_=rrpN$tm$udgRYtpyV*o;3Mq+K{8(OzOZj4x z9#IUvQ3AH%IE0%Gy?KJ5wEtF~w!~3B4e}e^$2sSg`rk7rs&U!7-$7)HxgD_*y`HNC zJ5lWZ_06G8;@K9I=xbO!3O8M#h`mfLFRTgDK9v)e~gz| zkv@$UeQl<|G{YUVJcSdb8x*VE2x27OVMsc$`he0bl82b17AP9RLU@?ZdAovd+9qKM zmUb<+_f|Jsptk}qBmWF5;(Ck#RF);ME;pd?nOAOF5F4qD3yiMbpx{Me-Gc`rO!R%& zW66-?#6rr=ClDsd98si}80YD(3Wdr1uW8Rsii6=Ar(61=fg%Ot#K&15C&$zu9MO-f z!VD?cvj$jeI~dB8JvEO|80v96 zyp9z)m$yW=-4>AC*+B)e$nh>oN1`Vl619BO+O_xkP2e4;yTeNMV|iLOSwZ3SfWt za8LhW$mJ;P)V8OdSyT-%Hu+jAKIz=k`-{#;s*darTbP|fZiFj*d@y^M%??_!1*r4g z^dvem`c{-jPVuF2v-7WaKsP0G4s|U-Gh4}vNxJQ)4*EeGJ;T0yV`-D>;fhmedMh0@ zrlHZsE5w*4O?a+ap4*n1thc)O!2XM}K5`t^IHp)ze$WNSTrjy0hdv6549=t=Wh8)a2&`|-9sfi+Q!}06*9BAZh(JGTWjzH@0sBTo4wQ-6062sin3H5|tPWfof*Qn!G z^k%t)g8FdK1MU~i% zE5~+?SVJ^Qb*-;KjKYj3U6;LKpC(V6^HUD- zu=z0ehu#O%-^ng>XcAUTIL1v_Bd=|2f$Fx6Myv+67HzSKFiWotBX2#qyyuz}dp~v0 zuiov0IsF%`P?d%$+KxSi6YE{BwjnEId~UT!P)*w#-*l9e)skP$YW&c&O9{fdgZTY&#m`2CLf>$pr)Q#(e89-wyeZixR_ zTm26C>saycS>$Bg9r8b(%72IYm62$3%8n7D9hi5A`p>79-?4se4I0Po7$G`|b$6_P Z$atm(K + +import { AppPage } from '../../support/pages/app/app.po'; + +describe('App Page', { tags: ['@dataPipelines'] }, () => { + before(() => { + return AppPage.recordHarIfSupported() + .then(() => cy.clearLocalStorageSnapshot('app')) + .then(() => AppPage.login()) + .then(() => cy.saveLocalStorage('app')); + }); + + after(() => { + AppPage.saveHarIfSupported(); + }); + + beforeEach(() => { + cy.restoreLocalStorage('app'); + }); + + it('App Page - Main Title Component have text: Data Pipelines', () => { + AppPage.navigateTo(); + + const page = AppPage.getPage(); + + page + .waitForInitialPageLoad(); + + page + .getMainTitle() + .should('have.text', 'Data Pipelines'); + }); +}); diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/app/getting-started/getting-started.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/app/getting-started/getting-started.spec.js new file mode 100644 index 0000000000..f13d4c1153 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/integration/app/getting-started/getting-started.spec.js @@ -0,0 +1,144 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { DataPipelinesBasePO } from '../../../support/application/data-pipelines-base.po'; +import { GettingStartedPage } from '../../../support/pages/app/getting-started/getting-started.po'; +import { DataJobsHealthPanelComponentPO } from '../../../support/pages/app/getting-started/data-jobs-health-panel-component.po'; +import { DataJobManageDetailsPage } from '../../../support/pages/app/lib/manage/data-job-details.po'; +import { DataJobManageExecutionsPage } from '../../../support/pages/app/lib/manage/data-job-executions.po'; +import { applyGlobalEnvSettings } from '../../../support/helpers/commands.helpers'; + +describe('Getting Started Page', { tags: ['@dataPipelines'] }, () => { + let testJob; + + before(() => { + return DataPipelinesBasePO.recordHarIfSupported() + .then(() => cy.clearLocalStorageSnapshot('getting-started')) + .then(() => DataPipelinesBasePO.login()) + .then(() => cy.saveLocalStorage('getting-started')) + .then(() => cy.prepareLongLivedFailingTestJob()) + .then(() => cy.createExecutionsLongLivedFailingTestJob()) + .then(() => cy.fixture('e2e-cy-dp-failing.job.json')) + .then((failingTestJob) => { + testJob = applyGlobalEnvSettings(failingTestJob); + + return cy.wrap({ context: 'getting-started.spec::before()', action: 'continue' }); + }); + }); + + after(() => { + DataPipelinesBasePO.saveHarIfSupported(); + }); + + beforeEach(() => { + cy.restoreLocalStorage('getting-started'); + + DataPipelinesBasePO.initBackendRequestInterceptor(); + }); + + it('Main Title Component have text: Get Started with Data Pipelines', () => { + GettingStartedPage + .navigateTo() + .getMainTitle() + //TODO : Discuss/agree what should be the assertion strategy of the UI Components. + // Do we assert text directly, or use some other form? + .should('have.text', 'Get Started with Data Pipelines'); + }); + + describe('Data Jobs Health Overview Panel', () => { + it('Verify Widgets rendered correct data and failing jobs navigates correctly', () => { + GettingStartedPage + .navigateTo(); + + let dataJobsHealthPanel = DataJobsHealthPanelComponentPO + .getComponent(); + dataJobsHealthPanel + .waitForViewToRender(); + dataJobsHealthPanel + .getDataJobsHealthPanel() + .scrollIntoView(); + + dataJobsHealthPanel + .getExecutionsSuccessPercentage() + .should('be.gte', 0) + .should('be.lte', 100); + dataJobsHealthPanel + .getNumberOfFailedExecutions() + .should('be.gte', 2); + dataJobsHealthPanel + .getExecutionsTotal() + .should('be.gte', 2); + + dataJobsHealthPanel + .getAllFailingJobs() + .should('have.length.gte', 1); + + dataJobsHealthPanel + .getAllMostRecentFailingJobsExecutions() + .should('have.length.gte', 1); + + // navigate to failing job details + dataJobsHealthPanel + .navigateToFailingJobDetails(testJob.job_name); + + const dataJobManageDetailsPage = DataJobManageDetailsPage + .getPage(); + dataJobManageDetailsPage + .getMainTitle() + .should('contain.text', testJob.job_name); + dataJobManageDetailsPage + .getDetailsTab() + .should('be.visible') + .should('have.class', 'active'); + dataJobManageDetailsPage + .getExecutionsTab() + .should('exist') + .should('not.have.class', 'active'); + dataJobManageDetailsPage + .showMoreDescription() + .getDescriptionFull() + .should('contain.text', testJob.description); + }); + + it('Verify most recent failing executions Widget navigates correctly', () => { + GettingStartedPage + .navigateTo(); + + let dataJobsHealthPanel = DataJobsHealthPanelComponentPO + .getComponent(); + dataJobsHealthPanel + .waitForViewToRender(); + dataJobsHealthPanel + .getDataJobsHealthPanel() + .scrollIntoView(); + + dataJobsHealthPanel + .getAllMostRecentFailingJobsExecutions() + .should('have.length.gte', 1); + + // navigate to most recent failing job executions + dataJobsHealthPanel + .navigateToMostRecentFailingJobExecutions(testJob.job_name); + + const dataJobManageExecutionsPage = DataJobManageExecutionsPage + .getPage(); + dataJobManageExecutionsPage + .getMainTitle() + .should('contain.text', testJob.job_name); + dataJobManageExecutionsPage + .getDetailsTab() + .should('be.visible') + .should('not.have.class', 'active'); + dataJobManageExecutionsPage + .getExecutionsTab() + .should('be.visible') + .should('have.class', 'active'); + dataJobManageExecutionsPage + .getDataGridRows() + .should('have.length.gte', 1); + }); + }); +}); diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-details.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-details.spec.js new file mode 100644 index 0000000000..147e5de69a --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-details.spec.js @@ -0,0 +1,158 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { DataJobsExplorePage } from '../../../support/pages/app/lib/explore/data-jobs.po'; +import { DataJobExploreDetailsPage } from '../../../support/pages/app/lib/explore/data-job-details.po'; +import { applyGlobalEnvSettings } from '../../../support/helpers/commands.helpers'; + +describe('Data Job Explore Details Page', { tags: ['@dataPipelines', '@exploreDataJobDetails'] }, () => { + let dataJobExploreDetailsPage; + let testJobs; + + before(() => { + return DataJobExploreDetailsPage.recordHarIfSupported() + .then(() => cy.clearLocalStorageSnapshot('data-job-explore-details')) + .then(() => DataJobExploreDetailsPage.login()) + .then(() => cy.saveLocalStorage('data-job-explore-details')) + .then(() => cy.cleanTestJobs()) + .then(() => cy.prepareBaseTestJobs()) + .then(() => cy.fixture('lib/explore/test-jobs.json')) + .then((loadedTestJobs) => { + testJobs = applyGlobalEnvSettings(loadedTestJobs); + + return cy.wrap({ context: 'explore::data-job-details.spec::before()', action: 'continue' }); + }); + }); + + after(() => { + cy.cleanTestJobs(); + + DataJobExploreDetailsPage.saveHarIfSupported(); + }); + + beforeEach(() => { + cy.restoreLocalStorage('data-job-explore-details'); + + DataJobExploreDetailsPage.initBackendRequestInterceptor(); + }); + + it('Data Job Explore Details Page - should load and show job details', () => { + cy.log('Fixture for name: ' + testJobs[0].job_name); + + const dataJobsExplorePage = DataJobsExplorePage.navigateTo(); + + dataJobsExplorePage + .openJobDetails(testJobs[0].team, testJobs[0].job_name); + + dataJobExploreDetailsPage = DataJobExploreDetailsPage + .getPage(); + + dataJobExploreDetailsPage + .getDetailsTab() + .should('be.visible'); + + dataJobExploreDetailsPage + .getMainTitle() + .should('be.visible') + .should('contains.text', testJobs[0].job_name); + + dataJobExploreDetailsPage + .getStatusField() + .should('be.visible') + // TODO: do the right assertion (once it gets implemented) + .should('have.text', 'Not Deployed'); + + dataJobExploreDetailsPage + .getDescriptionField() + .should('be.visible') + .should('contain.text', testJobs[0].description); + + dataJobExploreDetailsPage + .getTeamField() + .should('be.visible') + .should('have.text', testJobs[0].team); + + dataJobExploreDetailsPage + .getScheduleField() + .should('be.visible') + .should('contains.text', 'At 12:00 AM, on day 01 of the month, and on Friday'); + + // DISABLED Because there is no Source at the moment. + // dataJobExploreDetailsPage + // .getSourceField() + // .should('be.visible') + // // TODO: do the right assertion (once it gets implemented) + // .should('contains.text', 'http://host/data-jobs/' + testJobs[0].job_name) + // .should("have.attr", "href", 'http://host/data-jobs/' + testJobs[0].job_name); + + dataJobExploreDetailsPage + .getOnDeployedField() + .should('be.visible') + .should('contains.text', testJobs[0].config.contacts.notified_on_job_deploy); + + dataJobExploreDetailsPage + .getOnPlatformErrorField() + .should('be.visible') + .should('contains.text', testJobs[0].config.contacts.notified_on_job_failure_platform_error); + + dataJobExploreDetailsPage + .getOnUserErrorField() + .should('be.visible') + .should('contains.text', testJobs[0].config.contacts.notified_on_job_failure_user_error); + + dataJobExploreDetailsPage + .getOnSuccessField() + .should('be.visible') + .should('contains.text', testJobs[0].config.contacts.notified_on_job_success); + }); + + it('Data Job Explore Details Page - should verify Details tab is visible and active', () => { + cy.log('Fixture for name: ' + testJobs[0].job_name); + + const dataJobsExplorePage = DataJobsExplorePage.navigateTo(); + + dataJobsExplorePage + .openJobDetails(testJobs[0].team, testJobs[0].job_name); + + const dataJobExploreDetailsPage = DataJobExploreDetailsPage + .getPage(); + + dataJobExploreDetailsPage + .getMainTitle() + .should('be.visible') + .should('contains.text', testJobs[0].job_name); + + dataJobExploreDetailsPage + .getDetailsTab() + .should('be.visible') + .should('have.class', 'active'); + }); + + it('Data Job Explore Details Page - should verify Action buttons are not displayed', () => { + cy.log('Fixture for name: ' + testJobs[0].job_name); + + const dataJobsExplorePage = DataJobsExplorePage.navigateTo(); + + dataJobsExplorePage + .openJobDetails(testJobs[0].team, testJobs[0].job_name); + + const dataJobExploreDetailsPage = DataJobExploreDetailsPage + .getPage(); + + dataJobExploreDetailsPage + .getMainTitle() + .should('be.visible') + .should('contains.text', testJobs[0].job_name); + + dataJobExploreDetailsPage + .getExecuteNowButton() + .should('not.exist'); + + dataJobExploreDetailsPage + .getActionDropdownBtn() + .should('not.exist'); + }); +}); diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-executions.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-executions.spec.js new file mode 100644 index 0000000000..a3fdd94044 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-executions.spec.js @@ -0,0 +1,84 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// + +import { DataJobsExplorePage } from '../../../support/pages/app/lib/explore/data-jobs.po'; +import { DataJobBasePO } from '../../../support/application/data-job-base.po'; +import { applyGlobalEnvSettings } from '../../../support/helpers/commands.helpers'; + +describe('Data Job Explore Executions Page', { tags: ['@dataPipelines', '@exploreDataJobExecutions'] }, () => { + let testJobs; + + before(() => { + return DataJobBasePO.recordHarIfSupported() + .then(() => cy.clearLocalStorageSnapshot('data-job-explore-executions')) + .then(() => DataJobBasePO.login()) + .then(() => cy.saveLocalStorage('data-job-explore-executions')) + .then(() => cy.cleanTestJobs()) + .then(() => cy.prepareBaseTestJobs()) + .then(() => cy.fixture('lib/explore/test-jobs.json')) + .then((loadedTestJobs) => { + testJobs = applyGlobalEnvSettings(loadedTestJobs); + + return cy.wrap({ context: 'explore::data-job-executions.spec::before()', action: 'continue' }); + }); + }); + + after(() => { + cy.cleanTestJobs(); + + DataJobBasePO.saveHarIfSupported(); + }); + + beforeEach(() => { + cy.restoreLocalStorage('data-job-explore-executions'); + + DataJobBasePO.initBackendRequestInterceptor(); + }); + + it(`Data Job Explore Executions Page - should open Details and verify Executions tab is not displayed`, () => { + cy.log('Fixture for name: ' + testJobs[0].job_name); + + const dataJobsExplorePage = DataJobsExplorePage.navigateTo(); + + dataJobsExplorePage + .openJobDetails(testJobs[0].team, testJobs[0].job_name); + + const dataJobBasePage = DataJobBasePO + .getPage(); + + dataJobBasePage + .getMainTitle() + .should('be.visible') + .should('contains.text', testJobs[0].job_name); + + dataJobBasePage + .getDetailsTab() + .should('have.class', 'active'); + + dataJobBasePage + .getExecutionsTab() + .should('not.exist'); + }); + + it('Data Job Explore Executions Page - should verify on URL navigate to Executions will redirect to Details', () => { + const dataJobBasePage = DataJobBasePO + .navigateToUrl(`/explore/data-jobs/${ testJobs[0].team }/${ testJobs[0].job_name }/executions`); + + dataJobBasePage + .getMainTitle() + .should('be.visible') + .should('contains.text', testJobs[0].job_name); + + dataJobBasePage + .getCurrentUrl() + .should('match', new RegExp(`\\/explore\\/data-jobs\\/${testJobs[0].team}\\/${testJobs[0].job_name}\\/details$`)); + + dataJobBasePage + .getDetailsTab() + .should('have.class', 'active'); + }); +}); diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-jobs.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-jobs.spec.js new file mode 100644 index 0000000000..e079552b17 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-jobs.spec.js @@ -0,0 +1,306 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { DataJobsExplorePage } from '../../../support/pages/app/lib/explore/data-jobs.po'; +import { DataJobExploreDetailsPage } from '../../../support/pages/app/lib/explore/data-job-details.po'; +import { applyGlobalEnvSettings } from '../../../support/helpers/commands.helpers'; + +describe('Data Jobs Explore Page', { tags: ['@dataPipelines', '@exploreDataJobs'] }, () => { + let dataJobsExplorePage; + let testJobs; + + before(() => { + return DataJobsExplorePage.recordHarIfSupported() + .then(() => cy.clearLocalStorageSnapshot('data-jobs-explore')) + .then(() => DataJobsExplorePage.login()) + .then(() => cy.saveLocalStorage('data-jobs-explore')) + .then(() => cy.cleanTestJobs()) + .then(() => cy.prepareBaseTestJobs()) + .then(() => cy.fixture('lib/explore/test-jobs.json')) + .then((loadedTestJobs) => { + testJobs = applyGlobalEnvSettings(loadedTestJobs); + + return cy.wrap({ context: 'explore::data-jobs.spec::before()', action: 'continue' }); + }); + }); + + after(() => { + cy.cleanTestJobs(); + + DataJobsExplorePage.saveHarIfSupported(); + }); + + beforeEach(() => { + cy.restoreLocalStorage('data-jobs-explore'); + + DataJobsExplorePage.initBackendRequestInterceptor(); + }); + + it('Main Title Component have text: Explore Data Jobs', () => { + dataJobsExplorePage = DataJobsExplorePage.navigateTo(); + + dataJobsExplorePage + .getMainTitle() + .should('be.visible') + .should('have.text', 'Explore Data Jobs'); + }); + + it('Data Jobs Explore Page - loaded and shows data jobs', () => { + dataJobsExplorePage = DataJobsExplorePage.navigateTo(); + + dataJobsExplorePage + .getDataGrid() + .should('be.visible'); + + testJobs.forEach((testJob) => { + cy.log('Fixture for name: ' + testJob.job_name); + + dataJobsExplorePage + .getDataGridCell(testJob.job_name) + .scrollIntoView() + .should('be.visible'); + + dataJobsExplorePage + .getDataGridCell(testJob.team) + .should('be.visible'); + }) + }); + + it('Data Jobs Explore Page - filters data jobs', () => { + cy.log('Fixture for name: ' + testJobs[0].job_name); + + dataJobsExplorePage = DataJobsExplorePage.navigateTo(); + + dataJobsExplorePage + .filterByJobName(testJobs[0].job_name); + + dataJobsExplorePage + .getDataGridCell(testJobs[0].job_name) + .should('be.visible'); + + dataJobsExplorePage + .getDataGridCell(testJobs[1].job_name) + .should('not.exist'); + }); + + it('Data Jobs Explore Page - refreshes data jobs', () => { + dataJobsExplorePage = DataJobsExplorePage.navigateTo(); + + cy.fixture('lib/explore/additional-test-job.json').then((additionalTestJob) => { + const normalizedTestJob = applyGlobalEnvSettings(additionalTestJob); + + cy.log('Fixture for name: ' + normalizedTestJob.job_name); + + dataJobsExplorePage + .getDataGridCell(testJobs[0].job_name) + .should('have.text', testJobs[0].job_name); + + dataJobsExplorePage + .getDataGridCell(normalizedTestJob.job_name) + .should('not.exist'); + + cy.prepareAdditionalTestJobs(); + + dataJobsExplorePage + .refreshDataGrid(); + + dataJobsExplorePage + .filterByJobName(normalizedTestJob.job_name); + + dataJobsExplorePage + .getDataGridCell(normalizedTestJob.job_name) + .should('have.text', normalizedTestJob.job_name); + }); + }); + + it('Data Jobs Explore Page - searches data jobs', () => { + cy.log('Fixture for name: ' + testJobs[0].job_name); + + dataJobsExplorePage = DataJobsExplorePage.navigateTo(); + + dataJobsExplorePage + .getDataGridCell(testJobs[1].job_name) + .should('be.visible'); + + dataJobsExplorePage + .searchByJobName(testJobs[0].job_name); + + dataJobsExplorePage + .getDataGridCell(testJobs[0].job_name) + .should('be.visible'); + + dataJobsExplorePage + .getDataGridCell(testJobs[1].job_name) + .should('not.exist'); + }); + + it('Data Jobs Explore Page - searches data jobs, search parameter goes into URL', () => { + cy.log('Fixture for name: ' + testJobs[0].job_name); + + dataJobsExplorePage = DataJobsExplorePage.navigateTo(); + + // verify 2 test rows visible + dataJobsExplorePage + .getDataGridCell(testJobs[0].job_name) + .should('be.visible'); + dataJobsExplorePage + .getDataGridCell(testJobs[1].job_name) + .should('be.visible'); + + // do search + dataJobsExplorePage + .searchByJobName(testJobs[0].job_name); + + // verify 1 test row visible + dataJobsExplorePage + .getDataGridCell(testJobs[0].job_name) + .should('be.visible'); + dataJobsExplorePage + .getDataGridCell(testJobs[1].job_name) + .should('not.exist'); + + // verify url contains search value + dataJobsExplorePage + .getCurrentUrl() + .should('match', new RegExp(`\\/explore\\/data-jobs\\?search=${ testJobs[0].job_name }$`)); + + // clear search with clear() method + dataJobsExplorePage + .clearSearchField(); + + // verify 2 test rows visible + dataJobsExplorePage + .getDataGridCell(testJobs[0].job_name) + .should('be.visible'); + dataJobsExplorePage + .getDataGridCell(testJobs[1].job_name) + .should('be.visible'); + + // verify url does not contain search value + dataJobsExplorePage + .getCurrentUrl() + .should('match', new RegExp(`\\/explore\\/data-jobs$`)); + }); + + it('Data Jobs Explore Page - searches data jobs, perform search when URL contains search parameter', () => { + cy.log('Fixture for name: ' + testJobs[1].job_name); + + // navigate with search value in URL + dataJobsExplorePage = DataJobsExplorePage.navigateToUrl(`/explore/data-jobs?search=${ testJobs[1].job_name }`); + + // verify url contains search value + dataJobsExplorePage + .getCurrentUrl() + .should('match', new RegExp(`\\/explore\\/data-jobs\\?search=${ testJobs[1].job_name }$`)); + + // verify 1 test row visible + dataJobsExplorePage + .getDataGridCell(testJobs[0].job_name) + .should('not.exist'); + dataJobsExplorePage + .getDataGridCell(testJobs[1].job_name) + .should('be.visible'); + + // clear search with button + dataJobsExplorePage + .clearSearchFieldWithButton(); + + // verify 2 test rows visible + dataJobsExplorePage + .getDataGridCell(testJobs[0].job_name) + .should('be.visible'); + dataJobsExplorePage + .getDataGridCell(testJobs[1].job_name) + .should('be.visible'); + + // verify url does not contain search value + dataJobsExplorePage + .getCurrentUrl() + .should('match', new RegExp(`\\/explore\\/data-jobs$`)); + }); + + it('Data Jobs Explore Page - show/hide column when toggling from menu', () => { + dataJobsExplorePage = DataJobsExplorePage.navigateTo(); + + // show panel for show/hide columns + dataJobsExplorePage + .toggleColumnShowHidePanel(); + + // verify correct options are rendered + dataJobsExplorePage + .getDataGridColumnShowHideOptionsValues() + .should('have.length', 9) + .invoke('join', ',') + .should('eq', 'Description,Deployment Status,Last Execution Duration,Success rate,Next run (UTC),Last Deployed (UTC),Last Deployed By,Source,Logs'); + + // verify column is not checked in toggling menu + dataJobsExplorePage + .getDataGridColumnShowHideOption('Last Execution Duration') + .should('exist') + .should('not.be.checked'); + + // verify header cell for column is not rendered + dataJobsExplorePage + .getDataGridHeaderCell('Last Execution Duration') + .should('have.length', 0); + + // toggle column to render + dataJobsExplorePage + .checkColumnShowHideOption('Last Execution Duration'); + + // verify column is checked in toggling menu + dataJobsExplorePage + .getDataGridColumnShowHideOption('Last Execution Duration') + .should('exist') + .should('be.checked'); + + // verify header cell for column is rendered + dataJobsExplorePage + .getDataGridHeaderCell('Last Execution Duration') + .should('have.length', 1); + + // toggle column to hide + dataJobsExplorePage + .uncheckColumnShowHideOption('Last Execution Duration'); + + // verify column is not checked in toggling menu + dataJobsExplorePage + .getDataGridColumnShowHideOption('Last Execution Duration') + .should('exist') + .should('not.be.checked'); + + // verify header cell for column is not rendered + dataJobsExplorePage + .getDataGridHeaderCell('Last Execution Duration') + .should('have.length', 0); + + // hide panel for show/hide columns + dataJobsExplorePage + .toggleColumnShowHidePanel(); + }); + + it('Data Jobs Explore Page - navigates to data job', () => { + cy.log('Fixture for name: ' + testJobs[0].job_name); + + dataJobsExplorePage = DataJobsExplorePage.navigateTo(); + + dataJobsExplorePage + .openJobDetails(testJobs[0].team, testJobs[0].job_name); + + const dataJobExploreDetailsPage = DataJobExploreDetailsPage + .getPage(); + + dataJobExploreDetailsPage + .getMainTitle() + .should('be.visible') + .should('contains.text', testJobs[0].job_name); + + dataJobExploreDetailsPage + .getDescriptionField() + .should('be.visible') + .should('contain.text', testJobs[0].description); + }); +}); diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-details.int.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-details.int.spec.js new file mode 100644 index 0000000000..48a4d339c0 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-details.int.spec.js @@ -0,0 +1,220 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// + +import { DataJobManageDetailsPage } from '../../../support/pages/app/lib/manage/data-job-details.po'; +import { DataJobsManagePage } from '../../../support/pages/app/lib/manage/data-jobs.po'; +import { applyGlobalEnvSettings } from '../../../support/helpers/commands.helpers'; + +describe('Data Job Manage Details Page', { tags: ['@dataPipelines', '@manageDataJobDetails'] }, () => { + const descriptionWordsBeforeTruncate = 12; + + let dataJobManageDetailsPage; + let testJobs; + let longLivedTestJob; + + before(() => { + return DataJobManageDetailsPage.recordHarIfSupported() + .then(() => cy.clearLocalStorageSnapshot('data-job-manage-details')) + .then(() => DataJobManageDetailsPage.login()) + .then(() => cy.saveLocalStorage('data-job-manage-details')) + .then(() => cy.cleanTestJobs()) + .then(() => cy.prepareLongLivedTestJob()) + .then(() => cy.createTwoExecutionsLongLivedTestJob()) + .then(() => cy.prepareBaseTestJobs()) + .then(() => cy.fixture('lib/explore/test-jobs.json')) + .then((loadedTestJobs) => { + testJobs = applyGlobalEnvSettings(loadedTestJobs); + + return cy.wrap({ context: 'manage::data-job-details.spec::1::before()', action: 'continue' }); + }) + .then(() => cy.fixture('lib/manage/e2e-cypress-dp-test.json')) + .then((loadedTestJob) => { + longLivedTestJob = applyGlobalEnvSettings(loadedTestJob); + + return cy.wrap({ context: 'manage::data-job-details.spec::2::before()', action: 'continue' }); + }); + }); + + after(() => { + cy.cleanTestJobs(); + + DataJobManageDetailsPage.saveHarIfSupported(); + }); + + beforeEach(() => { + cy.restoreLocalStorage('data-job-manage-details'); + + DataJobManageDetailsPage.initBackendRequestInterceptor(); + }); + + it('Data Job Manage Details Page - disable/enable job', { tags: '@integration' }, () => { + dataJobManageDetailsPage = DataJobManageDetailsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobManageDetailsPage.clickOnContentContainer(); + + //Toggle job status twice, enable to disable and vice versa. + dataJobManageDetailsPage.toggleJobStatus(longLivedTestJob.job_name); + dataJobManageDetailsPage.toggleJobStatus(longLivedTestJob.job_name); + }); + + it('Data Job Manage Details Page - edit job description', { tags: '@integration' }, () => { + let newDescription = 'Test if changing the description is working'; + + dataJobManageDetailsPage = DataJobManageDetailsPage + .navigateTo(testJobs[0].team, testJobs[0].job_name); + + dataJobManageDetailsPage.clickOnContentContainer(); + + dataJobManageDetailsPage + .openDescription(); + + dataJobManageDetailsPage + .enterDescriptionDetails(newDescription); + + dataJobManageDetailsPage + .saveDescription(); + + dataJobManageDetailsPage + .getDescription() + .should('be.visible') + .should('contain.text', newDescription.split(' ').slice(0, descriptionWordsBeforeTruncate).join(' ')); + }); + + it('Data Job Manage Details Page - execute now', () => { + dataJobManageDetailsPage = DataJobManageDetailsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobManageDetailsPage + .waitForTestJobExecutionCompletion(); + + dataJobManageDetailsPage + .executeNow(); + + dataJobManageDetailsPage + .confirmInConfirmDialog(); + + dataJobManageDetailsPage + .getToastTitle(10000) + .should('exist') + .contains(/Data job Queued for execution|Failed, Data job is already executing/); + }); + + it('Data Job Manage Details Page - download job key', () => { + dataJobManageDetailsPage = DataJobManageDetailsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobManageDetailsPage + .openActionDropdown(); + + dataJobManageDetailsPage + .downloadJobKey(); + + dataJobManageDetailsPage + .readFile('downloadsFolder', `${ longLivedTestJob.job_name }.keytab`) + .should('exist'); + }); + + it('Data Job Manage Details Page - delete job', { tags: '@integration' }, () => { + cy.fixture('lib/explore/additional-test-job.json').then((additionalTestJob) => { + const normalizedTestJob = applyGlobalEnvSettings(additionalTestJob); + + cy.prepareAdditionalTestJobs(); + + dataJobManageDetailsPage = DataJobManageDetailsPage + .navigateTo(normalizedTestJob.team, normalizedTestJob.job_name); + + dataJobManageDetailsPage.clickOnContentContainer(); + + dataJobManageDetailsPage + .openActionDropdown(); + + dataJobManageDetailsPage + .deleteJob(); + + dataJobManageDetailsPage + .confirmDeleteJob(); + + dataJobManageDetailsPage + .getToastTitle(20000) // Wait up to 20 seconds for the job to be deleted. + .should('contain.text', 'Data job delete completed') + + dataJobManageDetailsPage + .waitForBackendRequestCompletion(); + + const dataJobsManagePage = DataJobsManagePage + .getPage(); + + dataJobsManagePage + .getDataGridCell(normalizedTestJob.job_name, 10000) // Wait up to 10 seconds for the jobs list to show. + .should('not.exist'); + }); + }); + + //TODO: Double-check and enable this test + it.skip('Data Job Manage Details Page - executions timeline', () => { + const jobName = longLivedTestJob.job_name; + const team = longLivedTestJob.team; + + cy.intercept({ + method: 'GET', + url: `/data-jobs/for-team/${ team }/jobs/${ jobName }/executions` + }).as('executionApiCall') + + dataJobManageDetailsPage = DataJobManageDetailsPage + .navigateTo(team, jobName); + + cy.wait('@executionApiCall').then((interception) => { + const response = interception.response.body; + const lastExecutions = response + .sort((left, right) => compareDatesAsc(left, right)) + .slice(response.length > 5 ? response.length - 5 : 0); + + const lastExecutionsSize = lastExecutions.length; + const lastExecution = lastExecutions.at(-1); + + cy.get('.clr-timeline-step') + .should('have.length', lastExecutionsSize + 1) // +1 next execution + + cy.get(`[data-cy=${ lastExecution.id }]`) + .as('lastExecution') + + let statusIconMap = []; + statusIconMap['cancelled'] = 'times-circle'; + statusIconMap['skipped'] = 'circle-arrow'; + statusIconMap['submitted'] = 'circle'; + statusIconMap['finished'] = 'success-standard'; + statusIconMap['failed'] = 'error-standard'; + + if (lastExecution.type !== 'manual') { + cy.get('@lastExecution') + .find('.manual-execution-label') + .scrollIntoView() + .should('be.visible'); + } + + if (lastExecution.status !== 'running') { + cy.get('@lastExecution') + .find(`[shape=${ statusIconMap[lastExecution.status] }]`) + .should('exist') + } + + cy.get('@lastExecution') + .find('.clr-timeline-step-header') + .invoke('attr', 'title') + .should('contain', 'Started ') + + if (lastExecution.status !== 'running') { + cy.get('@lastExecution') + .find('u') + .last() + .invoke('attr', 'title') + .should('contain', 'Ended ') + } + }) + }) +}); diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-executions.int.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-executions.int.spec.js new file mode 100644 index 0000000000..cc1ed08375 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-executions.int.spec.js @@ -0,0 +1,644 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// + +import { DataJobBasePO } from '../../../support/application/data-job-base.po'; +import { DataJobsManagePage } from '../../../support/pages/app/lib/manage/data-jobs.po'; +import { DataJobManageExecutionsPage } from '../../../support/pages/app/lib/manage/data-job-executions.po'; +import { applyGlobalEnvSettings } from '../../../support/helpers/commands.helpers'; + +describe('Data Job Manage Executions Page', { tags: ['@dataPipelines', '@manageDataJobExecutions'] }, () => { + let longLivedTestJob; + + before(() => { + return DataJobManageExecutionsPage.recordHarIfSupported() + .then(() => cy.clearLocalStorageSnapshot('data-job-manage-executions')) + .then(() => DataJobManageExecutionsPage.login()) + .then(() => cy.saveLocalStorage('data-job-manage-executions')) + .then(() => cy.prepareLongLivedTestJob()) + .then(() => cy.createTwoExecutionsLongLivedTestJob()) + .then(() => cy.fixture('lib/manage/e2e-cypress-dp-test.json')) + .then((loadedTestJob) => { + longLivedTestJob = applyGlobalEnvSettings(loadedTestJob); + + return cy.wrap({ context: 'manage::data-job-executions.spec::before()', action: 'continue' }); + }); + }); + + after(() => { + DataJobManageExecutionsPage.saveHarIfSupported(); + }); + + beforeEach(() => { + cy.restoreLocalStorage('data-job-manage-executions'); + + DataJobManageExecutionsPage.initBackendRequestInterceptor(); + }); + + describe('Sanity', { tags: '@integration' }, () => { + it(`Data Job Manage Executions Page - should open Details and verify Executions tab is displayed and navigates`, () => { + cy.log('Fixture for name: ' + longLivedTestJob.job_name); + + const dataJobsManagePage = DataJobsManagePage.navigateTo(); + + dataJobsManagePage + .clickOnContentContainer(); + + dataJobsManagePage + .chooseQuickFilter(0); + + dataJobsManagePage + .openJobDetails(longLivedTestJob.team, longLivedTestJob.job_name); + + const dataJobBasePage = DataJobBasePO + .getPage(); + + dataJobBasePage + .getDetailsTab() + .should('exist') + .should('have.class', 'active'); + + dataJobBasePage + .getExecutionsTab() + .should('exist') + .should('not.have.class', 'active'); + + dataJobBasePage + .openExecutionsTab(); + + const dataJobExecutionsPage = DataJobManageExecutionsPage + .getPage(); + + dataJobExecutionsPage + .getDetailsTab() + .should('exist') + .should('not.have.class', 'active'); + + dataJobExecutionsPage + .getExecutionsTab() + .should('exist') + .should('have.class', 'active'); + + dataJobExecutionsPage + .getDataGrid() + .should('exist'); + }); + + it('Data Job Manage Executions Page - should verify on URL navigate to Executions will open the page', () => { + const dataJobExecutionsPage = DataJobManageExecutionsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobExecutionsPage + .getCurrentUrl() + .should('match', new RegExp(`\\/manage\\/data-jobs\\/${longLivedTestJob.team}\\/${longLivedTestJob.job_name}\\/executions$`)); + + dataJobExecutionsPage + .getDetailsTab() + .should('exist') + .should('not.have.class', 'active'); + + dataJobExecutionsPage + .getExecutionsTab() + .should('exist') + .should('have.class', 'active'); + + dataJobExecutionsPage + .getDataGrid() + .should('exist'); + }); + + it('Data Job Manage Executions Page - should verify elements are rendered in DOM', () => { + const dataJobExecutionsPage = DataJobManageExecutionsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobExecutionsPage + .getMainTitle() + .should('be.visible') + .should('contains.text', longLivedTestJob.job_name); + + dataJobExecutionsPage + .getDetailsTab() + .should('exist') + .should('not.have.class', 'active'); + + dataJobExecutionsPage + .getExecutionsTab() + .should('exist') + .should('have.class', 'active'); + + dataJobExecutionsPage + .getExecuteNowButton() + .should('exist'); + + dataJobExecutionsPage + .getActionDropdownBtn() + .should('exist'); + + dataJobExecutionsPage + .openActionDropdown(); + + dataJobExecutionsPage + .getDeleteJobBtn() + .should('exist'); + + dataJobExecutionsPage + .clickOnContentContainer(); + + dataJobExecutionsPage + .waitForSmartDelay(); + + dataJobExecutionsPage + .getTimePeriod() + .should('exist'); + + dataJobExecutionsPage + .getStatusChart() + .should('exist'); + + dataJobExecutionsPage + .getDurationChart() + .should('exist'); + + dataJobExecutionsPage + .getDataGrid() + .should('exist'); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + }); + + it('Data Job Manage Executions Page - should verify cancel execution button works properly', () => { + const dataJobExecutionsPage = DataJobManageExecutionsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobExecutionsPage + .initPostExecutionInterceptor(); + dataJobExecutionsPage + .initDeleteExecutionInterceptor(); + dataJobExecutionsPage + .initGetExecutionInterceptor(); + dataJobExecutionsPage + .initGetExecutionsInterceptor(); + + dataJobExecutionsPage + .getExecutionsTab() + .should('exist') + .should('have.class', 'active'); + + // Execute data job and check if the execution status after that is Running or Submitted + dataJobExecutionsPage + .getExecuteNowButton() + .should('exist'); + dataJobExecutionsPage + .executeNow(); + dataJobExecutionsPage + .getConfirmDialogButton() + .should('exist') + .click({ force: true }); + dataJobExecutionsPage + .waitForPostExecutionCompletion(); + dataJobExecutionsPage + .waitForDataJobStartExecute(); + + dataJobExecutionsPage + .waitForViewToRenderShort(); + + dataJobExecutionsPage + .getExecutionStatus() + .first() + .contains(/Running|Submitted/); + + // Cancel data job execution and check if the status after that is Cancelled + dataJobExecutionsPage + .getCancelExecutionButton() + .should('exist') + .click({ force: true }) + dataJobExecutionsPage + .getConfirmDialogButton() + .should('exist') + .click({ force: true }); + dataJobExecutionsPage + .waitForDeleteExecutionCompletion(); + dataJobExecutionsPage + .waitForDataJobStopExecute(); + + dataJobExecutionsPage + .waitForViewToRenderShort(); + + dataJobExecutionsPage + .getExecutionStatus() + .first() + .should('contains.text', 'Canceled'); + }); + }); + + it('Data Job Manage Executions Page - should verify time period is in correct format', () => { + const dataJobExecutionsPage = DataJobManageExecutionsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobExecutionsPage + .getTimePeriod() + .invoke('text') + .invoke('trim') + .should('match', new RegExp(`^\\w+\\s\\d+,\\s\\d+,\\s\\d+:\\d+:\\d+\\s(AM|PM)\\sto\\s\\w+\\s\\d+,\\s\\d+,\\s\\d+:\\d+:\\d+\\s(AM|PM)$`)); + }); + + it('Data Job Manage Executions Page - should verify refresh button will show spinner and then load data', () => { + const dataJobExecutionsPage = DataJobManageExecutionsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobExecutionsPage + .getDataGrid() + .should('exist'); + + dataJobExecutionsPage + .getExecLoadingSpinner() + .should('not.exist'); + + dataJobExecutionsPage + .getDataGridSpinner() + .should('not.exist'); + + dataJobExecutionsPage + .waitForActionThinkingTime(); + + dataJobExecutionsPage + .refreshExecData(); + + dataJobExecutionsPage + .getDataGrid() + .should('exist'); + + dataJobExecutionsPage + .getExecLoadingSpinner() + .should('exist'); + + dataJobExecutionsPage + .getDataGridSpinner() + .should('exist'); + + dataJobExecutionsPage + .waitForBackendRequestCompletion(); + + dataJobExecutionsPage + .waitForViewToRender(); + + dataJobExecutionsPage + .getDataGrid() + .should('exist'); + + dataJobExecutionsPage + .getExecLoadingSpinner() + .should('not.exist'); + + dataJobExecutionsPage + .getDataGridSpinner() + .should('not.exist'); + }); + + describe('DataGrid Filters', () => { + it('Data Job Manage Executions Page - should verify status filter options are rendered', () => { + const dataJobExecutionsPage = DataJobManageExecutionsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobExecutionsPage + .openStatusFilter(); + + dataJobExecutionsPage + .getDataGridPopupFilter() + .should('exist'); + + dataJobExecutionsPage + .getDataGridExecStatusFilters() + .then((elements) => Array.from(elements).map((el) => el.innerText)) + .should('deep.equal', ['Success', 'Platform Error', 'User Error', 'Running', 'Submitted', 'Skipped', 'Canceled']); + + dataJobExecutionsPage + .closeFilter(); + + dataJobExecutionsPage + .getDataGridPopupFilter() + .should('not.exist'); + }); + + it('Data Job Manage Executions Page - should verify type filter options are rendered', () => { + const dataJobExecutionsPage = DataJobManageExecutionsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobExecutionsPage + .openTypeFilter(); + + dataJobExecutionsPage + .getDataGridPopupFilter() + .should('exist'); + + dataJobExecutionsPage + .getDataGridExecTypeFilters() + .then((elements) => Array.from(elements).map((el) => el.innerText)) + .should('deep.equal', ['Manual', 'Scheduled']); + + dataJobExecutionsPage + .closeFilter(); + + dataJobExecutionsPage + .getDataGridPopupFilter() + .should('not.exist'); + }); + + it('Data Job Manage Executions Page - should verify id filter render input and filters correctly', () => { + const dataJobExecutionsPage = DataJobManageExecutionsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + + dataJobExecutionsPage + .openIDFilter(); + + dataJobExecutionsPage + .getDataGridPopupFilter() + .should('exist'); + + dataJobExecutionsPage + .typeToFilterInput('xyxyxy'); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length', 0); + + dataJobExecutionsPage + .clearFilterInput(); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + + dataJobExecutionsPage + .closeFilter(); + + dataJobExecutionsPage + .getDataGridPopupFilter() + .should('not.exist'); + }); + + it('Data Job Manage Executions Page - should verify version filter render input and filters correctly', () => { + const dataJobExecutionsPage = DataJobManageExecutionsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + + dataJobExecutionsPage + .openVersionFilter(); + + dataJobExecutionsPage + .getDataGridPopupFilter() + .should('exist'); + + dataJobExecutionsPage + .typeToFilterInput('xyxyxy'); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length', 0); + + dataJobExecutionsPage + .clearFilterInput(); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + + dataJobExecutionsPage + .closeFilter(); + + dataJobExecutionsPage + .getDataGridPopupFilter() + .should('not.exist'); + }); + }); + + describe('DataGrid Sort', () => { + it('Data Job Manage Executions Page - should verify duration sort works', () => { + const dataJobExecutionsPage = DataJobManageExecutionsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + + dataJobExecutionsPage + .getDataGridExecDurationHeader() + .should('exist') + .invoke('attr', 'aria-sort') + .should('eq', 'none'); + + dataJobExecutionsPage + .sortByExecDuration(); + + dataJobExecutionsPage + .getDataGridExecDurationHeader() + .should('exist') + .invoke('attr', 'aria-sort') + .should('eq', 'ascending'); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + + dataJobExecutionsPage + .getDataGridExecDurationCell(1) + .invoke('text') + .invoke('trim') + .then((elText1) => { + return dataJobExecutionsPage + .getDataGridExecDurationCell(2) + .invoke('text') + .invoke('trim') + .then((elText2) => { + return dataJobExecutionsPage.convertStringContentToSeconds(elText1) - + dataJobExecutionsPage.convertStringContentToSeconds(elText2); + }); + }) + .should('be.lte', 0); + + dataJobExecutionsPage + .sortByExecDuration(); + + dataJobExecutionsPage + .getDataGridExecDurationHeader() + .should('exist') + .invoke('attr', 'aria-sort') + .should('eq', 'descending'); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + + dataJobExecutionsPage + .getDataGridExecDurationCell(1) + .invoke('text') + .invoke('trim') + .then((elText1) => { + return dataJobExecutionsPage + .getDataGridExecDurationCell(2) + .invoke('text') + .invoke('trim') + .then((elText2) => { + return dataJobExecutionsPage.convertStringContentToSeconds(elText1) - + dataJobExecutionsPage.convertStringContentToSeconds(elText2); + }); + }) + .should('be.gte', 0); + }); + + it('Data Job Manage Executions Page - should verify execution start sort works', () => { + const dataJobExecutionsPage = DataJobManageExecutionsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + + dataJobExecutionsPage + .getDataGridExecStartHeader() + .should('exist') + .invoke('attr', 'aria-sort') + .should('eq', 'descending'); + + dataJobExecutionsPage + .getDataGridExecStartCell(1) + .invoke('text') + .invoke('trim') + .then((elText1) => { + return dataJobExecutionsPage + .getDataGridExecStartCell(2) + .invoke('text') + .invoke('trim') + .then((elText2) => { + return new Date(elText1) - new Date(elText2) + }); + }) + .should('be.gte', 0); + + dataJobExecutionsPage + .sortByExecStart(); + + dataJobExecutionsPage + .getDataGridExecStartHeader() + .should('exist') + .invoke('attr', 'aria-sort') + .should('eq', 'ascending'); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + + dataJobExecutionsPage + .getDataGridExecStartCell(1) + .invoke('text') + .invoke('trim') + .then((elText1) => { + return dataJobExecutionsPage + .getDataGridExecStartCell(2) + .invoke('text') + .invoke('trim') + .then((elText2) => { + return new Date(elText1) - new Date(elText2) + }); + }) + .should('be.lte', 0); + }); + + it('Data Job Manage Executions Page - should verify execution end sort works', () => { + const dataJobExecutionsPage = DataJobManageExecutionsPage + .navigateTo(longLivedTestJob.team, longLivedTestJob.job_name); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + + dataJobExecutionsPage + .getDataGridExecEndHeader() + .should('exist') + .invoke('attr', 'aria-sort') + .should('eq', 'none'); + + dataJobExecutionsPage + .sortByExecEnd(); + + dataJobExecutionsPage + .getDataGridExecEndHeader() + .should('exist') + .invoke('attr', 'aria-sort') + .should('eq', 'ascending'); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + + dataJobExecutionsPage + .getDataGridExecEndCell(1) + .invoke('text') + .invoke('trim') + .then((elText1) => { + return dataJobExecutionsPage + .getDataGridExecEndCell(2) + .invoke('text') + .invoke('trim') + .then((elText2) => { + const d1 = elText1 + ? new Date(elText1) + : new Date(); + const d2 = elText2 + ? new Date(elText2) + : new Date(); + + return d1 - d2 + }); + }) + .should('be.lte', 0); + + dataJobExecutionsPage + .sortByExecEnd(); + + dataJobExecutionsPage + .getDataGridExecEndHeader() + .should('exist') + .invoke('attr', 'aria-sort') + .should('eq', 'descending'); + + dataJobExecutionsPage + .getDataGridRows() + .should('have.length.gt', 0); + + dataJobExecutionsPage + .getDataGridExecEndCell(1) + .invoke('text') + .invoke('trim') + .then((elText1) => { + return dataJobExecutionsPage + .getDataGridExecEndCell(2) + .invoke('text') + .invoke('trim') + .then((elText2) => { + const d1 = elText1 + ? new Date(elText1) + : new Date(); + const d2 = elText2 + ? new Date(elText2) + : new Date(); + + return d1 - d2 + }); + }) + .should('be.gte', 0); + }); + }); +}); diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-jobs.int.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-jobs.int.spec.js new file mode 100644 index 0000000000..4f43de737a --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-jobs.int.spec.js @@ -0,0 +1,494 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +// Contain tests on this page: http://localhost.vmware.com:4200/manage/data-jobs +// 1. Is search working +// When you click on details does a modal pop-up? + +import { DataJobsManagePage } from '../../../support/pages/app/lib/manage/data-jobs.po'; +import { DataJobManageDetailsPage } from '../../../support/pages/app/lib/manage/data-job-details.po'; +import { applyGlobalEnvSettings } from '../../../support/helpers/commands.helpers'; + +describe('Data Jobs Manage Page', { tags: ['@dataPipelines', '@manageDataJobs'] }, () => { + const descriptionWordsBeforeTruncate = 12; + + /** + * @type {DataJobsManagePage} + */ + let dataJobsManagePage; + let testJobs; + let longLivedTestJob; + let longLivedFailingTestJob; + + before(() => { + return DataJobsManagePage.recordHarIfSupported() + .then(() => cy.clearLocalStorageSnapshot('data-jobs-manage')) + .then(() => DataJobsManagePage.login()) + .then(() => cy.saveLocalStorage('data-jobs-manage')) + .then(() => cy.cleanTestJobs()) + .then(() => cy.prepareBaseTestJobs()) + .then(() => cy.prepareLongLivedTestJob()) + .then(() => cy.createTwoExecutionsLongLivedTestJob()) + .then(() => cy.prepareLongLivedFailingTestJob()) + .then(() => + cy.fixture('lib/explore/test-jobs.json') + .then((loadedTestJobs) => { + testJobs = applyGlobalEnvSettings(loadedTestJobs); + + return cy.wrap({ context: 'manage::1::data-jobs.int.spec::before()', action: 'continue' }); + }) + ) + .then(() => + cy.fixture('lib/manage/e2e-cypress-dp-test.json') + .then((loadedTestJob) => { + longLivedTestJob = applyGlobalEnvSettings(loadedTestJob); + + return cy.wrap({ context: 'manage::2::data-jobs.int.spec::before()', action: 'continue' }); + }) + ) + .then(() => + cy.fixture('e2e-cy-dp-failing.job.json') + .then((failingTestJob) => { + longLivedFailingTestJob = applyGlobalEnvSettings(failingTestJob); + + return cy.wrap({ context: 'manage::3::data-jobs.int.spec::before()', action: 'continue' }); + }) + ) + .then(() => + // Enable data job after job end + DataJobsManagePage.changeJobStatus(longLivedTestJob.team, longLivedTestJob.job_name, true) + ); + }); + + beforeEach(() => { + cy.restoreLocalStorage('data-jobs-manage'); + + DataJobsManagePage.initBackendRequestInterceptor(); + + dataJobsManagePage = DataJobsManagePage.navigateTo(); + }); + + after(() => { + cy.cleanTestJobs(); + + // Enable data job after job end + DataJobsManagePage.changeJobStatus(longLivedTestJob.team, longLivedTestJob.job_name, true) + + DataJobsManagePage.saveHarIfSupported(); + }); + + it('Data Jobs Manage Page - loads title', () => { + dataJobsManagePage + .getPageTitle() + .should('be.visible'); + }); + + it('Data Jobs Manage Page - grid contains test jobs', () => { + dataJobsManagePage + .chooseQuickFilter(0); + + dataJobsManagePage + .sortByJobName(); + + dataJobsManagePage + .getDataGrid() + .should('be.visible'); + + testJobs.forEach((testJob) => { + cy.log('Fixture for name: ' + testJob.job_name); + + dataJobsManagePage + .getDataGridCell(testJob.job_name) + .scrollIntoView() + .should('be.visible'); + }) + }); + + it('Data Jobs Manage Page - grid filter by job name', { tags: '@integration' }, () => { + dataJobsManagePage + .chooseQuickFilter(0); + + dataJobsManagePage + .filterByJobName(testJobs[0].job_name); + + dataJobsManagePage + .getDataGridCell(testJobs[0].job_name) + .should('be.visible'); + + dataJobsManagePage + .getDataGridCell(testJobs[1].job_name) + .should('not.exist'); + }); + + it('Data Jobs Manage Page - grid search by job name', { tags: '@integration' }, () => { + dataJobsManagePage + .clickOnContentContainer(); + + dataJobsManagePage + .chooseQuickFilter(0); + + dataJobsManagePage + .searchByJobName(testJobs[1].job_name); + + dataJobsManagePage + .getDataGridCell(testJobs[0].job_name) + .should('not.exist'); + + dataJobsManagePage + .getDataGridCell(testJobs[1].job_name) + .should('be.visible'); + }); + + it('Data Jobs Manage Page - grid search parameter goes into URL', () => { + dataJobsManagePage + .chooseQuickFilter(0); + + dataJobsManagePage + .sortByJobName(); + + // verify 2 test rows visible + dataJobsManagePage + .getDataGridCell(testJobs[0].job_name) + .scrollIntoView() + .should('be.visible'); + dataJobsManagePage + .getDataGridCell(testJobs[1].job_name) + .scrollIntoView() + .should('be.visible'); + + // do search + dataJobsManagePage + .searchByJobName(testJobs[0].job_name); + + dataJobsManagePage + .waitForViewToRender(); + + // verify 1 test row visible + dataJobsManagePage + .getDataGridCell(testJobs[0].job_name) + .scrollIntoView() + .should('be.visible'); + dataJobsManagePage + .getDataGridCell(testJobs[1].job_name) + .should('not.exist'); + + // verify url contains search value + dataJobsManagePage + .getCurrentUrl() + .should('match', new RegExp(`\\/manage\\/data-jobs\\?search=${ testJobs[0].job_name }$`)); + + // clear search with clear() method + dataJobsManagePage + .clearSearchField(); + + dataJobsManagePage + .waitForViewToRender(); + + // verify 2 test rows visible + dataJobsManagePage + .getDataGridCell(testJobs[0].job_name) + .scrollIntoView() + .should('be.visible'); + dataJobsManagePage + .getDataGridCell(testJobs[1].job_name) + .scrollIntoView() + .should('be.visible'); + + // verify url does not contain search value + dataJobsManagePage + .getCurrentUrl() + .should('match', new RegExp(`\\/manage\\/data-jobs$`)); + }); + + it('Data Jobs Manage Page - grid search perform search when URL contains search parameter', () => { + // navigate with search value in URL + dataJobsManagePage = DataJobsManagePage.navigateToUrl(`/manage/data-jobs?search=${ testJobs[1].job_name }`); + + dataJobsManagePage + .chooseQuickFilter(0); + + dataJobsManagePage + .sortByJobName(); + + // verify url contains search value + dataJobsManagePage + .getCurrentUrl() + .should('match', new RegExp(`\\/manage\\/data-jobs\\?search=${ testJobs[1].job_name }$`)); + + // verify 1 test row visible + dataJobsManagePage + .getDataGridCell(testJobs[0].job_name) + .should('not.exist'); + dataJobsManagePage + .getDataGridCell(testJobs[1].job_name) + .scrollIntoView() + .should('be.visible'); + + // clear search with button + dataJobsManagePage + .clearSearchFieldWithButton(); + + // verify 2 test rows visible + dataJobsManagePage + .getDataGridCell(testJobs[0].job_name) + .scrollIntoView() + .should('be.visible'); + dataJobsManagePage + .getDataGridCell(testJobs[1].job_name) + .scrollIntoView() + .should('be.visible'); + + // verify url does not contain search value + dataJobsManagePage + .getCurrentUrl() + .should('match', new RegExp(`\\/manage\\/data-jobs$`)); + }); + + it('Data Jobs Manage Page - refresh shows newly created job', () => { + cy.fixture('lib/explore/additional-test-job.json').then((additionalTestJob) => { + const normalizedTestJob = applyGlobalEnvSettings(additionalTestJob); + + cy.log('Fixture for name: ' + normalizedTestJob.job_name); + + dataJobsManagePage + .chooseQuickFilter(0); + + dataJobsManagePage + .sortByJobName(); + + dataJobsManagePage + .getDataGridCell(testJobs[0].job_name) + .should('have.text', testJobs[0].job_name); + + dataJobsManagePage + .getDataGridCell(normalizedTestJob.job_name) + .should('not.exist'); + + dataJobsManagePage + .prepareAdditionalTestJob(); + + dataJobsManagePage + .refreshDataGrid(); + + dataJobsManagePage + .filterByJobName(normalizedTestJob.job_name); + + dataJobsManagePage + .getDataGridCell(normalizedTestJob.job_name) + .should('have.text', normalizedTestJob.job_name); + }); + }); + + it('Data Jobs Manage Page - click on edit button opens new page with Job details', { tags: '@integration' }, () => { + dataJobsManagePage + .clickOnContentContainer(); + + dataJobsManagePage + .chooseQuickFilter(0); + + dataJobsManagePage + .sortByJobName(); + + dataJobsManagePage + .openJobDetails(testJobs[0].team, testJobs[0].job_name); + + const dataJobManageDetailsPage = DataJobManageDetailsPage + .getPage(); + + dataJobManageDetailsPage + .getMainTitle() + .should('be.visible') + .should('contains.text', testJobs[0].job_name); + + dataJobManageDetailsPage + .getDescription() + .should('be.visible') + .should('contain.text', testJobs[0].description.split(' ').slice(0, descriptionWordsBeforeTruncate).join(' ')) + + dataJobManageDetailsPage + .getSchedule() + .should('be.visible'); + + dataJobManageDetailsPage + .getDeploymentStatus('not-deployed') + .should('be.visible') + .should('have.text', 'Not Deployed'); + }); + + it('Data Jobs Manage Page - disable/enable job', { tags: '@integration' }, () => { + dataJobsManagePage + .clickOnContentContainer(); + + dataJobsManagePage + .chooseQuickFilter(0); + + const jobName = longLivedTestJob.job_name; + const team = longLivedTestJob.team; + + dataJobsManagePage + .searchByJobName(jobName); + + //Toggle job status twice, enable to disable and vice versa. + dataJobsManagePage.toggleJobStatus(longLivedTestJob.job_name); + dataJobsManagePage.toggleJobStatus(longLivedTestJob.job_name); + }); + + it('Data Jobs Manage Page - quick filters', { tags: '@integration' }, () => { + // Disable data job before test start + DataJobsManagePage + .changeJobStatus(longLivedFailingTestJob.team, longLivedFailingTestJob.job_name, true) + .then(() => + DataJobsManagePage.changeJobStatus(longLivedTestJob.team, longLivedTestJob.job_name, false) + ) + .then(() => { + dataJobsManagePage + .clickOnContentContainer(); + + dataJobsManagePage + .waitForClickThinkingTime(); + dataJobsManagePage + .chooseQuickFilter(0); + dataJobsManagePage + .waitForViewToRender(); + + dataJobsManagePage + .getDataGridStatusIcons() + .then(($icons) => { + for (const icon of Array.from($icons)) { + cy.wrap(icon).invoke('attr', 'data-cy') + .should('match', new RegExp('data-pipelines-job-(enabled|disabled|not-deployed)')); + } + }); + + dataJobsManagePage + .waitForClickThinkingTime(); + dataJobsManagePage + .chooseQuickFilter(1); + dataJobsManagePage + .waitForViewToRender(); + + dataJobsManagePage + .getDataGridStatusIcons() + .then(($icons) => { + for (const icon of Array.from($icons)) { + cy.wrap(icon).should('have.attr', 'data-cy', 'data-pipelines-job-enabled'); + } + }); + + dataJobsManagePage + .waitForClickThinkingTime(); + dataJobsManagePage + .chooseQuickFilter(2); + dataJobsManagePage + .waitForViewToRender(); + + dataJobsManagePage + .getDataGridStatusIcons() + .then(($icons) => { + for (const icon of Array.from($icons)) { + cy.wrap(icon).should('have.attr', 'data-cy', 'data-pipelines-job-disabled'); + } + }); + + dataJobsManagePage + .waitForClickThinkingTime(); + dataJobsManagePage + .chooseQuickFilter(3); + dataJobsManagePage + .waitForViewToRender(); + + dataJobsManagePage + .getDataGridStatusIcons() + .then(($icons) => { + for (const icon of Array.from($icons)) { + cy.wrap(icon).should('have.attr', 'data-cy', 'data-pipelines-job-not-deployed'); + } + }); + + // Enable data job after job end + DataJobsManagePage.changeJobStatus(longLivedTestJob.team, longLivedTestJob.job_name, true); + }); + }); + + it('Data Jobs Manage Page - show/hide column when toggling from menu', () => { + // show panel for show/hide columns + dataJobsManagePage + .toggleColumnShowHidePanel(); + + // verify correct options are rendered + dataJobsManagePage + .getDataGridColumnShowHideOptionsValues() + .should('have.length', 10) + .invoke('join', ',') + .should('eq', 'Description,Deployment Status,Last Execution Duration,Success rate,Next run (UTC),Last Deployed (UTC),Last Deployed By,Notifications,Source,Logs'); + + // verify column is not checked in toggling menu + dataJobsManagePage + .getDataGridColumnShowHideOption('Notifications') + .should('exist') + .should('not.be.checked'); + + // verify header cell for column is not rendered + dataJobsManagePage + .getDataGridHeaderCell('Notifications') + .should('have.length', 0); + + // toggle column to render + dataJobsManagePage + .checkColumnShowHideOption('Notifications'); + + // verify column is checked in toggling menu + dataJobsManagePage + .getDataGridColumnShowHideOption('Notifications') + .should('exist') + .should('be.checked'); + + // verify header cell for column is rendered + dataJobsManagePage + .getDataGridHeaderCell('Notifications') + .should('have.length', 1); + + // toggle column to hide + dataJobsManagePage + .uncheckColumnShowHideOption('Notifications'); + + // verify column is not checked in toggling menu + dataJobsManagePage + .getDataGridColumnShowHideOption('Notifications') + .should('exist') + .should('not.be.checked'); + + // verify header cell for column is not rendered + dataJobsManagePage + .getDataGridHeaderCell('Notifications') + .should('have.length', 0); + + // hide panel for show/hide columns + dataJobsManagePage + .toggleColumnShowHidePanel(); + }); + + it('Data Jobs Manage Page - execute now', { tags: '@integration' }, () => { + const jobName = longLivedTestJob.job_name; + const team = longLivedTestJob.team; + + dataJobsManagePage + .chooseQuickFilter(0); + + dataJobsManagePage + .sortByJobName(); + + dataJobsManagePage + .executeDataJob(jobName); + + // TODO better handling toast message. If tests are executed immediately it will return + // error 409 conflict and it will say that the job is already executing + dataJobsManagePage + .getToastTitle(10000) // Wait up to 10 seconds for Toast to show. + .should('exist') + .contains(/Data job Queued for execution|Failed, Data job is already executing/); + }); +}); diff --git a/projects/frontend/data-pipelines/gui/e2e/plugins/index.js b/projects/frontend/data-pipelines/gui/e2e/plugins/index.js new file mode 100644 index 0000000000..d6d18c30ec --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/plugins/index.js @@ -0,0 +1,48 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +const { install, ensureBrowserFlags } = require('@neuralegion/cypress-har-generator'); +const path = require('path'); + +/** + * @type {Cypress.PluginConfig} + */ +module.exports = (on, config) => { + const options = { + outputRoot: config.env.CYPRESS_TERMINAL_LOGS, + // Used to trim the base path of specs and reduce nesting in the + // generated output directory. + specRoot: path.relative(config.fileServerFolder, config.integrationFolder), + outputTarget: { + 'cypress-logs|json': 'json', + }, + printLogsToConsole: 'always', + printLogsToFile: 'always', + includeSuccessfulHookLogs: true, + logToFilesOnAfterRun: true + }; + + require('cypress-terminal-report/src/installLogsPrinter')(on, options); + install(on, config); + + on('before:browser:launch', (browser = {}, launchOptions) => { + ensureBrowserFlags(browser, launchOptions); + return launchOptions; + }); +}; diff --git a/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-base.po.js b/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-base.po.js new file mode 100644 index 0000000000..231a3aa2f6 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-base.po.js @@ -0,0 +1,164 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataPipelinesBasePO } from './data-pipelines-base.po'; + +export class DataJobBasePO extends DataPipelinesBasePO { + static getPage() { + return new DataJobBasePO(); + } + + initGetExecutionInterceptor() { + cy.initGetExecutionInterceptor(); + } + + initGetExecutionsInterceptor() { + cy.initGetExecutionsInterceptor(); + } + + initPostExecutionInterceptor() { + cy.initPostExecutionInterceptor(); + } + + initDeleteExecutionInterceptor() { + cy.initDeleteExecutionInterceptor(); + } + + waitForPostExecutionCompletion() { + cy.waitForPostExecutionCompletion(); + } + + waitForDeleteExecutionCompletion() { + cy.waitForDeleteExecutionCompletion(); + } + + waitForDataJobStartExecute() { + cy.waitForInterceptor('@getExecutionsRequest', 20, (response) => { + return response?.data?.content?.findIndex((e) => { + return e.status?.toLowerCase() === 'running' || e.status?.toLowerCase() === 'submitted' + }); + }); + } + + waitForDataJobStopExecute() { + cy.waitForInterceptor('@getExecutionRequest', 20, (response) => { + return response?.status === 'cancelled'; + }); + } + + /* Selectors */ + + getNavigateBackBtn() { + return cy.get('[data-cy=data-pipelines-job-navigate-back]'); + } + + getMainTitle() { + return cy.get('[data-cy=data-pipelines-job-main-title]'); + } + + getExecuteNowButton() { + return cy.get('[data-cy=data-pipelines-job-execute-btn]'); + } + + getCancelExecutionButton() { + return cy.get('[data-cy=data-pipelines-job-cancel-execution-btn]'); + } + + getConfirmDialogButton() { + return cy.get('[data-cy="confirmation-dialog-ok-btn"]'); + } + + getActionDropdownBtn() { + return cy.get('[data-cy=data-pipelines-job-action-dropdown-btn]'); + } + + getDownloadKeyBtn() { + return cy.get('[data-cy=data-pipelines-job-download-btn]'); + } + + getDeleteJobBtn() { + return cy.get('[data-cy=data-pipelines-job-delete-btn]'); + } + + getConfirmDeleteBtn() { + return cy.get('#removeBtn'); + } + + getDetailsTab() { + return cy.get('[data-cy=data-pipelines-job-details-tab]'); + } + + getExecutionsTab() { + return cy.get('[data-cy=data-pipelines-job-executions-tab]'); + } + + getExecutionStatus() { + return cy.get('[data-cy="data-pipelines-job-execution-status"]'); + } + + /* Actions */ + + executeNow() { + this.getExecuteNowButton() + .scrollIntoView() + .click({ force: true }); + } + + openActionDropdown() { + this.getActionDropdownBtn() + .scrollIntoView() + .should('be.visible') + .click({ force: true }); + + this.waitForClickThinkingTime(); + } + + downloadJobKey() { + this.getDownloadKeyBtn() + .scrollIntoView() + .should('be.visible') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + } + + deleteJob() { + this.getDeleteJobBtn() + .scrollIntoView() + .should('be.visible') + .click({ force: true }); + } + + confirmDeleteJob() { + this.getConfirmDeleteBtn() + .scrollIntoView() + .should('be.visible') + .click({ force: true }); + } + + navigateBackToDataJobs() { + this.getNavigateBackBtn() + .should('be.visible') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + } + + openDetailsTab() { + this.getDetailsTab() + .should('exist') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + } + + openExecutionsTab() { + this.getExecutionsTab() + .should('exist') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + } +} diff --git a/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-details-base.po.js b/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-details-base.po.js new file mode 100644 index 0000000000..571663d9bd --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-details-base.po.js @@ -0,0 +1,74 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataJobBasePO } from './data-job-base.po'; + +export class DataJobDetailsBasePO extends DataJobBasePO { + static getPage() { + return new DataJobDetailsBasePO(); + } + + static navigateTo(context, teamName, jobName) { + return this.navigateToUrl(`/${ context }/data-jobs/${ teamName }/${ jobName }`); + } + + // Selectors + + getStatusField() { + return cy.get('[data-cy=data-pipelines-job-details-status]'); + } + + getDescriptionField() { + return cy.get('[data-cy=data-pipelines-job-details-description]'); + } + + getDescriptionFull() { + return cy.get('[data-cy=description-show-less]'); + } + + getShowDescriptionMoreBtn() { + return cy.get('[data-cy=description-show-more]'); + } + + getTeamField() { + return cy.get('[data-cy=data-pipelines-job-details-team]'); + } + + getScheduleField() { + return cy.get('[data-cy=data-pipelines-job-details-schedule]'); + } + + getSourceField() { + return cy.get('[data-cy=data-pipelines-job-details-source]'); + } + + getOnDeployedField() { + return cy.get('[data-cy=data-pipelines-job-details-on-deployed]'); + } + + getOnPlatformErrorField() { + return cy.get('[data-cy=data-pipelines-job-details-on-platform-error]'); + } + + getOnUserErrorField() { + return cy.get('[data-cy=data-pipelines-job-details-on-user-error]'); + } + + getOnSuccessField() { + return cy.get('[data-cy=data-pipelines-job-details-on-success]'); + } + + // Actions + + showMoreDescription() { + this.getShowDescriptionMoreBtn() + .should('exist') + .click({ force: true }); + + this.waitForViewToRenderShort(); + + return this; + } +} diff --git a/projects/frontend/data-pipelines/gui/e2e/support/application/data-jobs-base.po.js b/projects/frontend/data-pipelines/gui/e2e/support/application/data-jobs-base.po.js new file mode 100644 index 0000000000..6627617e4a --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/application/data-jobs-base.po.js @@ -0,0 +1,180 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataPipelinesBasePO } from './data-pipelines-base.po'; + +export class DataJobsBasePO extends DataPipelinesBasePO { + static getPage() { + return new DataJobsExplorePage(); + } + + static navigateTo(linkSelector) { + cy.visit('/') + + cy.wait(DataJobsBasePO.INITIAL_PAGE_LOAD_WAIT_TIME); + + cy.get(linkSelector) + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + + cy.wait(DataJobsBasePO.VIEW_RENDER_WAIT_TIME); + + return this.getPage(); + } + + /* Selectors */ + + getDataGrid() { + return cy.get('clr-datagrid'); + } + + getQuickFilters() { + return cy.get('[data-cy=data-pipelines-quick-filters] > span'); + } + + getHeaderColumnJobName() { + return cy.get('[data-cy=data-pipelines-jobs-name-column]'); + } + + getHeaderColumnJobNameSortBtn() { + return this.getHeaderColumnJobName() + .should('exist') + .find('.datagrid-column-title'); + } + + getDataGridHeaderCell(content) { + return this.getDataGrid() + .should('exist') + .find('clr-dg-column:not(.datagrid-hidden-column)') + .contains(new RegExp(`${ content }`)); + } + + // Rows and Cells + + getDataGridRows() { + return this.getDataGrid() + .should('exist') + .find('clr-dg-row.datagrid-row'); + } + + getDataGridRow(rowIndex) { + return this.getDataGridRows() + .should('have.length.gte', rowIndex - 1) + .then((rows) => Array.from(rows)[rowIndex - 1]); + } + + getDataGridCells(rowIndex) { + return this.getDataGridRow(rowIndex) + .should('exist') + .find('clr-dg-cell.datagrid-cell'); + } + + getDataGridCellByIndex(rowIndex, cellIndex) { + return this.getDataGridCells(rowIndex) + .should('have.length.gte', cellIndex - 1) + .then((cells) => Array.from(cells)[cellIndex - 1]); + } + + getDataGridCellByIdentifier(rowIndex, identifier) { + return this.getDataGridRow(rowIndex) + .should('exist') + .find(identifier) + } + + getDataGridStatusCells() { + return this.getDataGrid() + .should('exist') + .find('[data-cy=data-pipelines-manage-grid-status-cell]'); + } + + getDataGridStatusIcons() { + return this.getDataGrid() + .should('exist') + .find('[data-cy=data-pipelines-manage-grid-status-cell] clr-icon[data-cy*=data-pipelines-job]'); + } + + getDataGridColumnToggle() { + return this.getDataGrid() + .should('exist') + .find('clr-dg-column-toggle button'); + } + + getDataGridColumnShowHidePanel() { + return cy.get('.column-switch'); + } + + getDataGridColumnShowHideOptions() { + return this.getDataGridColumnShowHidePanel() + .should('exist') + .find('clr-checkbox-wrapper'); + } + + getDataGridColumnShowHideOptionsValues() { + return this.getDataGridColumnShowHidePanel() + .should('exist') + .find('clr-checkbox-wrapper') + .then((elements) => Array.from(elements).map((el) => el.innerText)); + } + + getDataGridColumnShowHideOption(option) { + return this.getDataGridColumnShowHideOptions() + .should('have.length.gt', 0) + .then((elements) => + Array.from(elements) + .find((el) => el.innerText === option) + ) + .find('input'); + } + + /* Actions */ + + chooseQuickFilter(filterPosition) { + this.getQuickFilters() + .then((filters) => { + return filters && filters[filterPosition]; + }) + .should('exist') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + + this.waitForViewToRender(); + } + + sortByJobName() { + this.getHeaderColumnJobNameSortBtn() + .should('exist') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + + this.waitForSmartDelay(); + } + + toggleColumnShowHidePanel() { + this.getDataGridColumnToggle() + .should('exist') + .click({ force: true }); + + this.waitForClickThinkingTime(); + } + + checkColumnShowHideOption(option) { + this.getDataGridColumnShowHideOption(option) + .should('exist') + .check({ force: true }); + + this.waitForViewToRenderShort(); + } + + uncheckColumnShowHideOption(option) { + this.getDataGridColumnShowHideOption(option) + .should('exist') + .uncheck({ force: true }); + + this.waitForViewToRenderShort(); + } +} diff --git a/projects/frontend/data-pipelines/gui/e2e/support/application/data-pipelines-base.po.js b/projects/frontend/data-pipelines/gui/e2e/support/application/data-pipelines-base.po.js new file mode 100644 index 0000000000..88db68899f --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/application/data-pipelines-base.po.js @@ -0,0 +1,194 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +export class DataPipelinesBasePO { + static API_MODIFY_CALL_TIME = 1000; // ms + static SMART_DELAY_TIME = 200; // ms + static CLICK_THINK_TIME = 500; // ms + static ACTION_THINK_TIME = 750; // ms + static VIEW_RENDER_SHORT_WAIT_TIME = 600; // ms + static VIEW_RENDER_WAIT_TIME = 1500; // ms + static INITIAL_PAGE_LOAD_WAIT_TIME = 2000; // ms + static INITIAL_PAGE_LOAD_LONG_WAIT_TIME = 2500; // ms + + static getPage() { + return new DataPipelinesBasePO(); + } + + static login() { + return cy.login(); + } + + static navigateToUrl(url) { + cy.visit(url); + + this.waitForBackendRequestCompletion(); + + cy.wait(DataPipelinesBasePO.VIEW_RENDER_SHORT_WAIT_TIME); + + return this.getPage(); + } + + /** + * ** Init API requests interceptor. + */ + static initBackendRequestInterceptor() { + cy.initBackendRequestInterceptor(); + } + + /** + * ** Wait for API request interceptor. + */ + static waitForBackendRequestCompletion(numberOfReqToWait = 1) { + const thinkTimeMillis = 1000; + + for (let i = 0; i < numberOfReqToWait; i++) { + cy.waitForBackendRequestCompletion(); + } + + cy.wait(thinkTimeMillis); + } + + /** + * ** Start HAR recording. + * + * @returns {Cypress.Chainable} + */ + static recordHarIfSupported() { + return cy.recordHarIfSupported(); + } + + /** + * ** Save recorded HAR. + * + * @returns {Cypress.Chainable} + */ + static saveHarIfSupported() { + return cy.saveHarIfSupported(); + } + + /** + * ** Response from changing Data Job status. + * @param {string} teamName + * @param {string} jobName + * @param {boolean} status + * @returns {Cypress.Chainable} + */ + static changeJobStatus(teamName, jobName, status) { + return cy.changeDataJobEnabledStatus(teamName, jobName, status); + } + + + // Interceptors and waiting + + waitForBackendRequestCompletion(numberOfReqToWait = 1) { + DataPipelinesBasePO.waitForBackendRequestCompletion(numberOfReqToWait); + } + + waitForTestJobExecutionCompletion() { + cy.waitForTestJobExecutionCompletion(); + } + + waitForApiModifyCall() { + cy.wait(DataPipelinesBasePO.API_MODIFY_CALL_TIME); + } + + waitForSmartDelay() { + cy.wait(DataPipelinesBasePO.SMART_DELAY_TIME); + } + + waitForClickThinkingTime() { + cy.wait(DataPipelinesBasePO.CLICK_THINK_TIME); + } + + waitForActionThinkingTime() { + cy.wait(DataPipelinesBasePO.ACTION_THINK_TIME); + } + + waitForViewToRenderShort() { + cy.wait(DataPipelinesBasePO.VIEW_RENDER_SHORT_WAIT_TIME); + } + + waitForViewToRender() { + cy.wait(DataPipelinesBasePO.VIEW_RENDER_WAIT_TIME); + } + + waitForInitialPageLoad() { + cy.wait(DataPipelinesBasePO.INITIAL_PAGE_LOAD_WAIT_TIME); + } + + /* Selectors */ + + getMainTitle() { + return cy.get('[data-cy=dp-main-title]'); + } + + getCurrentUrl() { + return cy.url(); + } + + getToast(timeout) { + return cy.get('vdk-toast-container vdk-toast', { timeout: this.resolveTimeout(timeout) }); + } + + getToastTitle(timeout) { + return this + .getToast(timeout) + .get('.toast-title'); + } + + getToastDismiss(timeout) { + return this + .getToast(timeout) + .get('.dismiss-bg'); + } + + getContentContainer() { + return cy.get('div.content-container'); + } + + /* Actions */ + + confirmInConfirmDialog() { + cy.get('[data-cy=confirmation-dialog-ok-btn]') + .should('exist') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + + this.waitForViewToRender(); + } + + clickOnContentContainer() { + this.getContentContainer() + .should('exist') + .click({ force: true }); + + this.waitForSmartDelay(); + } + + resolveTimeout(timeout) { + return timeout === undefined + ? Cypress.config('defaultCommandTimeout') + : timeout; + } + + readFile(folderName, fileName) { + const path = require('path'); + const downloadsFolder = Cypress.config(folderName); + + return cy.readFile(path.join(downloadsFolder, fileName)); + } + + clearLocalStorage(key) { + if (key) { + cy.clearLocalStorage(key); + + return; + } + + cy.clearLocalStorage(); + } +} diff --git a/projects/frontend/data-pipelines/gui/e2e/support/commands.js b/projects/frontend/data-pipelines/gui/e2e/support/commands.js new file mode 100644 index 0000000000..4f2589f646 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/commands.js @@ -0,0 +1,252 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import 'cypress-localstorage-commands'; + +import { + applyGlobalEnvSettings, + createExecutionsForJob, + createTestJob, + deleteTestJobIfExists, + deployTestJobIfNotExists, + waitForJobExecutionCompletion +} from './helpers/commands.helpers'; + +Cypress.Commands.add('login', () => { + return cy.request({ + method: 'POST', + // See project README.md how to set this login url + url: Cypress.env('login_url'), + form: true, + body: { + // See project README.md how to set this token + api_token: Cypress.env('OAUTH2_API_TOKEN') + }, + followRedirect: false, + failOnStatusCode: false + }) + .its('body') + .then((body) => { + const token = body.access_token; + const expiresIn = body.expires_in; + const idToken = body.id_token; + const expiresAt = JSON.stringify(Date.now() + (expiresIn * 1000)); + + window.localStorage.setItem('expires_at', expiresAt); + window.localStorage.setItem('access_token', token); + window.localStorage.setItem('id_token', idToken); + + // Set the CSP token in the session storage to enable the Integration testing against Management UI(TMS STG) + sessionStorage.setItem('expires_at', expiresAt); + sessionStorage.setItem('access_token', token); + sessionStorage.setItem('id_token', idToken); + + return cy.wrap({ context: 'commands::login()', action: 'continue' }); + }); +}); + +Cypress.Commands.add('initBackendRequestInterceptor', () => { + cy.intercept({ + method:'GET', + url:'**/data-jobs/for-team/**'}) + .as('getJobsRequest'); +}); + +Cypress.Commands.add('waitForBackendRequestCompletion', () => { + cy.wait('@getJobsRequest'); +}); + +Cypress.Commands.add('initGetExecutionsInterceptor', () => { + cy.intercept('GET', '**/data-jobs/for-team/**', (req) => { + if (req?.query?.operationName === 'jobsQuery' && req?.query?.query?.includes('executions')) { + req.alias = 'getExecutionsRequest'; + } + }) +}); + +Cypress.Commands.add('initGetExecutionInterceptor', () => { + cy.intercept({ + method: 'GET', + url: '**/data-jobs/for-team/**/executions/**' + }) + .as('getExecutionRequest'); +}); + +Cypress.Commands.add('waitForInterceptor', (aliasName, retries, predicate) => { + cy.wait(aliasName).its('response.body').then((json) => { + + if (predicate(json)) { + return; + } + + if (retries > 0) { + cy.waitForInterceptor(aliasName, retries - 1, predicate); + } + }); +}); + +Cypress.Commands.add('initPostExecutionInterceptor', () => { + cy.intercept({ + method: 'POST', + url: '**/data-jobs/for-team/**/executions' + }).as('postExecuteNow'); +}); + +Cypress.Commands.add('waitForPostExecutionCompletion', () => { + cy.wait('@postExecuteNow'); +}); + +Cypress.Commands.add('initDeleteExecutionInterceptor', () => { + cy.intercept({ + method: 'DELETE', + url: '**/data-jobs/for-team/**/executions/**' + }).as('deleteExecution'); +}); + +Cypress.Commands.add('waitForDeleteExecutionCompletion', () => { + cy.wait('@deleteExecution'); +}); + +Cypress.Commands.add('recordHarIfSupported', () => { + if (Cypress.browser.name === 'chrome') { + return cy.recordHar({ excludePaths: ['vendor.js$', 'clr-ui.min.css$', 'scripts.js$', 'polyfills.js$'] }); + } + + return cy.wrap({ context: 'commands::recordHarIfSupported()', action: 'continue' }); +}); + +Cypress.Commands.add('saveHarIfSupported', () => { + if (Cypress.browser.name === 'chrome') { + return cy.saveHar(); + } + + return cy.wrap({ context: 'commands::saveHarIfSupported()', action: 'continue' }); +}); + +Cypress.Commands.add('prepareBaseTestJobs', () => { + return cy.fixture('lib/explore/test-jobs.json') + .then((testJobs) => { + return Promise.all( + testJobs.map((testJob) => { + const normalizedTestJob = applyGlobalEnvSettings(testJob); + + return createTestJob(normalizedTestJob); + }) + ); + }); +}); + +Cypress.Commands.add('prepareAdditionalTestJobs', () => { + return cy.fixture('lib/explore/additional-test-job.json') + .then((testJob) => { + const normalizedTestJob = applyGlobalEnvSettings(testJob); + + return createTestJob(normalizedTestJob); + }) +}); + +Cypress.Commands.add('prepareLongLivedTestJob', () => { + return deployTestJobIfNotExists('lib/manage/e2e-cypress-dp-test.json', 'lib/manage/e2e-cypress-dp-test.zip'); +}); + +Cypress.Commands.add('prepareLongLivedFailingTestJob', () => { + return deployTestJobIfNotExists('e2e-cy-dp-failing.job.json', 'e2e-cy-dp-failing.job.zip'); +}); + +Cypress.Commands.add('cleanTestJobs', () => { + return cy.fixture('lib/explore/test-jobs.json') + .then((testJobs) => { + return Promise.all( + testJobs.map((testJob) => { + const normalizedTestJob = applyGlobalEnvSettings(testJob); + + return deleteTestJobIfExists(normalizedTestJob); + }) + ); + }) + .then(() => { + return cy.fixture('lib/explore/additional-test-job.json') + .then((testJob) => { + const normalizedTestJob = applyGlobalEnvSettings(testJob); + + return deleteTestJobIfExists(normalizedTestJob); + }); + }); +}); + +Cypress.Commands.add('waitForTestJobExecutionCompletion', () => { + const waitForJobExecutionTimeout = 180000; // Wait up to 3 min for job execution to complete + + return cy.fixture('lib/manage/e2e-cypress-dp-test.json') + .then((testJob) => { + const normalizedTestJob = applyGlobalEnvSettings(testJob); + + return waitForJobExecutionCompletion(normalizedTestJob.team, normalizedTestJob.job_name, waitForJobExecutionTimeout); + }); +}); + +Cypress.Commands.add('createTwoExecutionsLongLivedTestJob', () => { + const waitForJobExecutionTimeout = 180000; // Wait up to 3 min for job execution to complete + + return cy.fixture('lib/manage/e2e-cypress-dp-test.json') + .then((testJob) => { + const normalizedTestJob = applyGlobalEnvSettings(testJob); + + return createExecutionsForJob(normalizedTestJob.team, normalizedTestJob.job_name, waitForJobExecutionTimeout, 2); + }); +}); + +Cypress.Commands.add('createExecutionsLongLivedFailingTestJob', () => { + const waitForJobExecutionTimeout = 180000; // Wait up to 3 min for job execution to complete + + return cy.fixture('e2e-cy-dp-failing.job.json') + .then((failingTestJob) => { + const normalizedTestJob = applyGlobalEnvSettings(failingTestJob); + + return createExecutionsForJob(normalizedTestJob.team, normalizedTestJob.job_name, waitForJobExecutionTimeout, 2); + }); +}); + +Cypress.Commands.add('changeDataJobEnabledStatus', (teamName, jobName, status) => { + return cy.request({ + url: Cypress.env('data_jobs_url') + `/data-jobs/for-team/${ teamName }/jobs/${ jobName }/deployments`, + method: 'get', + auth: { + 'bearer': window.localStorage.getItem('access_token') + }, + failOnStatusCode: false + }).then((outerResponse) => { + if (outerResponse.status === 200) { + const lastDeployment = outerResponse.body[outerResponse.body.length - 1]; + const lastDeploymentHash = lastDeployment.job_version; + + return cy.request({ + url: Cypress.env('data_jobs_url') + `/data-jobs/for-team/${ teamName }/jobs/${ jobName }/deployments/${ lastDeploymentHash }`, + method: 'patch', + body: { enabled: status }, + auth: { + 'bearer': window.localStorage.getItem('access_token') + }, + failOnStatusCode: false + }).then((innerResponse) => { + if (innerResponse.status >= 200 && innerResponse.status < 300) { + cy.log(`Change enable status to [${ status }] for data job [${ jobName }]`); + } else { + cy.log(`Cannot change enabled status to [${ status }] for data job [${ jobName }]`); + + console.log(`Http request:`, innerResponse); + } + + return cy.wrap({ context: 'commands::1::changeDataJobEnabledStatus()', action: 'continue' }); + }); + } else { + cy.log(`Cannot change enabled status to [${ status }] for data job [${ jobName }]`); + + console.log(`Http request:`, outerResponse); + + return cy.wrap({ context: 'commands::2::changeDataJobEnabledStatus()', action: 'continue' }); + } + }); +}); diff --git a/projects/frontend/data-pipelines/gui/e2e/support/helpers/commands.helpers.js b/projects/frontend/data-pipelines/gui/e2e/support/helpers/commands.helpers.js new file mode 100644 index 0000000000..e5596b7319 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/helpers/commands.helpers.js @@ -0,0 +1,433 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +const DEFAULT_TEST_ENV_VAR = 'lib'; + +const applyGlobalEnvSettings = (loadedElement, injectedTestEnvVar = null) => { + const TEST_ENV_VAR = injectedTestEnvVar ?? _loadTestEnvironmentVar(); + + if (typeof loadedElement === 'string') { + return loadedElement.replace('$env-placeholder$', TEST_ENV_VAR); + } + + if (typeof loadedElement === 'object') { + Object.entries(loadedElement) + .forEach(([key, value]) => { + if (typeof value === 'string') { + if (value.includes('$env-placeholder$')) { + value = value.replace('$env-placeholder$', TEST_ENV_VAR); + } + + loadedElement[key] = value; + } + + if (typeof value === 'object') { + loadedElement[key] = applyGlobalEnvSettings(value); + } + }); + } + + return loadedElement; +}; + +const createExecutionsForJob = (teamName, jobName, waitForJobExecutionTimeout, numberOfExecutions = 2) => { + cy.log(`Trying to provide at least ${ numberOfExecutions } executions for Job "${ jobName }"`); + + return _getJobExecutions(teamName, jobName) + .then((executions) => { + cy.log(`Found ${ executions.length } executions for Job "${ jobName }" and target is ${ numberOfExecutions } executions`); + + if (executions.length >= numberOfExecutions) { + cy.log(`Skipping manual execution, expected number of executions found`); + + return cy.wrap({ context: 'commands.helpers::1::createExecutionsForJob()', action: 'continue' }); + } + + return waitForJobExecutionCompletion(teamName, jobName, waitForJobExecutionTimeout) + .then(() => { + return _getJobLastDeployment(teamName, jobName) + .then((lastDeployment) => { + if (lastDeployment) { + return _executeCreateExecutionsForJob( + teamName, + jobName, + waitForJobExecutionTimeout, + lastDeployment, + executions, + numberOfExecutions + ); + } + + return cy.wrap({ context: 'commands.helpers::2::createExecutionsForJob()', action: 'continue' }); + }); + }); + }); +}; + +const waitForJobExecutionCompletion = (teamName, jobName, jobExecutionTimeout, loopStep = 0) => { + const pollInterval = 5000; + + return cy.request({ + url: Cypress.env('data_jobs_url') + `/data-jobs/for-team/${ teamName }/jobs/${ jobName }/executions`, + method: 'get', + auth: { + 'bearer': window.localStorage.getItem('access_token') + } + }).then((response) => { + if (response.status !== 200) { + cy.log(`Job execution poll failed. Status: ${ response.status }; Message: ${ response.body }`); + + return cy.wrap({ context: 'commands.helpers::1::waitForJobExecutionCompletion()', action: 'continue' }); + } + + const lastExecutions = response.body + .sort((left, right) => _compareDatesAsc(left, right)) + .slice(response.body.length > 5 ? response.body.length - 5 : 0); + + if (!lastExecutions.length || lastExecutions.length === 0) { + cy.log(`No valid Job executions available.`); + + return cy.wrap({ context: 'commands.helpers::2::waitForJobExecutionCompletion()', action: 'continue' }); + } + + const lastExecution = lastExecutions[lastExecutions.length - 1]; + const lastExecutionStatus = lastExecution.status.toLowerCase(); + + if (lastExecutionStatus !== 'running' && lastExecutionStatus !== 'submitted') { + return ( + loopStep > 0 + ? cy.wait(pollInterval) + : cy.wrap({ context: 'commands.helpers::3::waitForJobExecutionCompletion()', action: 'continue' }) + ).then(() => { + cy.log(`Job "${ jobName }" executed successfully, polling completed!`); + + return cy.wrap({ context: 'commands.helpers::4::waitForJobExecutionCompletion()', action: 'continue' }); + }); + } + + if (jobExecutionTimeout <= 0) { + cy.log('Time to wait exceeded! Will not wait for job execution to finish any longer.'); + + return cy.wrap({ context: 'commands.helpers::5::waitForJobExecutionCompletion()', action: 'continue' }); + } + + cy.log(`Job ${ jobName }, still executing... Retrying after: ${ pollInterval / 1000 } seconds`); + + return cy.wait(pollInterval) + .then(() => waitForJobExecutionCompletion(teamName, jobName, jobExecutionTimeout - pollInterval, jobExecutionTimeout++)) + }); +}; + +const createTestJob = (testJob) => { + let teamName = testJob.team; + + return cy.request({ + // TODO: use jobsQuery - deprecated jobsList in favour of jobsQuery + url: Cypress.env('data_jobs_url') + `/data-jobs/for-team/${ teamName }/jobs`, + method: 'post', + body: testJob, + auth: { + 'bearer': window.localStorage.getItem('access_token') + }, + failOnStatusCode: false + }).then((response) => { + if (response.status >= 400) { + cy.log(`Http request fail for url:`, `/data-jobs/for-team/${ teamName }/jobs`); + + console.log(`Http request error:`, response); + } + + return cy.wrap(response); + }); +}; + +const deleteTestJobIfExists = (testJob) => { + let teamName = testJob.team; + let jobName = testJob.job_name; + + return cy.request({ + url: Cypress.env('data_jobs_url') + `/data-jobs/for-team/${ teamName }/jobs/${ jobName }`, + method: 'get', + auth: { + 'bearer': window.localStorage.getItem('access_token') + }, + failOnStatusCode: false + }).then((outerResponse) => { + if (outerResponse.status === 200) { + return cy.request({ + url: Cypress.env('data_jobs_url') + `/data-jobs/for-team/${ teamName }/jobs/${ jobName }`, + method: 'delete', + auth: { + 'bearer': window.localStorage.getItem('access_token') + }, + failOnStatusCode: false + }).then((innerResponse) => { + if (innerResponse.status >= 400) { + cy.log(`Http request fail for url:`, `/data-jobs/for-team/${ teamName }/jobs/${ jobName }`); + + console.log(`Http request error:`, innerResponse); + } + + return cy.wrap({ context: 'commands.helpers::1::deleteTestJobIfExists()', action: 'continue' }); + }); + } else if (outerResponse.status >= 400) { + cy.log(`Http request fail for url:`, `/data-jobs/for-team/${ teamName }/jobs/${ jobName }`); + + console.log(`Http request error:`, outerResponse); + } + + return cy.wrap({ context: 'commands.helpers::2::deleteTestJobIfExists()', action: 'continue' }); + }); +}; + +const deployTestJobIfNotExists = (pathToJobData, pathToJobBinary) => { + return cy.fixture(pathToJobData) + .then((testJob) => { + const normalizedTestJob = applyGlobalEnvSettings(testJob); + const teamName = normalizedTestJob.team; + const jobName = normalizedTestJob.job_name; + + // Check if data job exists and wether it has deployment or not. + return cy.request({ + url: Cypress.env('data_jobs_url') + `/data-jobs/for-team/${ teamName }/jobs/${ jobName }/deployments`, + method: 'get', + auth: { + 'bearer': window.localStorage.getItem('access_token') + }, + failOnStatusCode: false + }).then((outerResponse) => { + if (outerResponse.status === 200 && outerResponse.body.length > 0) { + cy.log(jobName + ' is already existing, therefore skipping creation and deployment.'); + } else if (outerResponse.status === 200 && outerResponse.body.length === 0) { + cy.log(jobName + ' exists, but it is not deployed. Deploying...'); + + return _deployTestDataJob(normalizedTestJob, pathToJobBinary); + } else if (outerResponse.status === 404) { + return createTestJob(normalizedTestJob) + .then((innerResponse) => { + if (innerResponse.status === 201) { + return _deployTestDataJob(normalizedTestJob, pathToJobBinary); + } else { + throw new Error('Failed to create job: ' + jobName); + } + }); + } else if (outerResponse.status >= 400) { + cy.log(`Http request fail for url:`, `/data-jobs/for-team/${ teamName }/jobs/${ jobName }/deployments`); + + console.log(`Http request error:`, outerResponse); + } + + return cy.wrap({ context: 'commands.helpers::deployTestJobIfNotExists()', action: 'continue' }); + }); + }); +}; + +function _loadTestEnvironmentVar() { + let testEnv = Cypress?.env('test_environment'); + + if (!testEnv) { + console.log(`DataPipelines test_environment is not set. Using default: ${ DEFAULT_TEST_ENV_VAR }`); + testEnv = DEFAULT_TEST_ENV_VAR; + } + + return testEnv; +} + +function _executeCreateExecutionsForJob(teamName, jobName, waitForJobExecutionTimeout, lastDeployment, executions, numberOfExecutions, counterOfExecutions = 0) { + const neededExecutions = numberOfExecutions - executions.length; + + if (counterOfExecutions === 0) { + cy.log(`Submitting ${ neededExecutions } serial executions`); + } else { + cy.log(`${ neededExecutions - counterOfExecutions } more executions left for executing`) + } + + if (neededExecutions === counterOfExecutions) { + return cy.wrap({ context: 'commands.helpers::1::_executeCreateExecutionsForJob()', action: 'continue' }); + } + + return _executeJob(teamName, jobName, lastDeployment.id) + .then(() => { + return cy.wait(5000) + .then(() => cy.wrap({ context: 'commands.helpers::2::_executeCreateExecutionsForJob()', action: 'continue' })); + }) + .then(() => { + return waitForJobExecutionCompletion(teamName, jobName, waitForJobExecutionTimeout); + }) + .then(() => { + return _executeCreateExecutionsForJob( + teamName, + jobName, + waitForJobExecutionTimeout, + lastDeployment, + executions, + numberOfExecutions, + counterOfExecutions + 1 + ); + }); +} + +function _getJobExecutions(teamName, jobName) { + cy.log(`Fetching Job executions for Job "${ jobName }"`); + + return cy + .request({ + url: `/data-jobs/for-team/${ teamName }/jobs/${ jobName }/executions`, + method: 'get', + auth: { + 'bearer': window.localStorage.getItem('access_token') + } + }) + .then((response) => { + if (response.status !== 200) { + cy.log(`Getting Job executions failed. Status: ${ response.status }; Message: ${ response.body }`); + + return cy.wrap([]); + } + + return cy.wrap( + response.body && response.body.length + ? response.body + : [] + ); + }); +} + +function _getJobLastDeployment(teamName, jobName) { + cy.log(`Fetching Job deployments for Job "${ jobName }"`); + + return cy + .request({ + url: Cypress.env('data_jobs_url') + `/data-jobs/for-team/${ teamName }/jobs/${ jobName }/deployments`, + method: 'get', + auth: { + 'bearer': window.localStorage.getItem('access_token') + } + }) + .then((response) => { + if (response.status !== 200) { + cy.log(`Getting Job deployments failed. Status: ${ response.status }; Message: ${ response.body }`); + + return cy.wrap(null); + } + + return cy.wrap( + response.body && response.body.length + ? response.body[response.body.length - 1] + : null + ); + }); +} + +function _executeJob(teamName, jobName, deploymentId) { + cy.log(`Submitting execution for Job "${ jobName }"`); + + return cy.request({ + url: Cypress.env('data_jobs_url') + `/data-jobs/for-team/${ teamName }/jobs/${ jobName }/deployments/${ deploymentId }/executions`, + method: 'post', + auth: { + 'bearer': window.localStorage.getItem('access_token') + }, + body: {} + }).then((response) => { + if (response.status >= 400) { + cy.log(`Executing Job failed. Status: ${ response.status }; Message: ${ response.body }`); + } + + return cy.wrap({ context: 'commands.helpers::_executeJob()', action: 'continue' }); + }); +} + +function _compareDatesAsc(left, right) { + const leftStartTime = left.start_time ? left.start_time : 0; + const rightStartTime = right.start_time ? right.start_time : 0; + + return new Date(leftStartTime).getTime() - new Date(rightStartTime).getTime(); +} + +function _deployTestDataJob(testJob, pathToJobBinary) { + const teamName = testJob.team; + const jobName = testJob.job_name; + const waitForJobDeploymentTimeout = 300000; // Wait up to 5 min for deployment to complete. + + cy.log(`Deploying Job ${ jobName }`); + + // Upload Data Job code + return cy.fixture(pathToJobBinary, 'binary') + .then((zippedDataJob) => { + return cy.request({ + url: Cypress.env('data_jobs_url') + '/data-jobs/for-team/' + teamName + '/jobs/' + jobName + '/sources', + method: 'post', + auth: { + 'bearer': window.localStorage.getItem('access_token') + }, + body: Cypress.Blob.binaryStringToBlob(zippedDataJob) + }).then((response1) => { + let jsonResponse = JSON.parse(Cypress.Blob.arrayBufferToBinaryString(response1.body)); + + // Deploy data job + return cy.request({ + url: Cypress.env('data_jobs_url') + '/data-jobs/for-team/' + teamName + '/jobs/' + jobName + '/deployments', + method: 'post', + auth: { + 'bearer': window.localStorage.getItem('access_token') + }, + body: { + job_version: jsonResponse.version_sha, + mode: 'release', + enabled: true + } + }).then((response2) => { + if (response2.status === 202) { + cy.log(`Job: ${ jobName } deployment in progress. Waiting for deployment to complete`); + + return _waitForDeploymentToFinish(testJob, waitForJobDeploymentTimeout); + } + + return cy.wrap({ context: 'commands.helpers::_deployTestDataJob()', action: 'continue' }); + }); + }); + }); +} + +function _waitForDeploymentToFinish(testJob, timeout) { + const teamName = testJob.team; + const jobName = testJob.job_name; + const waitInterval = 5000; + + return cy.request({ + url: Cypress.env('data_jobs_url') + '/data-jobs/for-team/' + teamName + '/jobs/' + jobName + '/deployments', + method: 'get', + auth: { + 'bearer': window.localStorage.getItem('access_token') + } + }).then((resp) => { + if (resp.status === 200 && resp.body.length > 0) { + cy.log('Job deployment finished!'); + + return cy.wrap({ context: 'commands.helpers::1::_waitForDeploymentToFinish()', action: 'continue' }); + } + + if (timeout <= 0) { + cy.log('Time to wait exceeded! Will not wait for job deployment to finish any longer.'); + + return cy.wrap({ context: 'commands.helpers::2::_waitForDeploymentToFinish()', action: 'continue' }); + } + + cy.log(`Job ${ jobName }, still not deployed.. retrying after: ${ waitInterval / 1000 } seconds`); + + return cy.wait(waitInterval) + .then(() => _waitForDeploymentToFinish(testJob, timeout - waitInterval)); + }) +} + +module.exports = { + applyGlobalEnvSettings, + createExecutionsForJob, + waitForJobExecutionCompletion, + createTestJob, + deleteTestJobIfExists, + deployTestJobIfNotExists +}; diff --git a/projects/frontend/data-pipelines/gui/e2e/support/index.js b/projects/frontend/data-pipelines/gui/e2e/support/index.js new file mode 100644 index 0000000000..8657d4c1ca --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/index.js @@ -0,0 +1,18 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import './commands' + +require('@neuralegion/cypress-har-generator/commands'); +require('cypress-grep')(); +require('cypress-terminal-report/src/installLogsCollector')(); + +Cypress.Server.defaults({ + delay: 500, + force404: false, + ignore: (xhr) => { + return true; + } +}) diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/app.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/app.po.js new file mode 100644 index 0000000000..14b4e0f847 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/app.po.js @@ -0,0 +1,20 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataPipelinesBasePO } from '../../application/data-pipelines-base.po'; + +export class AppPage extends DataPipelinesBasePO { + static getPage() { + return new AppPage(); + } + + static navigateTo() { + cy.visit('/'); + } + + getMainTitle() { + return cy.get('[data-cy=app-main-title]'); + } +} diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/data-jobs-health-panel-component.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/data-jobs-health-panel-component.po.js new file mode 100644 index 0000000000..902c09c550 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/data-jobs-health-panel-component.po.js @@ -0,0 +1,159 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { GettingStartedPage } from './getting-started.po'; + +export class DataJobsHealthPanelComponentPO extends GettingStartedPage { + /** + * ** Returns instance of the page object. + * + * @returns {DataJobsHealthPanelComponentPO} + */ + static getComponent() { + return new DataJobsHealthPanelComponentPO(); + } + + /** + * ** Get Execution status gauge Widget. + * + * @returns {Cypress.Chainable>} + */ + getExecutionStatusGaugeWidget() { + return cy.get('[data-cy=dp-data-jobs-status-gauge-widget]'); + } + + /** + * ** Get percentage of successful executions against total from Execution status gauge Widget. + * + * @returns {Cypress.Chainable} + */ + getExecutionsSuccessPercentage() { + return this.getExecutionStatusGaugeWidget() + .should('exist') + .find('[data-cy=dp-jobs-executions-status-gauge-widget-percentage]') + .invoke('text') + .invoke('replace', /%/, '') + .then((value) => +value); + } + + /** + * ** Get number of failed executions from Execution status gauge Widget. + * + * @returns {Cypress.Chainable} + */ + getNumberOfFailedExecutions() { + return this.getExecutionStatusGaugeWidget() + .should('exist') + .find('[data-cy=dp-jobs-executions-status-gauge-widget-failed]') + .invoke('text') + .invoke('replace', /\s\w+/, '') + .then((value) => +value); + } + + /** + * ** Get number of total executions from Execution status gauge Widget. + * + * @returns {Cypress.Chainable} + */ + getExecutionsTotal() { + return this.getExecutionStatusGaugeWidget() + .should('exist') + .find('[data-cy=dp-jobs-executions-status-gauge-widget-total]') + .invoke('text') + .invoke('replace', /\s\w+/, '') + .then((value) => +value); + } + + /** + * ** Get Failing Data jobs Widget. + * + * @returns {Cypress.Chainable>} + */ + getFailingJobsWidget() { + return cy.get('[data-cy=dp-failed-data-jobs-widget]'); + } + + /** + * ** Get all Data jobs from Failing Data jobs Widget. + * + * @returns {Cypress.Chainable>} + */ + getAllFailingJobs() { + return this.getFailingJobsWidget() + .should('exist') + .find('[data-cy=dp-failed-data-jobs-widget-job-name-link]'); + } + + /** + * ** Get Most Recent Failing Data jobs executions Widget. + * + * @returns {Cypress.Chainable>} + */ + getMostRecentFailingJobsExecutionsWidget() { + return cy.get('[data-cy=dp-failed-data-jobs-executions-widget]'); + } + + /** + * ** Get all executions from Most Recent Failing Data jobs executions Widget. + * + * @returns {Cypress.Chainable>} + */ + getAllMostRecentFailingJobsExecutions() { + return this.getMostRecentFailingJobsExecutionsWidget() + .should('exist') + .find('[data-cy=dp-failed-data-jobs-executions-widget-job-name-link]'); + } + + // Actions + + navigateToFailingJobDetails(jobName) { + this._navigateToDataJob( + this.getAllFailingJobs(), + jobName, + 3 + ); + } + + navigateToMostRecentFailingJobExecutions(jobName) { + this._navigateToDataJob( + this.getAllMostRecentFailingJobsExecutions(), + jobName, + 2 + ); + } + + /** + * ** Navigate to Data job + * + * @param {Cypress.Chainable>} chainable + * @param {string} jobName + * @param {number} numberOfReqToWait + * @private + */ + _navigateToDataJob(chainable, jobName, numberOfReqToWait = 2) { + chainable + .then((elements) => { + /** + * @type {HTMLElement} + */ + let foundElement; + elements.each((_index, el) => { + if (el.innerText.trim().includes(jobName)) { + foundElement = el; + + return false; + } + }); + + return foundElement; + }) + .should('exist') + .click({ force: true }); + + this.waitForBackendRequestCompletion(numberOfReqToWait); + + this.waitForViewToRender(); + } +} diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/getting-started.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/getting-started.po.js new file mode 100644 index 0000000000..affb579826 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/getting-started.po.js @@ -0,0 +1,50 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataPipelinesBasePO } from '../../../application/data-pipelines-base.po'; + +export class GettingStartedPage extends DataPipelinesBasePO { + /** + * ** Navigate to getting started page. + * + * @returns {GettingStartedPage} + */ + static navigateTo() { + cy.visit('/get-started'); + + this.waitForBackendRequestCompletion(3); + + return this.getPage(); + } + + /** + * ** Returns instance of the page object. + * + * @returns {GettingStartedPage} + */ + static getPage() { + return new GettingStartedPage(); + } + + // Selectors + + /** + * ** Returns main title of getting started page. + * + * @returns {Cypress.Chainable>} + */ + getMainTitle() { + return cy.get('[data-cy=getting-started-main-title]'); + } + + /** + * ** Get Data jobs health panel. + * + * @returns {Cypress.Chainable>} + */ + getDataJobsHealthPanel() { + return cy.get('[data-cy=dp-data-jobs-health-panel]'); + } +} diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-job-details.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-job-details.po.js new file mode 100644 index 0000000000..0c900f725c --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-job-details.po.js @@ -0,0 +1,16 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataJobDetailsBasePO } from '../../../../application/data-job-details-base.po'; + +export class DataJobExploreDetailsPage extends DataJobDetailsBasePO { + static getPage() { + return new DataJobExploreDetailsPage(); + } + + static navigateTo(teamName, jobName) { + return super.navigateTo('explore', teamName, jobName); + } +} diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-jobs.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-jobs.po.js new file mode 100644 index 0000000000..b7091d73dd --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-jobs.po.js @@ -0,0 +1,134 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataJobsBasePO } from '../../../../application/data-jobs-base.po'; + +export class DataJobsExplorePage extends DataJobsBasePO { + static getPage() { + return new DataJobsExplorePage(); + } + + static navigateTo() { + return super.navigateTo('[data-cy=navigation-link-explore-datajobs]'); + } + + // Selectors + + getMainTitle() { + return cy.get('[data-cy=data-pipelines-explore-page-main-title]'); + } + + getDataGrid() { + return cy.get('[data-cy=data-pipelines-explore-grid]'); + } + + getDataGridCell(content) { + return cy.get('[id^="clr-dg-row"] > .datagrid-row-scrollable > .datagrid-scrolling-cells > .ng-star-inserted') + .contains(new RegExp(`^${ content }$`)); + } + + getDataGridNavigateBtn(team, job) { + return cy.get('[data-cy=data-pipelines-explore-grid-details-link][data-job-params="' + team + ';' + job + '"]'); + } + + getDataGridNameFilter() { + //TODO : Resolve better selectors with custom filter + return this.getHeaderColumnJobName() + .should('exist') + .find('clr-dg-filter button'); + } + + getDataGridNameFilterInput() { + //TODO : Resolve better selectors with custom filter + return cy.get('div.datagrid-filter input'); + } + + getDataGridRefreshButton() { + return cy.get('[data-cy=data-pipelines-explore-refresh-btn]'); + } + + getDataGridSearchInput() { + return cy.get('[data-test-id=search-input]'); + } + + getDataGridClearSearchButton() { + return cy.get('[data-test-id="clear-search-btn"]').first(); + } + + // Actions + + refreshDataGrid() { + this.getDataGridRefreshButton() + .should('be.visible') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + + // TODO find better way how to wait DataGrid to be render in DOM. + this.waitForViewToRender(); + } + + searchByJobName(jobName) { + this.getDataGridSearchInput() + .should('be.visible') + .type(jobName); + + this.waitForBackendRequestCompletion(); + + // TODO find better way how to wait DataGrid to be render in DOM. + this.waitForViewToRender(); + } + + clearSearchField() { + this.getDataGridSearchInput() + .should('be.visible') + .clear(); + + this.waitForBackendRequestCompletion(); + + // TODO find better way how to wait DataGrid to be render in DOM. + this.waitForViewToRender(); + } + + clearSearchFieldWithButton() { + this.getDataGridClearSearchButton() + .should('be.visible') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + + // TODO find better way how to wait DataGrid to be render in DOM. + this.waitForViewToRender(); + } + + filterByJobName(jobName) { + this.getDataGridNameFilter() + .click({ force: true }); + + this.getDataGridNameFilterInput() + .should('be.visible') + .type(jobName); + + this.getMainTitle() + .should('be.visible') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + + // TODO find better way how to wait DataGrid to be render in DOM. + this.waitForViewToRender(); + } + + openJobDetails(team, jobName) { + this.getDataGridNavigateBtn(team, jobName) + .scrollIntoView() + .should('exist') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + + this.waitForViewToRenderShort(); + } +} diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-details.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-details.po.js new file mode 100644 index 0000000000..1889ee63ea --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-details.po.js @@ -0,0 +1,123 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataJobDetailsBasePO } from '../../../../application/data-job-details-base.po'; + +export class DataJobManageDetailsPage extends DataJobDetailsBasePO { + static getPage() { + return new DataJobManageDetailsPage(); + } + + static navigateTo(teamName, jobName) { + return super.navigateTo('manage', teamName, jobName); + } + + // Deployment methods + + // Acceptable values are "not-deployed", "enabled", "disabled" + getDeploymentStatus(status) { + return cy.get('[data-cy=data-pipelines-job-details-status-' + status + ']'); + } + + // Description methods + + getDescription() { + return cy.get('[data-cy=data-pipelines-data-job-details-description] .form-section-readonly'); + } + + getDescriptionEditButton() { + return cy.get('[data-cy=data-pipelines-data-job-details-description] .form-section-header > .btn'); + } + + getDescriptionEditTextarea() { + return cy.get('[data-cy=data-pipelines-data-job-details-description] textarea'); + } + + getDescriptionSaveButton() { + return cy.get('[data-cy=data-pipelines-data-job-details-description] button:contains(Save)') + } + + openDescription() { + this.getDescriptionEditButton() + .click({ force: true }); + } + + enterDescriptionDetails(description) { + this.getDescriptionEditTextarea() + .clear() + .type(description) + } + + saveDescription() { + this.getDescriptionSaveButton() + .click({ force: true }); + + this.waitForBackendRequestCompletion() + } + + // Schedule methods + + getSchedule() { + return cy.get('[data-cy=data-pipelines-data-job-details-schedule] .form-section-readonly'); + } + + // Disable/Enable methods + + getStatusEditButton() { + return cy.get('[data-cy=data-pipelines-data-job-details-status] .form-section-header > .btn') + } + + getStatusSaveButton() { + return cy.get('[data-cy=data-pipelines-data-job-details-status] button:contains(Save)'); + } + + changeStatus(currentStatus) { + const newStatus = currentStatus.trim().toLowerCase() === 'enabled' + ? 'disable' + : 'enable'; + + return cy.get(`[data-cy=data-pipelines-data-job-details-status-${ newStatus }]`) + .should('exist') + .check({ force: true }); + } + + toggleJobStatus() { + cy.get('[data-cy=data-pipelines-job-details-status]') + .invoke('text') + .then((jobStatus) => { + this.getStatusEditButton() + .scrollIntoView() + .click({ force: true }); + + this.changeStatus(jobStatus); + + this.waitForClickThinkingTime(); + + this.getStatusSaveButton() + .scrollIntoView() + .click({ force: true }); + + let newStatus = jobStatus === 'Enabled' + ? 'Disabled' + : 'Enabled'; + + this.waitForBackendRequestCompletion(); + + this.getToastTitle() + .should('exist') + .should('contain.text', 'Status update completed'); + + this.waitForActionThinkingTime(); // Natural wait for User action + + this.getToastDismiss() + .should('exist') + .click({ force: true }); + + cy.get('[data-cy=data-pipelines-job-details-status]') + .scrollIntoView() + .should('have.text', newStatus); + }) + } +} diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-executions.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-executions.po.js new file mode 100644 index 0000000000..b64b8540b3 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-executions.po.js @@ -0,0 +1,360 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataJobBasePO } from '../../../../application/data-job-base.po'; + +export class DataJobManageExecutionsPage extends DataJobBasePO { + static getPage() { + return new DataJobManageExecutionsPage(); + } + + static navigateTo(teamName, jobName) { + return super.navigateToUrl(`/manage/data-jobs/${ teamName }/${ jobName }/executions`); + } + + /* Utils */ + + /** + * @example + * + * - val=1m 34s + * - val=45s + */ + convertStringContentToSeconds(val) { + const params = val.split(' '); + + if (params.length === 2) { + return parseInt(params[0].trim().replace(/m$/, ''), 10) * 60 + parseInt(params[1].trim().replace(/s$/, ''), 10); + } else { + return parseInt(params[0].trim().replace(/s$/, ''), 10); + } + } + + /* Selectors */ + + // General + + getExecRefreshBtn() { + return cy.get('[data-cy=data-pipelines-job-executions-refresh-btn'); + } + + getExecLoadingSpinner() { + return cy.get('[data-cy=data-pipelines-job-executions-loading-spinner]'); + } + + // Charts + + getStatusChart() { + return cy.get('[data-cy=data-pipelines-job-executions-status-chart]'); + } + + getDurationChart() { + return cy.get('[data-cy=data-pipelines-job-executions-duration-chart]'); + } + + getTimePeriod() { + return cy.get('[data-cy=data-pipelines-job-executions-time-period]'); + } + + // DataGrid + + getDataGrid() { + return cy.get('[data-cy=data-pipelines-job-executions-datagrid]'); + } + + getDataGridPopupFilter() { + return cy.get('.datagrid-filter.clr-popover-content'); + } + + getDataGridInputFilter() { + return this.getDataGridPopupFilter() + .should('exist') + .find('input'); + } + + getDataGridPopupFilterCloseBtn() { + return this.getDataGridPopupFilter() + .should('exist') + .find('.close'); + } + + getDataGridSpinner() { + return this.getDataGrid() + .should('exist') + .find('.datagrid-spinner'); + } + + // Header + + getDataGridExecStatusHeader() { + return this.getDataGrid() + .should('exist') + .find('[data-cy=data-pipelines-job-executions-status-header]'); + } + + getDataGridExecStatusFilterOpenerBtn() { + return this.getDataGridExecStatusHeader() + .should('exist') + .find('.datagrid-filter-toggle'); + } + + getDataGridExecStatusFilters() { + return this.getDataGridPopupFilter() + .should('exist') + .find('[data-cy=data-pipelines-job-executions-status-filters]'); + } + + getDataGridExecTypeHeader() { + return this.getDataGrid() + .should('exist') + .find('[data-cy=data-pipelines-job-executions-type-header]'); + } + + getDataGridExecTypeFilterOpenerBtn() { + return this.getDataGridExecTypeHeader() + .should('exist') + .find('.datagrid-filter-toggle'); + } + + getDataGridExecTypeFilters() { + return this.getDataGridPopupFilter() + .should('exist') + .find('[data-cy=data-pipelines-job-executions-type-filters]'); + } + + getDataGridExecDurationHeader() { + return this.getDataGrid() + .should('exist') + .find('[data-cy=data-pipelines-job-executions-duration-header]'); + } + + getDataGridExecDurationSortBtn() { + return this.getDataGridExecDurationHeader() + .should('exist') + .find('.datagrid-column-title'); + } + + getDataGridExecStartHeader() { + return this.getDataGrid() + .should('exist') + .find('[data-cy=data-pipelines-job-executions-start-header]'); + } + + getDataGridExecStartSortBtn() { + return this.getDataGridExecStartHeader() + .should('exist') + .find('.datagrid-column-title'); + } + + getDataGridExecEndHeader() { + return this.getDataGrid() + .should('exist') + .find('[data-cy=data-pipelines-job-executions-end-header]'); + } + + getDataGridExecEndSortBtn() { + return this.getDataGridExecEndHeader() + .should('exist') + .find('.datagrid-column-title'); + } + + getDataGridExecIDHeader() { + return this.getDataGrid() + .should('exist') + .find('[data-cy=data-pipelines-job-executions-id-header]'); + } + + getDataGridExecIDFilterOpenerBtn() { + return this.getDataGridExecIDHeader() + .should('exist') + .find('.datagrid-filter-toggle'); + } + + getDataGridExecVersionHeader() { + return this.getDataGrid() + .should('exist') + .find('[data-cy=data-pipelines-job-executions-version-header]'); + } + + getDataGridExecVersionFilterOpenerBtn() { + return this.getDataGridExecVersionHeader() + .should('exist') + .find('.datagrid-filter-toggle'); + } + + // Rows and Cells + + getDataGridRows() { + return this.getDataGrid() + .should('exist') + .find('clr-dg-row.datagrid-row'); + } + + getDataGridRow(rowIndex) { + return this.getDataGridRows() + .should('have.length.gte', rowIndex - 1) + .then((rows) => Array.from(rows)[rowIndex - 1]); + } + + getDataGridCells(rowIndex) { + return this.getDataGridRow(rowIndex) + .should('exist') + .find('clr-dg-cell.datagrid-cell'); + } + + getDataGridCellByIndex(rowIndex, cellIndex) { + if (rowIndex) { + return this.getDataGridCells(rowIndex) + .should('have.length.gte', cellIndex - 1) + .then((cells) => Array.from(cells)[cellIndex - 1]); + } + + return this.getDataGridRows() + .should('have.length.gte', 0) + .then(($rows) => { + const _rows = $rows.reduce((accumulator, row) => { + const _cells = Array.from(row.querySelectorAll('clr-dg-cell.datagrid-cell')); + + if (_cells[cellIndex - 1]) { + accumulator.push(_cells[cellIndex - 1]); + } + + return accumulator; + }, []); + + return _rows; + }) + } + + getDataGridCellByIdentifier(rowIndex, identifier) { + return this.getDataGridRow(rowIndex) + .should('exist') + .find(identifier) + } + + getDataGridExecTypeCell(rowIndex) { + return this.getDataGridCellByIdentifier(rowIndex, '[data-cy=data-pipelines-job-executions-type-cell]'); + } + + getDataGridExecTypeContainer(rowIndex) { + return this.getDataGridExecTypeCell(rowIndex) + .should('exist') + .find('[data-cy=data-pipelines-job-executions-type-container]'); + } + + getDataGridExecDurationCell(rowIndex) { + return this.getDataGridCellByIdentifier(rowIndex, '[data-cy=data-pipelines-job-executions-duration-cell]'); + } + + getDataGridExecStartCell(rowIndex) { + return this.getDataGridCellByIdentifier(rowIndex, '[data-cy=data-pipelines-job-executions-start-cell]'); + } + + getDataGridExecEndCell(rowIndex) { + return this.getDataGridCellByIdentifier(rowIndex, '[data-cy=data-pipelines-job-executions-end-cell]'); + } + + // Pagination + + getDataGridPagination() { + return this.getDataGrid() + .should('exist') + .find('[data-cy=data-pipelines-job-executions-datagrid-pagination]'); + } + + /* Actions */ + + // General + + refreshExecData() { + this.getExecRefreshBtn() + .should('exist') + .click({ force: true }) + } + + // DataGrid + + typeToFilterInput(value) { + this.getDataGridInputFilter() + .should('exist') + .type(value); + + this.waitForViewToRenderShort(); + } + + clearFilterInput() { + this.getDataGridInputFilter() + .should('exist') + .clear({ force: true }); + + this.waitForViewToRenderShort(); + } + + closeFilter() { + this.getDataGridPopupFilterCloseBtn() + .should('exist') + .click({ force: true }); + + this.waitForViewToRenderShort(); + } + + // Header + + openStatusFilter() { + this.getDataGridExecStatusFilterOpenerBtn() + .should('exist') + .click({ force: true }); + + this.waitForViewToRenderShort(); + } + + openTypeFilter() { + this.getDataGridExecTypeFilterOpenerBtn() + .should('exist') + .click({ force: true }); + + this.waitForViewToRenderShort(); + } + + openIDFilter() { + this.getDataGridExecIDFilterOpenerBtn() + .should('exist') + .click({ force: true }); + + this.waitForViewToRenderShort(); + } + + openVersionFilter() { + this.getDataGridExecVersionFilterOpenerBtn() + .should('exist') + .click({ force: true }); + + this.waitForViewToRenderShort(); + } + + sortByExecDuration() { + this.getDataGridExecDurationSortBtn() + .should('exist') + .click({ force: true }); + + this.waitForViewToRenderShort(); + } + + sortByExecStart() { + this.getDataGridExecStartSortBtn() + .should('exist') + .click({ force: true }); + + this.waitForViewToRenderShort(); + } + + sortByExecEnd() { + this.getDataGridExecEndSortBtn() + .should('exist') + .click({ force: true }); + + this.waitForViewToRenderShort(); + } +} diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-jobs.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-jobs.po.js new file mode 100644 index 0000000000..fd5572d8c0 --- /dev/null +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-jobs.po.js @@ -0,0 +1,210 @@ +/* + * Copyright 2021 VMware, Inc. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataJobsBasePO } from '../../../../application/data-jobs-base.po'; + +export class DataJobsManagePage extends DataJobsBasePO { + static getPage() { + return new DataJobsManagePage(); + } + + static navigateTo() { + return super.navigateTo('[data-cy=navigation-link-manage-datajobs]'); + } + + getPageTitle() { + return cy.get('[data-cy=data-pipelines-manage-page-main-title]'); + } + + getDataGrid() { + return cy.get('[data-cy=data-pipelines-manage-grid]'); + } + + getDataGridCell(content, timeout) { + return cy.get('[id^="clr-dg-row"] > .datagrid-row-scrollable > .datagrid-scrolling-cells > .ng-star-inserted') + .contains(new RegExp(`^\\s*${ content }\\s*$`), { timeout: this.resolveTimeout(timeout) }); + } + + getDataGridRow(jobName) { + cy.log(`Looking for Job row: ${ jobName }`); + + return this.getDataGridCell(jobName) + .parents('clr-dg-row'); + } + + getDataGridNavigateBtn(team, job) { + return cy.get('[data-cy=data-pipelines-manage-grid-details-link][data-job-params="' + team + ';' + job + '"]'); + } + + getDataGridSearchInput() { + return cy.get('[data-test-id=search-input]') + .first(); + } + + getDataGridClearSearchButton() { + return cy.get('[data-test-id="clear-search-btn"]').first(); + } + + searchByJobName(jobName) { + this.getDataGridSearchInput() + .type(jobName); + + this.waitForBackendRequestCompletion(); + + this.waitForViewToRender(); + } + + clearSearchField() { + this.getDataGridSearchInput() + .should('be.visible') + .clear(); + + this.waitForBackendRequestCompletion(); + + // TODO find better way how to wait DataGrid to be render in DOM. + this.waitForViewToRender(); + } + + clearSearchFieldWithButton() { + this.getDataGridClearSearchButton() + .should('be.visible') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + + // TODO find better way how to wait DataGrid to be render in DOM. + this.waitForViewToRender(); + } + + refreshDataGrid() { + cy.get('[data-cy=data-pipelines-manage-refresh-btn]') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + + this.waitForViewToRender(); + } + + openJobDetails(teamName, jobName) { + this.getDataGridNavigateBtn(teamName, jobName) + .scrollIntoView() + .should('exist') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + + this.waitForViewToRenderShort(); + + cy.get('[data-cy=data-pipelines-job-page]') + .should('be.visible'); + } + + selectRow(jobName) { + this.getDataGridRow(jobName) + .find('.datagrid-select input') + .check({ force: true }); + } + + getExecuteNowGridButton() { + return cy.get('[data-cy=data-pipelines-manage-grid-execute-btn]'); + } + + changeStatus(newStatus) { + cy.get(`[data-cy=data-pipelines-job-${ newStatus }-btn]`) + .should('exist') + .should('be.enabled') + .click({ force: true }); + } + + getJobStatus(jobName) { + return this.getDataGridRow(jobName) + .then(($row) => { + if ($row.find('[data-cy=data-pipelines-job-disabled]').length) { + return 'disable'; + } + + if ($row.find('[data-cy=data-pipelines-job-enabled]').length) { + return 'enable'; + } + + return 'not_deployed'; + }); + } + + toggleJobStatus(jobName) { + this.selectRow(jobName); + + this.getJobStatus(jobName) + .then((currentStatus) => { + if (currentStatus === 'not_deployed') { + throw new Error('Data job is not Deployed.'); + } + + const newStatus = currentStatus === 'enable' + ? 'disable' + : 'enable'; + + cy.log(`Current status: ${ currentStatus }, new status: ${ newStatus }`); + + this.changeStatus(newStatus) + + this.confirmInConfirmDialog(); + + this.getToastTitle() + .should('exist') + .should('contain.text', 'Status update completed'); + + this.getToastDismiss() + .should('exist') + .click({ force: true }); + + this.waitForClickThinkingTime(); // Natural wait for User action + + this.refreshDataGrid(); + + this.getJobStatus(jobName) + .then((changedStatus) => { + expect(changedStatus).to.equal(newStatus); + }); + }); + } + + filterByJobName(jobName) { + cy.get('[data-cy=data-pipelines-jobs-name-column] > .datagrid-column-flex > clr-dg-string-filter.ng-star-inserted > clr-dg-filter > .datagrid-filter-toggle > cds-icon') + .click({ force: true }); + + cy.get('div.datagrid-filter > input') + .should('be.visible') + .type(jobName); + + this.getPageTitle() + .should('be.visible') + .click({ force: true }); + + this.waitForBackendRequestCompletion(); + + this.waitForViewToRenderShort(); + } + + executeDataJob(jobName) { + this.selectRow(jobName); + + this.waitForClickThinkingTime(); + + this.getExecuteNowGridButton() + .should('exist') + .click({ force: true }); + + this.waitForViewToRenderShort(); + + this.confirmInConfirmDialog(); + } + + prepareAdditionalTestJob() { + cy.prepareAdditionalTestJobs(); + + this.waitForApiModifyCall(); + } +} From 7cd65790c110f0da786cec6473fa9d446628e1ad Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 20 Feb 2023 08:51:30 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- .../frontend/data-pipelines/gui/e2e/integration/app/app.spec.js | 2 +- .../e2e/integration/app/getting-started/getting-started.spec.js | 2 +- .../gui/e2e/integration/lib/explore/data-job-details.spec.js | 2 +- .../gui/e2e/integration/lib/explore/data-job-executions.spec.js | 2 +- .../gui/e2e/integration/lib/explore/data-jobs.spec.js | 2 +- .../gui/e2e/integration/lib/manage/data-job-details.int.spec.js | 2 +- .../e2e/integration/lib/manage/data-job-executions.int.spec.js | 2 +- .../gui/e2e/integration/lib/manage/data-jobs.int.spec.js | 2 +- projects/frontend/data-pipelines/gui/e2e/plugins/index.js | 2 +- .../gui/e2e/support/application/data-job-base.po.js | 2 +- .../gui/e2e/support/application/data-job-details-base.po.js | 2 +- .../gui/e2e/support/application/data-jobs-base.po.js | 2 +- .../gui/e2e/support/application/data-pipelines-base.po.js | 2 +- projects/frontend/data-pipelines/gui/e2e/support/commands.js | 2 +- .../data-pipelines/gui/e2e/support/helpers/commands.helpers.js | 2 +- projects/frontend/data-pipelines/gui/e2e/support/index.js | 2 +- .../frontend/data-pipelines/gui/e2e/support/pages/app/app.po.js | 2 +- .../app/getting-started/data-jobs-health-panel-component.po.js | 2 +- .../e2e/support/pages/app/getting-started/getting-started.po.js | 2 +- .../e2e/support/pages/app/lib/explore/data-job-details.po.js | 2 +- .../gui/e2e/support/pages/app/lib/explore/data-jobs.po.js | 2 +- .../gui/e2e/support/pages/app/lib/manage/data-job-details.po.js | 2 +- .../e2e/support/pages/app/lib/manage/data-job-executions.po.js | 2 +- .../gui/e2e/support/pages/app/lib/manage/data-jobs.po.js | 2 +- 24 files changed, 24 insertions(+), 24 deletions(-) diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/app/app.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/app/app.spec.js index 6d3f4228df..fa088eef37 100644 --- a/projects/frontend/data-pipelines/gui/e2e/integration/app/app.spec.js +++ b/projects/frontend/data-pipelines/gui/e2e/integration/app/app.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/app/getting-started/getting-started.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/app/getting-started/getting-started.spec.js index f13d4c1153..a4225878c9 100644 --- a/projects/frontend/data-pipelines/gui/e2e/integration/app/getting-started/getting-started.spec.js +++ b/projects/frontend/data-pipelines/gui/e2e/integration/app/getting-started/getting-started.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-details.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-details.spec.js index 147e5de69a..de62bba157 100644 --- a/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-details.spec.js +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-details.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-executions.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-executions.spec.js index a3fdd94044..b346dfb456 100644 --- a/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-executions.spec.js +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-executions.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-jobs.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-jobs.spec.js index e079552b17..9877cef132 100644 --- a/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-jobs.spec.js +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-jobs.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-details.int.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-details.int.spec.js index 48a4d339c0..8af498d8b1 100644 --- a/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-details.int.spec.js +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-details.int.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-executions.int.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-executions.int.spec.js index cc1ed08375..20563bbca5 100644 --- a/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-executions.int.spec.js +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-job-executions.int.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-jobs.int.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-jobs.int.spec.js index 4f43de737a..36c8959d53 100644 --- a/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-jobs.int.spec.js +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-jobs.int.spec.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/plugins/index.js b/projects/frontend/data-pipelines/gui/e2e/plugins/index.js index d6d18c30ec..1fd0b4f108 100644 --- a/projects/frontend/data-pipelines/gui/e2e/plugins/index.js +++ b/projects/frontend/data-pipelines/gui/e2e/plugins/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-base.po.js b/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-base.po.js index 231a3aa2f6..54a0782984 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-base.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-base.po.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-details-base.po.js b/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-details-base.po.js index 571663d9bd..609099be4a 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-details-base.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/application/data-job-details-base.po.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/application/data-jobs-base.po.js b/projects/frontend/data-pipelines/gui/e2e/support/application/data-jobs-base.po.js index 6627617e4a..d4b5dd9a4a 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/application/data-jobs-base.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/application/data-jobs-base.po.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/application/data-pipelines-base.po.js b/projects/frontend/data-pipelines/gui/e2e/support/application/data-pipelines-base.po.js index 88db68899f..3cd77e6c1c 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/application/data-pipelines-base.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/application/data-pipelines-base.po.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/commands.js b/projects/frontend/data-pipelines/gui/e2e/support/commands.js index 4f2589f646..d815b4706e 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/commands.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/commands.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/helpers/commands.helpers.js b/projects/frontend/data-pipelines/gui/e2e/support/helpers/commands.helpers.js index e5596b7319..7c5a904835 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/helpers/commands.helpers.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/helpers/commands.helpers.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/index.js b/projects/frontend/data-pipelines/gui/e2e/support/index.js index 8657d4c1ca..dfe8fbb0bc 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/index.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/index.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/app.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/app.po.js index 14b4e0f847..6a0e3e6cbb 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/app.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/app.po.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/data-jobs-health-panel-component.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/data-jobs-health-panel-component.po.js index 902c09c550..13fcc897f5 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/data-jobs-health-panel-component.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/data-jobs-health-panel-component.po.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/getting-started.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/getting-started.po.js index affb579826..0f23a016bb 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/getting-started.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/getting-started/getting-started.po.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-job-details.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-job-details.po.js index 0c900f725c..222bdee83a 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-job-details.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-job-details.po.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-jobs.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-jobs.po.js index b7091d73dd..d48f8e931c 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-jobs.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-jobs.po.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-details.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-details.po.js index 1889ee63ea..f0ac3c8b2b 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-details.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-details.po.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-executions.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-executions.po.js index b64b8540b3..af3c744c6a 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-executions.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-job-executions.po.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-jobs.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-jobs.po.js index fd5572d8c0..65f506d58d 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-jobs.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-jobs.po.js @@ -1,5 +1,5 @@ /* - * Copyright 2021 VMware, Inc. + * Copyright 2021-2023 VMware, Inc. * SPDX-License-Identifier: Apache-2.0 */ From c8f246d46a3bf96088c1781b45f9de458bdcc7e7 Mon Sep 17 00:00:00 2001 From: ivakoleva Date: Mon, 20 Feb 2023 18:07:16 +0200 Subject: [PATCH 3/5] frontend: e2e tests This is a stacked PR #6: data-pipelines e2e tests. End-to-end tests sources added. Testing done: fetched the `frontend/shared-components` changes (from person/mdilyan/os-shared-components feature branch in review), and verified `frontend/data-pipelines` manual start + unit + e2e tests. Signed-off-by: ivakoleva --- .../e2e/fixtures/e2e-cy-dp-failing.job.json | 2 +- .../e2e/fixtures/e2e-cy-dp-failing.job.zip | Bin 6688 -> 6386 bytes .../lib/manage/e2e-cypress-dp-test.json | 2 +- .../lib/manage/e2e-cypress-dp-test.zip | Bin 4853 -> 4605 bytes 4 files changed, 2 insertions(+), 2 deletions(-) diff --git a/projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.json b/projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.json index 6a4c95e6b8..b1019e21ee 100644 --- a/projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.json +++ b/projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.json @@ -15,5 +15,5 @@ "generate_keytab": true, "enable_execution_notifications": true }, - "team": "supercollider" + "team": "taurus" } diff --git a/projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.zip b/projects/frontend/data-pipelines/gui/e2e/fixtures/e2e-cy-dp-failing.job.zip index 38f4fc48dd2cfd556e73ac273d1fde4d1531e4d5..a6c08016ee1625217eaeac16c3ae43ffef6a5e46 100644 GIT binary patch delta 2779 zcmai02{@E%8~(Y!tnIhIUiDa&N+G8w*P&5|YQ zB$1`n)`Ey46m8Cwh#^u*%m0n-@avrOUe`O{cU|xG-uHXI-#yR$F#r#EXnT;|j*#8`$(}AWADZ8O4XW)L04|#`<;I%-3jk68KG|1V4BDDws^-d@3tKPcTf;C*yZpRuk#Y5UKS3D5 z07ynEfSdW8r52a7f*Y(Ae7j$rhL)$GDG%Vms}e*Ws)u~Fvv>N^fj~a^S=8dTO#cS! zeVA;dBiIVBUIOE(wa=5;`5Zc-H5>pGAh7cYCB}j}`PJ&EDaHr-HP=Ruf0Kv`+8*;b zmH_8y=1KLC>xI#KPn60(&~o_Iny7DX_r5MEg-|nfyZXz!cgY=;U`a7-(B>3FJF4UW zp!~Iahvis-flx*GP9yQa*(QSDlrfdg?{hI=4Apf)hN1OGRQW+8xgIK&HY%GKSoOx| z8M-G+_*3cIs{?f`JK{mi!KxTR^$;LIbmB;1y|Q})KMVFBNh4-mVa{*~-wdLq0hU4K zdJSuIX=@)+01;flAf4>a$jcih5`W)W(qH=iTiBF%sK4?H?j(36~-h`|d01V3Fr4Ju22>rVM7H zbeaSt?xg3x>N0R-7Q=iw5B8njpV+1r4UOwm-QjS1v|X4)|2$70fxhUpY_*q?1f{w$L}*T_gT4cDOYaj2TRDu z1ez6uiCL0Z^y0@-$1Qfsy*&vpvy=2UOc(lM0q4^5a^PcAs|vC)6)i5nle9GPrjg=} zbt*ae8*c0HgPDxzF;UWNhz&hMCCICV(6?`sXkm$IASbnmEiOo1~6yUU*a~8<>WTm%$T``6NHr9 z$os_^wNcI>!J!XZ{n6Rzmagq-=kDy?DQy;*p!C7Bd$#R6?uP3X6=s5xB(my zeyRX`(zN@MYUw!9a*H@7~ zL~7jZ>f#r4?Ihtm+Sx7jCV$aS^n^kR$=`;NTKWsC&5Epg2#sN{C9n;m3(n0-e9-F6 z`J>rtgF=3irgMFOv0myV%tneopV0Z)uhVhB;>2lJLe=fQ(?8En-k!SHQFru#Pv^9S zT%5(3;)(yhoFI&JYu>HsC5?MHobgr?u6dxg&$IpYb9bh6QC`oaNelOJ_BQPCc73jk zF2lSGt{gf2TBKBC{KG7z&3DY}x=le%tw3d>OoTes9p51(n3c;oRucJ+))JO|WL%{7 zzKYPDmJ8%7PwTlI_w`2ebavJ9q3tF@1XduCL!!RVL1glrclwE`U!%V2_;Q z-wY~pX0Dx=VNDs?=KHVT`ArI-(e}TlxxWC`rxjt$T2H9Xvm&RX?n4OR~0A z>OoQ_*_XvOnS|P%;*L8#b`O3re9!GI=ro?2n`%15mu65HQxjF-ZQfiMGW}=OEgbR8 z(fCP@*0YH%q3MGjheJtR`Ma-1$bV&~*_mkD*owZAbg0}2K&hk*$O51ak^m-%-dd^? zysCqnX;nVRi!G?~$U${t7f$g98CY3~3WcqxIu>P|vJB8ld{Pz0Ck_9*(1+4`ZioV7 z#sB~&OQL_F+Van5{;Is^0sUR#F9HETVZ_ckpyXpn69{Qp{6i`hItLgZdJ$BkueGocu^7w389f2^L_U;qFB delta 2909 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 delta 2881 zcmai02{=^m9zNC?J0Zr-SS#01miP;a$&#$u88IaM8{M%lQwb%NeTX7kwg{0Wj9s#4 zO{peI)(|b0?m6M#bnoryoadW!&NJtI&-Z=r`@Fy3CUR9m(Aa>6mJ5K6+@MC&A2&Nc zT!2V?1`Rixg*giVLr7O*_`rdXj_+sV zu2BKNkI&5$4UC#mTskG%mrYegx{^h(bR1Z^yJus0Q5Wehe4`Rug?VwHI@4MoaIQ7O z@4RI?`$7RT$T$&OqEzy!oXY5$CnYrWQA@ELfZe8^5!5W;> zhI&SGxS^pGUYDmQqL&VT9F5<82zEy}9AQQ)J-(ZqY&1P*X>clm(41uE+53I1_cKAq zWD6zfY5(xnmwNUCPQA~Mbq-kqLu^!);laZ~t?Nwl47dBk%R}MPoi!3)XpIeEaN>oD zK__U_L2ZtuNIMWr9iXy1GKRABAbj$yzA6CGB#J9DLuc6+v7ZevOR*Bgi zJL1MqP~QXTkY0mRw?O~|`u}wMX#hat2c0zJlU*9<`d>b=h|@ZkhGGw2+=$|+dtfm# z6Of~B9#xQQpC2pcbMrTu>o zUK_ZfiyXwX9vm9@=46QB`Lwlv=<|w>365K!q=JyyiYHq#Cnh+oN>3F_GlsET?zgl} zZ@%BWU<{;}9avdenf580bShT**n&NYLDB{zyk7IqUcA%@u+SN!4D+2;XE2epS2J{7 z6)PsTTy1n*7tBpbfgc|>y5^=u=FX{#c1#EGF-ine z4R7pdMjNl&ne|V?#iUW8kV$xA*d6AYaJ9kwAJe@wX&^u-HrTRVNOFJGczEEg)Gpz#e3ftj5ZW=W9S8Ft+*V=X_FW3C&gF`)5#itp=jI>|3lB52%pr^|`Hwaglia z6LA)&V(AAK?ni{_-$##|A=wGQY46D15tZdSHXXXCE-BH`p{#0Ub!AoFJ$mR<*gh77_QsDmkQ-fQ&EWvSL4kU+_;jnys)WDMwpejaQ@P znQmcb(8|awhN#1wwk~&6Zi_mro)U@8Qjj@X>N5B2&;}{6!(60aCej=~n=g;MHpY7O zNuYEuEJCkm+AT)%vP(gJnC2Eq^a45U@j;^-hM^gw_@%V^ zY=VI*kA0|z`t>%OGy16WrZ!e_l^nM>rz%c0y(JgVwU5bY2HR=52}MaY%hx(*i#GAR zKqQlD_bDu(_mVOXdyDunX6NN{q*U?FT3ua{A>IrhOzLCxJ}bvV&o|Ga5_^dsd4SQo zA?w`z2ku$K^Q=uzIk_;k>tjA5GJVhYg`6bld$5JKAdZmkk0lf#;1Zc4Xf08WGrd)E z)46lRhbK#YQ5t7DMv&gZ`BS9Av^Ud}>i1OjBWkeYa<(kzEVb?Seaw?>pK9fCw7*06 z_tGl1!ga(PA#&=8CE_#UxnGePU35T+X^7nTfW6{3oN!RiN{jV(eu)iEhNwhg!CGyC z3O%ADqK(B%s$W%;`Qvpa^U%jZFWY?!o`%1J4Xq|wy1Ww=&o93g-8|_1Fmx5EBWRfJ zI{)@^9qdwfNQ^*xnbL$8&etqz3_c@?w&%HWj_qY0^~iI-YXscPcQ-u~!edf+%*{lf z=%A9j%Z{ows(S*K7G}`v!E$fzEhw_u5RheCK!fLo8_AyDv+7ak3@?$Jjqh7Gazi5X zQ11#dwX!f^L$TT zd?0af{4Q@4S%Rt5U~t6OPO0-?fI%o@11f4Rn%}lgVC~yJ=C#1mgurGrcz_yCzm$nM*#klEyP{jMNiP8vB1M!U1df?i(r?$IT%7P45aSGxm zAOa+YYC6RHvk*hf|4a_rFv{!(OE(3_C=J9R{Rcc{RR6j-4*KmmfN}o^4oW@2P^XxK r9m6&U81aALpp+04o`NHqWt#)E!T)e@a?#O4uR=SD108ja4SM Date: Mon, 20 Feb 2023 18:12:21 +0200 Subject: [PATCH 4/5] frontend: e2e tests This is a stacked PR #6: data-pipelines e2e tests. End-to-end tests sources added. Testing done: fetched the `frontend/shared-components` changes (from person/mdilyan/os-shared-components feature branch in review), and verified `frontend/data-pipelines` manual start + unit + e2e tests. Signed-off-by: ivakoleva --- .../app/getting-started/getting-started.spec.js | 2 -- .../e2e/integration/lib/explore/data-job-details.spec.js | 2 -- .../gui/e2e/integration/lib/manage/data-jobs.int.spec.js | 2 +- .../gui/e2e/support/pages/app/lib/explore/data-jobs.po.js | 7 ------- .../gui/e2e/support/pages/app/lib/manage/data-jobs.po.js | 2 -- 5 files changed, 1 insertion(+), 14 deletions(-) diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/app/getting-started/getting-started.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/app/getting-started/getting-started.spec.js index a4225878c9..6a3d354a5f 100644 --- a/projects/frontend/data-pipelines/gui/e2e/integration/app/getting-started/getting-started.spec.js +++ b/projects/frontend/data-pipelines/gui/e2e/integration/app/getting-started/getting-started.spec.js @@ -43,8 +43,6 @@ describe('Getting Started Page', { tags: ['@dataPipelines'] }, () => { GettingStartedPage .navigateTo() .getMainTitle() - //TODO : Discuss/agree what should be the assertion strategy of the UI Components. - // Do we assert text directly, or use some other form? .should('have.text', 'Get Started with Data Pipelines'); }); diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-details.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-details.spec.js index de62bba157..e876c43134 100644 --- a/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-details.spec.js +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/explore/data-job-details.spec.js @@ -62,7 +62,6 @@ describe('Data Job Explore Details Page', { tags: ['@dataPipelines', '@exploreDa dataJobExploreDetailsPage .getStatusField() .should('be.visible') - // TODO: do the right assertion (once it gets implemented) .should('have.text', 'Not Deployed'); dataJobExploreDetailsPage @@ -84,7 +83,6 @@ describe('Data Job Explore Details Page', { tags: ['@dataPipelines', '@exploreDa // dataJobExploreDetailsPage // .getSourceField() // .should('be.visible') - // // TODO: do the right assertion (once it gets implemented) // .should('contains.text', 'http://host/data-jobs/' + testJobs[0].job_name) // .should("have.attr", "href", 'http://host/data-jobs/' + testJobs[0].job_name); diff --git a/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-jobs.int.spec.js b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-jobs.int.spec.js index 36c8959d53..9fd6c538a6 100644 --- a/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-jobs.int.spec.js +++ b/projects/frontend/data-pipelines/gui/e2e/integration/lib/manage/data-jobs.int.spec.js @@ -485,7 +485,7 @@ describe('Data Jobs Manage Page', { tags: ['@dataPipelines', '@manageDataJobs'] .executeDataJob(jobName); // TODO better handling toast message. If tests are executed immediately it will return - // error 409 conflict and it will say that the job is already executing + // error 409 conflict and it will say that the job is already executing dataJobsManagePage .getToastTitle(10000) // Wait up to 10 seconds for Toast to show. .should('exist') diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-jobs.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-jobs.po.js index d48f8e931c..373f0c96e4 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-jobs.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/explore/data-jobs.po.js @@ -34,14 +34,12 @@ export class DataJobsExplorePage extends DataJobsBasePO { } getDataGridNameFilter() { - //TODO : Resolve better selectors with custom filter return this.getHeaderColumnJobName() .should('exist') .find('clr-dg-filter button'); } getDataGridNameFilterInput() { - //TODO : Resolve better selectors with custom filter return cy.get('div.datagrid-filter input'); } @@ -66,7 +64,6 @@ export class DataJobsExplorePage extends DataJobsBasePO { this.waitForBackendRequestCompletion(); - // TODO find better way how to wait DataGrid to be render in DOM. this.waitForViewToRender(); } @@ -77,7 +74,6 @@ export class DataJobsExplorePage extends DataJobsBasePO { this.waitForBackendRequestCompletion(); - // TODO find better way how to wait DataGrid to be render in DOM. this.waitForViewToRender(); } @@ -88,7 +84,6 @@ export class DataJobsExplorePage extends DataJobsBasePO { this.waitForBackendRequestCompletion(); - // TODO find better way how to wait DataGrid to be render in DOM. this.waitForViewToRender(); } @@ -99,7 +94,6 @@ export class DataJobsExplorePage extends DataJobsBasePO { this.waitForBackendRequestCompletion(); - // TODO find better way how to wait DataGrid to be render in DOM. this.waitForViewToRender(); } @@ -117,7 +111,6 @@ export class DataJobsExplorePage extends DataJobsBasePO { this.waitForBackendRequestCompletion(); - // TODO find better way how to wait DataGrid to be render in DOM. this.waitForViewToRender(); } diff --git a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-jobs.po.js b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-jobs.po.js index 65f506d58d..1de29752f5 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-jobs.po.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/pages/app/lib/manage/data-jobs.po.js @@ -63,7 +63,6 @@ export class DataJobsManagePage extends DataJobsBasePO { this.waitForBackendRequestCompletion(); - // TODO find better way how to wait DataGrid to be render in DOM. this.waitForViewToRender(); } @@ -74,7 +73,6 @@ export class DataJobsManagePage extends DataJobsBasePO { this.waitForBackendRequestCompletion(); - // TODO find better way how to wait DataGrid to be render in DOM. this.waitForViewToRender(); } From 7363ad3b7f8026958d4fa3b304050f636dca325d Mon Sep 17 00:00:00 2001 From: ivakoleva Date: Mon, 20 Feb 2023 18:25:45 +0200 Subject: [PATCH 5/5] frontend: e2e tests This is a stacked PR #6: data-pipelines e2e tests. End-to-end tests sources added. Testing done: fetched the `frontend/shared-components` changes (from person/mdilyan/os-shared-components feature branch in review), and verified `frontend/data-pipelines` manual start + unit + e2e tests. Signed-off-by: ivakoleva --- projects/frontend/data-pipelines/gui/e2e/support/commands.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/frontend/data-pipelines/gui/e2e/support/commands.js b/projects/frontend/data-pipelines/gui/e2e/support/commands.js index d815b4706e..e1e3ebd8d9 100644 --- a/projects/frontend/data-pipelines/gui/e2e/support/commands.js +++ b/projects/frontend/data-pipelines/gui/e2e/support/commands.js @@ -38,7 +38,7 @@ Cypress.Commands.add('login', () => { window.localStorage.setItem('access_token', token); window.localStorage.setItem('id_token', idToken); - // Set the CSP token in the session storage to enable the Integration testing against Management UI(TMS STG) + // Set the CSP token in the session storage to enable the Integration testing against Management UI sessionStorage.setItem('expires_at', expiresAt); sessionStorage.setItem('access_token', token); sessionStorage.setItem('id_token', idToken);