From 5c283eceefd1382f523539dfac80ad7f69462a5d Mon Sep 17 00:00:00 2001 From: Jarian Cottingham Date: Thu, 20 Aug 2026 21:34:15 +0000 Subject: [PATCH] chore: remove test artifacts, fix hardcoded paths, ruff clean, license - Remove committed .coverage and test-results/ (196K screenshots); gitignore them - Fix hardcoded /home/userpath + venv/bin/python in test_endpoints.py (relative backend dir + sys.executable) - Fix concatenated 'import json' in settings.py; bare excepts -> Exception; SQLAlchemy-safe is_active.is_(True); __all__ on models/schemas barrels - ruff clean (93 fixes), MIT LICENSE, PLAN.md -> docs/, README Tests section - 300 tests pass, 93.7% coverage --- .coverage | Bin 106496 -> 0 bytes .gitignore | 4 +- LICENSE | 21 +++ README.md | 45 +++++ backend/app/main.py | 3 - backend/app/models/__init__.py | 18 +- backend/app/models/events.py | 2 +- backend/app/routers/account.py | 2 +- backend/app/routers/events.py | 2 +- backend/app/routers/import_.py | 4 +- backend/app/routers/lofi.py | 2 +- backend/app/routers/mood.py | 3 +- backend/app/routers/playlists.py | 4 +- backend/app/routers/radio.py | 2 +- backend/app/routers/releases.py | 3 +- backend/app/routers/settings.py | 6 +- backend/app/routers/shareplay.py | 11 +- backend/app/schemas/__init__.py | 30 +++- backend/app/schemas/mood.py | 2 +- backend/app/services/audio.py | 2 - backend/app/services/mood_engine.py | 1 - backend/tests/test_deep_coverage.py | 2 +- backend/tests/test_endpoints.py | 11 +- backend/tests/test_routers_lofi.py | 2 - backend/tests/test_routers_shareplay.py | 3 - backend/tests/test_services.py | 1 - backend/tests/test_services_advanced.py | 4 +- backend/tests/test_services_extended.py | 7 - PLAN.md => docs/PLAN.md | 0 .../error-context.md | 155 ------------------ .../error-context.md | 155 ------------------ .../error-context.md | 155 ------------------ .../error-context.md | 155 ------------------ .../error-context.md | 155 ------------------ .../error-context.md | 155 ------------------ .../error-context.md | 130 --------------- .../error-context.md | 130 --------------- .../error-context.md | 130 --------------- .../error-context.md | 130 --------------- .../error-context.md | 130 --------------- .../error-context.md | 130 --------------- .../error-context.md | 155 ------------------ .../error-context.md | 155 ------------------ .../error-context.md | 150 ----------------- .../error-context.md | 150 ----------------- 45 files changed, 141 insertions(+), 2376 deletions(-) delete mode 100644 .coverage create mode 100644 LICENSE rename PLAN.md => docs/PLAN.md (100%) delete mode 100644 test-results/features-Internet-Radio-Page-shows-internet-radio-title-chromium/error-context.md delete mode 100644 test-results/features-Internet-Radio-Page-shows-search-bar-chromium/error-context.md delete mode 100644 test-results/features-Internet-Radio-Page-shows-stations-section-chromium/error-context.md delete mode 100644 test-results/features-LoFi-Channel-Page-shows-LoFi-label-on-channels-chromium/error-context.md delete mode 100644 test-results/features-LoFi-Channel-Page-shows-lofi-channel-title-chromium/error-context.md delete mode 100644 test-results/features-LoFi-Channel-Page-shows-lofi-channels-chromium/error-context.md delete mode 100644 test-results/features-Mood-Radio-Page-clicking-set-the-mood-changes-mood-chromium/error-context.md delete mode 100644 test-results/features-Mood-Radio-Page-shows-circular-progress-indicator-chromium/error-context.md delete mode 100644 test-results/features-Mood-Radio-Page-shows-mood-name-chromium/error-context.md delete mode 100644 test-results/features-Mood-Radio-Page-shows-mood-radio-title-chromium/error-context.md delete mode 100644 test-results/features-Mood-Radio-Page-shows-play-mood-button-chromium/error-context.md delete mode 100644 test-results/features-Mood-Radio-Page-shows-set-the-mood-button-chromium/error-context.md delete mode 100644 test-results/features-New-Releases-Page-shows-empty-state-or-releases-chromium/error-context.md delete mode 100644 test-results/features-New-Releases-Page-shows-new-releases-title-chromium/error-context.md delete mode 100644 test-results/features-SharePlay-Page-shows-create-room-option-chromium/error-context.md delete mode 100644 test-results/features-SharePlay-Page-shows-shareplay-title-chromium/error-context.md diff --git a/.coverage b/.coverage deleted file mode 100644 index 1e3f2c219120df9cf40f5c7bda0e46d2472ecfee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 106496 zcmeFa37izg-T&X+)kk$t&&=-5-uKSzaKmyaAYR~oAYQ15C@iqZBJ3i&APS;8vpa_| zYP_SyE5?U-#dyW2k1@s=V~mN$D@GGz3>w7?Zz0bAQ}dnghUjl)wjQ0ee{ttyZgIZm-Mb$(b?ZRNh{NIU7Oz8s%e^w|LXA{|8W2&C-ASn z9setIwQ*;h8Vnz+`Su(wd{VGJJk~$ie?M@QKh-@Z(Cv(I$Ki|8pOOVi7ARSuWPyJh z3k)3TJ2iE6dg9Ff&V|dn`d4)>>RLsA2lqW{{~5FQZ=F43pPBo&(r2wByjJ`fJGOO3 z>#E+>+~`C0}5B`?^@eWAK*v2x=-#=N3@P8oIbL3Ro9ZPRb4%c zy80*=Bf1xl^hPGn@|>DTL{E3+%hb7Q5&j?eN@?)Hmqj^v34Ul%@2bTr_X}5b_AFZ3 z)iFe(A?(J#qTHCc~b${35e_FsX%4qxt z#@oI$_^k!1Knv*1!nL-u&X*_ulQRp47fxP+%=PrLS-(6{T^c9n^W3v~#X^{T+3FQ5 zRpsCex^RE*zdG|@o!j4ADC|zx%|EHjFU$Lt>SU9v8l|&V_jI4Sx~o|EV_LsFb4aw!L@7lZ~0eEr`QDMPbi`{P-ieUmI{Q|yVj6{s`LTs5lWZT zf7VwDp+5Pt3;e@Cu=Ckp+1bCe^}ps_eT<9`x9pnTtNvBSRTkz?P=DKlWkx>B8IiaC z3W&b?PkbfI>XR?Cd}!C{W7l*phnlSH>|TW~3k{|bpj92XxDy9=F6>?1pKre^nQxwI z?D;ak^aEd}+4eYUWd3k;?<0Eq`noZ!s$r{d8h_e4d}(K&QlY-#tqXg5mv?pc(Aa=8 z4(`J>yZc)6dHAoR(%xVGM&Ie`H%?wNJf9~XT$IeuMyKQc1z547vuAN(yi&t3{VKFy zbxw615)qu(Ivky{4_)*hev^N+@U2oT{{3+)VH5SMD>@fbcUP6N-7lWDwClh89F0c% zb@q4lcdzJbRfps=lW$n3b@sI`TGfRw3u9?MW3brLf6NvA%t82uHIzy-a=6%0+fh|K ze}ej3t&*jh$_=#WPR;1i`oQW!4<>P%--nsNicT(TA!&T2W&OjKUpBJCq0*QlZQr<* z!MC%aO41dSB2u+4B-8Oxg$a^3GKgI2;jJ3}U;0zBK*<6n3zRHSvOviKB@2`+P_jVD z0woKSEKssQ$pZg=77)6jJ8J!JgkRCZ@8GY}pOOVi7ARSuWPy?eN){+tpk#rP1xgku zS)gQrk_Ac@_+M!O-w;h<4hxx3ri+HULJYu^aT9hOH{L_3jPM&;_>J&K|0@ev%5=#B zB@2`+P_jVD0woKSEKssQ$pR${lq^uPK*<6n3luHjizc1j0EQEz7kWMPBe9OZxNKjmNT_xQW}e&F|kn*wJ94h__J zuX%TPXL+-{CU>)YzngaFxFh7d@fYr1;y@bt>lcO5IVQ+IGeU)QQN-Pm4$a)7-7x_i#RX%of>t$PQjan4qC z;o;7{E_I~|qj&CrqRO&lS#Q+99?7iRl&#ts)+=T4lnymv`AF3~Y;=jx@e3s1!^#R*g5LR-Cq-!B$q zRp;XF-uVmhpd~)SiR0RZwrB?@a$)l4;rOw!oh!(S-rmJHd}4IxY^6-Qyt^;o#yZ+| zT=-&HI#;7CxJXCq*PM`lDzy*W0<2ik+oMis*|`&n=Iv0WJ1%9TQ5Dv+SjpN zgCF0xvvoqe37rT-MSrDIiFK#yW?ti+@kpKVvDNTxt}kz zs0r0OS7>fgs?ApI+!gwIdrrpT6DoJ^3iIc8_jLEqS2{8-vg0yyFH%>?i(AExFTuxe zcZqnp(2m?eM_mD_MNY<-eN8K3fnI+4s_sR3Ne}O=q@zpE@9H@j`)=Su6GLCqlK4W~ zm0?_P$7Wj5ySQt4Uq`VQjrVt4n8NYfccQ>fov~eynBeWWDhk*5N{4WF+@3gFN@`)L zF;ZyrcT{s!>O%J!=V01Gh;tGt_Ed-QhoJ4WpI zXtpKwv+>4G<$b%~AE^uN_#IesyF=Ce{|@^jE&N>gpm4qabohqgrJ-{}_l4dFofv#H z_-SZVc#VIJ{gE@=`AA;i9`9z{k5;oa%J?HwMN8Mry{Vql$jw%6?a zHBjpZgA3gU{Lg};ojLM7IsAWo%2&!j$pR${lq^uPK*<7{HbI=M?_hZ3S>=~2h>r21 zYiG_V&dA117N_p$gyPyP?|~?;|Hn=g-8(vsbGF?~Zp==evfWf}^iG!l%k}@LUBrqV z<*Qhn?WcbuCy7&bbP^Y4`*TK&6P-JAPGJ%_d?)LrxIi5?Rjl68?-vX5kL&+FXlFP4SZ|7~A)!auJ6TX*t=qIp|(@`PePY5tnl zTU-Qx>F{d$n%2vweA#;&ck&|Jb&-azsSm!$_HDQRYpR^jDYV_XuW3lelLUW5kgR0)iD-_oMRXcfw?N%F=UsGo8MZErxe9a~J`0XxHF-#n}qmEiw|CfJV zE8=B&as6Mm(~|y`^?&&5S`uGqyE25n#!Q9vVX+qlzosy}(EhR$`8#*Uc0D5SHC4fD z*6kd?-q*Az&Q@Xl?~WDocUW^@S^vwOJG#&toSiH0cAdfgn&1EG`rrEcqqnPH^J|V~ zThjVp>|EZr`+Z}iINsgC1ZTTLOZ)$oCd4HRlq^uPK*<6n3zRHSvOviKB@2`+P_jVD z0woLlTU$UNN+782|Lfsx8vbAUQ?fwG0woKSEKssQ$pR${lq^uPK*<6n3zRHSvOviK z|ArP2BIv67|619EsI+!RLKG*3zRHSvOviKB@2`+P_jVD0woKSEKssQ$pZhr7N|M59fhmP zF2moE^hEq!KDZ8lmnHT!@Q)q78h>l`;rGI?gnt`;Fnk+*pgt`9DOsRofszGE7ARSu zWPy?eN){+tpk#rP1xgkuS)gQr{|_us8?bd(*NstzuOy*0e3+%4+pi4bd&4lI zT1rcs4nC=oU4gWR#2i*VvdHBBY55hNuza2g=oD8oG z_krTeLr+f=#tPmp|e7(L#Kom zgpLUv652boOK4=MITQ)W;NOE=g0BZ(3O*HlG~d z-{e>LPGDQ${lKQc9|FG({5)`9;0J*l0^bgt7f1%y237%EoUVsDN&)0^(?;*Ibcy)w^qKXo^|e|Dd9 zA9o*g?{IH)uW-+E6Yd&!nR}9ZlzV_X%^l}P-D=m9U&t+TlYCJ=DIbEy~Lht&$9QiC)=a!X1l_+t<|gw+^GWkz^B(gi z^J?=#Gi9D(_LvLJW6eX%z08T`Ftg4Kn!5N%{7t+po)SM7KNh!&>%=7@E7pr1(J5w& z1H@D@N;Ha)(2Om{YsPcNW5#{PEymTx1xCW?H?v$y2d>A zFfGSCYLoT_^N3N}--|p#+r~V6l(x0VBec(%hYi<0V{VUXpEAeVwNHv1)jnp9#T3^`Eo>K@1cQ~g# z%SUW#(4Jxb-8k)c%y-Vxo@TzVS9_}984u^yX=&}Z>W8FmgmT(%^1mq+K3O*89%GiS_6uf5YL7Boj`nkAl>Ct*=d_aSfq2xhB z`)Q%%!JPIGAF<8Ueo|oH->N;xjGX-^GjjF-Gjeu6Gjetxvn{nBGow!KEpkr#5wozg zdzeutcNcQz8`@okocTHJPCjC*rQK2BfVWlqAv1DtJ2P_d17_slHfH4D)*|P$TbPl9 zo0*Y=n+iDy7~1y>ISAymjeG=h@E-;C>fY6EWUj5#zQ?OJB^pKFSo)2?O?Z`HoTjQ(>KGg{G=%xFbdFr%)%&5XLboEet7j2V`?lo^(~ zgxMKta2D$9&gWtdT_G&4$-Vn(Tkm{F=hX7r6DGx|n?8GU1*$T{uoeD-w+fO(di7X{!3 zMe2XKGx_Un=K2E4;I?&)e((&&fPXrp7g)>adZ#f;cMYQ>S2J2pKci{&6*0Gp(J)Uf zVJMa(T>ta`tT zv9fA0W2ABsV?|^kV_8KfW4LBP5zFQ?2DhHX=m$?^4EXaHy}${Kt~Zxay2mrxat@;v zKCX!Nv5dkxhS3nS3&wMdqYK7!a!2vkTdgA*-wz+b__vzF3uxEAGmEjN_AtiknwgAM z)rT@hst#eSh#XwR+(C?GTMuLmmmR$=ffbfo6 zy%}w}7o%0NXA$i^7=^VvqamghY-1a{6>MYYrt;TYttpJ}R7@_QRrBVqjMcea7%ShO z#8^=|k+HmD0%KYEc*byb2V*cijxi7%%P5^Oj80&55#=aG%NfZitPzZc7+xrjWeh76 z$I8X|>-Wpr8Q-jlsqdw(o4IKINC|2yd@UkdTNx`NEsW(A&5UK`O^o5PM#fOMfiW1W zXY_-0i~+xv(F@ctx?VM-bgLL`S;=VGks{_Q7==~NXo#{x!G#en6kOy&{Pk8VSU|(g z`Ha#HFgns>v>lhxvL&NwIgG-z84Y0-E^8QO;j%_fNZVDG%^40rmu}AUuh6y{D-Eps z9|_+Zz9oEZ_~LLTygs}VtNuCRL&JN9$A@F#s<0FK3@iSZL%#|AG<0X^d!frh+0eSs ziqQPfk)eG6UWTOMeC)jG z{Ly*ZdDOYjxy`xWxzstw+2E{lmN;{rS{K|8z0H2le$9Tte!_mpzRTWd zUu9ol587+(<@N%5wtbMjhdthIw`*{Ve-&&7Y_gc4D*IE}_8Ed_@(pqfI zv1VG+tzE1UR-;vBndYbFX7d&E8S^po0rPhA2J>?BT=Q(R-&|^*XdY$mXHGUpnayT} zX^XAmJ@J}&K|CQI5_gG>;wo{07!+&8akgj!**sdTo~IUEv-7wWFyTjI?rSy&CNQ`WFt+@I?rSyO`CO| z$wnHRbe_pZ8X9$;$wulMbe_pZYO8df$wq2wb)LyaYO3_RISpYu{n3m47*@!T8p3O!Kpp36oqU#4GCSXo7K-P*VHZx>Sw zwQKdu)z4y`Rgu##%U?k%W{K(ll&u7bzI8x{NY&kq2JfAIx2ZZOd<%iAEc|Kb{^Dv#~v*m}* z)X(PpA9ARE7W2V}=o^?1I#@rG`M`tp^~?txsIOz*_W=D2=6&|nPiLO7kG__9+6?_P z<|)(kHOyn4(^oT(9;5d&k9uD3V;(t0UsdE$`l-yrM(Qh>qfL4*b6ZsJVQy{HS1_ZG zFK0#{KZUtzn7*vY&3ZR8`t?%gx<>tEX0J|P!t8o_7qjE)i%X#E7{TaMM|GGDVuKfb_ayL_V0VV<;$ zejM|JPxND%J3i5mVIDU@pIzh*{b=Sf%a)~7KCKGAn$_JaDQBET@m>BR=u;4)!f+m*>{$;|kwY z`Z=t=HBJS5!(jFThR*C_UCs<`&oM*WzhFjt{yQ^rx{Vp_c`Gy8^XJUS^=AcbuQc>e z3))_p(?4N#)^5x#)ITn!zNg)&e^gA}pnXsOu$cO;c7y&wF?Bu8+fqzjr+rs{f4kK6 z`g_IHwc2(1yT#Ns+SU3y%8hJbywR@7>2DXmx>~zN-&{;xnd{cyDyFW`uGHTwroN5T z-?mF#p}$c~U5*R9zFq1veN!=YnRdDU*Me~(y_f5+G57T9e_>wHqrb|0$_o9@%*#&E zUn%&%Bi-6%`k(lV?xp%4nU{9!FEdYjT7QXow`uwxn5XWhzsNlKY5n)iyH3?#DDq_e zdFEYq)t_UY*r7kmJYk~#Op!bE-!YHx(4S`RSgJo&>SFB@ePl6p5qjr{?NS%(!;7g4w3I%qm^xp(K#vzw=V|Bb?ZwnLwe$2?F?Fu? zO+8vneM37}Z!4zG(Y~R#7E@X69KEHO%4k`=xtL088NI2PN};rk1qlmlLpi;nm`Gfx z*B6gFTTAG51;q%TrJb$U7K|4@>ny#7dBa(HHS@X+dR39v>6OfDSLqSv)7I)0%xg~5 z%b8cN(aV_oSLK#Q*F28W*bj|L=ug5C1Xz zO!)EeL*aYi3AjFdIU)cC!|TGU!b`&^g=dEk2~Q7C4v#?$Ky5e_Hbb9>-V1FCy%c&n z^cbQ5?g`xzx<2&n&;_AXXnkl^XldxA&}_s3Ob<;CjR{3VwV@DJo1X{Y3vLR&9DFAD zc<`a%J;7Uo*9R{To);Vpo`G0^CBb>Yqk;zo_X_S392IO0RtJ4V1AOYgTf#yIYAickPTfEo3m%OLEN4@*K+p)I3 z!aLs^^iKDBy)O7V56Aj?H*cI5^J=}IXSkmfA_IPfHTK=^P3|@B#ctN!;P$yo5gRbu zJ=opLo#c*io7@W5k=x|^@^$&Ld|LiOJ|KT6ZIBE5(Ym@AKv`^#yvL$=F0 z_(g>Csq;1>1fF-Ea2|HHR;?AZ4D%ECNd9a-Xa353$h;fT0@s)q6`}_b>pD-$ycSS=IN2`eYb#NGw0 zl8L03B909OetVMCMFZQIhoj% z&~{~F7eYAV5|appB@+_~4dEmv0A0Pza1!Iyhi)0p@1Ux3N>WkMqy*C7YaA{={|9N0iOYLgr|lW@c+Ik3KfBjmt3!r}Pzi~{1*(+P(Smji1F z+oN*eG{RWB99UC8e7c%28j}P41#FW8eS~eprGPkK8DafcInYg5Q!fXW5?0s9fs+X%t{hlGSP_u}U4-cI1B(gK z;|CTIqQ?&`Bn*Y+Kqp}^BnK7{`awA`pD^Idfs+WmfE+lH(5;pO^9UVJ4xB(}J91zy zA#`BictX>X19J$4DF==tgd_|c3v~6c;S3z3K6Jx5IWU{P-qaumjwbxwI5}_>;hnSO zz>$O(_Id+H0HGNwp1~P790{q1a?Zdkb%YGz#}3OMi(m#hFq2Rf@=!um$U_KKArB^0 zg*=E5F>C_|5=vP2076H~f&B?Bly^TuRY=n5pot5UP6rLs8BjXy>OmATFVk)iUz17) zx4|5w(uiJ=1EkWv$_%MA9N}_+RN7aWA(i%3W=N%d8<`=M_Lap+rF{#RAeHtZ!ULqz zzOuMdX;=4=nY>85K8_@f_P63lrO}8o@{pOlNb3Q0SyJhMx~x)Z6^4Y%DvbuJ%PNId zh#yn>3{;m@>I_tuRod(VodG4xh?ByRN|o`ox~$Sgx9ms&q#PZWg8M+PZ@-S*HZ>j!t3fD z43gJf*FzRbc0FYfl3njz{1nM9ViDv(UUtz-(0fUBT~!8BU00QXRM%ByAk}qM8Ax?q zRR&UBSCxTO*HvX8)y14l4v^})s$rArx~gH5>bhvyB)W(`aFphvCJiUAxGrV`q_-{_ zHOVa^4E0G_PT8ZqC(n{opgjOOmq_YxvA(@rP3aP9_PDo@Wa-uXA z{oQc#3hT<8Bz=|03CSyB4J4_n)6^_UT%E=yN!sc(G)j_Ir@ldwvO2Z(l7!Vk`y*X- zYI2ff)v0=4lBzmrd?cz)q*9WmI?!K|RHv*$lA=1{8cBlcp!!Hpo#0kUa_Yc7q^1t+ zLt^T{KBT3%%PC1p9oUDI)IozIA$6>W<;$Mbve2 zk|fkYOH&GpGv5zOC7+If!~B%d}cKJc4siuuaOQLDR0;HKXETAM4kpi$lUNUtX79hz)^nfJAv|#}f zOxvoEq?a}5Al9a2Ap0!R`+16LsJGhjKAJ_FMZ zQa%IMBH=Tj6{LFx<{Tt@2Ck)44^vkIJp`9`jht;*Dgq#9>-blfIkoFN=L8Q6?h~969D}>e)xkhe^FQ+6 z^#9~PxX<1_%!e~R{GBeo(TLD z_s(w)TpPFq>-@6<{ekYlNr9sS2L<-T-Sgpr#sEAh-d68D@2}pAxPShL_haui@4MdR z9#%OB1Y7QPddGT)B64R}ZKReCP&qkrJO;lAuX?f$}j!2KcO|F6WI^da{Qx7S_l z9`7FJ?&D4=#67rJLkz@#xyC%j>@<%vXPPq*1u+^c{3_EEIq@N$2l%7-op? z2jU>UB~oIYSSh;1TyePAPwXbfi5Mawg2FI9HQq7)V!UAd+IZOb5n>^(GcGm0fd*Ij zBL`{ltVKr~q|vh$J#CPN&sub~K^i}6(Gvz~0Ify)AEXhq7JXrmhR|AchCv!bYt4up zq(QV69b=G2(OQh1gK8Lc^;*LjRKuw5*5>43ejL?nF?9HddT1|#ht4XZU3@5@0NS8JeUgEX+#K+FbdWUUET%0U`hYanccG`7|R zKbM0vxYj_>25EG~6EJd+hSwTpXc}K@l%Z*Wtx<-i5w=DdnugdKJ1hrjjIB{NSA(ps z*O;(*ewfv3pgV&!(AGeB2GvOG>NPNQex!9VnHZ#zw&ru3L_=*22I4^)Yil;63^dr* z{1sN9(YCq~=BMGdS{a(g+iGQK8gQ%Y-jIVd;#Mm|(~w(TyG0Jtm|I;9o713Mtqe`0 zZnd&54ZGEt$PCiBTa7VekOtmrWoR0CtCgW?=&e@PrLngv>tqe`0aJ7w^pkcUL8Jfo7YGr5|h^v*M zX(X;zhE_wdt5?I&`JvdwC_YF-akVlujm6a;<5y`guHJ&o&}dx!25dvaaka8JjmK3j zusIFLRam_bsu5Y&t5oaHkI8zKYW*}SSE)*&VYy0GiW--3tg4j!$gEeXN};j2N>vIC z&Q+>XXmqYpl|sXFm8ujPpQ}`*&;VVfDuqVqDpe^oL|3Uwp)tBjRSFH#RjN{Gl&(^h zLc?^G|B)P|ak?si!qY%qrRs-9>MD1u9HgPTO0|9(tE*I{sKFWqQZ_>+1Q0g4$`2FJ1cUKMs4`$y`++0N?h+Hl?toGwc2%FQt7Zt;2yG@R7z~<>K3w> zTweGBcaXiL5@U4~ZXkO}CC4g(`^R2V39?Gy_OX{#l1zy!yrdFkmB7toFR5gi5|?>N zCCn;;TgNg<(u_ODGD*@L>FJe8lIF;Y9+@O*j-0YWCKupDU5_j~#ZBg=*-$r)Ws^4m%Nt+{6caurd=E!6mK-wJHb*fB~Hb-`yER&?o zkzIC`Nz&%X#15GxZH`QsD3eE1ekR~k(&or`AZc@?1KCm9j10rUn9PeaaDq&dI!8Lj z%Ot6Dq+^^+&ZJ|<;Q&(S2%M2gQs>C%7iE&vIWh`J>Kqw4S|&-IBO^!2B&l;`#7LPW zb&d?j?~*!4h7XfTQs)T#l1WnMNVG{NNu48YQJI`hd1!5uNm6Ho56UE|GolA&lGGW& zgEC3#jMzb$By~pUpiGiFBXUqCNu3cmD3helh#QniQfGt>$|R{Xq6TG>)EPm8GD+$j z!LXg2MA@mvr=-q^7?ep;X9Nt&B&jpv1!YpHGfWMUOXlSnSSgdF&j=Qj$uV>Qq%TSO zj8H+DBz;DtpiGiJM<8cO(&q@)DM`|2gbB(d=`*4PWs>w6H-u!8^cgXNGD-T35J8zF zeMW?!Op-n$Ku{)IDG#dCD}8qL2s(XUp4~`JCP|$U94M2d&WH_^Nm6Ho2FfI`M zq*7;eA{7|uC6zv_1mXgnq*7?9SLB>zUZ5q`%t_K`1O>_@=`&&iWio;zbsZsrGD-T3 zh(MVneMUf_Op-n$9#AGppUY2}CzGVlhz68N(q{w%$|UJCVgY56^ckUmGO6?#mpJw~ znam5cjzB<}B!xyCpiGiNBMeX`Nud!1D3hen2m+KzQfR~g$|NZ?LI7ow6dDnLGD!-J z06>}4=xT?~lnGMk@xf)u)Z-veZV z6uNw$ePu!^G)ggJADPHYv|c`KhD<1p#-~%J$%Im9;F#xRLg_Pb^cb1wBOLX-Oek%} zrz5Axgi>aOqhvzqGH}>PnNX?>M2Ac$O$MSvCX^xr(IFE`kb&rs2_?rsbjXAfV_?%T znNU)!uvsRQ5ChQ*6H11Gb&WEiL>TDR$%K+%AcnF832?cCTWuu3j0AamO&*Gq`qZP$pooy*_d%MLF!wk%uniD zHUbwW^(`AdLMBLk%OHXYQs1%|ewWm@EDAf4`j+92OoG(6tQ9pu>RYDFPwHFNh|@@Y z%NkGHbf9y8a^ky0r3M%!gH~Mz&?l`7!_^~SA=cE4!je3HS`?z4|p(iN9e}T6`}J& z3HT3|A!^|0&_SU+LgPd2h#BxhTJXc*8`wMGx4}n(_aa{4+Tg{(OmID-1s211Ff%wE zu>vE4jlr^@iAaIX{woE~!5#jM{uMhNaeob>12Td2ft7*9hz*z-m>$>#kpYc?vViG* z>TNFU7x18Whj%050&tVmTjMSBPD0fE0p2u31w`?rhzEbc7I%~TqWh%#uzL?;0i*OZ^i&GHrb47>#o$lK)&@^X2uJX`k5rE*>&8ep7^ z%4+F3UpQNwP0owXlZXYl$GOS5+PTn4IcGRM&O+x{L;~#POmv1h^-kC^?N9B^_AB-? z_G9(~hy%F6zT7_7KHKiMm)i5}BkcX`sfYq-wJU9D{oUGPZL(gpp0pme?lGm(9WG{tLyi;t;Wym?(zhS^qL&VpYG{cm?Za1#y=NpVJV=k}k zOaK0FvOpmwsM(RiEie{}#%N~54UJ+(+|bAZW36b6U~Xu6x6XGS}7_ZAIQx5^ca|HROJk=p=R3=vT$^>WebPdv;;?d zHJdrX5ns)QDa=BYaI+yqAxgM8hYKN27rV?hZ!-mMcjmdN>RrKc%1y8_$0;|##vG^I z1RHalauaOKamr2QIfLVro5CL(9H-m_yMD&M9}F3vG9%3E6K2@;V`i`1_^8MM<3nb- z)%buJHr~Pv8^6zNdB%H1wvBh0VdHn0VdJ+87HKk!%>|1z<&3xZ2<)2sCiBOh@i*po z%Z)b*+*tjt@j5eF<|bxX?XS$R8V4aa!fG6Z+z6|25OO1|_Gdmm{E6`jGpxoj$c?ZX z#~?StY8->y2&-`nawDS6IR?4WEjKs@xe<})9E05GY&Bls--p#W2D#C44UR!>M9btD zu>~e1FXXl$PKUv zM<6$#-~N=3N1J+x8P@p;vlTWTEV6C|+Rert%r&*f51FfLjN6&f9)G}GS#R7{TbN<-o0-chjhl*GZhW6P^trK-8SU{um|^i7nPKtoF~j0FFvH^CWroGCXNJYE zW46nTYm4j{*D#y5aWyk$A>Uz!#jh$@yxuUbELglgXI#NYd~O@xW`3{CxSV-&rEwYa zrh4Pj0@wN5j7ykNl8c#9l8cy8k_(wpl5a7iBo{EF?VQhy-gF+bX&K)va?UuH*)WZ7 z6iQNO80Qp9QkOHbe8e`>$P~D?=4~U*j5<#-qt1tzp+SSp=w-|im|>~YnPI85%&^pH%&^oNW>{)9vl%h^i)=?6|(ff{OM(;a{8NKhwLQT{d z25r>f8Z|lNa6SUPZx%E3?l5NbzM0IJRUTSk-1RXIVMa|HT;!Z_5Hn_s2Qs534k+># zV}EAM824jFP3+5zKCuro`os)oj6BnsQ4@PJqbBxZMxWS|88gN`iX1j}XZAlfrZJ-? zc4PKJ#?&GQj490Wb7L|yYGPMr+cS14vSUnQMomm)Momm$Mor{*a=?tS+AwG*2iK_1 z8RPf})Wle3%x1=@`~OpnfgRodKRJA2_^9xK;XT3=!o$KCdBYY~@9&0QE8hRVEp!9o z{?7{~Lu;`kz{1dRp_!o>i25HLY7JF|ykHJ1_`e1J82nxEF+5{_NAN$svisi&c*^{M z;O@ck!FaGf7{)68GsOG<#eV_MnLq6R$iKzE4$=PKDDM7ujDM&<-QU$8<+mW#-^H`@ z9|YbAyc~Es@Qc6$fgc8L3|twwATWfd=X|{?-K7E?<}w1>&ANiXzw6zFYNy}(rfl22zmQER`jpCFS$>-kGl7}w<7}J zD)(Dh)30+^x?S#E_i%SVcQ@<-*zVT5VXW#ulkdpC$QR_V^Sc1vB)@~b57O8La23|| zC(5G`2e5~nfZY!oWEn!eK6l=AUUPnrmHp2U<$o*oKe)^}*BNlu{I{a~XWBEc8{lZH z@2hOj%2^*^7lfCsr>$RD_glAH-?c796u^3`*IH;DYaMLuX^lsOf3@YB+YkZpD&qTp zi9Ha0fcyWKm>F{&_5xgF&M^-&_c5oyyVGV?o4#p?PY~h%XYri)6?Q>TUV>}H#n=yU zgXj}W#fjo5aUk|Vm>`CU2KWgq<8$L(>+%=u!9})8s>s zHpOI`eCSd5Ak*YSkHQC;CLemVp+lz0haPRHmud2$M{!diO+NH!%@&y^A9}R9LZ-=w z9)(LkO+NG}ocC$+p+|AwGOc{*x*n~lk?Fh>9T<^m@}fs^3o}h#^eAp&rpb#Q4dcS( zMURF2^yo#sGEE-zXyyzrtvu+45zU=}Cj-*Thi({c z+Womhy|nV8tHgaOq5SA7@nh|NFReW3l(^4JD_^=w+>66f%A2kdKhl2erIbIN68E|( zqtj`32;y{-~BYj?XTG^+>Wl1pFQuI9>I?N~L`u2Y zRYE-)ky4I!mB6c8yp(datHdCl)JQ33yGkUqK`*7;?J6;#C1h$j61v`Y)_OOUceopE z8?*tLB9D6;9*#(n$Gz>$4KhU@_qO$C$`pCr+t#mhQ+bcO(Y8*zQKrb{-nMSNOp(jI z?eujrMK1TYwWrGzx!l{*LkSX%Iw=M3H zDe}6vEm|y7)wVNMJe*Sw=GyIQ{;7TJE>Eq$m`yA;z=?^UiY?nC(0Cg z-P`7#DpTZjZ#(W?nVLx^KIS-?BCmTJ?gORB>)v+oY?&gjdmHZ8q{!>uHvJ%(BCmVf z_~|l5UiY>Z97|sJw&w9NMPBzdh*N4`x&#y{wGUxKlT6JZg!ZJS6Cy4*wKpMdNT&88 z#9heLo`jI0)EHQj-Z0S&-V55RnC` zT?nNoQ4~y??z;b)Vl4)c``+6-FAMTmr`ntmY+LDbzP;`DsiaxBbg$_ZVh}cQ>56fD#xVQ zt+;`dBE@cnM5aiwTUF*sv0GKZbgtyiWIvQv0^Eu*sk7+9OnhwZN-tK z*R6=GNs(T+c*p_ib&I+z>2-^`Ea`QNx-98+i@Gf7b&I+z>2-^`Ea`QNvKi@hi?W&0 zYgcbU4)SvCw%{bC*1F!Ja*&s6z1iO;Q>53;ssjD|0nN%Bq}RP~CzMQ)Uc=Rk zO_}Iel`qn3xDjQF^txHqrP6C%hZ9kzlv)FoWt3LCKqsYyT6dfAwNhz(jVQX5(r8_W z|4*GqG=8(bp~Qs98%&8#IQJ|q*-B34k=*oy{{q`QsEHl(|72g)JRU3dfK5a}+QfpUm+7rsC_ zM7j%Cpd2FIg(pxBk?z6~D2GUQu^N#>q`Pnf$|2HScmd@Q>28Cv0_kppvI6NYT!3bNOc>O6-ad(5cxPnstd2593s`N$8FjnQr&tC0z;&__4W7#Qr-I6&2orTw;toc z5UFlG#)BbJ-FnrqNOkKg>*bJAU0ttNZ7eUldVR%va)@-dUYV41x4sNEC*7@A)>XQT z(?XxiA*H%N)v!o&>y=4KbL*8!NptI!NlA0-l}Sl+>y=4KbL*8!NptIM)Ea4Sy|ON8 zZas8xNNFyv1|1yAi>_X;OsZ7Z1v*1{(RGzUNOS9=GLimZavq`Gww$|0q?u3m?X=SA18!`Gy_br8xS(p*p$4GT+%TY2?-C9)NAgOMx3XCMxt%XDklIqr?CI?A%YyF^lLEtvARYR2c z)z*bp$~ptjRhKX@L!aRe>dexq-v6 zC&1Lem_Tcw5<3EH!?Oafc`v|I@Q`olCHJ@PBksNKE$+403n1gJhmTc+=-R{xL2)5fFN-TyYM;x9GMF*X>hj3vfg zW0v^)m&^GQ{jafrPg^rZwPeP$3qC>2)J1;RSG9~Y1nv6jV$Lnb@DcAu#AxOl=ZR6w z=l2QT;3Yp_7rense!?z>7mmY>T?}LP0wT`rdZL{fZ!V27BO)QnjQibfMb3#uM&Ua+pwniGwD#OJnXC@^O4qMjK!sAEPBYMGIP8fN66y2v?E#f%(O zG7D2gm|@R~f^Qr%dfFA%HCl3_jE_KW!Ue`mUWAwfz6dhIPCheo87OQ#6NN_rfr!P_ z+1h|1#NYXg)7Oe^ z%xg~EUoAdk?&}wyGM{>p_=I_7pZK`Qr;3l5dsm7NnS0iX4~o1(Y++u} zBi?6Two$yt+`UY^%e=H(yu*C*Qt>wPl9R<|=EWPuTg;1=h&PM8Sp1E7;Ue({^MZ}y zb>{gC#ik-J5PxMpX})-kdEQ3x7v>XA60a6{p7=BK+!Mqr%*U?~e=71E@ki!4$BUPl zkGV^{#60^L@dxIkXNwn^k2+fXp81Ho#0$)aA0?hI@)6=W=2?e}XPFP1Bc3VpOz}JB znTLs|nGb$gJjHy_!Q!{f2OcDT!@U22;z{QH_7}fq-sfTQ1oMpj#IK6Hk9eGU`V8?) z=DlZ%$BMj{_yzM`dy7YzcgMSFn0MPrCT=Qn zv-m!9W0Tm(+|VKZqsaB*M&|kk@jd36E#d~|>I(5)=BjFOJ#%H1xQ;neDXwL%s1er` zIU=rRF0T;ZVJ<5dS22g{#g#=4i7S}#faCgO z7cnEE>OyAd`?r{(?-vyG9lrGQ3wNQTIdL8zv87CWlX+94IG6dU3E~^f4^J28FyA#x zWSKY46&dD>dPSN!bB0K<>MK{e7%HUlzH~8IOu?BhlEoCf=_0XR>OL`0Ou?Tn&Mv0l zP#0%ym%3MMC`=cWOI@5<{0csGvA&psQ(deprr=c-zF`O=9Ub9v#WL~{SbTVUTUBHZ?bv|DJ9(QpPf6;TJIFT7+>oS4as@$OLOgJ+9Fm@x(&%sl-daS-$P>Eb}<7QCyBxp}^~ zeCP?h4d9;8&7o@xZ~yBLbr<6I_Y6%84G%Sj%0qT=E7tFS4Zaxs4I=n|9K0>~UF`Vx z&0qp6_~pUQ;IY9&5yQVL_WNrIR$`a=FZ>VuH?WF-+JDr)-@o1e9-{cq_mkK+zuP|% zJN@mKU&%N5Wq9`gDA@2rM5>B9gxbD|+4i*nP`=#eEiU z{`(2`(EmPS`7gqn{!Dk3d$K#vJreK!+ua@S#{cPk|6AmB@>2N?#PhF~%jA4K|9>#v zdN4_jlr31>nwJT$D0rKb*4IFov2fTcL3`4 z$M#!@>VMXL-2MsPeeiwzYWpIr@6W_r08d6-|B?0q_U?H5LENs#3g5Io!+QY#V!dGf z8j<}!vTntl|I4j!;!S|3S<9_X-1|S&nr`iCjj~#-O5FYb!u-H|!+hC%+WZCX|Nju) zoh!`?%ptrDu-9B{9uNP{zIeW4tQj?H5CNczkHuT?89XZ<7eB#!5WbJ+1TGR8ai&-$ zP8RdTk$5NI?syYIT-1v&VpBf*n&1RfgW$2F0j@mK^BuvI?0c2YNgVBnNsNUezo)(Btr`X32pb#|AiA za-hfIV9k;PJ&w(AvgAOI!_Ax}2YMWC<}5kTRqk_`^an@DY~Fva zw|6`*v*bW;$AfBFa-g@5LA8rk!aLT|^816gvRx7XrRa-p}Y)<7=w_8NRj zF7)y^A?{#gNPXLJ2O~r3+m2f=8B*VNw7rZ{UsrE8oJ?MP z-S(W!koLBJR4y~5z3p!`$P8(3`=8rnhP1c+`Oz{%+S~rj7?~mMjU9KV%#ilRp!6Bi z-WZfVL)sgI(q~9}V@Kgr(%#sSvt@>~H+JMvGDF%M!=tzv(%u*z#m$iR#%3KRGo-z- zS;#MGZwyc6W=MNuhdeGbq`fide}=R-w*SF0L)sgIyk|&zW03a@X>V-CzA{7F8{2yi znIY|s?X|bekoLy*+)HLidt-adkQvh67?eIk+8f&)r;+x?cH3QMNPASY<`)5ddW6V-&1eqc2jo|_Q3~6ty<3*Vv?TwAbX{5ce zQK%Nu-q?syGDF%M8;+}y_Qvo~eulI+2FcEl_Qqnb%M596tSu%pq`fi3-(^U9W9XzA z(%u+CTr#A+u|}Lm+8e`GpBd8L7`kqTv^Q3dtC9A`>g!~Nv^Q2)FEgaQvFd7>A?=M- zzaulGy)o7CNPAX|PJ1i~W8xN`+P89Q6ZAhgBkr z{eCk_i79ao-jH2Ls5rKaQe*W^T1&YZrN@SjM9#}7K~`U2&FyBCB&#oy884$WS$zSY zwU<$=4kIqnI>h9_TUnv%+a0|GEK@HMVwojlsUR=qfC=BN4uBFG%0fw`+%fLnWHB! zm1$Du=#rCVnv^-Z7^jgkM;9%TX;S7WLf+D(%u$5ArIj)x9CQJW%}cW$oxf0~Nt>hd z7sxbebM&P7GELeXorhydo1-V3B-5nLQEZ-&CT)&l^Mo{MbM$x=h_pF+{2ZAkZH{6e zg*0h%^q9M3nzT6z#ZHqpM=>`~lQu^&H&2r`M~}d(HV)KMFX>)Y?44Ecvj_!>MlQu{9-bM?<<|t<2Y0~B>)ICkw z93785kTyrLl}nnmISQdqlQu`kY?5ix<|w9XY0~B>gg#B$933%Crb(Nln2@JQo1?=< zsptQtxJa|UZ(WHs;#tp*KaYqZs5m1BRn&&;>6`}?!F_x}^~PV+yoIy~P@ znx|oRxcTPM*!_KXv%`#GZ5a4JeDD9E;9bFucyizZ>>Rijo`VI!*?3dH9);%yLU>QW z$JjUUk9ohreg19!^>}LF9Df6L4P1gZ1kCdH@h2k!pxLkR?ZD@Ocd%#RbAiVL58~Yb zHwLZc`=W8hJN1Mpseae-){I^e-;u*KVi{Q{r#9>&uGH+ffk7ka6J&tQ%> z6T1cOf_LsUdS#yJeu_5&yy8CNK8C#lZ+CBSFL%#%&vyIqK7e`d5qM5Oy&K6P_!0K)zXmJ%|Hj)5)!P6IzNO*+Ywm*o|1YBj z6Ap{VtuS+Y+zJ)>11ref)@1q2t!-9-xuw*E*laIy(2Oy=z8PhfuGz-yNVApMcFY!L%Ql;t zP0MUz7N*(AYzQ;|PFLLRju|Gs)73R%IkS$B_|P|Nnct~2YnWeeFsqqg9AQ>5KR(s0 zWWMuoGg7!Kke^wZ6~z>$R%UrIg}IekR!m`XWrm9>%&yE(F@@=s87!tSzcT&pQsP2qiJG==w=(G=b-+=UxvnC}$s!VSxrZ}Sl!gw4$bj?ejl`4;nWbIdoH zk3G)(zuG(VD5fD8skiiTv6i?2XKgBE>{y16C)CQ_a*83J^icSS-I=El6UXBZ@snN zy8iXCsGjQV{i)jD-|y`G8|LE|i(fM@I$nIlyl|2Dka@vE@h{BtyTl>pIg`XcGtZtQ ze#Ja%w)lX#W0p9`JhMX_V4g8kyw5y+hWI7(wCUnK=Bd-fyUbIjig%bNPZ4i3w@()P znI}CX_AyVKFWzDvcTl{^Ja(LTgL%wB@p{P5iC-|c4HK_1k7^UIhCEDg-5-&pc$ps` zF;e_I33N61$k|8^nXmb@k!_<{ph=XUKJe>;8x=#r^y^ zX6Ah%*NA(WadguUnUiU;Bjl9$0dqVl?qQC_#rK(`F>yDuZWebjOD*nXu9o6^%$S*X zRA%Ooi1==0W)A5RxAQZ;h>F{o4-OEwGCwy)+`{}!ySSP8M+?MvDvTIY+{9cgiElIK zi{i$R-w@lG?Yy{wIcJG)h1?~sXT~V`Ci9R1;yPxGfNPlt4Hnlh4;&<}X2u98hx~!? znVT))g?y}VnU8H24s+vg#Wv=Cjp7^3eHz47%)R@FD?@G&S1@DrTpn_R_&RgLvEpkX z_Ys$c+*@24axZa7$o1l4=K5aZqL3TJh0HyUhzpo&dx-Oy(+RPa86)hxkQ3ruX1oua z6LLbF&77zgXN8;=XNH^-XM~&-TSAVD&CD37Uky1SPG{C}@fBvJ#U^IFpKN5t`^jm{ zct1In8Kd@;%BUS25gRI_c5s(i&(AngD^6zqZ9lP&`GW!CB<6j#Sj+rIUYuBA1fXIK zGv0GfV8(mSYG%CWtYTiUSgd58zeubId4X8YJa4{O#@sniEM=ZMM=S}sODtxdGglnX z-0^}~#Ekc%h0IfDiUrJgpPA2$_nCRjlO~H!<_TlPT;}l;#2n^v%>;X3YHKn469j!`kSJTu-;Z04TzA{W&EM@POv_x~SE zzm+pv;96Z`+KN-RiB$60?ZiAT?>==A8=XiGE`?T0=6BzE-v1J?C#>zDPj`cZu!&H&i1JsebX zj^2os{qed(Pte2B1<<7H(BJMix6dPY60cBtFX<-bi`tj@&leygy;pRUHL7L`Gte~psz zU*#X7WxB98-UATAP7VH*yRrQ6{TdLPrujoDn zU=+^!Yp$-xS%1O(1HZ+7fp11$h&&y6DDs2It&wkH(EZJS+M^^shw7?FNqi2WyhllV zE|$<9CGj~FPd!TFa|q=`AES$tKMg&VQGAc+U!Mr<$0;ei)?p}io52X4h62{L#fl4>tV;DI>1 z&I|H);MtqC7o_h%gw|e=y#oxmJvQA#9 zUCP@n+??%F-fpd3rCrL~E!>gqQr>Q@IZ?Zmw_Av(UCP@n#MACM^qZ|(r(Me1E$r>< zQr>Q@T&7*h+bty3F6HeOl4_Uoc5B%N?NZ)uVaH#W@^%YJwM%)sh29dE@^)+SsoJHy z-CDF$yOg(E2(4Yp+pPt7l=60K{zC0i-fp4d=u+Np&0DNp%G<5EyR=JryESK?b}4VS zX6@E4PMsdg!Ew~$o3l($<*s$I(4t#*6|%G)g@)h^}j7Lsb0 z@^%YJwM%)sHDQi+DQ~yNwrQ90b_-=jm-2RN^cd|frq{P^zji5aw}y_^F6Hf3aj14F zZ@19b;Zoji*?2DH?N+X!UCP@nB+4%3?G~#3F6Hf(IYGOWw_8I7YM1hM3k?7+BG18^H-Rje< zUCP@noD%3#-fp30?^51wp-AXb-fq>^XqWPK3*|tU@^%Y_dYAHctEOJNl($>Bht8$E z-AduhC~votrgkZBw{TjhOL@D6z7BT;y}sCu+oinSf|^{)+btZ>M7u)>x1Xk6@~?Hph1wBSX z>C`Uy*F>epCI6bU@F@A$oHbj!$l3BjNgpxYqn!vk$p{cXSrlwGnh}w8x zMZ08Q6CGDB+1G5vbIHDDYm0Wtz9z~(F4@;C?$Zw0*DL_Zz9zbK9I~&OFKCDCYohGq zkbTWui+0GqCK`4evagBx>X3cS%mM9?ea#_2vadNfqaCuZiK*+5eN9YVhwN((#5W`R znga)DhwN*jjmIJTnoWnaL-sZM;|XM6vwx#@$i5~fv_tkavGcb>_BGLYUmdcqiTUb~eN7bg9kQ>PsL>AD*F;g@A^Vyr>N{j# z6O+^-`#) zYoe&{kb6zsoa~T$%|lJvA@`aG25E=fYob}kA@`cE4ATy|*WBBt9dfU^dy;m@z1eYl zwL|XBjvb{Pa&H#WcgVe2j8li)o5eVFI=Wxo?5J_tA@^ocsdUJ_*^&5t$-UX(2em`) z&0?H7%_|G?qE!-t2&p+9CI5 z2Q+Jk+?z$CibL+r_QxQHAA)BKwLm!Gm_RZGe%gDaj+Me1W`)0A9g+unuq8H2|`)1Jq=8%1}!8j%RX5$z; zWZx|IvvA11S-jggWZx_fqIJl=*RTZ+0&R9Jx1R!VTo!42sGQxi=GBDspcogG)v3 z%>-AC+>64NcF4V%Ks4mu%;3Y?A@^pmy_ZAo%>-AC+?zprkwfmypuNZ;_h$OzcOds> z`r>6G_oAMq9dd7`7hZmHFG?iZA@^o*3Wr1P&GhW09dd652XZ*%-VCngHga#KCptpO zy&1F@Z6o()a3lUUa&HEH+D7ip)HdmD1@xWd~4_eQHSXc_HR zZ#07+OXkfSMyD#77nP8p{*POsA~@-9b9!BRX`1iR@Ba^X>0gkVmKvQZrUs@Ou*ctD zlbVSIEzaWW zz)pOvu}rLQEES8OAOGX%`#6bjPxP_q{n0z3H$bvz#x{M>b&p{{tYV5!_4JYyaM|ajwVfFnd^|5+i{X*?gkE#3B z9qI<OX*;0G_LUwEEua?^fS{y%K5?0;enfE`Bin26jt$BEBpyXE`LlcvaDhTJaSW1cuk zzRNsej(mrCY@2+WdCXY3pLz5cxsSPRzkG{%=xF&Sb8)DAgSk+YuQS{GsnNv0LG3KNxf5IF;Dj#Kz#pREgqe=M)vyRG#L++9fF-t9XF$*alWUdzS0p>`x z+*ujLRz&`&GK#G(c|Skn3oY+sJ{ptvG9OOKA2RQ^cg>YtowRn&mt38+ zb}W&1^K-VJChua#E@O9A7^_r|dFCv6Q^+0i+srdM<&DhKXUgp%&yY7TPwSN5V#aK|KICcgo6M7U%j=li zr^;(Xo-D6np42X{W}diEmP4K(edY-hrN=yGuXLF)Gacr((Q+H}s0otmlUVUeu1{jg zE3f3|41Yyl!8~lZyqvjpr2KlwE%Iy3Ev@o0=HfniDRW_;yo5Plloy9wkQXuAd3hmo zu0>uDvL(-Fh6T1VXAa2on1>vY=Q0n@$a6v-BF|=q5zb;BXvs4}9w5(P9xzaDVQxAk zH#7GiAio-Nqdc7%ruYhTpGLWfxuK8T$P5dd#$5NQJe3(1IE5J&*uV@6tPi{c(#R6tTE~{9; z?2=3Q8J{QQ66Q~9lKJ$_N-%HQuBw?go~|MxZ&Fpvr)^Z>_c`?f*~O1<*dhPQeDX^97v^;* z%Re)(T_yh~^NDNazcQ~mQT`9+6V}Ktm{*@5k20@XC;!B}aVx%e-Kx{Fr(ELiwAJ7sy{T z&s!`%VxGH8e#ks$p8OZ)S-a&SW;pYonP<$Dzp5~T4mp<@pC0Bg!%?%D;iy^6qsGaO zklW-;=8;`;2J`TPayl~{HI2D-n4HQCFHK=CS#mOSu_W7>@rh&-bG{%aGQ&#~m~*xq z&uq2GaUtjASmx|OIfgl7${hDPdRu~0BRl|J2 z7b?xXb-PM2pSM*dna@2>C793Ntm4dPovmWbXP%{^%x9dbH1n1-lo8bb>qOh%+y8%k zdPRC(dTM%fx|ANA?vuv$D%iX46P(_+FZJ`(v#Cc@|ET`|7i0q;;RL_el6#VmCwC_A zO5TJW{4P&!MLKX2&hVR)Y)_6%=COxgLo%K`CUFFP{0Azh`0YyElei^uUE-?5g^4Zb zyeVFXJ^lWIe*TYQ@5Nq=?TI}e+ZnqHr})Z~#61d!tW9cSY}s-hvbTuENfKTaY`fKv)0N=xFQ$@LyH`zsb1TxZK!^ zll@LYcmEut-56=)jR8i35jT!eN02}qz}bF#(ciyI-J@<%*Qu+p%ik8YUai3CepAum zUs8jy3&8(g{ePkyfxA4KWxb4vE+i4Z5$}uF#q;7x?D6=0akIEqT#0i4Hj9(Ra?y!X z9^3xY3)=tw!vEi10bQmHzJ%Rt%ap;Fkd>DygD+vz+cIVFCB)`s%HT^4HM&e0dNh?dLU z5j+szfin1F@G4LSU#$5|mnnlUrtw_L;EQSek(9v~QAa3K249RBx=a~-5lu2>%HWGA za+N8AFBi+uz`h$kCGZ7Qc700V3uu$@DS}Y@Rlps8es=_4RlpV;K4tKQ)_1i}8GNB-xb{z@=c2mipGr7% zmiAAnV2k!Q5TaGXUr(4fwSO`ps%!o_LR8oMlL%2#^VbrZMeUzRn6O|6Jjd(^9XC}wBJdXPH2BFA;!NyhY(ZWpG}yk)&49( z?1Oqm0a(Z^N%c=FJDgkj$GOj`2k1&7+*_lX>$f=lW#ceCwOqC-dfmu}9|3 zm+&Z=H;?UKd@^qyMP8rGn@5q?C-dg>_#Mc+`8>uRnKvJdNHTBUJgj{(Z@vjbmdu-P z#CRg}=F!>XlX>$!@LV!)9?JL0ym=_!C-dgh7?FW_tE%$o6ZE^)Ta{1Y2a|jAD75+H z-h40`$h~<~*L-qs9@RCU+?z*r%_sNfQC;)Nz4>4WkbCp6zfbPX!~Qg zNA9&dW^0e!YooKrBlp_qg7e6|HrjbSa<4rVKZ4wAPnoVga<2{3d*oglruWFbcKc%O zk$dg-N!la#+L%`!xz`^5y7tJu_HpC2NA9)ZUXR>sV=8#$Ub|(O_Q<_9Hqh|My*BQv z_Q<_<5kG?5YZnXJBlp@E{~o#5&K=Soxz~npJ#w#|ZPp&S*M?<1a<4roqdju3jcx^x z+-swM#v}LI=vMH^y*9SN@yNY4O5h&3*KWcS$h~%d{5s@b8x1xdxz~oAJ#w$zyGDED zUK_&o$h|gt<2-V&jgKK7x!1-gFpu18L%1Hf*TyF>kKAixI(p<@8`IGv_uBXb=8=1C zd;;^xy>=?4J#w#&Ip~pl?F4>Fa<7dKV;;HJ#)mPF+-u{*m`Col@nOs(_u80_Uf^EL z3rt6^TfS8`rlUvpwJ{yNz`oHc8y&pe@{QW);3fCk_%P;?du_Dcc;sID^^*3;z4p_S zwMXu?cP!N&xi{DTN9~b&bHUIe_vV`MH;{XCXh!hJy*V@^c;wz3-X=YAZ>|v|huoVB zE(*Cfhqp|=P zl6!MCc$D0m3oaG8H&=sRYI1L`2CojeH;{3$viM5HviH^khL@WC5`(pha!5(}c$KS{4dwVMT=?C5Sf2TtH zCDr~-{}1-lf7sYz+-h8hZu|4G+FolcHad*)MyruA`Wk<)+P_{cS94W6&e_YUCe=eJ z^x6Mj{z|?jUyx792j$)JCRwiRwfBEe?MFTSci3g`jq2yCpQzqheP{KJ*qi^->a%dl z-pcBE)l;geyS`_&j{G_DDbCp2A9*SA40`MDiQF8yCUSY?yvU}=n#iKC*8g1kvGo1v zJJL6#9o*G%MtXgEd3tWTJv}0wOE>*ZuSc+_<1?v;Qum~8PF<6_949(%O07vPO3g@( zO$|*APW4VDQpezaj$b3sc@_IPKB_qV#z%AY*I=1j$(OJMFcfoVm%(W}76AsQ?N|jE zoVH^bU~t-wb%4QXI~D>)_&uji`}aSOzyIVZiqm#11{A05SPdvn+p!!_oVH^i(V#c4a%1d7vkED98-?N}8kPTR38P@J}7U7$E^$HG8y+K!ch;P_bQqv{Rjp1suTA=j&4FxT}|uQAuw zsaKiP3H1tdDy?2-PNvk)nG?0@C1%`{{~|L2)fbqfL)2bo6;;nOOQrsaxmv0{%#mvK zT%{6Lh^U`cDq)2#^(;T*XiWW-`Q41#&HTVT^-P7al2A`GPij|BF;ASNo@5^Xyn2Fp z+(h+w$m7*x%oycAVIDnJJ<8lRO8q$GF7*gAM)1ST!&}uu%)^GOUCgb+)Po_vsUBd) zsNKn2dQ<&~x!9ua54oi7V=ffcz0CPh>W3lQY6mk$^ADKK!|ER9CR2T%xv@#z&D^h1 z-NoGFu)34Ewuky2b4{(fgE`$#eK+J9bvtv)R=0(mRJSr?^xVRXeH(6O)-m-RW<#r+ zn3bWv&5RLqBeRfdJ9D*AH!x%Le5*2g@)31?W%T5`)HnGVhtujh<~IxKTIT0Rt7|HZ zm4~{TdEE|GW?s8a`OGJ-RUY%26P3$+!W!i;uRcL-V_vmdeS>-BDs>g}@*V0*=4C6@ z6(KKImoqP2roPU+WP|!z$cxow%!`+(OPLowtuA3+uuxshJa2)zh`Do~x{!Hxr@DZ- zW41bU3tf|0~RxQJa`shN+FraQtb^rJ?Fn=HeuEO2`GZff=)CJ#+4mI++>1 zU&ownRwpsT@oSj}Wz>nx0|%)!%mW6h6GCoQtC^cEwJPKRY9(`1vs%I2zo3?f+^Ci@ zfQWN!dT9z(aac;ZOqM$Y83Oa&1xib z)3Is8@8Cn3aYaka^}SS)8CS8u+@nV2nQ`Up zkZV+qxu%D*LatS2$Z3@gxl3i3)3s_y$Tey(b0VzWjfw4doYuuNvwv246K4U<|%$sK6L})lh;l z_^P1>WAIf&5ys%FhAND~R}EztgRdIuFa}>W6k-g%YN*5*eAQ5jG5D&X7Gv;LLovqS ztA=Wf!B-9C7=y1G>M;giH56nFzG|q*7<|=Gk}>$I1vQxgmHC6BOmk)al)4PQW+=-T ze9cgoG5DIHFk|pFLuJP3S9xVonlbu@+-3A(#;entx#lyY7jwGKXb3rF)HA2jMo;FL zVbn24V@3~V> Dict[str, Any]: # pragma: no cover from mutagen.oggvorbis import OggVorbis from mutagen.wave import WAVE from mutagen.mp4 import MP4 - from mutagen.id3 import ID3 metadata: Dict[str, Any] = {} ext = os.path.splitext(file_path)[1].lower() diff --git a/backend/app/services/mood_engine.py b/backend/app/services/mood_engine.py index ec90efc..5bff8a5 100644 --- a/backend/app/services/mood_engine.py +++ b/backend/app/services/mood_engine.py @@ -1,5 +1,4 @@ import json -import os from typing import Dict, List, Optional, Tuple from sqlalchemy.orm import Session from ..models.song import Song diff --git a/backend/tests/test_deep_coverage.py b/backend/tests/test_deep_coverage.py index bba5ae5..d9ce42b 100644 --- a/backend/tests/test_deep_coverage.py +++ b/backend/tests/test_deep_coverage.py @@ -1,5 +1,5 @@ """Tests for deep coverage of services and middleware.""" -from unittest.mock import MagicMock, patch, AsyncMock +from unittest.mock import MagicMock, patch import tempfile import os diff --git a/backend/tests/test_endpoints.py b/backend/tests/test_endpoints.py index d6cf8c0..95f73f3 100644 --- a/backend/tests/test_endpoints.py +++ b/backend/tests/test_endpoints.py @@ -1,12 +1,10 @@ #!/usr/bin/env python3 """Comprehensive backend endpoint tests for music-app.""" -import json import httpx import sys import os import time import subprocess -import signal BASE_URL = "http://localhost:8000" passed = 0 @@ -25,13 +23,14 @@ def test(name, condition, detail=""): def start_server(): """Start the backend server.""" - os.chdir("/home/user/playground/music-app/backend") + # backend/ directory (parent of this tests/ dir) + os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # Remove old DB if os.path.exists("app.db"): os.remove("app.db") - + proc = subprocess.Popen( - ["venv/bin/python", "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"], + [sys.executable, "-m", "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, ) @@ -285,7 +284,7 @@ def main(): print(f"\n📊 Results: {passed}/{total} passed, {failed} failed") if errors: - print(f"\n❌ Failed tests:") + print("\n❌ Failed tests:") for e in errors: print(f" - {e}") diff --git a/backend/tests/test_routers_lofi.py b/backend/tests/test_routers_lofi.py index d5ac3c6..267bf33 100644 --- a/backend/tests/test_routers_lofi.py +++ b/backend/tests/test_routers_lofi.py @@ -1,6 +1,4 @@ """Router tests - LoFi endpoints.""" -from app.models.lofi import LofiChannel -from app.db.database import SessionLocal class TestListChannels: diff --git a/backend/tests/test_routers_shareplay.py b/backend/tests/test_routers_shareplay.py index d54bc0e..6722a67 100644 --- a/backend/tests/test_routers_shareplay.py +++ b/backend/tests/test_routers_shareplay.py @@ -1,7 +1,4 @@ """Router tests - SharePlay endpoints.""" -from app.models.song import Song -from app.models.shareplay import SharePlayRoom -from app.db.database import SessionLocal class TestCreateRoom: diff --git a/backend/tests/test_services.py b/backend/tests/test_services.py index ddda1f3..5c2e9ec 100644 --- a/backend/tests/test_services.py +++ b/backend/tests/test_services.py @@ -2,7 +2,6 @@ import pytest import sys import os -import json import tempfile import shutil diff --git a/backend/tests/test_services_advanced.py b/backend/tests/test_services_advanced.py index 879bd7e..ad22fbc 100644 --- a/backend/tests/test_services_advanced.py +++ b/backend/tests/test_services_advanced.py @@ -1,6 +1,4 @@ """Extended mood engine and SharePlay service tests.""" -from unittest.mock import MagicMock, patch -from sqlalchemy.orm import Session class TestAnalyzeLyrics: @@ -11,7 +9,7 @@ class TestAnalyzeLyrics: assert len(scores) == len(MOOD_NAMES) def test_none_input(self): - from app.services.mood_engine import analyze_lyrics, MOOD_NAMES + from app.services.mood_engine import analyze_lyrics scores = analyze_lyrics(None) assert all(v == 0.0 for v in scores.values()) diff --git a/backend/tests/test_services_extended.py b/backend/tests/test_services_extended.py index 8d0760f..f32b2b9 100644 --- a/backend/tests/test_services_extended.py +++ b/backend/tests/test_services_extended.py @@ -31,7 +31,6 @@ class TestTranscode: class TestScanDirectory: def test_empty_dir(self): from app.services.audio import scan_directory - from unittest.mock import MagicMock with tempfile.TemporaryDirectory() as tmpdir: mock_db = MagicMock() mock_db.query.return_value.first.return_value = None @@ -42,7 +41,6 @@ class TestScanDirectory: def test_skips_non_audio(self): from app.services.audio import scan_directory - from unittest.mock import MagicMock with tempfile.TemporaryDirectory() as tmpdir: with open(os.path.join(tmpdir, "readme.txt"), "w") as f: f.write("test") @@ -55,7 +53,6 @@ class TestScanDirectory: class TestDeleteSong: def test_delete_files(self): from app.services.audio import delete_song - from unittest.mock import MagicMock tmpdir = tempfile.mkdtemp() f1 = os.path.join(tmpdir, "t1.ogg") f2 = os.path.join(tmpdir, "t2.mp3") @@ -73,7 +70,6 @@ class TestDeleteSong: def test_delete_no_paths(self): from app.services.audio import delete_song - from unittest.mock import MagicMock song = MagicMock() song.file_path = None song.transcoded_path = None @@ -84,7 +80,6 @@ class TestDeleteSong: class TestGetStreamPath: def test_transcoded_priority(self): from app.services.audio import get_stream_path - from unittest.mock import MagicMock tmpdir = tempfile.mkdtemp() f1 = os.path.join(tmpdir, "t.ogg") f2 = os.path.join(tmpdir, "t.mp3") @@ -99,7 +94,6 @@ class TestGetStreamPath: def test_fallback_original(self): from app.services.audio import get_stream_path - from unittest.mock import MagicMock tmpdir = tempfile.mkdtemp() f = os.path.join(tmpdir, "t.mp3") open(f, "w").close() @@ -112,7 +106,6 @@ class TestGetStreamPath: def test_no_files(self): from app.services.audio import get_stream_path - from unittest.mock import MagicMock song = MagicMock() song.transcoded_path = None song.file_path = None diff --git a/PLAN.md b/docs/PLAN.md similarity index 100% rename from PLAN.md rename to docs/PLAN.md diff --git a/test-results/features-Internet-Radio-Page-shows-internet-radio-title-chromium/error-context.md b/test-results/features-Internet-Radio-Page-shows-internet-radio-title-chromium/error-context.md deleted file mode 100644 index 4a8ad1d..0000000 --- a/test-results/features-Internet-Radio-Page-shows-internet-radio-title-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Internet Radio Page >> shows internet radio title -- Location: e2e/features.spec.ts:74:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/radio -Call log: - - navigating to "http://localhost:5173/radio", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { -> 71 | await page.goto('/radio'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/radio - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-Internet-Radio-Page-shows-search-bar-chromium/error-context.md b/test-results/features-Internet-Radio-Page-shows-search-bar-chromium/error-context.md deleted file mode 100644 index 2337c91..0000000 --- a/test-results/features-Internet-Radio-Page-shows-search-bar-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Internet Radio Page >> shows search bar -- Location: e2e/features.spec.ts:78:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/radio -Call log: - - navigating to "http://localhost:5173/radio", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { -> 71 | await page.goto('/radio'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/radio - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-Internet-Radio-Page-shows-stations-section-chromium/error-context.md b/test-results/features-Internet-Radio-Page-shows-stations-section-chromium/error-context.md deleted file mode 100644 index bf86f6f..0000000 --- a/test-results/features-Internet-Radio-Page-shows-stations-section-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Internet Radio Page >> shows stations section -- Location: e2e/features.spec.ts:83:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/radio -Call log: - - navigating to "http://localhost:5173/radio", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { -> 71 | await page.goto('/radio'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/radio - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-LoFi-Channel-Page-shows-LoFi-label-on-channels-chromium/error-context.md b/test-results/features-LoFi-Channel-Page-shows-LoFi-label-on-channels-chromium/error-context.md deleted file mode 100644 index 3a39d9e..0000000 --- a/test-results/features-LoFi-Channel-Page-shows-LoFi-label-on-channels-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> LoFi Channel Page >> shows LoFi label on channels -- Location: e2e/features.spec.ts:55:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/lofi -Call log: - - navigating to "http://localhost:5173/lofi", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { -> 42 | await page.goto('/lofi'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/lofi - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-LoFi-Channel-Page-shows-lofi-channel-title-chromium/error-context.md b/test-results/features-LoFi-Channel-Page-shows-lofi-channel-title-chromium/error-context.md deleted file mode 100644 index 8a2661b..0000000 --- a/test-results/features-LoFi-Channel-Page-shows-lofi-channel-title-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> LoFi Channel Page >> shows lofi channel title -- Location: e2e/features.spec.ts:45:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/lofi -Call log: - - navigating to "http://localhost:5173/lofi", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { -> 42 | await page.goto('/lofi'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/lofi - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-LoFi-Channel-Page-shows-lofi-channels-chromium/error-context.md b/test-results/features-LoFi-Channel-Page-shows-lofi-channels-chromium/error-context.md deleted file mode 100644 index 33309f4..0000000 --- a/test-results/features-LoFi-Channel-Page-shows-lofi-channels-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> LoFi Channel Page >> shows lofi channels -- Location: e2e/features.spec.ts:49:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/lofi -Call log: - - navigating to "http://localhost:5173/lofi", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { -> 42 | await page.goto('/lofi'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/lofi - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-Mood-Radio-Page-clicking-set-the-mood-changes-mood-chromium/error-context.md b/test-results/features-Mood-Radio-Page-clicking-set-the-mood-changes-mood-chromium/error-context.md deleted file mode 100644 index 8ca2ade..0000000 --- a/test-results/features-Mood-Radio-Page-clicking-set-the-mood-changes-mood-chromium/error-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Mood Radio Page >> clicking set the mood changes mood -- Location: e2e/features.spec.ts:25:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood -Call log: - - navigating to "http://localhost:5173/mood", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { -> 5 | await page.goto('/mood'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -``` \ No newline at end of file diff --git a/test-results/features-Mood-Radio-Page-shows-circular-progress-indicator-chromium/error-context.md b/test-results/features-Mood-Radio-Page-shows-circular-progress-indicator-chromium/error-context.md deleted file mode 100644 index 5ab5345..0000000 --- a/test-results/features-Mood-Radio-Page-shows-circular-progress-indicator-chromium/error-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Mood Radio Page >> shows circular progress indicator -- Location: e2e/features.spec.ts:33:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood -Call log: - - navigating to "http://localhost:5173/mood", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { -> 5 | await page.goto('/mood'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -``` \ No newline at end of file diff --git a/test-results/features-Mood-Radio-Page-shows-mood-name-chromium/error-context.md b/test-results/features-Mood-Radio-Page-shows-mood-name-chromium/error-context.md deleted file mode 100644 index ad9412e..0000000 --- a/test-results/features-Mood-Radio-Page-shows-mood-name-chromium/error-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Mood Radio Page >> shows mood name -- Location: e2e/features.spec.ts:12:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood -Call log: - - navigating to "http://localhost:5173/mood", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { -> 5 | await page.goto('/mood'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -``` \ No newline at end of file diff --git a/test-results/features-Mood-Radio-Page-shows-mood-radio-title-chromium/error-context.md b/test-results/features-Mood-Radio-Page-shows-mood-radio-title-chromium/error-context.md deleted file mode 100644 index 313d4cd..0000000 --- a/test-results/features-Mood-Radio-Page-shows-mood-radio-title-chromium/error-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Mood Radio Page >> shows mood radio title -- Location: e2e/features.spec.ts:8:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood -Call log: - - navigating to "http://localhost:5173/mood", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { -> 5 | await page.goto('/mood'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -``` \ No newline at end of file diff --git a/test-results/features-Mood-Radio-Page-shows-play-mood-button-chromium/error-context.md b/test-results/features-Mood-Radio-Page-shows-play-mood-button-chromium/error-context.md deleted file mode 100644 index bc0593c..0000000 --- a/test-results/features-Mood-Radio-Page-shows-play-mood-button-chromium/error-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Mood Radio Page >> shows play mood button -- Location: e2e/features.spec.ts:17:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood -Call log: - - navigating to "http://localhost:5173/mood", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { -> 5 | await page.goto('/mood'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -``` \ No newline at end of file diff --git a/test-results/features-Mood-Radio-Page-shows-set-the-mood-button-chromium/error-context.md b/test-results/features-Mood-Radio-Page-shows-set-the-mood-button-chromium/error-context.md deleted file mode 100644 index a023ec0..0000000 --- a/test-results/features-Mood-Radio-Page-shows-set-the-mood-button-chromium/error-context.md +++ /dev/null @@ -1,130 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> Mood Radio Page >> shows set the mood button -- Location: e2e/features.spec.ts:21:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood -Call log: - - navigating to "http://localhost:5173/mood", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { -> 5 | await page.goto('/mood'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/mood - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -``` \ No newline at end of file diff --git a/test-results/features-New-Releases-Page-shows-empty-state-or-releases-chromium/error-context.md b/test-results/features-New-Releases-Page-shows-empty-state-or-releases-chromium/error-context.md deleted file mode 100644 index 3feb8db..0000000 --- a/test-results/features-New-Releases-Page-shows-empty-state-or-releases-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> New Releases Page >> shows empty state or releases -- Location: e2e/features.spec.ts:97:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/releases -Call log: - - navigating to "http://localhost:5173/releases", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { -> 90 | await page.goto('/releases'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/releases - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-New-Releases-Page-shows-new-releases-title-chromium/error-context.md b/test-results/features-New-Releases-Page-shows-new-releases-title-chromium/error-context.md deleted file mode 100644 index 3bf8d38..0000000 --- a/test-results/features-New-Releases-Page-shows-new-releases-title-chromium/error-context.md +++ /dev/null @@ -1,155 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> New Releases Page >> shows new releases title -- Location: e2e/features.spec.ts:93:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/releases -Call log: - - navigating to "http://localhost:5173/releases", waiting until "load" - -``` - -# Test source - -```ts - 1 | import { test, expect } from '@playwright/test'; - 2 | - 3 | test.describe('Mood Radio Page', () => { - 4 | test.beforeEach(async ({ page }) => { - 5 | await page.goto('/mood'); - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { -> 90 | await page.goto('/releases'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/releases - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { - 106 | await page.goto('/shareplay'); - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-SharePlay-Page-shows-create-room-option-chromium/error-context.md b/test-results/features-SharePlay-Page-shows-create-room-option-chromium/error-context.md deleted file mode 100644 index 8939dc4..0000000 --- a/test-results/features-SharePlay-Page-shows-create-room-option-chromium/error-context.md +++ /dev/null @@ -1,150 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> SharePlay Page >> shows create room option -- Location: e2e/features.spec.ts:113:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/shareplay -Call log: - - navigating to "http://localhost:5173/shareplay", waiting until "load" - -``` - -# Test source - -```ts - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -> 106 | await page.goto('/shareplay'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/shareplay - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file diff --git a/test-results/features-SharePlay-Page-shows-shareplay-title-chromium/error-context.md b/test-results/features-SharePlay-Page-shows-shareplay-title-chromium/error-context.md deleted file mode 100644 index ce70607..0000000 --- a/test-results/features-SharePlay-Page-shows-shareplay-title-chromium/error-context.md +++ /dev/null @@ -1,150 +0,0 @@ -# Instructions - -- Following Playwright test failed. -- Explain why, be concise, respect Playwright best practices. -- Provide a snippet of code with the fix, if possible. - -# Test info - -- Name: features.spec.ts >> SharePlay Page >> shows shareplay title -- Location: e2e/features.spec.ts:109:7 - -# Error details - -``` -Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/shareplay -Call log: - - navigating to "http://localhost:5173/shareplay", waiting until "load" - -``` - -# Test source - -```ts - 6 | }); - 7 | - 8 | test('shows mood radio title', async ({ page }) => { - 9 | await expect(page.getByText('Mood Radio')).toBeVisible(); - 10 | }); - 11 | - 12 | test('shows mood name', async ({ page }) => { - 13 | // Should show one of the mood names - 14 | await expect(page.getByText(/Sad|Happy|Energetic|Focused|Chill/)).toBeVisible(); - 15 | }); - 16 | - 17 | test('shows play mood button', async ({ page }) => { - 18 | await expect(page.getByText(/Play Mood|Playing/)).toBeVisible(); - 19 | }); - 20 | - 21 | test('shows set the mood button', async ({ page }) => { - 22 | await expect(page.getByText('Set the Mood')).toBeVisible(); - 23 | }); - 24 | - 25 | test('clicking set the mood changes mood', async ({ page }) => { - 26 | const currentMood = await page.getByRole('heading', { level: 2 }).textContent(); - 27 | await page.getByText('Set the Mood').click(); - 28 | await page.waitForTimeout(500); - 29 | const newMood = await page.getByRole('heading', { level: 2 }).textContent(); - 30 | // Mood should change (might rarely be same by random chance) - 31 | }); - 32 | - 33 | test('shows circular progress indicator', async ({ page }) => { - 34 | // SVG circle should be present - 35 | const circle = page.locator('svg circle').last(); - 36 | await expect(circle).toBeVisible(); - 37 | }); - 38 | }); - 39 | - 40 | test.describe('LoFi Channel Page', () => { - 41 | test.beforeEach(async ({ page }) => { - 42 | await page.goto('/lofi'); - 43 | }); - 44 | - 45 | test('shows lofi channel title', async ({ page }) => { - 46 | await expect(page.getByText('LoFi Channel')).toBeVisible(); - 47 | }); - 48 | - 49 | test('shows lofi channels', async ({ page }) => { - 50 | // Should show at least 3 channels - 51 | const channels = page.locator('[class*="aspect-video"]'); - 52 | await expect(channels).toHaveCount(atLeast(3)); - 53 | }); - 54 | - 55 | test('shows LoFi label on channels', async ({ page }) => { - 56 | await expect(page.getByText('LoFi')).toBeVisible(); - 57 | }); - 58 | - 59 | function atLeast(n: number) { - 60 | return { - 61 | pass(received: number) { - 62 | return received >= n; - 63 | }, - 64 | message: () => `expected at least ${n} elements`, - 65 | }; - 66 | } - 67 | }); - 68 | - 69 | test.describe('Internet Radio Page', () => { - 70 | test.beforeEach(async ({ page }) => { - 71 | await page.goto('/radio'); - 72 | }); - 73 | - 74 | test('shows internet radio title', async ({ page }) => { - 75 | await expect(page.getByText('Internet Radio')).toBeVisible(); - 76 | }); - 77 | - 78 | test('shows search bar', async ({ page }) => { - 79 | const searchInput = page.getByPlaceholder(/What music is calling/); - 80 | await expect(searchInput).toBeVisible(); - 81 | }); - 82 | - 83 | test('shows stations section', async ({ page }) => { - 84 | await expect(page.getByText('Stations')).toBeVisible(); - 85 | }); - 86 | }); - 87 | - 88 | test.describe('New Releases Page', () => { - 89 | test.beforeEach(async ({ page }) => { - 90 | await page.goto('/releases'); - 91 | }); - 92 | - 93 | test('shows new releases title', async ({ page }) => { - 94 | await expect(page.getByText('New Releases')).toBeVisible(); - 95 | }); - 96 | - 97 | test('shows empty state or releases', async ({ page }) => { - 98 | // Either shows releases or empty state with "Add music" message - 99 | const hasContent = await page.locator('h2.font-display').count(); - 100 | expect(hasContent >= 0).toBeTruthy(); - 101 | }); - 102 | }); - 103 | - 104 | test.describe('SharePlay Page', () => { - 105 | test.beforeEach(async ({ page }) => { -> 106 | await page.goto('/shareplay'); - | ^ Error: page.goto: net::ERR_CONNECTION_REFUSED at http://localhost:5173/shareplay - 107 | }); - 108 | - 109 | test('shows shareplay title', async ({ page }) => { - 110 | await expect(page.getByText('SharePlay')).toBeVisible(); - 111 | }); - 112 | - 113 | test('shows create room option', async ({ page }) => { - 114 | await expect(page.getByText(/Create Room|Start a SharePlay/)).toBeVisible(); - 115 | }); - 116 | - 117 | test('can create a room', async ({ page }) => { - 118 | await page.getByRole('button', { name: /Create Room/ }).click(); - 119 | await page.waitForTimeout(500); - 120 | // Should show room UI - 121 | await expect(page.getByText(/Currently Playing|Add Song to Cue/)).toBeVisible(); - 122 | }); - 123 | }); - 124 | - 125 | test.describe('Playlist Page', () => { - 126 | test('shows 404 for non-existent playlist', async ({ page }) => { - 127 | await page.goto('/playlist/nonexistent-id'); - 128 | await expect(page.getByText(/not found|back to library/i)).toBeVisible(); - 129 | }); - 130 | }); -``` \ No newline at end of file