From bf598c19ed4384da52edae1f964db90b50dbb86b Mon Sep 17 00:00:00 2001 From: Eva Marco Date: Wed, 17 Apr 2024 12:18:13 +0200 Subject: [PATCH] :sparkles: Add documentation to testing best practices --- img/a11y-tree-btn.webp | Bin 0 -> 7538 bytes img/locate_by_label.webp | Bin 0 -> 924 bytes img/locate_by_label2.webp | Bin 0 -> 11658 bytes img/locate_by_text.webp | Bin 0 -> 3924 bytes img/login-btn.webp | Bin 0 -> 2452 bytes img/login-locators.webp | Bin 0 -> 9374 bytes img/page-item-locator1.webp | Bin 0 -> 482 bytes img/page-item-locator2.webp | Bin 0 -> 5622 bytes technical-guide/developer/frontend.md | 426 +++++++++++++++++++++++--- 9 files changed, 381 insertions(+), 45 deletions(-) create mode 100644 img/a11y-tree-btn.webp create mode 100644 img/locate_by_label.webp create mode 100644 img/locate_by_label2.webp create mode 100644 img/locate_by_text.webp create mode 100644 img/login-btn.webp create mode 100644 img/login-locators.webp create mode 100644 img/page-item-locator1.webp create mode 100644 img/page-item-locator2.webp diff --git a/img/a11y-tree-btn.webp b/img/a11y-tree-btn.webp new file mode 100644 index 0000000000000000000000000000000000000000..02fdccf7435823b567b44b2465a407bfa4962b24 GIT binary patch literal 7538 zcmV-&9gX5rNk&F$9RL7VMM6+kP&go79RL83Yyh1BDnS9j06vjIol2#nqM@eHs)(=> z31x2Kap3E(O=J01ad`tSL-8mk`Vs%V)92KGJeQ+Bg|GMj|NY5-p?|;s|Ly_jgX-P> zyVB#(1ONX6pN0Roc6a_O`4f>z%AG^Qc zy^{ZH;IG7=@clr)E&gf#m-}~(U-Nybniu!)^nRdTpM1CdzxRLMkMh3QUa|iN`;VyK zkbhJEPxmwS0sOD}zxmJaKaxKuf0^)1@?Y0afFI_c+JEAImHg8BQvO^2cloc)Uu*xJ zf0_T+{)6Bf`JeW0{9ogJxc!%Y^m|Bt=9H$k;6Vkd#=+n2KbZP#j zSdmE2?WI%3zP}RNooeNi*yPWR=-9AXEH%!g{Pr^GVvG{_9 zw%aNBoUcFu=91NVH~&!RtusW$mLGNLHO!AVdj3~*kU^qut%))&YJZS2uP%|D0y`nR z^UX9RkJZ6F=|z?uZ+5ggw3DrQiQL~uT6e8uth zIx5nJSfX4i)HN zM+rbR)JVcUttgfdqvDI(gBj0m`GKZ8gu5Vt*viRfZVO4@F@OZ!s^mnQgxp;rsFo1P zO&&G(9OU9E*^7tL>mu&V;Z2@u3rZ{GjSemDG&NgltObbJLM7vhv0juhKD`cRHHvt) z$!jL{+{g1Z#N|Duh_BJi6Z$6EDMRi>%Zlkc164}z;k2SyLnS!jys&BWL692HN>n0L zdZ<3c-`j~c^^I!V#8{Vzg%NgtrGVF%IW_f$gFUc17A`M~6!C4L`t1dlSgFs^NWfkC{I8SHz_BY%1 zZw}QZuEy#1p~#Oyd5tEX_qGhOn@C_oiaRjt#2tCtQGEAiG2sQya*o-fmZicHc4vv| zVY5Gu{IqlSstvD0oUN-Rjnt)_M8)$~pE2oaxo1nxE`QtG{{P@<%fb3~q~Y*K`(wO- zqCu&fC8E^*3LGL}5-#QIMQ^s4J_2wn?Z0m~7>@#i^zdL}W}Z)`QMij}Syy=D2P>D> zP4jp^w4E~Ikd5R4EEu1Y>mU{tI-<@ZM4Uy9Eo@ut;E(zuh77Zo(V8Ks1zp218!}U~ zgX1WbySY`If~~RfPA54NoQcjv`c4WE`kJJQr~U(^7#?JGwkb8 zq_QIOF{zN?!8h9B#1^Tcu8pq`-4*d{N1V8-WL1<->I!!Fh?K}iAK)nh9h@8Oe#Do# z8Gm%w-b7->8_=8U|NiaQVT>Bsi(4hdI&aW~_;*AJw;So_=oF5tTBxK3kC$>=flzvC9U?V)O;=s zBd4~rDv`^@No{#(9Tff&eAmN*DJAip7H2Yhxi-2=6&aTu1}C;BCS^C&kj3SV3w)o( zx;e25^ZLE_WhHi8AD`k{gaX!VS{E;Y(lYbazAor<^|>}cbh6VA+IHcm5FfhD+;F>e zCPG^gwnlYGEPxSeG6P_V82>(l&6MY1-~ga>C#>exC~?HC$d`&ae(;BtaIzg#vZ>0b0Q8{M2_?v3iyO z-|KmJCFmXc`=6XO#v(=A!fe4t$#6|)^X)NwY!NylvpvuLFQ zm0&TfE08~tmY*5(MA`Ai5_8W6O8l=w49i27QdoF8ouFA<9wepvO;Se*b!j<$?4hAk zfnn=aaw@PgCtBQqtfQscgHB8wVTexnfXAwKSs^6r^2SH^ro@oQlMqK5Av8To4T7sSU^4qY7+QKM3UXV zF8@6tDtYoP2v@$}No}CU9&~a}2NfT@F>ByXUyN_E{`vgd*#yb$<2nfV`6Q1HAW-by z1zuF08H5XA=mvF_6T!K|8SkF59zSR-&P2#zG@W6yjpA}JbEuKFnM9E4mkMx%wTsjd z7Mr`dS$pDiL0K488p+{BOjJb~T-d+eZI9~Em!2-NK&_>>kSMvlX)lrD1^DRS4e)1+ z1WYvGNVAozc*>Od4mV4t9DjS>8!H^4{|?UGALb?mHNwgNykEkdG|4-~o9v^z??eM) zXx=-{ZL!nrXSw+Yc4Q`j5&)UyV=ngT<8$S(wxc>)qTK4xfiJ_VoGRZ~{%hMYcTjRxekW!C2YMWa+5BE&fo65Vn=2Nz(9-?7tn z79MK1N=`Y|U=JJSnEx9LjIVi{8 zP%coo0Kq>J(p%cO!<-h;AhO{ye?-VulkL{<#VD_^2Bvr+O3RQBIr@i`XuT5Ne!13> z+R^+PFKjB!Yc-lK=_XL$E2tK`9xSA-7vaVeCk+?k%2ps*teeBVNm0~O8+EvOtasq9 z>6dP`R8py@G~>9TU~^NT*NUMb|9D;8cXg7PE^hF9Bf!oN0-eM_$G?denczlHgnGJy zc^`C^T6#dpA!?^!z9L?CtG7F_;J6*~L>o@fiB{eF@iL5UN)?+SDnmH7D^P+_>g@|b z2eI#wrTt48l_l(mf68J0#M3K$eZc=QtWCZclah}<)Kh*NA`OjXZgnIiH6S#g#GZvl z(FhU^t58x9Ho)ZfWl3!is!@z+J>}8X&r@ycirRq7Wd|7KJ>AXTaa`?t&x~HbGWLbF z;inGDdaa)4*gv{Sz=n})B|*urmu$k;N$qImrE2kyTme-J?c+8dFk--7Ncrw3r zRD2TF7)LWZCAh<5`L1IiIeH<@38G$C$wy^;e0K|L;Tkl@9}>OsL!SmswwZrjx56Ss z8@L$4IMM;vRi2LUR3-nzy%qrymlU_qMj{X*6_*XB(j9 zUlR1rlJCO4?Vrpph2! zd7dc*iRB@l*t4MK2sApj7cYIm3AP8SWc6po9Uh;vxLl=6+YWVqnpV)MC;~Kx!yLK( z3Z7|FGa$y`1LR7tg1B;)xcbdL$*m11ntrVAENCsx@RBRb0zG6eCI0Zxog>IZjfJ$P zGW7x6Q&tE<7E)6ys=2hAeWUrhk^jBSwXDUJU)^A+gVx^LQn>__0$39v%sfAL2mmLT z_Mp#%^_*ZzFUTO2PJmEwlmGx1EKuUeLRJalL{co?Rh-$w;fg_sF%UpfHRM?l=;$DS?4;lWQn5o)8u=H+MVZ^jN@qlQM{sdkig2QvUou+{%~{6DV?^b;LUFSk5Anr zG3);(2E(P8PdaJt0PIFP-SRWi>KUvJXg4tlq?B*OPU|BXg}?;f_?UwKVa_zh+0yPk z&DfB&0kI~WSO}WA{${V+AU;~&Q;1?8;m8Q!6^`EMYb->%R(pKCPI{5=Oq8vhIZl5= zO=kNA+47}+2-0id*fPe)Yl$eqMt+LT@!~>D8!*5Hp#sJiw4O2W)qnFzYtL*G-|B-Y z2#?SdNYT2G2izT$IU~yhJWZQA1{x8Fc>kVTbmJKfO!#RF=N<|J@`_0=f8cGgmJfPb z@h?LIaGL$e?thCH9s{xIGbd26e0`e6idxp?jxxn)f5~SCONlAmOsxf-PsYP8MFscI z!r&iY0=xG{Ha+w?|EU`-bs zg>_7BEg9pF?@H0~IedEjIaU|4N_a98KcvgJ8!ksocNg4gakKx=q(cxDEDZnIrDePO zzaz(#ugYb_#x-0>$6FHubuS@?GCT1rM9YT#|DqB&Z|kq3l5oQI(fhoX3ZW6 zHzg^Fz!M@mZVuA*f79{}_c^DEe*cdhHh}cJh#0tM7v6?t_i<0_fjC34AkPHnS4r{+L9txPN1=wDblpCSWQCTrFF_1BolX z)C7^;>fj3z)J~6Hymt^NkJ*b!_fBXhcd&>cdUaufjXF7Lk{3qn*YS)Gdj4wfK(4Dk zXN0=en!`ipXDQS0*M;r}J{%$@H3Sb=wtU+KgPuhd_@T#?xAa0r#N61}A%aA{ub-3} zZ^l)u&fkBswMK6A_HvXt4gxw(2d~*nC_Jp$tjdYAuj(Sz2)orwUQGuB^^ z%_M5QMDmrG%CJ$OS}>l74xz$1@cZph(ztR3)s4$5Cpb;d+TrHEko$C9Wo^_a<^|rL z@|l%3pY@-stdI9AD2VlGQ#UUREr$&H0FLTCiM=G7v}D+%e_U)I(T1G;al_h20Y8le zK9J$;weqyMJGLmKsNq87PS_@ZUM1J!jTn3p^3wm@?{}S;9w_X9VO-K8W3wi7q#`XZz{-1Rg z&+3!}*uo$+?WrBsJEeJ> z@81_}gEad|Vh*?di`>-NZ%>io>eX)?4pk@_$YJ;NvB^e5LW!tqi}%AQjr>fi>AQdd zvDkVgDDvp2yuytDuOUoQwd8JSyFkAUF}iV+uB$v`YHb@&*0xq|n>si|FDt)pUp_9` zkP418Xef^kCKArT5xk)XM*_nxbWv7z$C=G>+|n6Ei^ALt!+_5VdG8bNMW{)Q4IbFM zvuM(&808?$mFwR5#fIp9`XsL=)6uN-%3sCU9E|Ry|L2~poZR!yhnmmt zX9ZQksQdlB0>^=wPJ&|maVlQt{Zk0^Fi^dR=sNjDy%pqjwftAX$!@}M%x)cL>Ptw< za&@>%nyf%>v|GMX9-f-f=g`ITCB4wl+KWjPA1dltf#TSFoh$;^8xqX8hen|0f1*Yc?W6;LEw6fHt+=(R+70x{aaq4;{cB7B@^bT@q_*p0ctl?HX zTIGQGrhuwKU}5mF;Onu&%U2#62C?(eFHLDzAq_22s#@^klZCF%ejtqto>A@P;j$k_ z%}`+-8;!z^>uyRz>6lqEl|@u)2$TeTd(XYZhnl=2YJteE;hWyqV0PmE7X~JsNN>@a zYpcjxDu1eIi}L4204Ej42EGyfn~Zk1>cWX@3XZ*F0N>0(WTCIhcwR!|tI%F`pL@Xx zGX^78hOe4<|G8xb6YD4t;dMC zZVqm-Ddx(TKlSgc@WnhKCSOd!c<}$GlN6LSh{{1^H#ZAC| zbLmf}zhkGJ1L3*en_A?J>h&+OMe;8M+m78J5ZeXKDLx4wNt#-k;Z-p!kx`sp=D${UzX*5}{ADqg@+~`f8)!?A9pH3- z#L(ddALXkkx$ALl_<`;%K3QY|Ff)Sl(LYIUvUMXXv|B6_fKjv14?`*kWrr38x~zM% zzzeyA`W%Qm^7Q^D4rLR3Q9jzue2fQ~p&*O1W?TF8zs1UA8~DEDQ`sy423Wsjy8Xje zQu#zBGjXbc20d|ki+B2$gKJbw-B(o%Bd3q;@tZ0f@rzk{0L0b;wj^#GeD(2YzSB(F zI?$(7%9?_Jb?8`=DM&{TIoVwF8q-n3DStEvkwo5TgH^6t5c{U?V7$E3#G5F2-o2a> z$8S$+ZL7s*|93~~1xLelrKasM8L`08xAsq~Hf7sViTKmiL!9fdKHlLkJc7kPxcAI@ zGvaBMMijfwuUML#A46-4bonNQ)6G8q@ZnpGqM(e2cv;KU3N;Pv&F^c_XP~fTZb~KluEr%us5=m2FdBC(#s9I1{fxi61F0_sEp87txWE zjb*iK@WO?+Aqe>^X3@)C-~7m>$7yJdu$Yl2?Z4**a4*JPl?7+`)!n$kwjIEUIi8wTn)7@*5PnT=kFHd_pQD*2YofZe;q??r9W2b#0$5 za4b+HjMTT&yaNTvEn(B3$ z^bwEfjZ|Ws3LHz*#=2mT@2n$H3Yzt=iOli`NrMM~F^5-1O6mjNdhCl*WV>~k;a{Wj zB^jo=;|=XJncdG_Lt!xGzZba{zT;MRekFVq!Ar}Fpf@$K-uhTQOcPrup+==Nvj;2` zaSS6`xDiLy1CnQRI&%2Vf77DBLh2pnsd9-)os+O8ZyJPm8g?Q%D z#WP=FQmSxq!w*RNi)+sxb591)&v)AUblASrsnG=(A#t%pZbBYZ z_B!XAe+vFX@+1R+sgz6yuOzJ;Ye^;yiXE*}=OzMnrhAhLzB<;W^*C9pi(?ICN&cZu z*!^Y)rUwTeNw0P@U-_Uc2xATZL@Y&_c|15Krx^ANf!7hMGJjX2xW#(sL{qpRbal@l zY9vOK{~IQuE~c$G$F*ntR-^8v2C|?R;}Hyi?Ey#v$geNkT>}dfgq489G4w1@J+r-H zrr2yeW02S-8UMSq*sxiB!C0N&s7;wcUA}8Hw_wt%s|3RwPnW%qYYT5Fu)P~@I3?ICRUr%RMfw6;c$9sT;N^YD~QMP9cWUXAx8|xKDJ= z$DI96rRsYI?yiq1H||Ocd*M0vD?C^P#6q~Por9I;87W}V;UV!PFCWTaacG=>?!t;t zFB2;jP5M-9>>!7vX)bI^d5}i>1M&+*Bkf{aR4mJjhR{|ipWMX3j=5%FWW(G$UUpP0 zJg#hfh%4pqZi?ewJihf{#mau`*KGVtcJyJTY}x1pV15I1LZ{kwWw7@5$-h`i?IX^1 zH}Bw6X7{abTI9*d?f?Kj%)`dW^XLHPc03YYMa3WB0;fsDGYS{j`2s4KxRa9-V-3!; zT^xa^2uYFuUQu4qis($4B$bq1swq;AM1f~f|Do%tRaFnk;P2>*Kg0Q}DFFG_xD4qo zr}g`wOVDg}Sb-LXP#~f*Z`QT%8aj=aO24eXepzE^(^;4{o8gNz@+Ms|&1+5)gHIF` zP1ozLZ{9jtpaX~zJ0C)geeWh9>0?Zf4>hJWBVm6-Tggh`rXQ^jd8VWjN-J-u!aEPr zkjAgiMW)HS+{q^|_#O+KZ=NL?sKRXo-XKJFvS+H+!J31x40>pGfB~}5;LubCeiJ({O&Tqr1-y#mS}KIO3+H6^qH2%< IesBN)0ManhGXMYp literal 0 HcmV?d00001 diff --git a/img/locate_by_label.webp b/img/locate_by_label.webp new file mode 100644 index 0000000000000000000000000000000000000000..7376290ef7b8400b39d48c860e197c0d24980de0 GIT binary patch literal 924 zcmV;N17rMBNk&GL0{{S5MM6+kP&gon0{{S!9RQsHDy0EU06vjOolB*pBB7*M=t!^< z32AQOZy@;`13&(NKmX!t?yYZOda2}YFz@a7!`sJ)en5_4T2y5>J#qxR1K3Ob&+oo{ zKB4vs{Y?Ld*aOM{GPbyLYj(~xUlqe9e#zb=t{AUEx08M{#=3wLK2|)`=R-{RX z@_JJ;&A2|B3>eW%z%1 zToMO#*r$LJ*Pf_A0RI0)jsHGd>dviHKf$c*Pr$LYAMt;)f9-dXbLvVjP)Ao~4{1l3 zL%pP3#l9YNi$>9dXxCvf(uY_oyN4I@ioG_}sm) ze=C>Xgo*msr5%aFCFqBYVzSLTwx;04@_yB}tVp4cX<=;~TGjqTN(0f~AfV8w$o8^B z-gFAT>_JUTbGDDKDnSS114|hPm;>+`tbpX~nMu#R56uzRzK*_Ii=}~K5(u9WW_y+Z zrte-GiOSO^GhE_N_~thEc5R+A1h1rCfxNlY8kzfn4iDFzw%XnOz;)Z06VKZ4o>6eB zdogE>P{8!M|1xp$zw#zWpe^w3?&7cv63M#36bV*r%75soKM;^)=UM$rK)#^x>}-CK z!&lY5PILd$;y3J5vNTD(f?|*!@;R(2_hQsA&6Y}$;;LPqO10d-m2SYxDQcp{F(;xb zqYDPhdXp(*vo1cAueDN~l*k77_#|mo^gVV8UXsq?N2s|AzEthXF_3BH`viVCyr=3K}UIO*oCV&|tA3}=Vu zX=oS#0h~o9WHf5lzBBuRe z1_MzT_6Z7fl!qPGt1-|+3{#m^;N9IHz!3$MMQMi$S_29~Y#$y%+2l$Ec2WF+f#Pbw zy)H?;{DYs^%uWuPLVcRF0DO#_Y8}e?vM!bNQdk6S-~j9L9PTNz6fO!&)Fo$N0MJ>x yhqax++uqB{l_TT%rrGJe4 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 UKAQBdhV4lt4VNDmTYvxn06P|Xpa1{> literal 0 HcmV?d00001 diff --git a/img/locate_by_text.webp b/img/locate_by_text.webp new file mode 100644 index 0000000000000000000000000000000000000000..82321fc58e59d067f6aa324bce89c44a0849c8ba GIT binary patch literal 3924 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 literal 0 HcmV?d00001 diff --git a/img/login-btn.webp b/img/login-btn.webp new file mode 100644 index 0000000000000000000000000000000000000000..32d862a6e653cba6400da7840c0322c344c9f9c1 GIT binary patch literal 2452 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 literal 0 HcmV?d00001 diff --git a/img/login-locators.webp b/img/login-locators.webp new file mode 100644 index 0000000000000000000000000000000000000000..d9f89129e016bbe324f8389fc8c2df060f2632c6 GIT binary patch literal 9374 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 ZH=glUtgQ}^)$A0Hh7$JuD~$g-{|ghMQ8WMm literal 0 HcmV?d00001 diff --git a/img/page-item-locator1.webp b/img/page-item-locator1.webp new file mode 100644 index 0000000000000000000000000000000000000000..ec70757d9b2727a7bd89782cdd798b32f80be03f GIT binary patch literal 482 zcmV<80UiEQNk&H60RRA3MM6+kP&gpY0RRBd3;>-0Dxm;A06vjOpi3pBqoJkQ_&Bf< z31+}(w+9(#sC2R{=ygXe_@VYg{#Hpmp|eF zvhpf=3?o?&?!6f>|DpOi3E->B$Aic2(T}Jyz}uKAYxC{{S`I(x)WZuE@a?=`PF;?Q zG4zih&4?+{~ehi@U0Cp(T+aTPlXa{K@*+O^(VjnO^o{X9dig)RkAZ#2af6w!W z^DPIHNugui0zK6KJzL%Q5ni}r4)4M zGE-ua^)#rOP&VHOD@ViqRy?Nzs?v*_Z1F9E>!yAJAfBAXzAouID*E{<{&H`~nRu=s zmG%f{0mjNnjDw*Z1%=0Gqr)|1@5uVaw_pOJy7gc=?Mt}~EHhl9ZD(rE4>0iyDXvPg z`NzTgp*>}O@GYl6hP}UV6@?dJ)X3c=zWGHp@iTJ)krlZHkUYGE06ff?s8K44s%nS4 Y(g6Ls%ktuHXq4oAytI~k;`Quk0N;%6xc~qF literal 0 HcmV?d00001 diff --git a/img/page-item-locator2.webp b/img/page-item-locator2.webp new file mode 100644 index 0000000000000000000000000000000000000000..729237f323d5ebe9adeb4e71e6850ba9c65404a2 GIT binary patch literal 5622 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 Qp#rQA7ytkO000000Ol++p#T5? literal 0 HcmV?d00001 diff --git a/technical-guide/developer/frontend.md b/technical-guide/developer/frontend.md index a096164..6f2356c 100644 --- a/technical-guide/developer/frontend.md +++ b/technical-guide/developer/frontend.md @@ -7,7 +7,6 @@ title: 3.5. Frontend Guide This guide intends to explain the essential details of the frontend application. - ## Icons & Assets The icons used on the frontend application are loaded using svgsprite @@ -28,7 +27,6 @@ Then, you can reference the icon from the sprite using the For performance reasons, all used icons are statically defined in the `src/app/main/ui/icons.cljs` file. - ## Logging, Tracing & Debugging ### Logging framework @@ -49,7 +47,7 @@ this kind of line and change to `:info` or `:debug`: Or you can change it live with the debug utility (see below): ```javascript -debug.set_logging("namespace", "level") +debug.set_logging("namespace", "level"); ``` ### Temporary traces @@ -102,7 +100,6 @@ object, interactively inspectable in the devtools.console. ![clj->js example](/img/traces3.png) - ### Breakpoints You can insert standard javascript debugger breakpoints in the code, with this @@ -120,7 +117,6 @@ One way of locating a source file is to output a trace with `(js/console.log)` and then clicking in the source link that shows in the console at the right of the trace. - ### Access to clojure from js console The penpot namespace of the main application is exported, so that is @@ -130,7 +126,7 @@ you can emit the event to reset zoom level by typing this at the console (there is autocompletion for help): ```javascript -app.main.store.emit_BANG_(app.main.data.workspace.reset_zoom) +app.main.store.emit_BANG_(app.main.data.workspace.reset_zoom); ``` ### Debug utility @@ -144,7 +140,7 @@ You can change the [log level](/technical-guide/developer/common/#system-logging of one namespace without reloading the page: ```javascript -debug.set_logging("namespace", "level") +debug.set_logging("namespace", "level"); ``` #### Dump state and objects @@ -153,31 +149,31 @@ There are some functions to inspect the global state or parts of it: ```javascript // print the whole global state -debug.dump_state() +debug.dump_state(); // print the latest events in the global stream -debug.dump_buffer() +debug.dump_buffer(); // print a key of the global state -debug.get_state(":workspace-data :pages 0") +debug.get_state(":workspace-data :pages 0"); // print the objects list of the current page -debug.dump_objects() +debug.dump_objects(); // print a single object by name -debug.dump_object("Rect-1") +debug.dump_object("Rect-1"); // print the currently selected objects -debug.dump_selected() +debug.dump_selected(); // print all objects in the current page and local library components. // Objects are displayed as a tree in the same order of the // layers tree, and also links to components are shown. -debug.dump_tree() +debug.dump_tree(); // This last one has two optional flags. The first one displays the // object ids, and the second one the {touched} state. -debug.dump_tree(true, true) +debug.dump_tree(true, true); ``` And a bunch of other utilities (see the file for more). @@ -192,7 +188,7 @@ This is also in the `debug` namespace. To activate it, open the javascript console and type: ```js -debug.toggle_debug("option") +debug.toggle_debug("option"); ``` Current options are `bounding-boxes`, `group`, `events` and @@ -201,8 +197,8 @@ Current options are `bounding-boxes`, `group`, `events` and You can also activate or deactivate all visual aids with ```js -debug.debug_all() -debug.debug_none() +debug.debug_all(); +debug.debug_none(); ``` ## Translations (I18N) @@ -210,7 +206,7 @@ debug.debug_none() ### How it works All the translation strings of this application are stored in -standard *gettext* files in `frontend/translations/*.po`. +standard _gettext_ files in `frontend/translations/*.po`. They have a self explanatory format that looks like this: @@ -300,9 +296,7 @@ msgstr[1] "%s projects" ;; => "1 project" ``` -## Tests - -### Unit tests +## Unit Tests Unit tests have to be compiled first, and then run with node. @@ -315,64 +309,406 @@ Or run the watch (that automatically runs the test): ```bash npx shadow-cljs watch tests ``` -### Integration tests -#### Setup +## Integration tests + +### Setup To run integration tests locally, follow these steps. Ensure your development environment docker image is up to date. 1. If it is not up to date, run: + ```bash ./manage.sh pull-devenv ``` 2. Once the update is complete, start the environment: + ```bash ./manage.sh start-devenv ``` -3. Open a new tab in the tmux opened by the development environment by pressing `Ctrl+B C` and navigate to the `frontend` folder: -```bash -cd penpot/frontend -``` - -4. Install dependencies, this is necessary only the first time: -```bash -yarn playwright install -``` - **NOTE** You can learn more about how to set up, start and stop our development environment [here](http://localhost:8080/technical-guide/developer/devenv/#getting-started) -#### Running the integration tests +### Running the integration tests + +1. To run the integration tests, open a new tab in your development environment: + +```bash +ctrl+b c +``` + +2. Go to the frontend folder: -1. To run the integration tests, go to the frontend folder if you are not already there: ```bash cd penpot/frontend ``` -2. Then, execute the following command: +3. Then, execute the following command: + ```bash yarn e2e:test ``` - These tests will use a headless browser and display the results accordingly. -#### Running tests with a browser +### Running tests with a browser To access the testing UI, please follow these steps: -1. In a terminal on your host machine, navigate to the frontend folder and install dependencies, only the first time: +1. In a terminal on your host machine, navigate to the frontend folder, then run the next command: + ```bash # cd /frontend -npx playwright install chromium -``` - -2. Then run the next command: -```bash npx playwright test --ui ``` + > ❗**WARNING** It is important to be on the right folder `frontend` of the project or we may have silent errors trying to run the tests. + +### How to write a test + +#### Page Object Model + +When conducting a significant number of tests, encountering repetitive code and common actions is typical. +To address this issue, we recommend leveraging Page Object Models (POM). + +Page Object Models allow us to consolidate information into a single class and encapsulate it. + +POMs do not necessarily refer to entire pages but can also represent specific regions of a page that are the focus of our tests. For example, we may have a POM for the login form, the footer of a complex page, or the projects section. + +In a POM, we define locators for page elements: + +```js +class LoginPage { + constructor(page) { + this.page = page; + this.loginButton = page.getByRole("button", { name: "Login" }); + this.passwordInput = page.getByLabel("Password"); + this.emailInput = page.getByLabel("Email"); + } + // Other functions and methods... +} +``` + +These locators are used in assertions as follows: + +```js +await expect(loginPage.loginButton).toBeVisible(); +``` + +In addition to locators, POMs also include methods that perform actions on those elements. + +We are simulating user actions and events users trigger, so in other to mirror real-world user interactions. To achieve this: + +**Use Realistic User Scenarios:** Design test cases that mimic real user scenarios and interactions with the application. + +**Simulate User Inputs**: Such as mouse clicks, keyboard inputs, form submissions, or touch gestures, using the testing framework's API. Mimic user interactions as closely as possible to accurately simulate user behavior. + +```js +async fillEmailAndPasswordInputs(email, password) { + await this.emailInput.fill(email); + await this.passwordInput.fill(password); +} +``` + +Lastly, POMs can include interception functions necessary to load that section or page. Only include common intercepts in the POM. + +```js +async setupLoginSuccess() { + await this.mockRPC("login-with-password", "logged-in-user/login-with-password-success.json"); +} +``` + +With all these elements, a login test could look like this: + +```js +test("User submits a wrong formatted email", async ({ page }) => { + const loginPage = new LoginPage(page); + + await loginPage.setupLoginSuccess(); + + await loginPage.fillEmailAndPasswordInputs("foo", "lorenIpsum"); + + await expect(loginPage.errorLoginMessage).toBeVisible(); +}); +``` + +#### Mocking (EN REVISIÓN¡¡¡) + +**API calls** + +In order to mock and API call we need the url and the body of the response. +The body should be + +```js +export const interceptRPC = async (page, path, jsonFilename, options = {}) => { + const defaults = { + status: 200, + }; + const interceptConfig = { ...defaults, ...options }; + + await page.route(`**/api/rpc/command/${path}`, (route) => { + route.fulfill({ + interceptConfig, + contentType: "application/transit+json", + path: `playwright/fixtures/${jsonFilename}`, + }); + }); +}; +``` + +**Websockets** + +NPI + +### Testing best practices + +Our best practices are based on [Testing library documentation](https://testing-library.com/docs/). + +This is a summary of the most important points to take into account: + +#### Query priority + +Queries are the methods to find elements on the page. +Your test should simulate as closely as possible the way users interact with the application. +Depending on the content of the page and the element to be selected, we will choose one method or the other following these priorities: + +Todo: añadir ejemplos + +- **Queries Accessible to Everyone**: Queries that simulate the experience of visual users or use assistive technologies. + + 1. [`page.getByRole`](https://playwright.dev/docs/locators#locate-by-role): This selector allows us to locate exposed elements in the [accessibility tree](https://developer.mozilla.org/en-US/docs/Glossary/Accessibility_tree). + + 2. [`page.getByLabel`](https://playwright.dev/docs/locators#locate-by-label): If we need to query for form fields we prefer this way. + + 3. [`page.getByPlaceholder`](https://playwright.dev/docs/locators#locate-by-placeholder): If your form field does not have a label you can use this locator. + + 4. [`page.getByText`](https://playwright.dev/docs/locators#locate-by-text): Use this selector to located non-interactionable elements such as div p, or span by its text content. + +- **Semantic Queries** -> These selectors comply with HTML5 and ARIA standards. However, it's important to note that the user experience when interacting with these attributes may differ significantly depending on the browser and assistive technology being used. + + 1. [`page.byAltText`](https://playwright.dev/docs/locators#locate-by-alt-text): If your element is one which supports alt text (img, area, input, and any custom element), then you can use this to find that element. + + 2. [`page.byTitle`](https://playwright.dev/docs/locators#locate-by-title): The title attribute is not consistently read by screen readers, and is not visible by default for sighted users. + +- **Test IDs** -> Finally, if none of the previous options is possible, we can choose to locate the element by its TestId. We must keep in mind that this type of locator is not user-oriented. + + 1. [`page.getByTestId`](https://playwright.dev/docs/locators#locate-by-test-id): Use this method if you can not locate by role or text. + +For our integration tests we use Playwright, you can find more info about this library and the different locators [here](https://playwright.dev/docs/intro). + +Simple how-to guide on locating elements for our tests: + +Given this DOM structure. +```html +
+

