From 5103624fe00d0c37109d2ca7f696b1bc4a623459 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Tue, 25 Jan 2022 09:33:52 +0100 Subject: [PATCH] :construction_worker: e2e tests for dashboard Including test for signing/singup, projects, files, teams, and misc --- .gitignore | 1 + frontend/cypress/fixtures/fonts/Viafont.otf | Bin 0 -> 11904 bytes frontend/cypress/fixtures/fonts/blkchcry.ttf | Bin 0 -> 56732 bytes .../cypress/fixtures/validuser-sample.json | 3 +- .../01-auth/create-account.spec.js | 11 + .../02-onboarding/onboarding-options.spec.js | 71 +++ .../integration/03-dashboard/files.spec.js | 489 ++++++++++++++++++ .../integration/03-dashboard/misc.spec.js | 150 ++++++ .../integration/03-dashboard/projects.spec.js | 153 ++++++ .../integration/03-dashboard/teams.spec.js | 155 ++++++ .../integration/03-projects/projects.spec.js | 25 - .../integration/09-draw/draw-shapes.spec.js | 34 +- frontend/cypress/support/commands.js | 18 +- frontend/cypress/support/utils.js | 63 +++ frontend/package-lock.json | 93 +++- frontend/package.json | 2 +- frontend/src/app/main/ui/auth/register.cljs | 2 +- .../app/main/ui/components/context_menu.cljs | 8 +- .../src/app/main/ui/dashboard/comments.cljs | 1 + .../src/app/main/ui/dashboard/file_menu.cljs | 23 +- frontend/src/app/main/ui/dashboard/files.cljs | 4 +- frontend/src/app/main/ui/dashboard/fonts.cljs | 8 +- .../src/app/main/ui/dashboard/import.cljs | 2 +- .../app/main/ui/dashboard/project_menu.cljs | 11 +- .../src/app/main/ui/dashboard/projects.cljs | 6 +- .../src/app/main/ui/dashboard/sidebar.cljs | 17 +- frontend/src/app/main/ui/dashboard/team.cljs | 2 +- .../app/main/ui/onboarding/team_choice.cljs | 4 +- .../src/app/main/ui/onboarding/templates.cljs | 2 +- .../src/app/main/ui/settings/sidebar.cljs | 2 +- .../app/main/ui/workspace/left_toolbar.cljs | 15 +- frontend/yarn.lock | 37 +- 32 files changed, 1285 insertions(+), 127 deletions(-) create mode 100644 frontend/cypress/fixtures/fonts/Viafont.otf create mode 100644 frontend/cypress/fixtures/fonts/blkchcry.ttf create mode 100644 frontend/cypress/integration/02-onboarding/onboarding-options.spec.js create mode 100644 frontend/cypress/integration/03-dashboard/files.spec.js create mode 100644 frontend/cypress/integration/03-dashboard/misc.spec.js create mode 100644 frontend/cypress/integration/03-dashboard/projects.spec.js create mode 100644 frontend/cypress/integration/03-dashboard/teams.spec.js delete mode 100644 frontend/cypress/integration/03-projects/projects.spec.js diff --git a/.gitignore b/.gitignore index 8edf733f8..67358d3dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *-init.clj *.jar *.penpot +*.orig .calva .clj-kondo .cpcache diff --git a/frontend/cypress/fixtures/fonts/Viafont.otf b/frontend/cypress/fixtures/fonts/Viafont.otf new file mode 100644 index 0000000000000000000000000000000000000000..40a8470b486cc5838fc788db6828858a4dace12d GIT binary patch literal 11904 zcma)hcR&=^^Z47l+r8s0EM+~Sck%ATjtx}MU~h;O6ch_6geDe1#5;RelxB@4me?Zp zj*3cDMA2BWMony(;>Vc8BS|ncY`s=FOWoGjA?5A|jNCWDe01 zzaGKCwoa9~u7tQ62=TG^4hs#xJLu<~goq0WAqzsoTebE&{XCQq&W81vxCyao-8$D@ zM2N0AA=+JI5@O@4&YwuZz9NLcG1#Dw)-A;HYlH@4CS*)8oVWTAqQ8gZL&hbfr;tRF zjQG1^fAVYqKx zv(fGy-w>_#Iku2%4NuO0|E*f~&nDe1XSh%Ihx<91meQ$tCm%5)7^4ns_v_P z-y{V2z&Fuorg6&%kA)&l7S?mBzuZIQ%J*ha8{X`Hl(_Bj`WEPJZ` z+-r?0A)AvUFY@SL`Y4;4GDq~pr>3mIa?_f!mUt4snzHVLF+7&L)wByAwCl+r5>nId z@j<&MmYd?Pb2=BK*^_)xQ|5?79@dmKSpKD^tVM1rYRb9~#_&Yq%r))82km;Yg=-oy zCV7%AF*PN_hTr5ATgI3KTiWDL$0x_x;!`KYCa1I*o=(y;w{vwG`5l48?Ekzw-y<_#_-X9^2AMFbO3CkxGBu zl-uE zlZ@2HIQdrR#9=AfxsvJ?RnM%R^WPO_;JjE|MS^n`>0}&Yj>Va3dor$b@&{N_5SxlK z4o6QyKAnGBd?Rr0HS=S2=bd%J3DrYB9QQ#>b@TA3kzKn*juo;?Br+qM5B zs6*#Y9S4U;3>li3eM7n?7+k1W=IZ9od+0p9mSvwW9rwv|CzH{=8x-IthRbS z^&2#7)VN7g-)7BQv~1<)-?~lPK3)12lLhB5FJGOx_}J0H6UR$0?caCc;Ne3BM|SMm zxqI)Pty{j@mN8>Y@`MnwF*PN2$y74;3YkX8*ngRvcQq-2piIcbbURs^vv&5J;NGKS zqDOs0Hq9q9?ZrjK~g{p$w^X1?vTgi2l6xd zoxCLqsU%gLKi8cL=caHgG^Lscn(uA3Y$lt<=3{GQ^Rorn_SEz7@nV{)DwRBO!wqdh zTA)I;L#6A23fBi!Ybc2%BXOgrlg(tClimTOcMR#BCO61k@`U_Io|9Ju@PxlZdfm9e zAJCI+wQV2UY)*Qg)X?LQ9#{1SDKqyXISCt?Xlc`l_@OuK=@UN;ATS(O%gkuQj z5$+&SYt z0oV8?DIpt4DX#hw`G#D@y*x)Yk!UiCILK(!rzv<`C!>bNp@AUWkm`k~Ulp_;5wp&Z zm^HtnCgPu_1@U*nWYS2JPa1LlRm(K%uxuyoo$dLggIZSSIonncU$tG^jkH$rXa|sn zT2E3-Tc0%7%A}Ta48{3~|9|5C06*ez@`3o@<9`3ud_Y6R{Xgg+FPuNpn1#IC$skQ0 z@?Z9KevbkYub>$Dsa> zY2t))Nar=;j3IS(E1bLsqHO#Se38#)I3`40r-q@GPobIJzU4 z5b9{Fke@ijKN4XA>8Ck~_%0x?uSullF=@=XlKR@|xIYNG#wbGs=RT=>)PmH$f*Td^k_Ns2E@V@>1 zw;5@yMfhLwAg&MTR`dH`+D_j8A3jx{Id}50b`2;AQdq^SzrTimY~q%+EeXgZJ< zD!gAGw$0H7lbHx(HLA`cykC#AjC`y6tBzN_o%&aGM2$QD*FW@sXc;(SK&z{c))BsH zK3at9|9nqAA~*iIL9P@2pRe(K&wXtfiGfpDcR!b+`VW!Go8(QsBAa<6573 z5rN|bgPUiYpq43<$B*wZc(k8?+qS(Xr>FjZ2YFMi^}}#w-NqW)QUAXc*C2qlk!$4i5vy zyJ%ol*^N>6NZqP#1civ!nK%f>zht)4`J-crAt+4rmL!%V@f;diwE1KgLEnjHTn!k| z0nH*LjYEr0CUayyK@*Mk8ci~nMKG2^%Z^4Iy#N|@wAf@XL4S)5o_tNvh?AKF-2kaa z$N@t35Ol~G0gxkv9OB4vLXL9e1R?0TPIBm<(a4ix4qXKL1oDiKazdUG@<^jPyX}O0 zOUQSG+~dgiggkKGSgvc(qmx@2^eE&ahkgRx1^O5=fkR(|4hH=ax(m{kpi?1R2-#22 z$DrFGI|#ZL)khvA=$Ft9kp=|A2;z%(LBx#efnw96qN)zhlgPvyoxX{DjEZZ+5e-oN zn&9B(&X~3}X-fi0dkjK4qEqaNv2O^v!~Pf(hdHC)p%@8|z)c#3!u}K^(nQ=9)k&tH z^Ge74v!jf!Z~W)s|`h(2dbO(kXl`zCXW^ zKh3`sw1T(LMi?q&2w&)f^d0p>^wIj$`rG>Z`XBVa8;Bv;5N}8}WE!>^-njI4DWnG4 zn{K0p;$-oVEAQ&zYIbez8sys7HN`dC^>f$Vt|weyy8h$V&aK$(hTAQ-J8s{)-FN%W z?V;Oax2JB;+2qnVv|jpB+9Yj} zzLri)#ZrlMUb-NCBVCrRO4p?u(k$vpLtk4!aWvwEcVFq$nnVYSmv?9W0l9+O0)DX zO~N<+x24Nz2F2=px`@uCE9vKS1r4FQ=~`;1gXm~Fg62~PT}HF%D!QGX&7v)-6har{ zr1vwXIp^#~L~Ci=|7+IHf9JKO(`buqXbopzF>3^kyh@-6^Z|eQ9>gf4jB;V|v17$2 z_l=D1z{awAOkxWyN-nf!D+K5XhO0ndn!uB3o-uEWe2Yk0Y#(g^gex1*y{}JCs}maBxr#-r6H@&IpBB zJSzcjC^{wjbQ{)xn)3 zlVZm>;;l!OqsDADpA|TmmnBD^y?AP>>63ic65^rRjW=(A_XW!Q2Ut$rfCjKQ+axvI zXjhIxQ$*Vi(FST^k8;F#K5NMM)P%9=2@_84KeBJ*A!|$}qJ{ZTuo1j0S^Dg}4cko* z)1W1bXU%%mtH=14jDr6+mZ)B=0aMYw%7o^M7qs&NGla8X81X%Ry1@YX2W-eNvX4RA z3O>e1_cMI_ZQ_oY&A6dk*Bm=}%>>4JPnnS!>jf}l-`zvMns1SRm^f>6Y8#Zj-vSDh*l-V<;F0y0@)0fR&`=v=@ zjW*ep6X451UvCH%pxY%{IhXmuHbMHD{_RsvrEWGm@)Carh98BnC%C!mp|1A$N+YBo zr*)yO08Q820|9g<;K%&N!dF<1LNtW2=qIdh;4FO}{d_1(5g-;m9Yt}wJW-)&YX)}beXX0 znH4pu_JW0ehe$cEO8^Od~tQte~rW zgYk+Lu zr%udKCx!^mS-s<|5%V`O|7g~*C99{-1P@W-@dAwdWlOxWfW|y~$*7g>IV@!N6Ly+Y4QI<{?NKXo(g94-0Yb88B$^ z1x7ZT@i3b$0t5EV0-nu+h0K6T)Uk~nHafVk7-s|@;aAwmvz|`ff+%R{)UO5lw6&RtX(3IhLA3^Hym0fx2;P$AL1if;L&E-+@l>yLM2|%{6EM=gb z)1jT=ET>1CE2J1ur8GWtTQeKF#%!{^p&?@9xr`2?~i77 z*o%Il0o3x^3C;UJI2L=#M=Rpy8K-aR`$s~_XP@8vWz`oQw%4A$j=A)mz1C206~wb9Mkcvc6H5kgMuExBoyE{W)=>Cj)#wgBWkVoBO1&ic&2}ZreXn7f?lPUDa>K{ zM(73k1(dxzh&}I)QFvT2I0uV{gB1Q``A5~_F{)fvTbTs;*f&Y>vqRbsUPTba>OGXp zfGB3;Lr4?kC#65Is5XLJRw}^qXU2Q1&I8=l2J8awBMb#HfRWxnhA<6ZUy#@g<@+;K z?h0=!rW#Wh%$R96%X2zmLlo_V3g)qoN=n~D^!y8`^HM(jM74ZR&Y=~nJZ!}3CseBJ z!bWaXCS9Y&59uv7@(G;^jg0lFMSe1lS|t=Sya9g>PQh?3y4*R&tV3(IUA$r_xv;0` zvI$6=%dAPaUh}4eSeCPP{Fd{3R-Z6mKR9GrH(19T8rPgV=}hb4hO zym?BGsOUz+RwH(0;h@36hJ@%TN$I8$2j^Ztv1j#`^G%}W;wq+eMU^rqno*h*fF^)E+c#In{&JLZSDcHdmyi_keQmeQgHo_;+lm!UifP>G; z`W0=Iuot}ea+=J%c-DCqtBsb&Vrp_d@Q+cJ2sT=^_{Z@iWvyP5y~d2e#9HJ2&3pH4 zvc$7S{OolLzubu4%#aPWp&teudT7R>K?&C?mn!-i*Nlu^mS|=@m&j{?Q=vB6nSRg+yji0)`g13bKijio(VVT8<>1RpXExbyI*%-za&Ql*wxSF4GqQv? zkbw3hP=MKRY9xip>=e%$qjd?d$HJKo!h6Din;5@HyI4Hvl{Mh`E9g1pT1AeLnL9-? zce{0A0^e4c%slyWp>yR(e)N3D^pR#?VG7gpsNPWf_tW6M$$C#2$wO0NGkEf>sj&PY ze{AK(bw%cv!k3`u(LXE3atd{o%|=$2Rj3Zp#_EEM=G@ckEVN-1`rLqi%mrG>_mmtI z!^XY?J_|E5Yx{wXqt=Z%JkBaV4@sIZJjB%I)F7xc$dV)r=#`ImgE6~E~>m9c*%q=xT$|4%;ip;Mit>tXye=|D=HY5bIo_*oYDITMbgZ8>bp z-?+74y+ta|zS|YG!~+I_=VRnuNxmabN~6D4-Zp|qgg!F%$m!EZj-Iy4dyBy{RA6qt zO)&~~d+7^q*6PW=N*|mU4IkYAH;C?bM@}MX^tw7YNg$Mh5qJ0-u2WMT1_{`9Y&i44OE+juJ?Tfes!8Erd7VX zLdzjad$*#C4;{PyEk-gtbV1kmwbJ{&*XxQLbgl7vUC`^X+)BIAro4Qsmz=JWyQ2Cn zUiC#7LCsJ)K7bAnxbg?k@xB)FDq0&)97v3cN=%9xnY91Ji30~tSRd$G_3YWIWm)j^ zXLqmv=f`fh+gPQ%nRv$#U)+8H6l!r`gfMMI4+u9#Mhr^tYYrN8=(}-NXe1nc!LtM* zYjyVOm8LD(t8!LbV4-jX8`lZDVw#dYKWCn0q}udGU~P-QwJmfp-zwX%|Dsi@jE8kk={}UoKBz+5))${x zeEQJ{+MOoRj)y-XTO%Lvk)dtJ2ASC~ z)&iR2oI$AJt?|au5huB@UiC-x4w`Mv64+?P#7kjxDxFO;X(COaL+J$i2sDG~8oH1M z(@;7P@7RGfoDQM=>13*$Ua7|9BaT2Ar?i83r9nHYyk_yusnid9% zyMKr~2?@?d!eJHjH4s*3nH4=a>qJmeb>?JhqrZR8|h;@iV9(rx)qvg`hz69y%Q# zHcgpqhfIu`lo(^VfM(l>X4r^E5igvX{C@fcOu~y)1N(hwx!U0+FRSROj5J1sL}rAW z<(PrYcCDYe-%xvGu$8sZpSfK8)j6}g>>}7gE}Ji(*>t+h3T;$vW^h-d+O5F<1F3u^Y`spw9EYH z(9VliIcC??y@?p)yeY6Nj~!fvsQhJQW7G%6eDVo6ODyRjmLLghNe>@l50+pIFTFVe z%`SodlIoXUf^V%S@DeY%zR-;M3b0UDQ)2VjLt}*i`gejL^`U)e2YeROotPV_&MRbR zR}9J)Sj%j^Qe3Md&A34(C2CH|3$sX8%pLh+u4o))eWqiwY87Up?&6P9e{=3!eXa=? z#C7F*V=llW!&=<;<3b!EKBoB2k3JH9L5ho8@9@elat{2%;Vfe51DBeWC3 zh2g>|VY#qg*eq-p{?Kdm2EB*Ay}pk=O+QsXSHD#Mh5nlUj{Y}&g+VZQ8fqJ?hIWRI zh5?31L#$zfVUodN*kw3mC^B3&lo|eZY3LH_GTCL8%QBbGUADM<<8saAvCEGxzflbp zs7PgMrdHYn4|W($rFocX$;S-M30h3A(mV7%{h7WJ4WcA^icLg6F+lu8>@0Q{2Z|Bm z7;%O;Tg(zyh^xi5;x2KYct|`Zo)k}uCE^Y7Tk(-tF8)V+VuppU;^yW9E!B%mzIX#LD40{JT)SGBS(5 zN%i7JZQV}PfPPIP#dPsu048m?fN6@6NqXqup!Kk`J-hW zf~#l+L#(*;$7j@)m)a<-R-QJb5Rd7I=2Wccrf^0U#oSpSzN0V@Q$+>OU?Tfb&un|+ zN=lERP?uP8;YS|Q1u@`u@DD%TDtr30N10#i?!f_;7v2YV?K*fMe|$nh+JwY}@jDM% z#8k@Ke@RQb+JD1X;Jo8HWxBJCpZ2o^vaV`GdJbIp+4~PIJZv6C!6O(n^)Fh_;(xQ{E3QT)n^2!t`twKV!@M z?K@3U3Vn2q=EK<5MYJ)b$v16OZr)ni?nCEf&CMIS)*#!KO`D!G%N#i@GQeTIFNmA$ z6=4o>HU0A%{Te1VEuw5Vq{}zkD%pL*;VZn47pU<+w31>PYmB7>7jPf~am( zc4Om|T0T^n;teC(>e~lU@m=TuDza$S9>pIa4nY!w9ejOLV*^uLaY}1p3(#{rH zIdP`wPL4H~j;kDQT#Clx+>#>;#+VXDXJ=bFJroMzpfQrN#A-{unR*?B1Yf7MKGB2r z^p*<_#BE3(ZyFdsq5b%=%T|uD+|ke3y*+c2Ssrm<%i8?)GqQ+>ib%|ca8-5lqAH7M7^z2X8*!bw`^+$OYc2VCx zx6k#*Ch5%g6mdj_!hi&bk}vvExh=CP`+ewY%<`S3n@|*uXytGvvJG{f8T5)P#Ku&% z!I=t85N%AsT^Ti)7Sg}YP=ESX6=Er?V+a2Ww8ERUG{Pu>O@w6r_e=ZVg1f01ji#R+ z&d&MFg4eTG#?v$%Q5Dcxv=L=zltF{(S>(sJh>oFhd+`2S?}J^q@iyiG`9T!VCGvEa zcB9M>Zr~|%-B0Jxd2@5-nP=V;q(XW~_4ZzHP<8kIG#5e+P^;Wg_4t+z^Z~l`dr-H0 z7xlsf!YG&n3G&NBiXUD#?z8Q1O8*&^zh|d%cvhg7UqV6oUV85a)a_L+NZme+PTEmv zDP?i2HpDHU;`SBmS8dvRe9~TX$*GcO8?7Q9ZPYS|7V*-FwhD%b4Y$}g+m+2VI;4eg zxln1JYdk?&0NlHEx za5fC^3&ZdU!e1dH#G_<`+zJ{*8*a73ZIIE3aH`z`or<^c{X2Qu51ab2TM?OmM}NST|gyu zWWF;nz&c<%-ei{mj^hEx<2;7*8RFrkzFG7j#Ay`|Z@@VA1^fa(Lj}*UT$@>WOhd=L z3&Vu5Sudf}fjkU(^6pUlsmGyJ+D@S?k2Qll=D;xy>}-b0lZDEo^+O&oLO}f;0z=W5 z_%nFMrap_|;S&LWI*H*iI1{R?^&#dBHsBES z>q;g><1aH1s&)Q6gVj0^T{#@%!8(F;GaJLJ-|>tTdsp;>0qmv#x0N;gyGYg%UJFtH zRb``?ycJCanj41Z2GHC{5S8Ol`BD$WU8*gr2-`!AJ*kqdmyD0D^={ujB&5AHK)gzK zz=mxY15iOS?%FzO{P;;33+dd)vYO^nN+ied?93W-&|EmuQE4 z2PbYpQU(}^r>+A8XrY60*oZ${8lawPCGz<#Xo!X!rs7$YmL2*#L_iV21E|XtDRJmw z(DY$aL#&+22HF4iad7AHSEutV(Fk=#)`^V<+RUyrMgoU{W;wh>U!-QiV6p*oedYB& zW)+W9FA>^fy(#lILIXkRaEY=8f>;q|6oUf;#d-7#Dhf;x(NJI`+S^{*!%A0Y#uyoV zO79{I5A8~GTn|KfiC>xT6qfkuZ3i>7iB{*PKE&@Tg9~Yzi}WQX^hU08tV%|a+xSx gN5o7**;u;X^(wy84OJybK@*HEQJl!?3L+u@2NV0QmH+?% literal 0 HcmV?d00001 diff --git a/frontend/cypress/fixtures/fonts/blkchcry.ttf b/frontend/cypress/fixtures/fonts/blkchcry.ttf new file mode 100644 index 0000000000000000000000000000000000000000..cca50917c0378b525e5f19603b86aa17c670075d GIT binary patch literal 56732 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 F{yzig5I+C_ literal 0 HcmV?d00001 diff --git a/frontend/cypress/fixtures/validuser-sample.json b/frontend/cypress/fixtures/validuser-sample.json index 6bf5894a9..7301b2b7d 100644 --- a/frontend/cypress/fixtures/validuser-sample.json +++ b/frontend/cypress/fixtures/validuser-sample.json @@ -1,4 +1,5 @@ { "email": "validuser@penpot.app", - "password": "password" + "password": "password", + "team": "test team" } \ No newline at end of file diff --git a/frontend/cypress/integration/01-auth/create-account.spec.js b/frontend/cypress/integration/01-auth/create-account.spec.js index 84d390013..3cc2b5269 100644 --- a/frontend/cypress/integration/01-auth/create-account.spec.js +++ b/frontend/cypress/integration/01-auth/create-account.spec.js @@ -23,6 +23,17 @@ describe("account creation", () => { cy.getBySel("register-form-submit").should("exist"); }); + it("create an account", () => { + let email = "mail" + Date.now() +"@mail.com"; + cy.get("#email").type(email); + cy.get("#password").type("anewpassword"); + cy.get("input[type=submit]").click(); + cy.getBySel("register-title").should("exist"); + cy.get("#fullname").type("Test user") + cy.get("input[type=submit]").click(); + cy.get(".dashboard-layout").should("exist"); + }); + it("create an account of an existent email fails", () => { cy.get("#email").type(validUser.email); cy.get("#password").type("anewpassword"); diff --git a/frontend/cypress/integration/02-onboarding/onboarding-options.spec.js b/frontend/cypress/integration/02-onboarding/onboarding-options.spec.js new file mode 100644 index 000000000..1db5d14d1 --- /dev/null +++ b/frontend/cypress/integration/02-onboarding/onboarding-options.spec.js @@ -0,0 +1,71 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) UXBOX Labs SL + */ + + "use strict"; + + describe("onboarding options solo or team", () => { + beforeEach(() => { + cy.demoLogin(); + cy.get(".modal-right button").click(); + cy.get(".onboarding button").click(); + cy.get(".onboarding .skip").click(); + }); + + it("choose solo option", () => { + cy.getBySel("onboarding-welcome-title").should("exist"); + cy.getBySel("fly-solo-op").click(); + cy.getBySel("onboarding-templates-title").should("exist"); + }); + + it("choose team option and cancel", () => { + cy.getBySel("onboarding-welcome-title").should("exist"); + cy.getBySel("team-up-button").click(); + cy.getBySel("onboarding-choice-team-up").should("exist"); + cy.get("button").click(); + cy.getBySel("onboarding-welcome-title").should("exist"); + }); + + it("choose team option, set team name and cancel", () => { + cy.getBySel("onboarding-welcome-title").should("exist"); + cy.getBySel("team-up-button").click(); + cy.getBySel("onboarding-choice-team-up").should("exist"); + cy.get("#name").type("test team"); + cy.get("input[type=submit]").first().click(); + cy.get("#email").should("exist"); + cy.get("button").click(); + cy.getBySel("onboarding-welcome-title").should("exist"); + }); + + it("choose team option, set team name and skip", () => { + cy.getBySel("onboarding-welcome-title").should("exist"); + cy.getBySel("team-up-button").click(); + cy.getBySel("onboarding-choice-team-up").should("exist"); + cy.get("#name").type("test team"); + cy.get("input[type=submit]").first().click(); + cy.get("#email").should("exist"); + cy.get(".skip-action").click(); + cy.getBySel("onboarding-templates-title").should("exist"); + }); + + it("choose team option, set team name and invite", () => { + cy.getBySel("onboarding-welcome-title").should("exist"); + cy.getBySel("team-up-button").click(); + cy.getBySel("onboarding-choice-team-up").should("exist"); + cy.get("#name").type("test team"); + cy.get("input[type=submit]").first().click(); + cy.get("#email").should("exist"); + cy.get("#email").type("test@test.com"); + cy.get("input[type=submit]").first().click(); + cy.getBySel("onboarding-templates-title").should("exist"); + }); + + + + }); + + \ No newline at end of file diff --git a/frontend/cypress/integration/03-dashboard/files.spec.js b/frontend/cypress/integration/03-dashboard/files.spec.js new file mode 100644 index 000000000..ff385c7a0 --- /dev/null +++ b/frontend/cypress/integration/03-dashboard/files.spec.js @@ -0,0 +1,489 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) UXBOX Labs SL + */ + + "use strict"; + + import { + + createProject, + deleteFirstProject, + deleteFirstFile, + createFile + +} from '../../support/utils.js'; + + + + describe("files", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password); + createProject("test project" + Date.now()); + }); + + }); + + afterEach(() => { + //cleanup + deleteFirstProject(); + }); + + + it("can create a new file", () => { + cy.get(".grid-item").then((files) => { + cy.get('.project').first().find("[data-test=project-new-file]").click(); + cy.get("#workspace").should("exist"); + cy.get(".project-tree").should("contain", "New File"); + + //Go back + cy.get(".main-icon a").click(); + cy.get(".grid-item").should('have.length', files.length + 1); + }) + + }) + + it("can create a new file inside a project", () => { + cy.get(".project").first().find("h2").click(); + cy.get(".grid-item").should('have.length', 0); + createFile(); + cy.get(".grid-item").should('have.length', 1); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can create a new file inside a project with shortcut", () => { + cy.get(".project").first().find("h2").click(); + cy.get(".grid-item").should('have.length', 0); + cy.get("body").type("+"); + cy.get(".grid-item").should('have.length', 1); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can delete a file inside a project", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + cy.get(".grid-item").should('have.length', 1); + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-delete").click(); + cy.get('.accept-button').click(); + cy.get(".grid-item").should('have.length', 0); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can cancel a file deletion inside a project", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + cy.get(".grid-item").should('have.length', 1); + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-delete").click(); + cy.get('.cancel-button').click(); + cy.get(".grid-item").should('have.length', 1); + + //Go back + cy.get(".recent-projects").click(); + }) + + + it("can delete a file outside a project", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + //Go back + cy.get(".recent-projects").click(); + + cy.get(".grid-item").then((files) => { + cy.get('.menu') + .first() + .trigger('mouseover') + .click(); + cy.getBySel("file-delete").click(); + cy.get('.accept-button').click(); + cy.get(".grid-item").should('have.length', files.length-1); + }); + }) + + it("can cancel a file deletion outside a project", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + //Go back + cy.get(".recent-projects").click(); + + cy.get(".grid-item").then((files) => { + cy.get('.menu') + .first() + .trigger('mouseover') + .click(); + cy.getBySel("file-delete").click(); + cy.get('.cancel-button').click(); + cy.get(".grid-item").should('have.length', files.length); + }); + }) + + it("can rename a file", () => { + const fileName = "test file " + Date.now(); + + cy.get(".project").first().find("h2").click(); + createFile(); + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-rename").click(); + cy.get(".edit-wrapper").should("exist"); + cy.get(".edit-wrapper").type(fileName + "{enter}"); + + cy.get(".grid-item").first().should("contain", fileName); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can duplicate a file", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + cy.get(".grid-item").should('have.length', 1); + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-duplicate").click(); + cy.get(".grid-item").should('have.length', 2); + + //Go back + cy.get(".recent-projects").click(); + }) + + + it("can move a file to another project", () => { + const projectToMoveName = "test project to move " + Date.now(); + const fileName = "test file " + Date.now(); + + createProject(projectToMoveName); + cy.get(".project").eq(1).find("h2").click(); + createFile(fileName); + + //TODO: Bug workaround. When a file is selected, it doesn't open context menu + cy.get(".dashboard-grid").click(); + + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.wait(500); + cy.getBySel("file-move-to").click(); + + cy.get('a').contains(projectToMoveName).click(); + + cy.getBySel("project-title").should("contain", projectToMoveName); + cy.get(".grid-item").should('have.length', 1); + cy.get(".grid-item").first().should("contain", fileName); + + + //Go back and cleanup: delete project + cy.get(".recent-projects").click(); + deleteFirstProject(); + }); + + + it("can move a file to another team", () => { + const fileName = "test file " + Date.now(); + cy.get(".project").first().find("h2").click(); + createFile(fileName); + + //TODO: Bug workaround. When a file is selected, it doesn't open context menu + cy.get(".dashboard-grid").click(); + + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.wait(500); + cy.getBySel("file-move-to").click(); + + cy.getBySel("move-to-other-team").click(); + cy.fixture('validuser.json').then((user) => { + cy.get('a').contains(user.team).click(); + cy.get('a').contains("Drafts").click(); + cy.get(".current-team").should("contain", user.team); + cy.get(".dashboard-title").should("contain", "Drafts"); + cy.get(".grid-item").first().should("contain", fileName); + }); + + + //cleanup + deleteFirstFile(); + cy.get(".current-team").click(); + cy.get(".team-name").contains("Your Penpot").click(); + }); + + + it("can make a file a shared library", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + cy.get(".icon-library").should('have.length', 0); + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-add-shared").click(); + cy.get(".accept-button").click(); + cy.get(".icon-library").should('have.length', 1); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can cancel make a file a shared library", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + cy.get(".icon-library").should('have.length', 0); + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-add-shared").click(); + cy.get(".modal-close-button").click(); + cy.get(".icon-library").should('have.length', 0); + + //Go back + cy.get(".recent-projects").click(); + }) + + + it("can remove a file as shared library", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-add-shared").click(); + cy.get(".accept-button").click(); + cy.get(".icon-library").should('have.length', 1); + + //TODO: Bug workaround. When a file is selected, it doesn't open context menu + cy.get(".dashboard-grid").click(); + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-del-shared").click(); + cy.get(".accept-button").click(); + cy.get(".icon-library").should('have.length', 0); + + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can cancel remove a file as shared library", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-add-shared").click(); + cy.get(".accept-button").click(); + cy.get(".icon-library").should('have.length', 1); + + //TODO: Bug workaround. When a file is selected, it doesn't open context menu + cy.get(".dashboard-grid").click(); + + cy.get('.menu') + .trigger('mouseover') + .click(); + cy.getBySel("file-del-shared").click(); + cy.get(".modal-close-button").click(); + cy.get(".icon-library").should('have.length', 1); + + + //Go back + cy.get(".recent-projects").click(); + }) + + + it("can search for a file", () => { + const fileName = "test file " + Date.now(); + + cy.get(".project").first().find("h2").click(); + createFile(fileName); + + cy.get("#search-input").type("bad name"); + cy.get(".grid-item").should('have.length', 0); + + cy.get("#search-input").clear().type(fileName); + cy.get(".grid-item").should('have.length', 1); + + //Go back + cy.get(".recent-projects").click(); + }) + + + it("can multiselect files", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + createFile(); + createFile(); + + cy.get(".selected").should('have.length', 0); + + cy.get(".grid-item").eq(0).click({shiftKey: true}); + cy.get(".selected").should('have.length', 1); + + cy.get(".grid-item").eq(2).click({shiftKey: true}); + cy.get(".selected").should('have.length', 2); + + cy.get(".grid-item").eq(1).click({shiftKey: true}); + cy.get(".selected").should('have.length', 3); + + cy.get(".grid-item").eq(1).click({shiftKey: true}); + cy.get(".selected").should('have.length', 2); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can delete multiselected files", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + createFile(); + createFile(); + + cy.get(".grid-item").eq(0).click({shiftKey: true}); + cy.get(".grid-item").eq(2).click({shiftKey: true}); + + cy.get(".grid-item").should('have.length', 3); + cy.get(".grid-item").eq(0).rightclick(); + cy.getBySel("delete-multi-files").should("contain", "Delete 2 files"); + cy.getBySel("delete-multi-files").click(); + cy.get('.accept-button').click(); + cy.get(".grid-item").should('have.length', 1); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can cancel delete multiselected files", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + createFile(); + createFile(); + + cy.get(".grid-item").eq(0).click({shiftKey: true}); + cy.get(".grid-item").eq(2).click({shiftKey: true}); + + cy.get(".grid-item").should('have.length', 3); + cy.get(".grid-item").eq(0).rightclick(); + cy.getBySel("delete-multi-files").should("contain", "Delete 2 files"); + cy.getBySel("delete-multi-files").click(); + cy.get('.cancel-button').click(); + cy.get(".grid-item").should('have.length', 3); + + //Go back + cy.get(".recent-projects").click(); + }) + + + it("can duplicate multiselected files", () => { + cy.get(".project").first().find("h2").click(); + createFile(); + createFile(); + createFile(); + + cy.get(".grid-item").eq(0).click({shiftKey: true}); + cy.get(".grid-item").eq(2).click({shiftKey: true}); + + cy.get(".grid-item").should('have.length', 3); + cy.get(".grid-item").eq(0).rightclick(); + cy.getBySel("duplicate-multi").should("contain", "Duplicate 2 files"); + cy.getBySel("duplicate-multi").click(); + cy.get(".grid-item").should('have.length', 5); + + //Go back + cy.get(".recent-projects").click(); + }) + + it("can move multiselected files to another project", () => { + const projectToMoveName = "test project to move " + Date.now(); + createProject(projectToMoveName); + + cy.get(".project").eq(1).find("h2").click(); + createFile(); + createFile(); + createFile(); + + cy.get(".grid-item").eq(0).click({shiftKey: true}); + cy.get(".grid-item").eq(2).click({shiftKey: true}); + + + cy.get(".grid-item").eq(0).rightclick(); + cy.getBySel("move-to-multi").should("contain", "Move 2 files to"); + cy.getBySel("move-to-multi").click(); + cy.get('a').contains(projectToMoveName).click(); + + cy.getBySel("project-title").should("contain", projectToMoveName); + cy.get(".grid-item").should('have.length', 2); + + + //Go back + cy.get(".recent-projects").click(); + deleteFirstProject(); + }) + + + it("can move multiselected files to another team", () => { + const fileName1 = "test file " + Date.now(); + const fileName2 = "test file " + Date.now(); + const fileName3 = "test file " + Date.now(); + + cy.get(".project").first().find("h2").click(); + createFile(fileName1) + createFile(fileName2) + createFile(fileName3) + + + //multiselect first and third file + cy.get(".grid-item").eq(0).click({shiftKey: true}); + cy.get(".grid-item").eq(2).click({shiftKey: true}); + + + cy.get(".grid-item").eq(0).rightclick(); + cy.getBySel("move-to-multi").should("contain", "Move 2 files to"); + cy.getBySel("move-to-multi").click(); + cy.getBySel("move-to-other-team").click(); + cy.fixture('validuser.json').then((user) => { + cy.get('a').contains(user.team).click(); + cy.get('a').contains("Drafts").click(); + cy.get(".current-team").should("contain", user.team); + cy.get(".dashboard-title").should("contain", "Drafts"); + cy.get(".grid-item").eq(0).should("contain", fileName1); + cy.get(".grid-item").eq(1).should("contain", fileName2); + }); + + + //cleanup + deleteFirstFile() + deleteFirstFile() + cy.get(".current-team").click(); + cy.get(".team-name").contains("Your Penpot").click(); + }) + + }); + + diff --git a/frontend/cypress/integration/03-dashboard/misc.spec.js b/frontend/cypress/integration/03-dashboard/misc.spec.js new file mode 100644 index 000000000..840d5b296 --- /dev/null +++ b/frontend/cypress/integration/03-dashboard/misc.spec.js @@ -0,0 +1,150 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) UXBOX Labs SL + */ + + "use strict"; + + import { + + deleteFirstFile, + deleteFirstFont + +} from '../../support/utils.js'; + + + +describe("comments", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password); + }); + + }); + + it("can open and close comments popup", () => { + cy.get(".comments-section").should("not.exist"); + cy.getBySel("open-comments").click(); + cy.get(".comments-section").should("exist"); + cy.getBySel("open-comments").click(); + cy.get(".comments-section").should("not.exist"); + }); + +}); + +describe("import and export", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password); + }); + }); + + it("can export a file", () => { + cy.get('.menu') + .first() + .trigger('mouseover') + .click(); + cy.getBySel("file-export").click(); + cy.get('.icon-tick').should("exist"); + }); + + it("can import a file", () => { + cy.get(".grid-item").then((files) => { + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.getBySel("file-import").click(); + + cy.uploadBinaryFile("input[type=file]", "test-file-import.penpot"); + + cy.get(".accept-button").should('not.be.disabled'); + cy.get(".accept-button").click(); + cy.get(".accept-button").should('not.be.disabled'); + cy.get(".accept-button").click(); + cy.get(".grid-item").should('have.length', files.length+1); + }); + + //cleanup + deleteFirstFile() ; + }) + +}) + +describe("release notes", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password); + }); + }); + + it("can show release notes", () => { + cy.get(".profile").click(); + cy.getBySel("profile-profile-opt").click(); + cy.get(".onboarding").should("not.exist"); + cy.getBySel("release-notes").click(); + cy.get(".onboarding").should("exist"); + }); +}); + +describe("fonts", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password); + }); + }); + + it("can upload a font file", () => { + cy.getBySel("fonts").click(); + cy.get(".font-item").should('have.length', 0); + cy.uploadBinaryFile("#font-upload", "fonts/Viafont.otf"); + cy.get(".upload-button").click(); + cy.get(".font-item").should('have.length', 1); + + //cleanup + deleteFirstFont(); + + }); + + it("can upload multiple font files", () => { + cy.getBySel("fonts").click(); + cy.get(".font-item").should('have.length', 0); + cy.uploadBinaryFile("#font-upload", "fonts/Viafont.otf"); + cy.uploadBinaryFile("#font-upload", "fonts/blkchcry.ttf"); + cy.getBySel("upload-all").click(); + cy.get(".font-item").should('have.length', 2); + + //cleanup + deleteFirstFont(); + deleteFirstFont(); + }); + + it("can dismiss multiple font files", () => { + cy.getBySel("fonts").click(); + cy.get(".font-item").should('have.length', 0); + cy.uploadBinaryFile("#font-upload", "fonts/Viafont.otf"); + cy.uploadBinaryFile("#font-upload", "fonts/blkchcry.ttf"); + cy.getBySel("dismiss-all").click(); + cy.get(".font-item").should('have.length', 0); + }); + + it("can rename a font", () => { + const fontName = "test font " + Date.now(); + + //Upload a font + cy.getBySel("fonts").click(); + cy.uploadBinaryFile("#font-upload", "fonts/Viafont.otf"); + cy.get(".upload-button").click(); + cy.get(".font-item").should('have.length', 1); + + //Rename font + cy.get(".font-item .options").first().click(); + cy.getBySel("font-edit").click(); + cy.get(".dashboard-installed-fonts input[value=Viafont]").type(fontName+"{enter}"); + cy.get(".dashboard-installed-fonts").should("contain", fontName); + + //cleanup + deleteFirstFont(); + + }); +}); \ No newline at end of file diff --git a/frontend/cypress/integration/03-dashboard/projects.spec.js b/frontend/cypress/integration/03-dashboard/projects.spec.js new file mode 100644 index 000000000..d2b94aac5 --- /dev/null +++ b/frontend/cypress/integration/03-dashboard/projects.spec.js @@ -0,0 +1,153 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) UXBOX Labs SL + */ + + "use strict"; + + import { + + createProject, + deleteFirstProject + +} from '../../support/utils.js'; + + + + describe("projects", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password) + }); + + }); + + it("displays the projects page", () => { + cy.get(".dashboard-title").should("contain", "Projects"); + }); + + it("can create a new project", () => { + let projectName = "test project " + Date.now(); + cy.get(".project").then((projects) => { + cy.getBySel("new-project-button").click(); + cy.get('.project').should('have.length', projects.length + 1); + cy.get('.project').first().find(".edit-wrapper").type(projectName + "{enter}") + cy.get('.project').first().find("h2").should("contain", projectName); + + //cleanup: delete project + deleteFirstProject(); + }) + + }) + + it("can rename a project", () => { + let projectName = "test project " + Date.now(); + let projectName2 = "renamed project " + Date.now(); + + createProject(projectName); + + cy.get('.project').first().find("h2").should("contain", projectName); + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.get('.project').first().find("[data-test=project-rename]").click(); + cy.get('.project').first().find(".edit-wrapper").type(projectName2 + "{enter}") + cy.get('.project').first().find("h2").should("contain", projectName2) + + //cleanup: delete project + deleteFirstProject(); + }); + + it("can delete a project", () => { + createProject(); + cy.get(".project").then((projects) => { + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.wait(500); + cy.getBySel("project-delete").click(); + cy.wait(500); + cy.get('.accept-button').click(); + cy.wait(500); + + cy.get('.project').should('have.length', projects.length - 1); + }) + }); + + it("can cancel the deletion of a project", () => { + createProject(); + cy.get(".project").then((projects) => { + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.wait(500); + cy.getBySel("project-delete").click(); + cy.wait(500); + cy.get('.cancel-button').click(); + cy.wait(500); + + cy.get('.project').should('have.length', projects.length); + + + //cleanup: delete project + deleteFirstProject(); + }) + }); + + it("can duplicate a project", () => { + let projectName = "test project " + Date.now(); + createProject(projectName); + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.wait(500); + cy.getBySel("project-duplicate").click(); + cy.getBySel("project-title").should("exist"); + cy.getBySel("project-title").should("contain", projectName+" ("); + + + //cleanup: delete project + cy.get(".recent-projects").click(); + deleteFirstProject(); + deleteFirstProject(); + }); + + + it("can move a project to a team", () => { + let projectName = "test project " + Date.now(); + createProject(projectName); + + cy.fixture('validuser.json').then((user) => { + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.get('.project').first().find("[data-test=project-move-to]").click(); + cy.get('a').contains(user.team).click(); + + cy.get(".current-team").should("contain", user.team); + cy.get(".project").first().should("contain", projectName); + + + //cleanup: delete project + deleteFirstProject(); + }); + }); + + + it("pin and unpin project to sidebar", () => { + let projectName = "test project " + Date.now(); + createProject(projectName); + + cy.get(".project").first().find(".icon-pin-fill").should("exist"); + cy.getBySel("pinned-projects").should("contain", projectName); + + //unpin + cy.get(".project").first().find(".pin-icon").click(); + cy.get(".project").first().find(".icon-pin-fill").should("not.exist"); + cy.getBySel("pinned-projects").should("not.contain", projectName); + + //pin + cy.get(".project").first().find(".pin-icon").click(); + cy.get(".project").first().find(".icon-pin-fill").should("exist"); + cy.getBySel("pinned-projects").should("contain", projectName); + + //cleanup: delete project + deleteFirstProject(); + }); + + }); + + diff --git a/frontend/cypress/integration/03-dashboard/teams.spec.js b/frontend/cypress/integration/03-dashboard/teams.spec.js new file mode 100644 index 000000000..98071e3d2 --- /dev/null +++ b/frontend/cypress/integration/03-dashboard/teams.spec.js @@ -0,0 +1,155 @@ +/** + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright (c) UXBOX Labs SL + */ + + "use strict"; + + import { + + createTeam, + deleteCurrentTeam + + } from '../../support/utils.js'; + + + describe("teams", () => { + beforeEach(() => { + cy.fixture('validuser.json').then((user) => { + cy.login(user.email, user.password); + }); + + }); + + it("can create a new team", () => { + const teamName = "test team " + Date.now(); + cy.get(".current-team").click(); + cy.getBySel("create-new-team").click(); + cy.get("#name").type(teamName); + cy.get("input[type=submit]").click(); + + cy.get(".current-team").should("contain", teamName); + + //cleanup + deleteCurrentTeam(); + + }) + + it("can cancel create a new team", () => { + cy.get(".current-team").click(); + cy.getBySel("create-new-team").click(); + cy.get(".modal-close-button").click(); + + cy.get(".current-team").should("contain", "Your Penpot"); + }) + + it("can delete a team", () => { + const teamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("delete-team").click(); + cy.get(".accept-button").click(); + cy.get(".current-team").should("contain", "Your Penpot"); + }) + + it("can cancel the deletion of a team", () => { + const teamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("delete-team").click(); + cy.get(".cancel-button").click(); + cy.get(".current-team").should("contain", teamName); + + + //cleanup + deleteCurrentTeam(); + }) + + it("can see the members page of a team", () => { + const teamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("team-members").click(); + + cy.get(".dashboard-title").should("contain", "Members"); + cy.fixture('validuser.json').then((user) => { + cy.get(".dashboard-table").should("contain", user.email); + }); + + //cleanup + deleteCurrentTeam(); + }) + + it("can invite someone to a team", () => { + const teamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("team-members").click(); + + cy.getBySel("invite-member").click(); + cy.get("#email").type("mail@mail.com"); + cy.get(".custom-select select").select("admin"); + cy.get("input[type=submit]").click(); + + cy.get(".success").should("exist"); + + //cleanup + deleteCurrentTeam(); + }) + + it("can see the settings page of a team", () => { + const teamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("team-settings").click(); + + cy.get(".dashboard-title").should("contain", "Settings"); + + cy.get(".team-settings .name").should("contain", teamName); + + //cleanup + deleteCurrentTeam(); + }) + + it("can rename team", () => { + const teamName = "test team " + Date.now(); + const newTeamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("rename-team").click(); + cy.get("#name").type(newTeamName); + cy.get("input[type=submit]").click(); + + cy.get(".current-team").should("contain", newTeamName); + + //cleanup + deleteCurrentTeam(); + }) + + it("can cancel the rename of a team", () => { + const teamName = "test team " + Date.now(); + createTeam(teamName); + + cy.get(".icon-actions").first().click(); + cy.getBySel("rename-team").click(); + cy.get(".modal-close-button").click(); + + cy.get(".current-team").should("contain", teamName); + + //cleanup + deleteCurrentTeam(); + }) + + + + +}) \ No newline at end of file diff --git a/frontend/cypress/integration/03-projects/projects.spec.js b/frontend/cypress/integration/03-projects/projects.spec.js deleted file mode 100644 index 97743c085..000000000 --- a/frontend/cypress/integration/03-projects/projects.spec.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * Copyright (c) UXBOX Labs SL - */ - - "use strict"; - - describe("projects", () => { - beforeEach(() => { - cy.fixture('validuser.json').then((user) => { - cy.login(user.email, user.password) - }); - - }); - - it("displays the projects page", () => { - cy.get(".dashboard-title").should("exist"); - }); - - }); - - \ No newline at end of file diff --git a/frontend/cypress/integration/09-draw/draw-shapes.spec.js b/frontend/cypress/integration/09-draw/draw-shapes.spec.js index c9ab4784f..46e7970f7 100644 --- a/frontend/cypress/integration/09-draw/draw-shapes.spec.js +++ b/frontend/cypress/integration/09-draw/draw-shapes.spec.js @@ -18,48 +18,48 @@ describe("draw shapes", () => { }); it("draw an artboard", () => { - cy.get(".viewport-controls rect").should("not.exist"); - cy.get(".left-toolbar-options li[alt='Artboard (A)']").click(); + cy.get(".render-shapes rect").should("not.exist"); + cy.getBySel("artboard-btn").click(); cy.drawInViewport(300, 300, 400, 450); - cy.get(".viewport-controls rect").first().as("artboard"); + cy.get(".render-shapes rect").first().as("artboard"); cy.get("@artboard").should("exist"); cy.get("@artboard").invoke("attr", "width").should("eq", "100"); cy.get("@artboard").invoke("attr", "height").should("eq", "150"); }); it("draw a square", () => { - cy.get(".viewport-controls rect").should("not.exist"); - cy.get(".left-toolbar-options li[alt='Rectangle (R)']").click(); + cy.get(".render-shapes rect").should("not.exist"); + cy.getBySel("rect-btn").click(); cy.drawInViewport(300, 300, 400, 450); - cy.get(".viewport-controls rect").should("exist"); - cy.get(".viewport-controls rect") + cy.get(".render-shapes rect").should("exist"); + cy.get(".render-shapes rect") .invoke("attr", "width") .should("eq", "100"); - cy.get(".viewport-controls rect") + cy.get(".render-shapes rect") .invoke("attr", "height") .should("eq", "150"); }); it("draw an ellipse", () => { - cy.get(".viewport-controls ellipse").should("not.exist"); - cy.get(".left-toolbar-options li[alt='Ellipse (E)']").click(); + cy.get(".render-shapes ellipse").should("not.exist"); + cy.getBySel("ellipse-btn").click(); cy.drawInViewport(300, 300, 400, 450); - cy.get(".viewport-controls ellipse").as("ellipse"); + cy.get(".render-shapes ellipse").as("ellipse"); cy.get("@ellipse").should("exist"); cy.get("@ellipse").invoke("attr", "rx").should("eq", "50"); cy.get("@ellipse").invoke("attr", "ry").should("eq", "75"); }); it("draw a curve", () => { - cy.get(".viewport-controls path").should("not.exist"); - cy.get(".left-toolbar-options li[alt='Curve (Shift+C)']").click(); + cy.get(".render-shapes path").should("not.exist"); + cy.getBySel("curve-btn").click(); cy.drawMultiInViewport([ { x: 300, y: 300 }, { x: 350, y: 300 }, { x: 300, y: 350 }, { x: 400, y: 450 }, ]); - cy.get(".viewport-controls path").as("curve"); + cy.get(".render-shapes path").as("curve"); cy.get("@curve").should("exist"); cy.get("@curve") .invoke("attr", "d") @@ -67,8 +67,8 @@ describe("draw shapes", () => { }); it("draw a path", () => { - cy.get(".viewport-controls path").should("not.exist"); - cy.get(".left-toolbar-options li[alt='Path (P)']").click(); + cy.get(".render-shapes path").should("not.exist"); + cy.getBySel("path-btn").click(); cy.clickMultiInViewport([ { x: 300, y: 300 }, { x: 350, y: 300 }, @@ -81,7 +81,7 @@ describe("draw shapes", () => { true ); cy.clickMultiInViewport([{ x: 300, y: 300 }]); - cy.get(".viewport-controls path").as("curve"); + cy.get(".render-shapes path").as("curve"); cy.get("@curve").should("exist"); cy.get("@curve") .invoke("attr", "d") diff --git a/frontend/cypress/support/commands.js b/frontend/cypress/support/commands.js index 9283cca5e..fa5cee741 100644 --- a/frontend/cypress/support/commands.js +++ b/frontend/cypress/support/commands.js @@ -88,7 +88,7 @@ Cypress.Commands.add('clickMultiInViewport', (coords) => { Cypress.Commands.add('clearViewport', () => { cy.get(".viewport-controls").type('{ctrl}a'); cy.get(".viewport-controls").type('{del}'); - cy.window().its("debug").invoke('reset_viewport') + cy.window().its("debug").invoke('reset_viewport'); }) Cypress.Commands.add('getBySel', (selector, ...args) => { @@ -97,4 +97,18 @@ Cypress.Commands.add('getBySel', (selector, ...args) => { Cypress.Commands.add('getBySelLike', (selector, ...args) => { return cy.get(`[data-test*=${selector}]`, ...args) -}) \ No newline at end of file +}) + +Cypress.Commands.add('uploadBinaryFile', (fileInputSelector, fileName) => { + cy.fixture(fileName, "binary") + .then(Cypress.Blob.binaryStringToBlob) + .then(fileContent => { + cy.get(fileInputSelector).attachFile({ + fileContent, + filePath: fileName, + encoding: 'utf-8', + lastModified: new Date().getTime() + }); + }); +}) + diff --git a/frontend/cypress/support/utils.js b/frontend/cypress/support/utils.js index ee5ea66af..595f71ced 100644 --- a/frontend/cypress/support/utils.js +++ b/frontend/cypress/support/utils.js @@ -5,8 +5,71 @@ export const checkOnboardingSlide = (number, checkSkip) => { cy.getBySel(`slide-${number}-btn`).should("exist"); cy.getBySel(`slide-${number}-btn`).click(); }; +export const deleteFirstProject = () => { + cy.get('.project').first().find("[data-test=project-options]").click(); + cy.wait(500); + cy.get('.project').first().find("[data-test=project-delete]").click(); + cy.wait(500); + cy.get('.accept-button').click(); + } + + export const createProject = (projectName="") => { + cy.getBySel("new-project-button").click(); + cy.wait(500); + cy.get('.project').first().find(".edit-wrapper").type(projectName + "{enter}"); + cy.wait(500); + } + + + export const deleteFirstFile = () => { + cy.get('.menu') + .first() + .trigger('mouseover') + .click(); + cy.getBySel("file-delete").click(); + cy.get('.accept-button').click(); + } + + + export const createFile = (fileName="", projectNum=0) => { + cy.getBySel("new-file").click(); + cy.wait(500); + if (fileName !=""){ + cy.get('.menu') + .first() + .trigger('mouseover') + .click(); + cy.getBySel("file-rename").click(); + cy.get(".edit-wrapper").type(fileName + "{enter}"); + //TODO: Bug workaround. When a file is selected, it doesn't open context menu + cy.get(".dashboard-grid").click(); + } + } + + export const createTeam = (teamName) => { + cy.get(".current-team").click(); + cy.getBySel("create-new-team").click(); + cy.get("#name").type(teamName); + cy.get("input[type=submit]").click(); + cy.wait(500); + } + + export const deleteCurrentTeam = () => { + cy.get(".icon-actions").first().click(); + cy.getBySel("delete-team").click(); + cy.get(".accept-button").click(); + } + + + export const goToSlideByNumber = (number) => { cy.get(`.step-dots li:nth-child(${number})`).click(); cy.getBySel(`slide-${number -1}-btn`).should("exist"); +}; + +export const deleteFirstFont = () => { + cy.get(".font-item .options").first().click(); + cy.getBySel("font-delete").click(); + cy.get(".accept-button").click(); }; \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index e48a53af4..2486f8650 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -32,7 +32,8 @@ }, "devDependencies": { "autoprefixer": "^10.4.1", - "cypress": "^9.2.1", + "cypress": "^9.3.1", + "cypress-file-upload": "^5.0.8", "gettext-parser": "^4.2.0", "gulp": "4.0.2", "gulp-concat": "^2.6.1", @@ -385,11 +386,10 @@ "license": "MIT" }, "node_modules/@types/sinonjs__fake-timers": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz", - "integrity": "sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A==", - "dev": true, - "license": "MIT" + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", + "dev": true }, "node_modules/@types/sizzle": { "version": "2.3.3", @@ -2539,20 +2539,21 @@ "license": "MIT" }, "node_modules/cypress": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.2.1.tgz", - "integrity": "sha512-LVEe4yWCo4xO0Vd8iYjFHRyd5ulRvM56XqMgAdn05Qb9kJ6iJdO/MmjKD8gNd768698cp1FDuSmFQZHVZGk+Og==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.3.1.tgz", + "integrity": "sha512-BODdPesxX6bkVUnH8BVsV8I/jn57zQtO1FEOUTiuG2us3kslW7g0tcuwiny7CKCmJUZz8S/D587ppC+s58a+5Q==", "dev": true, "hasInstallScript": true, "dependencies": { "@cypress/request": "^2.88.10", "@cypress/xvfb": "^1.2.4", "@types/node": "^14.14.31", - "@types/sinonjs__fake-timers": "^6.0.2", + "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", - "bluebird": "3.7.2", + "bluebird": "^3.7.2", + "buffer": "^5.6.0", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", @@ -2594,6 +2595,42 @@ "node": ">=12.0.0" } }, + "node_modules/cypress-file-upload": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz", + "integrity": "sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==", + "dev": true, + "engines": { + "node": ">=8.2.1" + }, + "peerDependencies": { + "cypress": ">3.0.0" + } + }, + "node_modules/cypress/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/cypress/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -12105,9 +12142,9 @@ "dev": true }, "@types/sinonjs__fake-timers": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz", - "integrity": "sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", "dev": true }, "@types/sizzle": { @@ -13707,19 +13744,20 @@ "integrity": "sha512-jXKhWqXPmlUeoQnF/EhTtTl4C9SnrxSH/jZUih3jmO6lBKr99rP3/+FmrMj4EFpOXzMtXHAZkd3x0E6h6Fgflw==" }, "cypress": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.2.1.tgz", - "integrity": "sha512-LVEe4yWCo4xO0Vd8iYjFHRyd5ulRvM56XqMgAdn05Qb9kJ6iJdO/MmjKD8gNd768698cp1FDuSmFQZHVZGk+Og==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-9.3.1.tgz", + "integrity": "sha512-BODdPesxX6bkVUnH8BVsV8I/jn57zQtO1FEOUTiuG2us3kslW7g0tcuwiny7CKCmJUZz8S/D587ppC+s58a+5Q==", "dev": true, "requires": { "@cypress/request": "^2.88.10", "@cypress/xvfb": "^1.2.4", "@types/node": "^14.14.31", - "@types/sinonjs__fake-timers": "^6.0.2", + "@types/sinonjs__fake-timers": "8.1.1", "@types/sizzle": "^2.3.2", "arch": "^2.2.0", "blob-util": "^2.0.2", - "bluebird": "3.7.2", + "bluebird": "^3.7.2", + "buffer": "^5.6.0", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", @@ -13755,6 +13793,16 @@ "yauzl": "^2.10.0" }, "dependencies": { + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -13808,6 +13856,13 @@ } } }, + "cypress-file-upload": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz", + "integrity": "sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g==", + "dev": true, + "requires": {} + }, "d": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 1d1a8d840..827e6a946 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,7 @@ }, "devDependencies": { "autoprefixer": "^10.4.1", - "cypress": "^9.2.1", + "cypress": "^9.3.1", "cypress-file-upload": "^5.0.8", "gettext-parser": "^4.2.0", "gulp": "4.0.2", diff --git a/frontend/src/app/main/ui/auth/register.cljs b/frontend/src/app/main/ui/auth/register.cljs index 9fabc8d5c..6516c43a4 100644 --- a/frontend/src/app/main/ui/auth/register.cljs +++ b/frontend/src/app/main/ui/auth/register.cljs @@ -237,7 +237,7 @@ (mf/defc register-validate-page [{:keys [params] :as props}] [:div.form-container - [:h1 (tr "auth.register-title")] + [:h1 {:data-test "register-title"} (tr "auth.register-title")] [:div.subtitle (tr "auth.register-subtitle")] [:& register-validate-form {:params params}] diff --git a/frontend/src/app/main/ui/components/context_menu.cljs b/frontend/src/app/main/ui/components/context_menu.cljs index b83efcc14..908e992e4 100644 --- a/frontend/src/app/main/ui/components/context_menu.cljs +++ b/frontend/src/app/main/ui/components/context_menu.cljs @@ -101,7 +101,7 @@ [:span i/arrow-slide] parent-option]] [:li.separator]]) - (for [[index [option-name option-handler sub-options]] (d/enumerate (:options level))] + (for [[index [option-name option-handler sub-options data-test]] (d/enumerate (:options level))] (when option-name (if (= option-name :separator) [:li.separator] @@ -111,12 +111,14 @@ (if-not sub-options [:a.context-menu-action {:on-click #(do (dom/stop-propagation %) (on-close) - (option-handler %))} + (option-handler %)) + :data-test data-test} (if (and in-dashboard? (= option-name "Default")) (tr "dashboard.default-team-name") option-name)] [:a.context-menu-action.submenu {:data-no-close true - :on-click (enter-submenu option-name sub-options)} + :on-click (enter-submenu option-name sub-options) + :data-test data-test} option-name [:span i/arrow-slide]])])))])]]))) diff --git a/frontend/src/app/main/ui/dashboard/comments.cljs b/frontend/src/app/main/ui/dashboard/comments.cljs index 4fd92a474..582ea47ac 100644 --- a/frontend/src/app/main/ui/dashboard/comments.cljs +++ b/frontend/src/app/main/ui/dashboard/comments.cljs @@ -55,6 +55,7 @@ [:div.dashboard-comments-section [:div.button {:on-click show-dropdown + :data-test "open-comments" :class (dom/classnames :open @show-dropdown? :unread (boolean (seq tgroups)))} i/chat] diff --git a/frontend/src/app/main/ui/dashboard/file_menu.cljs b/frontend/src/app/main/ui/dashboard/file_menu.cljs index dba16d15b..085f26b9b 100644 --- a/frontend/src/app/main/ui/dashboard/file_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/file_menu.cljs @@ -203,27 +203,28 @@ (for [sub-project (:projects team)] [(get-project-name sub-project) (on-move (:id team) - (:id sub-project))])])])) + (:id sub-project))])]) + "move-to-other-team"])) options (if multi? - [[(tr "dashboard.duplicate-multi" file-count) on-duplicate] + [[(tr "dashboard.duplicate-multi" file-count) on-duplicate nil "duplicate-multi"] (when (or (seq current-projects) (seq other-teams)) - [(tr "dashboard.move-to-multi" file-count) nil sub-options]) + [(tr "dashboard.move-to-multi" file-count) nil sub-options "move-to-multi"]) [(tr "dashboard.export-multi" file-count) on-export-files] [:separator] - [(tr "labels.delete-multi-files" file-count) on-delete]] + [(tr "labels.delete-multi-files" file-count) on-delete nil "delete-multi-files"]] [[(tr "dashboard.open-in-new-tab") on-new-tab] - [(tr "labels.rename") on-edit] - [(tr "dashboard.duplicate") on-duplicate] + [(tr "labels.rename") on-edit nil "file-rename"] + [(tr "dashboard.duplicate") on-duplicate nil "file-duplicate"] (when (or (seq current-projects) (seq other-teams)) - [(tr "dashboard.move-to") nil sub-options]) + [(tr "dashboard.move-to") nil sub-options "file-move-to"]) (if (:is-shared file) - [(tr "dashboard.remove-shared") on-del-shared] - [(tr "dashboard.add-shared") on-add-shared]) - [(tr "dashboard.export-single") on-export-files] + [(tr "dashboard.remove-shared") on-del-shared nil "file-del-shared"] + [(tr "dashboard.add-shared") on-add-shared nil "file-add-shared"]) + [(tr "dashboard.export-single") on-export-files nil "file-export"] [:separator] - [(tr "labels.delete") on-delete]])] + [(tr "labels.delete") on-delete nil "file-delete"]])] [:& context-menu {:on-close on-menu-close :show show? diff --git a/frontend/src/app/main/ui/dashboard/files.cljs b/frontend/src/app/main/ui/dashboard/files.cljs index 9d050206a..770b67f54 100644 --- a/frontend/src/app/main/ui/dashboard/files.cljs +++ b/frontend/src/app/main/ui/dashboard/files.cljs @@ -70,7 +70,7 @@ (with-meta {::ev/origin "project"})))) (swap! local assoc :edition false)))}] [:div.dashboard-title - [:h1 {:on-double-click on-edit} + [:h1 {:on-double-click on-edit :data-test "project-title"} (:name project)]])) [:& project-menu {:project project @@ -82,7 +82,7 @@ :on-import on-import}] [:div.dashboard-header-actions - [:a.btn-secondary.btn-small {:on-click on-create-clicked} + [:a.btn-secondary.btn-small {:on-click on-create-clicked :data-test "new-file"} (tr "dashboard.new-file")] (when-not (:is-default project) diff --git a/frontend/src/app/main/ui/dashboard/fonts.cljs b/frontend/src/app/main/ui/dashboard/fonts.cljs index 0199f4213..706bc1fb6 100644 --- a/frontend/src/app/main/ui/dashboard/fonts.cljs +++ b/frontend/src/app/main/ui/dashboard/fonts.cljs @@ -149,10 +149,10 @@ [:span (tr "dashboard.fonts.fonts-added" (i18n/c (count (vals @fonts))))] [:div.table-field.options [:div.btn-primary - {:on-click #(on-upload-all (vals @fonts))} + {:on-click #(on-upload-all (vals @fonts)) :data-test "upload-all"} [:span (tr "dashboard.fonts.upload-all")]] [:div.btn-secondary - {:on-click #(on-dismiss-all (vals @fonts))} + {:on-click #(on-dismiss-all (vals @fonts)) :data-test "dismiss-all"} [:span (tr "dashboard.fonts.dismiss-all")]]]]) (for [item (sort-by :font-family (vals @fonts))] @@ -277,8 +277,8 @@ :fixed? false :top -15 :left -115 - :options [[(tr "labels.edit") #(reset! edit? true)] - [(tr "labels.delete") on-delete]]}]])])) + :options [[(tr "labels.edit") #(reset! edit? true) nil "font-edit"] + [(tr "labels.delete") on-delete nil "font-delete"]]}]])])) (mf/defc installed-fonts diff --git a/frontend/src/app/main/ui/dashboard/import.cljs b/frontend/src/app/main/ui/dashboard/import.cljs index 6edfbb955..1f80a452d 100644 --- a/frontend/src/app/main/ui/dashboard/import.cljs +++ b/frontend/src/app/main/ui/dashboard/import.cljs @@ -21,7 +21,7 @@ [potok.core :as ptk] [rumext.alpha :as mf])) -(log/set-level! :warn) +(log/set-level! :debug) (def ^:const emit-delay 1000) diff --git a/frontend/src/app/main/ui/dashboard/project_menu.cljs b/frontend/src/app/main/ui/dashboard/project_menu.cljs index 1df238537..c374ef16d 100644 --- a/frontend/src/app/main/ui/dashboard/project_menu.cljs +++ b/frontend/src/app/main/ui/dashboard/project_menu.cljs @@ -108,19 +108,20 @@ :top top :left left :options [(when-not (:is-default project) - [(tr "labels.rename") on-edit]) + [(tr "labels.rename") on-edit nil "project-rename"]) (when-not (:is-default project) - [(tr "dashboard.duplicate") on-duplicate]) + [(tr "dashboard.duplicate") on-duplicate nil "project-duplicate"]) (when-not (:is-default project) [(tr "dashboard.pin-unpin") toggle-pin]) (when (and (seq teams) (not (:is-default project))) [(tr "dashboard.move-to") nil (for [team teams] - [(:name team) (on-move (:id team))])]) + [(:name team) (on-move (:id team))]) + "project-move-to"]) (when (some? on-import) - [(tr "dashboard.import") on-import-files]) + [(tr "dashboard.import") on-import-files nil "file-import"]) (when-not (:is-default project) [:separator]) (when-not (:is-default project) - [(tr "labels.delete") on-delete])]}]])) + [(tr "labels.delete") on-delete nil "project-delete"])]}]])) diff --git a/frontend/src/app/main/ui/dashboard/projects.cljs b/frontend/src/app/main/ui/dashboard/projects.cljs index 9a3ddc966..b8352a11f 100644 --- a/frontend/src/app/main/ui/dashboard/projects.cljs +++ b/frontend/src/app/main/ui/dashboard/projects.cljs @@ -30,7 +30,7 @@ [:div.dashboard-title [:h1 (tr "dashboard.projects-title")]] - [:a.btn-secondary.btn-small {:on-click create} + [:a.btn-secondary.btn-small {:on-click create :data-test "new-project-button"} (tr "dashboard.new-project")]])) (mf/defc project-item @@ -140,11 +140,11 @@ i/pin)]) [:a.btn-secondary.btn-small.tooltip.tooltip-bottom - {:on-click create-file :alt (tr "dashboard.new-file")} + {:on-click create-file :alt (tr "dashboard.new-file") :data-test "project-new-file"} i/close] [:a.btn-secondary.btn-small.tooltip.tooltip-bottom - {:on-click on-menu-click :alt (tr "dashboard.options")} + {:on-click on-menu-click :alt (tr "dashboard.options") :data-test "project-options"} i/actions]] [:& line-grid diff --git a/frontend/src/app/main/ui/dashboard/sidebar.cljs b/frontend/src/app/main/ui/dashboard/sidebar.cljs index f632ca2f4..5f0c3f715 100644 --- a/frontend/src/app/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/app/main/ui/dashboard/sidebar.cljs @@ -221,7 +221,7 @@ [:span.team-text {:title (:name team)} (:name team)]]]) [:hr] - [:li.action {:on-click on-create-clicked} + [:li.action {:on-click on-create-clicked :data-test "create-new-team"} (tr "dashboard.create-new-team")]])) (s/def ::member-id ::us/uuid) @@ -349,21 +349,21 @@ :on-accept delete-fn}))] [:ul.dropdown.options-dropdown - [:li {:on-click go-members} (tr "labels.members")] - [:li {:on-click go-settings} (tr "labels.settings")] + [:li {:on-click go-members :data-test "team-members"} (tr "labels.members")] + [:li {:on-click go-settings :data-test "team-settings"} (tr "labels.settings")] [:hr] - [:li {:on-click on-rename-clicked} (tr "labels.rename")] + [:li {:on-click on-rename-clicked :data-test "rename-team"} (tr "labels.rename")] (cond (get-in team [:permissions :is-owner]) - [:li {:on-click on-leave-as-owner-clicked} (tr "dashboard.leave-team")] + [:li {:on-click on-leave-as-owner-clicked :data-test "leave-team"} (tr "dashboard.leave-team")] (> (count members) 1) [:li {:on-click on-leave-clicked} (tr "dashboard.leave-team")]) (when (get-in team [:permissions :is-owner]) - [:li {:on-click on-delete-clicked} (tr "dashboard.delete-team")])])) + [:li {:on-click on-delete-clicked :data-test "delete-team"} (tr "dashboard.delete-team")])])) (mf/defc sidebar-team-switch @@ -466,13 +466,14 @@ [:div.sidebar-content-section [:ul.sidebar-nav.no-overflow - [:li.recent-projects + [:li {:on-click go-fonts + :data-test "fonts" :class-name (when fonts? "current")} [:span.element-title (tr "labels.fonts")]]]] [:hr] - [:div.sidebar-content-section + [:div.sidebar-content-section {:data-test "pinned-projects"} (if (seq pinned-projects) [:ul.sidebar-nav (for [item pinned-projects] diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index 35758737d..6d98ddae7 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -48,7 +48,7 @@ [:a {:on-click go-settings} (tr "labels.settings")]]]] (if (and members-section? (:is-admin permissions)) - [:a.btn-secondary.btn-small {:on-click invite-member} + [:a.btn-secondary.btn-small {:on-click invite-member :data-test "invite-member"} (tr "dashboard.invite-profile")] [:div])])) diff --git a/frontend/src/app/main/ui/onboarding/team_choice.cljs b/frontend/src/app/main/ui/onboarding/team_choice.cljs index abaaa8d93..56f3c5b2d 100644 --- a/frontend/src/app/main/ui/onboarding/team_choice.cljs +++ b/frontend/src/app/main/ui/onboarding/team_choice.cljs @@ -51,7 +51,7 @@ [:h2 (tr "onboarding.choice.fly-solo")] [:p (tr "onboarding.choice.fly-solo-desc")]]] [:div.modal-right - [:div.content-button {:on-click on-team-up} + [:div.content-button {:on-click on-team-up :data-test "team-up-button"} [:h2 (tr "onboarding.choice.team-up")] [:p (tr "onboarding.choice.team-up-desc")]]]] [:img.deco {:src "images/deco-left.png" :border "0"}] @@ -72,7 +72,7 @@ [:div.modal-overlay [:div.modal-container.onboarding-team [:div.title - [:h2 (tr "onboarding.choice.team-up")] + [:h2 {:data-test "onboarding-choice-team-up"} (tr "onboarding.choice.team-up")] [:p (tr "onboarding.choice.team-up-desc")]] [:& fm/form {:form form diff --git a/frontend/src/app/main/ui/onboarding/templates.cljs b/frontend/src/app/main/ui/onboarding/templates.cljs index 84bfc1558..c8ef3e327 100644 --- a/frontend/src/app/main/ui/onboarding/templates.cljs +++ b/frontend/src/app/main/ui/onboarding/templates.cljs @@ -74,7 +74,7 @@ :data-test "close-templates-btn"} i/close]] [:div.modal-content - [:h3 (tr "onboarding.templates.title")] + [:h3 {:data-test "onboarding-templates-title"} (tr "onboarding.templates.title")] [:p (tr "onboarding.templates.subtitle")] [:div.templates diff --git a/frontend/src/app/main/ui/settings/sidebar.cljs b/frontend/src/app/main/ui/settings/sidebar.cljs index 102a66cb4..0b9cce30d 100644 --- a/frontend/src/app/main/ui/settings/sidebar.cljs +++ b/frontend/src/app/main/ui/settings/sidebar.cljs @@ -87,7 +87,7 @@ [:hr] - [:li {:on-click show-release-notes} + [:li {:on-click show-release-notes :data-test "release-notes"} i/pencil [:span.element-title (tr "labels.release-notes")]] diff --git a/frontend/src/app/main/ui/workspace/left_toolbar.cljs b/frontend/src/app/main/ui/workspace/left_toolbar.cljs index 94c48e702..be41b8e6d 100644 --- a/frontend/src/app/main/ui/workspace/left_toolbar.cljs +++ b/frontend/src/app/main/ui/workspace/left_toolbar.cljs @@ -75,17 +75,20 @@ [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.frame" (sc/get-tooltip :draw-frame)) :class (when (= selected-drawtool :frame) "selected") - :on-click (partial select-drawtool :frame)} + :on-click (partial select-drawtool :frame) + :data-test "artboard-btn"} i/artboard] [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.rect" (sc/get-tooltip :draw-rect)) :class (when (= selected-drawtool :rect) "selected") - :on-click (partial select-drawtool :rect)} + :on-click (partial select-drawtool :rect) + :data-test "rect-btn"} i/box] [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.ellipse" (sc/get-tooltip :draw-ellipse)) :class (when (= selected-drawtool :circle) "selected") - :on-click (partial select-drawtool :circle)} + :on-click (partial select-drawtool :circle) + :data-test "ellipse-btn"} i/circle] [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.text" (sc/get-tooltip :draw-text)) @@ -98,12 +101,14 @@ [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.curve" (sc/get-tooltip :draw-curve)) :class (when (= selected-drawtool :curve) "selected") - :on-click (partial select-drawtool :curve)} + :on-click (partial select-drawtool :curve) + :data-test "curve-btn"} i/pencil] [:li.tooltip.tooltip-right {:alt (tr "workspace.toolbar.path" (sc/get-tooltip :draw-path)) :class (when (= selected-drawtool :path) "selected") - :on-click (partial select-drawtool :path)} + :on-click (partial select-drawtool :path) + :data-test "path-btn"} i/pen] [:li.tooltip.tooltip-right diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 4f4095cce..9ad2d6ad5 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -167,10 +167,10 @@ resolved "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz" integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== -"@types/sinonjs__fake-timers@^6.0.2": - version "6.0.4" - resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.4.tgz" - integrity sha512-IFQTJARgMUBF+xVd2b+hIgXWrZEjND3vJtRCvIelcFB5SIXfjV4bOHbHJ0eXKh+0COrBRc8MqteKAz/j88rE0A== +"@types/sinonjs__fake-timers@8.1.1": + version "8.1.1" + resolved "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz" + integrity sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g== "@types/sizzle@^2.3.2": version "2.3.3" @@ -538,7 +538,7 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base64-js@^1.0.2: +base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -590,7 +590,7 @@ blob-util@^2.0.2: resolved "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz" integrity sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ== -bluebird@3.7.2: +bluebird@^3.7.2: version "3.7.2" resolved "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== @@ -766,6 +766,14 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +buffer@^5.6.0: + version "5.7.1" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz" @@ -1406,22 +1414,23 @@ csstype@^3.0.2: cypress-file-upload@^5.0.8: version "5.0.8" - resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1" + resolved "https://registry.npmjs.org/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz" integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g== -cypress@^9.2.1: - version "9.2.1" - resolved "https://registry.npmjs.org/cypress/-/cypress-9.2.1.tgz" - integrity sha512-LVEe4yWCo4xO0Vd8iYjFHRyd5ulRvM56XqMgAdn05Qb9kJ6iJdO/MmjKD8gNd768698cp1FDuSmFQZHVZGk+Og== +cypress@^9.3.1: + version "9.3.1" + resolved "https://registry.npmjs.org/cypress/-/cypress-9.3.1.tgz" + integrity sha512-BODdPesxX6bkVUnH8BVsV8I/jn57zQtO1FEOUTiuG2us3kslW7g0tcuwiny7CKCmJUZz8S/D587ppC+s58a+5Q== dependencies: "@cypress/request" "^2.88.10" "@cypress/xvfb" "^1.2.4" "@types/node" "^14.14.31" - "@types/sinonjs__fake-timers" "^6.0.2" + "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" arch "^2.2.0" blob-util "^2.0.2" - bluebird "3.7.2" + bluebird "^3.7.2" + buffer "^5.6.0" cachedir "^2.3.0" chalk "^4.1.0" check-more-types "^2.24.0" @@ -2808,7 +2817,7 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.1.4: +ieee754@^1.1.13, ieee754@^1.1.4: version "1.2.1" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==