Penpot is the free open-...

+ + + + +
+``` +That represent this part of the app. + +![Login page](/img/login-locators.webp) + +Our first task will be to locate the login button. + +![Login Button](/img/login-btn.webp) + +Our initial approach involves following the instructions of the first group of locators, **Queries Accessible to Everyone**. To achieve this, we inspect the accessibility tree to gather information. + +![Accessibility tree Login Button](/img/a11y-tree-btn.webp) + +Having examined the accessibility tree, we identify that the button can be located by its role and name, which is our primary option. + +```js +page.getByRole("button", { name: "Login" }); +``` + +For selecting the input within the form, we opt for `getByLabel` as it is the recommended method for locating form inputs with available labels. + +![Password input](/img/locate_by_label.webp) + +So we can use this in our assertions: + +```js +page.getByLabel("Password"); +``` + +In cases where the previous input does not have a proper label, we can locate it by its placeholder. + +```js +page.getByPlaceholder("Password"); +``` + +When we need to locate a text with no specific role, we employ the `getByText` method. + +```js +page.getByText("Penpot is the free open-"); +``` +To locate the rest of the elements we continue exploring the list of queries according to the order of priority. If none of the above options match the item, we resort to `getByTestId` as a last resort. + +For example, we use this approach when we try to select a page element within the list of pages in our file. + +![Page item](/img/page-item-locator1.webp) + +This element has a generic role, no label or placeholder, and no title or alt text. + +![Page item accessibility tree information](/img/page-item-locator2.webp) + +Moreover, its text may change. + +```html +
Page 1
+``` +In these cases, the only way to locate it is to assign a test id. + +```js +page.getByTestId("page-name") +``` + + +#### Assertions + +Assertions follow this structure: + +```js +expect(query).toBeTruthy(); +``` + +**Keep Assertions Clear and Concise:** Each assertion should verify a single expected behavior or outcome. Avoid combining multiple assertions into a single line to maintain clarity and readability. + +**Use Descriptive Assertions:** Use descriptive assertion messages that clearly communicate the purpose of the assertion. + +**Preferably choose assertions from the user's point of view:**. + +The title exists or is visible. + +```js +await expect( + page.getByRole("heading", { name: "Log into my account" }) +).toBeVisible(); +``` + +The url contains a given substring or regex. + +```js +await expect(page).toHaveURL(/dashboard/); +``` + +Avoid asking for something user can not see. + +```js +const locator = page.locator(".my-element"); +await expect(locator).toBeHidden(); +``` + +**Avoid hard-coded values:** Avoid hard-coding expected values in assertions whenever possible. + +In this example we have the error message hard-coded on the test. + +```js +test("User submits a wrong formatted email", async ({ page }) => { + const loginPage = new LoginPage(page); + const errorMessage = "Enter a valid email please"; + + await loginPage.setupLoginSuccess(); + + await loginPage.fillEmailAndPasswordInputs("foo", "lorenIpsum"); + + await expect(errorMessage).toBeVisible(); +}); +``` + +It is preferable to obtain these values from a POM in which all data are encapsulated, stored and can be consulted, used and modified if necessary. + +```js +test("User submits a wrong formatted email", async ({ page }) => { + const loginPage = new LoginPage(page); + + await loginPage.setupLoginSuccess(); + + await loginPage.fillEmailAndPasswordInputs("foo", "lorenIpsum"); + + await expect(loginPage.errorLoginMessage).toBeVisible(); +}); +``` + +**Cover the error state of a page**: Verify that the application handles errors gracefully by asserting the presence of error messages. We do not have to cover all error cases, that will be taken care of by the unit tests. + +```js +await expect( + page.getByRole("alert", { name: "Email or password is incorrect" }) +).toBeVisible(); +``` + +**Preferably positive assertions:** Avoid using `expect(query).not.toBeTruthy();` + +```js +test("Check if user is not logged in", async () => { + const loginPage = new LoginPage(page); + const isLoggedIn = await loginPage.checkUserLoggedIn(); + + expect(isLoggedIn).not.toBeTruthy(); // Negative assertion +}); +``` + +Instead, it's better to write tests with positive assertions that explicitly verify the expected behavior. For example, we could rewrite the test to explicitly check if the user is logged out: + +```js +test("Check if user is logged out", async () => { + const loginPage = new LoginPage(page); + const isLoggedIn = await loginPage.checkUserLoggedIn(); + + expect(isLoggedIn).toBeFalsy(); // Positive assertion for user being logged out +}); +``` + +#### Naming tests + +**User-Centric Approach:** Tests should be named from the perspective of user actions. + +Instead of + +`testLoginFunctionality`, + +use + +`shouldLoginSuccessfully` or `verifyLoginFailureMessage`. + +**Descriptive Names:** Test names should be descriptive, clearly indicating the action being tested. + +`shouldDisplayErrorMessageOnInvalidCredentials` + +communicates the expected behavior more effectively than + +`test1`. + +**Clarity and Conciseness:** Keep test names clear and concise, avoiding unnecessary verbosity. + +`verifyErrorMessageShownOnInvalidCredentials` + +is clearer than + +`ensureThatAnErrorMessageIsDisplayedWhenIncorrectCredentialsAreEntered`. + +**Use Action Verbs:** Start test names with action verbs to denote the action being tested. + +`shouldNavigateToLoginPage` or `verifySuccessfulLogout`.