From 2a66c1bed70d3c1ab1e058b739bae518eb5f9bbc Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Mon, 26 Oct 2026 15:21:18 -0700 Subject: [PATCH 1/4] fix: preserve Syslog result contrast across theme row states Signed-off-by: Thomas Vincent --- README.md | 7 +++ css/search.css | 16 +++++-- docs/RESULT_THEME_TESTS.md | 27 ++++++++++++ docs/images/result-theme-dark.png | Bin 0 -> 15144 bytes docs/images/result-theme-light.png | Bin 0 -> 15271 bytes tests/E2E/playwright/theme-contrast-check.cjs | 40 ++++++++++++++++++ 6 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 docs/RESULT_THEME_TESTS.md create mode 100644 docs/images/result-theme-dark.png create mode 100644 docs/images/result-theme-light.png create mode 100644 tests/E2E/playwright/theme-contrast-check.cjs diff --git a/README.md b/README.md index 218d8810..36f978c2 100644 --- a/README.md +++ b/README.md @@ -553,3 +553,10 @@ nesting levels. Invalid searches return no results and CSV export rejects them. Copyright (c) 2004-2026 - The Cacti Group, Inc. Optional [Alarm Manager forwarding](docs/ALARM_MANAGER.md) uses Cacti native in-process hooks with existing Syslog email as fallback. + +## Result theme regression fixture + +![Synthetic Syslog interface alerts on a dark result surface](docs/images/result-theme-dark.png) + +See [result theme tests](docs/RESULT_THEME_TESTS.md) for the populated light +fixture and browser reproduction command. diff --git a/css/search.css b/css/search.css index f3bf546a..0bb55400 100644 --- a/css/search.css +++ b/css/search.css @@ -601,9 +601,9 @@ #syslog_workspace table { width: 100%; } /* Severity row tints: a translucent hue per priority mixed over the live theme surface, so the same rules read correctly on both light and dark themes. */ -#syslog_workspace tr.syslogRow { --syslog-sev: transparent; background: color-mix(in srgb, var(--syslog-sev) 16%, transparent); } -#syslog_workspace tr.syslogRow:nth-child(even) { background: color-mix(in srgb, var(--syslog-sev) 22%, color-mix(in srgb, currentColor 5%, transparent)); } -#syslog_workspace tr.syslogRow:hover { background: color-mix(in srgb, var(--syslog-sev) 32%, color-mix(in srgb, currentColor 9%, transparent)); } +#syslog_workspace tr.syslogRow { --syslog-sev: transparent; color: var(--search-text); background: color-mix(in srgb, var(--syslog-sev) 16%, var(--search-card)); } +#syslog_workspace tr.syslogRow:nth-child(even) { background: color-mix(in srgb, var(--syslog-sev) 22%, var(--search-card)); } +#syslog_workspace tr.syslogRow:hover { background: color-mix(in srgb, var(--syslog-sev) 32%, var(--search-card)); } #syslog_workspace tr.syslogRow td:first-child { border-left: 4px solid transparent; } #syslog_workspace tr.syslogRow.logEmergency { --syslog-sev: #e5484d; } #syslog_workspace tr.syslogRow.logAlert { --syslog-sev: #e5533b; } @@ -1125,3 +1125,13 @@ width: 100%; margin: 8px auto 0; } + +#syslog_workspace tr.syslogRow a, +#syslog_workspace tr.syslogRow a:hover { + color: var(--search-text); +} +#syslog_workspace tr.syslogRow .syslogSeverity, +#syslog_workspace tr.syslogRow .syslogSeverity:hover { + color: var(--search-text); + background: var(--search-card); +} diff --git a/docs/RESULT_THEME_TESTS.md b/docs/RESULT_THEME_TESTS.md new file mode 100644 index 00000000..3963c9ee --- /dev/null +++ b/docs/RESULT_THEME_TESTS.md @@ -0,0 +1,27 @@ +# Result theme regression checks + +Result rows, links, and severity buttons use the foreground and card surface +sampled from the active Cacti theme. This keeps host selection colors from +replacing the foreground on a plugin-owned severity surface. + +![Dark result fixture with synthetic interface alerts](images/result-theme-dark.png) + +![Light result fixture with synthetic interface alerts](images/result-theme-light.png) + +The screenshots contain two synthetic devices and interface-down messages. +They are browser component fixtures, not a running poller or database session. + +Run with the existing Playwright dependency installed: + +```sh +node tests/E2E/playwright/theme-contrast-check.cjs +``` + +Set `CHROMIUM_EXECUTABLE` to use an existing Chromium installation and +`THEME_TEST_OUTPUT` to choose the artifact directory (default +`/tmp/syslog-theme-contrast`). The test checks both palettes in resting, +hovered, selected, and selected-hovered rows. It also writes Chromium CSS +coverage to `changed-coverage.json`. + +The Cacti theme compatibility suite additionally loads this plugin's real CSS +and theme-token JavaScript against all twelve host theme variants. diff --git a/docs/images/result-theme-dark.png b/docs/images/result-theme-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..4c7702a6245324a6c2acd2a0aa9f67ad76333001 GIT binary patch literal 15144 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 D?u}ka literal 0 HcmV?d00001 diff --git a/docs/images/result-theme-light.png b/docs/images/result-theme-light.png new file mode 100644 index 0000000000000000000000000000000000000000..b4b8d132be6ea6ca61c5906179c4d8acb6464b8a GIT binary patch literal 15271 zcmeI3XH=70x9_8F3!<>MY(PYa4N$3ybO>8fQE5t*mKXs+dJi?RP&U#81f;7d9YPNf znu&mb5RgD`X$k@z1U~K`IIhaj<2!q@k7T1(O?X9;#2Us7JcCor6Uavz4S(b6#Io4za{-##XBrBY3pD}f7m>^xFIJ*mJ4+L=rYJL1oKFSHum1e?W!Rv#<4@tBM}hSo?yAH#aG(2m*YnfSHdd8?wQ5x?Ojg_~&vu)rb)tEBa!6OoY0$9EDPUFx5>K~x z)mN}S|4ncXYgFG5I+7Ky^!SwOD=O=&NoRk<>=cRXx8~($rRCC@-BD=Aa#=hPX-g~> zL0;RTYw)L6NU8?jKJw$Y%C_K*Z7a`4T7s@!z)FXGKegeh8M$5_htu+E-u~8KC}&+? zJG`N<_H?m(2BaX=xOfWS6aNQC6lHiBYNrKnGGR+p++_Bt)9?4Lw2u) z3>X#Ea-do{phuKnf$2avPBmPQ>XEmuy}y0*aKWm;DK3NEA?r+l)lZK@PG8wxNUOV{ zi{SM7iE^0h3OP2OlQAoWWQ^Wtff3leD>96W0diX2~ogFRQXJ32H zUDdJ-iXodILL%s9XaNC-&i6?C%95~w8rn+bSWf|x_+#LeOl>(h%3P=&S%fT~Y3?cV zalmhn#!Q>>M<<^8qe8|o<8(!qihK5`tkf&vwxZr(qa`&%kZ!DWU8%W7k@-^K*S5;{ zge)};1(airUhkV@Nm+`@C<@VpKHKMaFIxINUvyk<;O9OQ8KgFrnQgwddpnBRGDEC~ z=S@BHl75Q+bIT(=?+pak=<lpY$!<5Jy5sNB&ku|{8cn|jyb@#Bl~mJQ+a z)8Xl54~1}bw`Z$WO4G_iy%{3gr7QkY8j->~;@aE}L=RY<2cvGMbuVf34>ut3jr?=EdniFc3GUM;Po7MqMU%sf zFx^81f!LKfyshNUNGJ!tJtc3Wv$-WCnmsrhNeUrk=&nr;^)Cc6EZj)t;_7?&0|sgC z>FPfTyHts#Cb~~fZcx4R!+Cj9AZ@e)Utd;+(%$|;M{iw*mOY%GHV*o zS9(zFE*kumilJ5;YvW@pIAJC5dKYCa;mEDB{&brX<#pgt*~ z9=16s?-s2ju$_hU9CbTNnzAm&!o8OA&|zC&jqIVJDD(Z#TB@Cu+i9xqP0I<+XkFf0 zsGwco!1E5=3M1C)iIWp&r`~zVTz8?BX2R;~o1f9%lW`}gdW|u+Rz&Sk+$#75m1C6%d$8j1FOaB~7U7(^Ju=)HVUyrqDI__1*XQ@UVW@AbGXJhbYKrRqlJ+LZgI zvcTc$+Um`o`$uKrwIS`5cWG+UCU?fF0?4|EmD8U7I;CU>OfGhaX;f@cZg);A7f(=N3p zm&iyCSNg}C0|IFI_13cTHEs0^tFfFYFT`g5@0jB77*mae0chP5&POpi-f5)ylk%J# zw8qonD=B7ZUaFO#f4)$XB+V#%@;NzG>XnG0qRC2&VRn)ozChkq_tcd-Cywic4&rAV zV+xjkJGDAyxqGQid&6?{eNUdE2@n8v&imLsq37|VGezAs__Ttc9q`7KQ0qzcuzN0p zM7VAw|D0fc&0<#@#M}R5eqXArRcwd_p8Pmc`B(M&*K^1HwdfNqgAoDp`i!tMrj52_ z=F7cytgb*h9t#UvhE-^41U$QMGx{omHrd$58TOsMC)xaF{Ovv#mUz7%EC^P8f@@Gp zpZIuvGYB`8uOBr~Zd2?V^Rokm>QGVC8Kfus^?MDM1@8^ABVlXf^)8eC1Et|;jy;J; zUbRQJ(mQPxHCdB4tAj*W_HO8Keb?;Oc*S>LMxFJ?v%PuO*P&!_jN#=j`+csBIUvI!&SVVztvGGcT&kmow48d+4sN8g83_A zVvXb1Adc%_i%YBZ+wlVobb`zMi^?e4?d}72D>UX83Ia;9DOJ|zSPqsNFsHGefMU{7 zEv3asU~wU*UtV#huHS43YkU;U_}*cwrX7T!_yv*cjodCIM+bZLq-dRQ(MIf44FSJo zHZ;}ej(9|8#%JZxSa;vH8s}W34e!gp!7Gdcfkxdz1CMM)EYf?@E@x^wbyStpA`E7I zr9!uPa_nvaHPggPrWbi1oJvhGu_Bk1!r0$#?i_xJcUU?dHc(+%?a0U!Wrk;lSQaht zYhf2hlcN(1KTb&b|QQYM<*0h}^BTYDzx6vo* zG5bmwA8=&u=BBuQ;AZLt;a(X*h@5rv&82t7iAm8&FT^~^=467`gVLd-+hz_H0z65-J@b~1}o1PQa1G&j%={o$^Faq zq4st+1((4P>K0Pxkub+4%rzO6N}iH)8Ti-XAyc!?lEMYC;v;4L=Jd=(l`u?4uT^vm zHPa$x*rwILD%)YBIOS<%@Qa(9zuB0LV@A1j{O;~DYVoQ5HT7$Zbk`B~C&dcuu|wE(52x&N$ye$I$GP?%W0~~E;+J{pWqUiN z2l2N*7X%-P?ayRFD&OcvLO1}rgmw+`ekDqpjsglsJ-iROSFVOFd` z(NhFb3in#TX3Wi<;*wmH+e6oP{_NUdt=!%cpYk4k)l#t~3^e}eVvQ*vTkHw$2T;`bN2&VJq0D+s-n5b_0*23Bw>(@r{H0BO0|MUkEbMJ>_<1 zHPpU01;?TfG(b6hwyM$M85tSkY91ldp*BI&o$aa-aFlqIe%kX}_jJ|IbJRE058JU`9#6FPpf? z(xx@c)yyl3Lm)omHw(Z*CFY_DjqK#dw^HzaLeeJ2sE~6~qE63C5lSiC(toN6*8&1SRvBB^^r2ryp+?Eg$H6K@7gTBw>11PW!{+ z`>7<4Q{ZIC?{Z}tN#WWy=Ym^HXF%4>toZLZqR+_&u(q#j(BM{o=l9cx)1_L+Zduno z(F^I$B#x`1I90L3G}Smxm~p;MYOA$#`H5TnJ4nS~CbP1CH_E7KFBQk^PSJ$4p_z9-+qKjtPdlQ zch~k%4L7-JBJ2Jy*#a!##5Bz zYmtyn3DiWgVC5J#6)k0fs4@*`o5~=U&1} zg6CkvA}4VtXgQ-Wlta(x+!kNn`rIosnS*iZ`)!?S%ImU1F~;*1kA-axe~wvh91+&~ zuVmKMN_U~fM4A#jiyl&L|Ll>tqtW6}L)`Zbp}QS;(p6By$~2>mgulrmX;ecN*$ zEFLHm6_+crW3t-9t=&pLQ5YfdQOS_EJAbI`VM{BZ=B7MPzCc^$-w2AH+Ia77wx+TC zAtK?p*LlMo$dI$k!xKA^{aPE9{VS&;bK>9U7gBw0&2MSq--QJcEP9;5_4ZTku_jN& zJ^g`HSc!O}DW~vp@2-_jv&-00_S#IQpQhbFV?e*uib4~*mrq_%bBt8EZ~3OHZH;W` z>Gqgq!lVMUzG?FLEx&$b#9#I`NQWw1#(M*^!YjuPV92X$0ey`UzRoj8h3er4c6y#A zd1Mh6{BORrFQ36ira#0Y9R_h3SjOIZX*m=N-vtY@eeOr3VOGm}&-&nb zQiZKmt6gKD$M|Xq@_C-V%U9x;#ROlGHNj%)&7DD4jrlYLtkFJ;0c5F6`({ zsq6iC7C_4?6aQ#$hg`nqxccoV9|*LU6hdwRbPx7Dl7lKYxJ zS_*``$1tcnLK06$W`ek)3|)bC6ZyLGG2y&@hjt!V+m5(fYq?Dne2U@R1MVjHs6}^b zp*QgFg~x#o?c92gR8tUt@XkMrv*q@BGu^%9`99E&%Knq1qLGbMP8Cv!8s@VxXJNJ} zj7|s0ChX^hhwZH{z@Pg+2<3r)6ICP|1f&wT%{YyiaC~)YDS~;ci!Q1-(eTu7KWH!V zTXxME_X0l!Ybj61n$nqMB;>f5nYS3Cjty3E#!pnd5tv&WpZI{{j51T&P|*TSy- zL?^aA{P^ezRmRhV{ef~?eYpbzalcT=fY~*6FXy95%!2Z3(oPZT_v`PhMz$m?7HEN4vOCV(qmu}4Tu0{0ij$Ts6 z21F*AiHHliaU#G{n~}zC`%Mmm#Z2IsWch(i$Y?r{l|^+%ei13#t3G zGaaF`^EilI))*^LZu5Skqi|01$HZDzb9tBFd;p~BF8L)mcqg88D6BbbHuJ^7P2M(> ze;nib#?RwyGXXorofhy4=@#pf0*&hF`hpPd6aE8*4uyTWd3$^jeVu9Yw)oo{pF z(OosjIvR>T1d#HGB$Mq9aH9n&jg37a))=LpaGP-!SorvtRnB?2cK|PJWk0x zi_!a*j%ME*3y_2!Wud}h+o6Md&vXL$=JI$ptuv(3`m;?mFZI&`_j zI`fSWi&zYIC1X`I@6==T^>Wv`QVUnqXYjp0W!XRV)TFCZ*5aSzJ&~2p)0&5=L!1xi zBCiVRRw*S-aB^i>f?~l43^m)^k^T#F0CKBW$}&ylOSP?jn6#8CaE~mZ?!Mf7 ztM^HtZ7sk#PeLm6Xq~a?UAfbErhsNGT@owp)1m!b!f0gd|U&MCQC?mYM_ z*mu4^N0yY(yL+P_|7uRb?v$#ytYtZQwNOh3$H;48MqZh|-!=tIt)T1RyC)~#7&&@n zOSW;ADt&aCpOQUAb55njvBIA0A4Q!GSwA}!GBbK*iaC_j%_B+0rU#+c^`}fe@7)!` zlYu(hZ?D8_(xr@k0Kkgz>Q-SWuPK;5S0yClufFne0^A@Yw;VVj4^(R5RR|z~-)?$% zo6}g}m83hX;T6B=TL!HED25ltBg?=>XQ?ev7ka4!em3X%T4dxzb#(}vUR4>D;GD}I zRW^xKId)1@ecy6&}wd)sU&Dar$3xIY1HF6(7Q;|~&w#<1Bq7YP_FAyx5R-C@=+BH9iybqOuFx0nBikOHK9IuDFoePO2g z5rC{f`a!88U4K#5h(nT^R~wzfCQ|1=ibnN4Y*4Ujas-~Zqod<#%4X$fJS6vj;k0jy zGTf%Fl*I6j+2se*$rh89#w}S~=KMZz_`vL&Aw3yg-aYhr0XH)xWo7L%c9+x8zvosz z-tO^cne1{@zKQ=rt^iM4^*8LQq>70sz^cxbIez{SIW28u*X;i^s63q-v=XJ|sWta? zheJWBHQ!%YaZN3q z7T_V|q8xsdt_d9XE5rn@e6L+Si_zZt_BWpyzSfU-HlGs2OA$wghM+OJM{kAipc9%F z5zd~wHoQGT))X^tI&su9xM)(859eNE05mhaeofsnzs<_0zjLetdIqphkX6Y z=X6h4FceZeJA66Kw$}gFsQzX94-XqyT$y~loX-(pJuc2iwG??^NXmZLIKE z^i=c~IQdubj2&Aa-xgPFob@6rLRNP^8oSf|ahx7v6%#lAjsgOWc zs)it!Z=%M?wqD!M1oJDe-8R+R6_J0_IF#zXdjpQ$>|VGFw@}>Tywl6-Je+#Thk86c zVixWvT>qxms+`X@rx^B40i%0x;9she1fNFT}cJVm<)mGkMsHFC%l%d1zg4KXa+l@-F!JG|N z-|+FsA_uQ9raCR=bGHau=&`4q0-|PvX@jJe)7+fAErG=e7w(nZBf`)t!6umJ_)fd9 zA-#!uC(hg(2l{NUSxHb5z#Rgx3&fwd_)`_lE)%_iEa}YXKnSz)s#I+BAVX<_?Z3ut z)7TyzO7vposU7wJU~8pNwRR}%T$xOZ_iSc>P+^}y`tP+vKtHR-GyE%w&&(NsteeL!lQ#*PL*~v6y9tX5Br1yVBcr^`V|Yjq7M1m%16)&>9-UNT& z&Gq{pkczO_39tTc$X^#d=xOp(nuygnFI4xkUbUHcO|1-c3BmC)zGPqhaQ`U!T8Wje zo$ffj*qhw!lM)p`ixSs?g+c7VBXYsUWg7H3N*HNHpUaFuwXEAUWI-le^FzAZjK!#7 zxAT2FX5eSez6EGo#jhUms%kw2JE9(E8fUu z%6)H)O>5o$yslImKEBbN--4rbG|JTHU7PN`o|kakmS>jWA@um+-?BpglLcIjt9N#s zah)im=;y`5GQufyO8&^lthC6ZBI?agHxoMpM03BFLE~$T)vq?hyVg+KAUOoh##PM<;n_=%9QSmQ;=9`vwy{ zvZ4%9H=Uqiw3q8LSYO8X;;uh0nR`zq3l~q^;uk%gCV>g|b&lxzm@qT8cuzzT#>i7U z^eNY(;r`sMLP=Wyx2i9FmwGi9c3_b(@Jb9^ z)mj+Z&)6*J|9NB?X1XEvUxR^!s!Z!2Z3!ZmUMn`DGh|5$UHkZFOdt@%Dw=0JQf#f< z+*6yOoT=(^4Y~Q{xAmH=4W~`HER~WsJpbkS7_SiZqEMGqZ=HaV=6pdne^aa_FRYmQ z4(@>ave}g{9XWd-D&c6=skvyu9`2-BiHsRa4A&msKw(n#9u-GN`p}gK*}GYe$3y?H z%JzHwR>9L88^W?YKIFFN3CO1Gwjk@iWmkDvy?H-6eEfBbpK0;r^O$A7H_LmBsnEPI zMsS`{JGLJokwdtOWG?ljuz1AHtu;BJpckHup!}l)^(oWmB z{hUwHo3*TXo!#LWAm!Qt7_jHF7EK zI0@tOeQRak8lYLpqlC62B$ZMNW|048*&3Xdrx`zTHZ`To?W4f#5rZ4~t<2Sd=3u(N z3pwcfmLmR}$4L=hKr}v(Zm~JrXVzlcC;g5#zg*u7uVsP>0PB__>)WXV%9jmR(L>DfL4@ja{)(J+eLV zW1xgU*<}&o-CFi{Bdb42Hf&&ud%@Viv0?Giw7L|p2hp(ki12Rmzc1Od3Hw_wQp zrok&=BV;EWH`9Ii0@tlp_TOkQw2-QEMx?V>^Yq|qvR^U`q8-q=rL38keMj>1TcEX| zK-dXJ;fU6vxxR*@8%5A6yK}*YVirg(F{`guts-|o}XFN9(AXfvnRe$L7QQxX<%GDk2SFsBv*Dg7O-oaT+c z`G%t6GX3#Vg@vn8yVPc6pBk9w$26_{i|c3N3if;>Z*gv4d+unB*bn0s8whXi-7f(k zuBhVxm+>IBr{}bf9J^b$QRGcx}wK@rnHsUg6eJsc8Ey;|%N7_7jm;`OARqBa9vu0M_=U zgvzwtNEe!jd{2ct6h^Eb|HSFYj@0pNluP3HWL98wOy^(scmX}ofL%_a{%*te=5Lj- zMnSg(7od?ZJh4!V1L`&Us-r4eq;d zyH;7Y%+Ekq&Z{9p{FirF+KoBPrF`%iBC5%xDp+U&9>7VXzpu*u*5>Gs+0s^MHGH}t zKwNdMH(e&qDcU}ABM)`Of9oUQ2GG_mr>ycw8rtuM9>{aK-)P~7kLykqiNKKolW#KK zUzn(4GeesJ!M`CdsAIM^Py&Z95MIZ6WDOgDkBE$9e}$}>|BK&ty&R&tzY!@&FrfwQ z^{yRjosWT%ICC?^{?26KQeViE*`*mjt8&hc`?}jmxk*LvP#Tdu>_$q=&^{lYuu$3m zD>cnx4kL8tt>A~}TF&NswrDdLl0~%dWDbD0lyrVeUf`lRNl1Gu=^+AdYD05A0Y=IeCZTUv^KA^eYZyU=ZTP@ zEWWrFKHpg zp~XLM-I%2?7Wjca9NT+ukD5a+hVFDFdWBRvFVL0FYWvT8@fHL0wUCq%qo++cYjehg}S@Zgk2d=kocdmKyrc1qAHi)QPj^mg0lGMvEzeT4; zVW0|3bY@Fo3>m&Vj>4JJu!0p_)4KobE*AA2W~oD4i;d_vMkPvYW#`KTc`^f>#0;)F4EpYTjBp=GjtK448mR(pVA?~YRi7v zaJdKm?Opl24R#j1yDT_%bG}c=65>7jc2Z32eP|n(tb7%qC?~H%0wf14<6R>&mMYnR z^T%#x3&n~UBe`zV1%1xY(A`(VZg_=zt}rR~3ulC;E7r?~Nh)1aEwc9LBFlhIP~F`E zXP?=J9}MdOS^yHB+A}zKu{eC-n!5(h(7j(-KDv=QOdAnZGzGGMeDPWXKfRR1-QJ*~ z{Cr24Vasy+Hdf+cqz}VD%4E9P zXR(kbDscJ1u3${~H$W~#B}z61lAAVXd+OYVRBPH5bdNTK;m8?IiT6M}c@9c@!{IA) zskD(lEuwf3P~#p5Xkw*Mvh^8i0%rHdXq9z?WAL5H51|eEe>~Q@xt2gxs{#NYF7m1w z_?ST3jbBag#iP%_xBf-81CRKVg%rqr`o=-DjV@@I+Yb9ys=(!(UpSa(U5pP1{6&MO z$5pXwQL#cG>O4S$@lyMi#c$|Ek>hf>7o9h_ubD;{+A7sh%%P6Z3jwN&60^q^*JX=f z&+-74>-}|{jPeFlN!}`%N0ePFv(4{`iq8_2g9ZXyv5LKJj05x+;H-iepv|j)ideR@ zVIUZvdo7$%QWsB)_-y7JyD!iRV5s6Ef{e4^1Kfj=2uTX+7fStRa!hhpB&mJMBiewQYy@cCWe4Oq=+LNE0DZG`+^!S6HBArR=y(fucZO|BP@JOb|Z!ZZfL;e#b|4pJKl6M#c;_=SE8$JaffkVGE^Z`({2;$1c6Xbf} QC!o8xjBi%lc=Y0b01DdCT>t<8 literal 0 HcmV?d00001 diff --git a/tests/E2E/playwright/theme-contrast-check.cjs b/tests/E2E/playwright/theme-contrast-check.cjs new file mode 100644 index 00000000..33a6eff6 --- /dev/null +++ b/tests/E2E/playwright/theme-contrast-check.cjs @@ -0,0 +1,40 @@ +/* Copyright (C) 2004-2026 The Cacti Group. GPL-2.0-or-later. */ +const assert = require('node:assert/strict'); +const { readFileSync, mkdirSync, writeFileSync } = require('node:fs'); +const { resolve } = require('node:path'); +const { chromium } = require('playwright'); + +(async () => { + const root = resolve(__dirname, '../../..'); + const output = resolve(process.env.THEME_TEST_OUTPUT || '/tmp/syslog-theme-contrast'); + mkdirSync(output, { recursive: true }); + const browser = await chromium.launch({ executablePath: process.env.CHROMIUM_EXECUTABLE || undefined }); + const page = await browser.newPage(); + await page.coverage.startCSSCoverage({ resetOnNavigation: false }); + try { + for (const [mode, foreground, surface] of [['light', 'rgb(35, 54, 47)', 'rgb(255, 255, 255)'], ['dark', 'rgb(238, 238, 238)', 'rgb(32, 32, 32)']]) { + await page.setContent(`
+ ${[1, 2].map(id => ``).join('')}
edge-rtr-0${id}ifDown Gi1/0/24
`); + await page.addStyleTag({ content: readFileSync(resolve(root, 'css/search.css'), 'utf8') }); + for (const state of ['rest', 'hover', 'selected', 'selected-hover']) { + await page.mouse.move(0, 0); + if (state === 'selected') await page.locator('tr').evaluateAll(rows => rows.forEach(row => row.classList.add('selected'))); + if (state.includes('hover')) await page.locator('#row1 button').hover(); + for (const selector of ['#row1 a', '#row2 a', '#row1 button', '#row2 button']) { + assert.equal(await page.locator(selector).evaluate(element => getComputedStyle(element).color), foreground, `${mode} ${state} ${selector}`); + } + assert.equal(await page.locator('#row1 button').evaluate(element => getComputedStyle(element).backgroundColor), surface); + } + await page.screenshot({ path: resolve(output, `syslog-${mode}.png`), fullPage: true }); + } + writeFileSync(resolve(output, 'changed-coverage.json'), JSON.stringify({ css: await page.coverage.stopCSSCoverage(), js: [] })); + console.log('PASS: light/dark result links and severity buttons in four row states'); + } finally { + await browser.close(); + } +})().catch(error => { console.error(error); process.exitCode = 1; }); From 6dbb62d68746b3a73ec16a84db888112277c09d0 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Mon, 26 Oct 2026 15:23:20 -0700 Subject: [PATCH 2/4] test: run Syslog result theme checks in browser CI Signed-off-by: Thomas Vincent --- .github/workflows/plugin-ci-workflow.yml | 25 +++++++++++++++++++ tests/E2E/playwright/theme-contrast-check.cjs | 3 ++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/.github/workflows/plugin-ci-workflow.yml b/.github/workflows/plugin-ci-workflow.yml index cfe9f521..e5d4fe5a 100644 --- a/.github/workflows/plugin-ci-workflow.yml +++ b/.github/workflows/plugin-ci-workflow.yml @@ -335,3 +335,28 @@ jobs: fi fi fi + + result-theme-browser: + runs-on: ubuntu-24.04 + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 + with: + persist-credentials: false + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 + with: + node-version: '22' + - name: Install existing browser tooling + working-directory: tests/E2E/playwright + run: | + npm install --ignore-scripts --package-lock=false + npx --no-install playwright install --with-deps chromium + - name: Check result foregrounds across theme row states + env: + THEME_TEST_OUTPUT: ${{ runner.temp }}/syslog-theme-contrast + run: node tests/E2E/playwright/theme-contrast-check.cjs + - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 + if: always() + with: + name: result-theme-browser + path: ${{ runner.temp }}/syslog-theme-contrast diff --git a/tests/E2E/playwright/theme-contrast-check.cjs b/tests/E2E/playwright/theme-contrast-check.cjs index 33a6eff6..fc7c077b 100644 --- a/tests/E2E/playwright/theme-contrast-check.cjs +++ b/tests/E2E/playwright/theme-contrast-check.cjs @@ -10,6 +10,7 @@ const { chromium } = require('playwright'); mkdirSync(output, { recursive: true }); const browser = await chromium.launch({ executablePath: process.env.CHROMIUM_EXECUTABLE || undefined }); const page = await browser.newPage(); + await page.route('http://syslog-fixture.test/css/search.css', route => route.fulfill({ contentType: 'text/css', body: readFileSync(resolve(root, 'css/search.css'), 'utf8') })); await page.coverage.startCSSCoverage({ resetOnNavigation: false }); try { for (const [mode, foreground, surface] of [['light', 'rgb(35, 54, 47)', 'rgb(255, 255, 255)'], ['dark', 'rgb(238, 238, 238)', 'rgb(32, 32, 32)']]) { @@ -20,7 +21,7 @@ const { chromium } = require('playwright'); button:hover { color: red; background: yellow; }
${[1, 2].map(id => ``).join('')}
edge-rtr-0${id}ifDown Gi1/0/24
`); - await page.addStyleTag({ content: readFileSync(resolve(root, 'css/search.css'), 'utf8') }); + await page.addStyleTag({ url: 'http://syslog-fixture.test/css/search.css' }); for (const state of ['rest', 'hover', 'selected', 'selected-hover']) { await page.mouse.move(0, 0); if (state === 'selected') await page.locator('tr').evaluateAll(rows => rows.forEach(row => row.classList.add('selected'))); From 97b84bfb50a65f89a0c0406e7448ac89e30ed298 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Mon, 26 Oct 2026 15:26:39 -0700 Subject: [PATCH 3/4] fix: isolate Syslog selection from forced host colors Signed-off-by: Thomas Vincent --- css/search.css | 12 ++++++++++-- docs/images/result-theme-dark.png | Bin 15144 -> 13603 bytes docs/images/result-theme-light.png | Bin 15271 -> 14939 bytes tests/E2E/playwright/theme-contrast-check.cjs | 7 ++++--- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/css/search.css b/css/search.css index 0bb55400..21243118 100644 --- a/css/search.css +++ b/css/search.css @@ -603,7 +603,7 @@ surface, so the same rules read correctly on both light and dark themes. */ #syslog_workspace tr.syslogRow { --syslog-sev: transparent; color: var(--search-text); background: color-mix(in srgb, var(--syslog-sev) 16%, var(--search-card)); } #syslog_workspace tr.syslogRow:nth-child(even) { background: color-mix(in srgb, var(--syslog-sev) 22%, var(--search-card)); } -#syslog_workspace tr.syslogRow:hover { background: color-mix(in srgb, var(--syslog-sev) 32%, var(--search-card)); } +#syslog_workspace tr.syslogRow:hover { background: color-mix(in srgb, var(--syslog-sev) 20%, var(--search-card)); } #syslog_workspace tr.syslogRow td:first-child { border-left: 4px solid transparent; } #syslog_workspace tr.syslogRow.logEmergency { --syslog-sev: #e5484d; } #syslog_workspace tr.syslogRow.logAlert { --syslog-sev: #e5533b; } @@ -1128,10 +1128,18 @@ #syslog_workspace tr.syslogRow a, #syslog_workspace tr.syslogRow a:hover { - color: var(--search-text); + color: var(--search-text) !important; } #syslog_workspace tr.syslogRow .syslogSeverity, #syslog_workspace tr.syslogRow .syslogSeverity:hover { color: var(--search-text); background: var(--search-card); } + +/* Some host themes force their selected foreground and background. */ +#syslog_workspace tr.syslogRow.selected { + color: var(--search-text) !important; + background: var(--search-card) !important; + outline: 1px solid var(--search-text); + outline-offset: -1px; +} diff --git a/docs/images/result-theme-dark.png b/docs/images/result-theme-dark.png index 4c7702a6245324a6c2acd2a0aa9f67ad76333001..39bcf8d5a4a5cd21dda9e2ca41c630f8607da60d 100644 GIT binary patch literal 13603 zcmeI3S6EYNyZ57vFe=E5Iw~TdqA-dS2@>h!$e>676%~;ZlmH?ofFPYj$I2ieAT@-b zG(joS0)!BX(u;H!ynt$>vBVaSbTVNzKGk%KR zsW_Q=r_lMTja4C9##zPWO$ev%*uX#1Hfhne0~6=N_MOC*p8oB)aqH#M-OjTAJh!~q zw{Bp_?e8cja!<7e-AQ5x&_rI?{_^&RdEQhOnI1q*iR_=L$b-|rzCEu8F1}{DnzReN z?)c>qxQXA-!Ng&()StE-fWaQ^{>c%%ap}K(BY~5Fl1E_eB*0I>pZHZR5!DyIxBN8h zKitZv*S@fjAKZde_ekDQP>~GrZQR=72ajFY<3gE88Bhr7;-7)Ry6G+L!&SjB@dRNs z4sny8ZObJJa%`vOTuyH1=qz?RO+`Q60&~&&4tF$pb0CpN$nL?q#5lD_O|5}jchT6Y zp`m!NuzCsZw2pZ&4re6NVX)sS7q|GY47j53f@O+K-BmNy<)nV3(8JDu4ds^N*R`v$ylgCzE(jo=eG=jz^o7PF%C%8;yS=BqOAzVFxO$*A5N~R_Goxxn$hP zv;Fn2WV!{mAuB~BhG(4=m%^>$9gny79f-OhKM>?L-WWxhtC&%Y)L-n3#1)IdgmM=8 z3!iD56Y2_^!xHpvW0t|{(9#@*YoWT(V`W+La&46+5-5n)8oDiwLPUa9R`60=R!ZRa zq7Dn%%+6gpqGv-kFD*zzKF}-f4x55ffp|Qy;E&%_=z-@ z!YSp?1QJ3x?kK&fWN+O36NT{(JKq84Jp>WI1D_JIGJq0%x87KdQ!Jma-PsT>8;`e^ zs6I@sg;%3CNB4Js%%aXYY-4L{Gkm(U2Oey2$gDeIsu8;1-}5phxgH_zarlKWlutLB_^_{uPFo1~@TvBR zN*XDs+cjfgp0mkZty&GW3?6NTcMM_3j6$Um&+s#S{@frI!O+0#w0gL_LExt^`(_+M z5*yoG!4|uOCVj{)y|w%Pt}$=YhTYY7Mecqi9o?V$? z8)5W`&+)(q9sB@2uPwIR`N49RnL5#cSK9PyPL{P+ABQXQ9P(-q%)c-~o-3)_rFV+9 zxhM$I;}myiYQZI|TLO=ZdFt()=1-Bh3hlGGq)9pLx);)xYdOWW3x}Ik*_!?)1G;KjOgYce(eKk)?1+5x5vwlg+gS zKX7SvJildEf*Qz6F0aZf6yKE0rW6G)HYu}w=@@o)(1h{&!qX1q({= zjbg;vJ0Nb`lfCafXWo7#1D_tmq`73#33EcBSuASrZmF+!X_wW-x&0x@F5PDR^~8qS03F@d$lFO zHBz|+Oc(C8z&tB;kz4>^&ohG$5Bi;7U{)M{A)Z?~c;fIs3in3GTGGPWFH1nt{gJQeR9@cBR zd{-3It=A6k&`*3hm&*rn-$S`0k&rEzbv0@k+guW{xH0+Qry13Ut|SqQrJ+n%(o(Iv zq>}5?bUc2@e=I!FO-Ho3gc7ayXC)!cqPJ?Ps8>wCdm?Rk)-uvy<(t+yr-lggplC$q zbnBtTg4=b4$z4lH?s=vvK6Q_LE1Ytc;O%D>gBmGaNzT$BMHzKB|MqW>lAd zPUN@Nw(D_&Ct|JhirRH!LRtn~Nd~uTpCWOk>KmYhJzV&*Kg+z)S~I)sAurLYEnb(! zdKGru(N!lOThef~2huG@e@oY@E85}lf8c<|`XHFAyyd|XubJfzaZ+R>sU&hWP7%ul%nP#2Pvf7*ZaZ|v> z(`rBg0Z}4Q76l8JNS0$MN0&bqwKoUtx4r-Af_wnFP%m-%$<=ljXW!Uei=b$VW>BKQ z#v?D#AvqnqU&7R@{3r2U+wuyVPf8o`zQ$M@4v3gO(gCw5{^vcGXltsh84R9}EKNUm zame25%WvJg(vvE|eP}8~_Z?n1BWs7_Ph{KdxA*A`nk!cxID34tl#V5w`=qtiBZy8f zRNBVdy7ScQteJcoZqjx!?zbB^k8E2V^6ntUhSo6tIM9BqV}m6UO33>k4*v!+Cf;($ zj}-#<@0UWFI3G}xwG8|q>v(d)Hc?p7E7>6DXQ%18PxD}G(p&qhdN!QBk--7*g4kd} z&0Mf$j7R*Eb*=^keFPHk9ep^lB#2 zHHF_`Bs^{2V2t1*%P{b|Y)qgEYjYCep>;U7?Us9P$=5%3X5_Hj#A#0tF+Ffg1nlt9 z5m4{*$ihzz@-1T(GkJ6=yFNnm6!bjPRBk{=P}j6zT-k^%uJi#uN|Iq0KbC`a-}BsZ zwp;0h!i84{qD~J=UUf15bJih<6RS35%UZvI(CAMZEhLAW>^a7qBH<0G*ATxLt5aqR zo958(q?`Y6qlDjhx427hwqT)Nmb}bKCiy%74F8<0J|ad@eE)fPa=E8Dcl*{%n?h_=G~=*fLLJW* z*X4mOtUglS_LUjLr6!&*uN(paOv0_RL9IUi#E##tq?>sz0Wajo*caKnTQpTBzG)rG zBwB+0`%^nqA5pU6Unl@w4y_bbiu314T z_W3gNIL%s%;PwZF6+%noHw!{HGuHGOlY|=Kn{s2u$lBF$%#C5P1dh_e>Yr4^VPe^G8YTlAsz2s%^AMIvhA zuiwS7(scS^m2SF@^Up#!dPQjrtJ;_Ou{GJ0yWcAMMNt9Brp(;4oAY(unrM^vtM$Hk zVv2V2%I=b}u}C8(^`=S#sQ(G0L15vHK;jN&)K>eHu8oFGowivB;Rc})o@PrE(`6mx~ykG`3RRFj39DXdZLR$cG$85%@dW%GH7F6gX zvS_}|7g>m^g>X5)q0>#3Vvaddb9<+evBz$f5-fehrhW!{WrN{qJ&BLO;z6UgNYFT< zx)y8gkXSQ_q+7TtBY-dyS04nTEYk-TL$w`C=YswkwDc=Ae7_Ak$~Qw{USSyO6yc`# znq{$NM|S!#16+en1e6^gseF@7h*G@ZtDYj(WzO(vGrRbV*wB-9;-$H$mKQI>kPLv7 zM`$o+j%=UsON$!i{_5!RuZ(I98>Ne6X5h?Ngq~VCBKKTQ1oFLwz_rATtW;5d@8HKz z^#}K(g1u}lRC}b7OZ!qvDF=pd8yrQGSB>*_q7xprAXZL(_*e+`zBCKcVmopySDKJ@ zP$xe%&XI#XIb8-DQsWu0(__F5kbz27wzLG=uBgLMPsMM*IsO$Y^9<_#r!QBrN?|U} z&riZ%CH3jJYa;2-U6d&?rba6E)~ll>rx|}V(WQKj$wb(L)>2eAPQSsn_&y$Ei356= z9vHz37%0O#o{_tqnX0VI+MW;!imw49tmrhO+UGLjwFmrA-1d=Uy~Wx<_M!-jZN(g8 zBNqq-zy>qBu zL$9s}qo8r#@0V8IzUp8f$j;axyK(LBzI_(xKKOw9XTg#4&&P-HyflT9tnA>f=-^2> z$&HloHD&vwj|2CM+6EaZgfsni-oSTsTo#^gI26-LGSdIjr3z{chP3$?kU}^275@o) za>k6}y7=amEMo34e9EImL6T!Lg0Vo@S6)VYpswTj%|2TDYI@~-JXRG z=y`udfN+~Ntzf!k5$9+kA@96QFeZKml$3~8TTSKUNDI98e2oNoXXs;@=EhY6K9SVH zF#oWZ=%%Jrk=9E4z3mGm_G-56Pw&+Q+VRHC+s}Y0dvM9fbX{HEXw%i~UfVBxJ?26L zc6hQCDqXq)bS-4B3R~1#x!L*FhbTKS@yE z*zCzNJk#0nXv#ZAKZXhwT;HNchq@r=2-+P#%SgZm&WRbJ^Q?KwNW?$wR6h%{Hrt_4 zDM;2J&?iFG29dTRFmiu$j&D43!yghd9EtF&;j<wlkt- zl&F_BSug8RDO{gf%G>owWWDviDF377y{}u!WXsZhK^tpAUew$9$uu`{U{La=TIGB# zrquWGwtZ)TPL4b~`jY2*!eqalXH71dx&Hk_zTx-xX_>__@o|xjcV~K7!DsBPN6Cbe z+Sa?`lCMvo;p`r%S3}M)HB~w=Tky_~g5{s9HRRIMPyrLM^Z|uE#LrPj(^rf33R=1k zKWhW6ajt4(&Nn71JHCMU{2gO52lY?Pt6{QOJE;H&Knu6ur5@D;4#4!w2#zJJorwqe zJ$o$99x2KPI-NuG;iarrW-)SZe_w^mq0`I#_P9Md(KO!m{z{tmkY`=MWTLVag0nkv z*V-zZ@w4MqLFg*Vl*Xve?!ln(1)031J%V$(_`i8OH4K0PruihKTs+7-FKyt{ns6Fy z$VDX&_(V|CqnbGhM{%#-N4sE;61#`D&ZmOq*d`M%clyrfqhY5s19{U->1|?_9|04w zy**t-9)brU_B8*SZRBG^wShodq0?YbqNgb}&6Xw4u$j?3%4T#&guI@$1gu)J{?IlJ z#lYF7{$uBrkP*Qwq%RROiSr)U5rS*Vw6;hLfAPZ=tG=R_ivgUI*weBpxf`&WO;^uo zdGLC?vh~DQc#rf7fKb(D{%qDz2;cpY!dY)&>HOQ67rwCW7TGCb!;bSLIRIH0NaxL` z1K4s`UqsDTSc1^OSG?ao^|8#AF0QzW(Zd;P5$fU05CeJqt-*ykldk{?9bT5vP`vaC zxlUx`ekzk)8yP(m5J3}zIf^U7#Nh9a?9?}cXR?$o7g1UizAVHeKq}+|NMhaxqSU8b;Jy!-7Rb7<&5tl%p_u_w_HG%mw+7}b z`l;B)*GH(!ko`1vt_QVC+wE}_1^^ssUW$t}i1Ge(1+Dwv3V_`#RLx3i79ELXj90q- zAyGX{twq!fde8)=pdkTQbQ^3cgVyV6v@sxs7@7xRXNGzJI)+@pcoar(`-NO7a5=^D z6HxkjHOT2XPDXJTO=K~=Fx2K9Qpk$wGQWr$Wg(Qvt~{)c2gfKL6_j)*6gb zfS%pp(u}Ap*;y&;vqjz9tsCce>7d8L5h>1bHu537PVk#S5jvCp;3KeQBhRm-nN0Ko zZe-MUW!Ew&8*Yf#;j0WkmNb5?jwP7$vphTUR47P4tTSINmJjOEwZqw#!p$io^|Il= zr484HAqPdvBMr6=)t(_CLI>*~UX72iHtW&o+f|#UAX5lteOTUf2Pm8$Z5%~3-1g45V30ky$ z9~WFI+Y6uu50X!pDf^RVDt%qU(Z#Fxy7j~Yw0QEsv~=Dm<-gOeSp)>@EkS9lA(93( z1diW=@`Y&7k+d#q7CVZzO67)(?jfCBYK3>BT+M4o4!VDgI+Dv{hlT~X0d*dxT6%N! zdy6sF&>eCXJ$67(L_2yPkhyEtOaxC!*Un0Q&}`#Mj9n;O@d3+)x)XZbVPml0qJn$~ zf5-b^p5zFiK(xUJEvhje0J~t%z9pNx_y7uoDCH5JfqR+*cX)e8jvVN7{E2ujiw;_q zc1GfDm_wh6j#%>BX5RGj|H7U*R*7TFsz`_-7J5b}bKbytd56xEJz)zn?%9X<0i2OhCT(+K0sDHtp=Q z@E6Ja_p`^~P3NCWHsuwbOL6I$1r+Ggj?rr$+`_t=!{zi=?74x~x6%QVq>5!WK_Hm< zpsIlXPX@Og&?ZuH1DfU_O2O%)Mj*og^rXcpJ76d%){Xx!aasb3qT5!-r8NQB3Nfqu zmhgLVI&|g-x8u8`SxYIa$O`gAoUPi0st&`A@v*A;XX@xSmT-k^JgTKg#h64KN_+I98VT3-X0tUGnX}qr@7UUfUd)8{VL%E^F@ZpYqm^ZLaPVBfWDy?$pdRY6`k6)SM~MwUcoI546pO3mmEFm-0%hzO0b6lmVi>5{ z=(Qv%HGm3><+Bg&@w4hzE9tpP$5JqZPk&;NsT_<=jEN&?w2j@sP0&hUDjy4Kp`Ho0 z(bf4<$HyiorGRLh0wA~`ULOKp+oB8vRFxa0;_)s0+{@a&DgeE)qd>52{(tZ(IVw9? zL3tw%lEf?)08gEH!4&nXwNc4`GU6_t#L9)3WNR8e1eFv0@#Gf0mVk!Rf1it0C&iLC)3em7Wzxg(JR6_WpG=xmOkQ)nhpHBoHlQDQRyo^)(# zV2XQQxu#fmyAgy6S@X{k`d~Afs34)HJ1-~@2aAcj7?c)^q`#5v>9tr(_{%sV(8g*l zi83j?Cz?w|11^LNdGl(Ey557~kOgYu3pcpjNBDEkGrCaZq$8*g=;2dnAH6f@De1n2 zg8=^3^wWgPO=Owb_CKyKa(0`Y@&e5-L*t@u+w9$(Fv)P|LaoQ} zr;8Av2)nJ``E}sdb7avhXfiU)gWC6RB0N=5K~5(bdHG&a8-xmwZ}Ze%oB?Ak8MV?T)q7Wv8PjwK>6{i7R=QdMD5^+X4q)22gT7kTo|T;~IJB z_2MKD4A}6FmcV`rGj{uTY;<=ny&Yne`Ys1`FjqEa){Rcz)=LxP*e6~qo5`b?M9MR^ z%&Q+5I#b_p!$F#@B_nE!q$UY!kDF6AG7mgJp7PXagSot!_Fe5q*8$U4`!R8?p^T( z?x^k9JOIOkCm*k5V6!#rc#P_wtwmYIZPC_N9mP@W*wh=Q1D>6K?RPd0YqRpk#~9Dd z>Z@LlgA3?@ANAcUdAtU+S%DusHw6OiF(az_`K5#RWG3lOkp_W?&&_)SMQS$xp}OUPq|=E#;!YOCzOb@wgPnM@6Wr_%<&gewBj?c(o+ceKeZJe*U2T{(0{}_;FE_`HN)XYV!lIO7iiVn zMf)J~Eg$n63zV~mV&uJUe%OwFZ{Tt zSN@n+(szsVJgE)1kv9#lDFK;;dkP!h8gQZw7P(&8>E2GYGDmI(a(;3FRFH?e^oq8D zFme>hN&|-5W8vmG*bP>;fg6AaqBeUBd3*aOMciv3}o*wlk_;)|GDPmn)n?X@^p4rF4< z308Vv=v;y>046lnEPA3BOa%BqCAzQP#Ykuv;3M}ozQR7QTsngsclV}onm2EP!6c>1 zeGd){j9mZr0ffwOu52ti2ymDj#XUJzA{$OuZfM@8Byo4Zm_XehinH4JP%JCAB#Wbs zgQV%pUlqGT;*Fz4s zHlj@Qxmtf1o*lg}fEBXZws~(o9b%?AAM%o%B6RkF>Bq4SbZ2MU1=k{xfF%N~G8~R1 z+-`q50>FA#$EE&Wsy_yac&ptN_iq-UG1U!$CsXsM?XpWJ62CXC&82hAiR;hQgO3|6#r_qtP4<2as5;W`7nzH?MKWK5lHO^YcT7}e(^B(9 z64r)Gi5(h`aR0<_~v&_VHaQix)+3Qa%a zmC8d%(P8_op7jgxZ~a>|EyidX0l>_8762*==pF!qr>MJd!${MFg>2uE>$>Yu69ZTctL{9k zcg6%UA7j>3kJ(g7Tj788)qZGM#RczL)-I{z7Q%eVmB*$aHPeC;K0tUXu1AKL%aOpRJU!~_5=>Mp~g9vlxDI+2y!C57BZHll@m-maYd zdS9k!^=UphMA4K&?%JZ^U&MPijLvw7gHDYszOWFA$gMYLHCs-30~=Ukl|=AyO3Z~2 zG&G{rwk5Fpj7Pzg`l$0AlRy3sVbc9={K^HnGa1Cn;$UMF+bm)Kg)V28`QH54>ejpC z8!4OZ9pFrf1{j;&*ujJFPRYaORUv_CiLTejtx3m3;K1Y+8*hZ%`>WMcUe(+GqD;~6 z=;kJw4z<3QQcdw5N57cXDKdM(4oyF21V#Z3uVj`Q6(iA$+E!KZk_TpS0NV_s5M?m^-@&t=t9J$-_*3K2 zuvhX16|3iKz<2~Oni~h!XH{2QnQc!W{2K**uzeS432pt@q-k*+t{|AGqR$Bl7 literal 15144 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 D?u}ka diff --git a/docs/images/result-theme-light.png b/docs/images/result-theme-light.png index b4b8d132be6ea6ca61c5906179c4d8acb6464b8a..a87d6a76eff5a2238fa4b5a41da2a60ef6989120 100644 GIT binary patch literal 14939 zcmeI3cT`hp+wP-`G8VuFC@teSiVYYXdG7nVe%G_3 zZ&{d0?N!(d0)eEi{eI;R2($}$yYtJBJAs$qz~(zZpaY<5S1wtHWHHC~kdlK$O>C1Z zJx`*~-#@hc=JzAXciuj8e&TxI`IX9+i$gzOetGKgeoCWO&C^c@l8@X!d$;#Z@||cg zZ#+s1~pG9YE0dJQuVj)i-_{mLJt)DdTdg!MJ;HQ6o z1lkP({j_K2Q4lENmmlu|Ctm!^6XoNU;;^XQh#=rovNXvv;A-4X$DKFg?EZvr#H-fb0k0$wHsAhbx?2q#%I|H7Czz|MFx^3gnn+4 zwBEy=g1Bo|#&NX^Z)#f{IZJtT!AzB9koOC?oZ9v}hbD*R+&qutFV_amb#>8=x!LTF z{aPtlo->m6rjfrw&k7pT!1( zj?nRfV-u>m=zN~GU5c939Dxt+5|YDa3RPFaCmNl-299YXJHgV>#b*-3GdS(Q_4j|) zj*)~lBf^C&P9UgB9EDU)^c{UQ8ILLJE;HTcDu2NxhHXfWVVBga7vqkJk)!qnrd=BY zF!E)>n&I-TYDz+125ao?mFPnRdi!HbBP1u=y?ClmESF$;Fm6a8uq#?Yx&nNvc$1YvA#{Ggjdt!;L01ET{k=481* zzyu+yx~Ok+VCEVhjG3P!7uPZTq^9ih=qiSYVR?z`5~k}K7_eoo(X>rb0lAqa7D}R$dY)qiS06ZMFwVPT^yvk zgPGms2a~!gb8Z`$+0(vm{mh{b0W#7|kA86`WzbV;L$SjK2YYv4IF_rleS=%y&`;bV5#l?t#zkW!O($IAs}F?4GnH>uOuzq71`Ilb?^F zN_l)R%G6BMswmuj@;YU$F+S6}s4kRbm8$M+z+Zl%=ySl3uzQ`b{mu?%y!KX_0Hd`V zt0HL4GFOC1?HFDtB9w1E<`M5#H>+_KOkB-`yMTor@yf-s@S%!!=@Vv&?K`~5*~s?_sd*H+ zaPrnt-rjoMxu-c`O(WCz>2ZvzU3pTm-;Gbf#7)8O(hO~1)I@Gsy^)6lQ0a|perdoH zF8CQ0*wM^#!!DGKZ!}>vXNf_*4X)@H=LD;h`%buI2-q*}MVvm&Fv17y_6395SE1^T z)_J?7GFf8}rlOLd+v`bn3VDyJ(9WAt-Kx~wnMk|!97CMTsNQM~nF2-#gr*iSq%Yk* zf!?T@e{cVn=@+qXnZE|C&4_TyNA}SYL)a}<7WjIqI-A!h2xLUWZ&6n~INaym>TYzFsL%_NfD;Lh1-(I_lCr5?{`(IkvH}-(dFBXPoO!-B4?Cot zE#m+5*uQsrS=GY{UPoe;4oIC2Tcs`Y1el(wRvb8ZY0EQ58GQ%1-QGa=juQWq9Y(iF zc#_dtwS8Z<%%~z<$I+%JSoqG4vJ9au8oy^Ds^&Vef%8@D-CM)&bu(DKZ#yZ9@)#e| z35q+RQfzEoK=^GQ63e7E#9^HVbABWR!#YA=H=&F{93R$xV@4ken{q@5B z!JHP#S-(zct#pIs*jhTqb5+@7is1U!@bn88&Dk}eK2hrQv5G;qb~2>nD@wFX3ES(; zQtWo*YnX=pfey)ZImWDYc9Mjc7`Y0m;sqKz_7;3~v zluSKh{SZT24bP+TIAjlA3~oW@ms9f@@;sni6hX; zTw4j8xvMHvaLB0sAzlKXb$4ziTl2>@%AtxJL443$x3cc!*k<}g_jd9r%M2(E>!UQT zKFZ?uK54i>>caA=pFFJ!DAGj1^3eQA$L2S!v94w=dArf)IfDU*$D_sZUN-wgCa${l zRy!BurGcvJzIFGdBl>Jephn(vN!`YlKvnrxnxKn|r1C zXB_iy0Ckcs7E!@N$R%I%5~3e>Yvg8oD&D4)ve&M%^Kfgz*BZ5b<7)N5vZ}I#G{9Kr zVnj8fZsJA1FTcu#A(yc?@f`+ALyhxrc{PV4sY|6iR-&$_XWHlS&WLJfXRAT2vmDqgQOj zV0!VoJzc^?S5;LwQBIfG7LKCmZ5XmoR$f#%-i_!{YYR*YigfoLSn$Gc+!|shJ zt?px1qaQEYpw6%4RYnMAI5ZVBw{UO|BAD1bst-twzVJyXqDP|y76h{8yMkycc0OLepMt08DZ0b-@ z^69`QMR>Vz;g_UD<*$=zo9Sz9R7W_WZye36K8x4Ssl7Yh5EK~|iYJS}9X|=kSt$Itu7<1p7NO#GqT*<~+-w?I9 zcj_=As_E+0`?~+q_5Z9!_(VIk#WaWwDBr4vgs?U}HmK+U;^698?OI8ltMLpRcz8XypY=WQT6UPh9 z`+NpMv6rhY;vePdkLRxAQ*QDgGZ7MSg|&g#%~7o_fn3evL3A~$8A5ih4=z?aJBuAv z%ZWeT?&oyME@UU;4Xtt`WtYU0LAVO1^W;8gij%+FJ2hfXpu-J$szrqy|6-Tyu_VD? z54G~CEpTo{Nm{IWy$1IFWZMVVoS)Ql6^%>WliZ=u@F9daL(>QdCbJ7n(Kl!1AxYJJ)c>#lKcOxAbSt-W^Mtt!c64r-lr zE<9=Q&D5Oqu}5O5>V34w$r!8oGQ=XKySl!5kz}(Oe!4kB!SEVynKUd8B! z6pkF*()3#RJ5mClYV=iIB=mZBh<8zA9SHGQ#PKp&(B%a^h0) z=6QCLa*Y_%!j()?^-%rR`Jg(>x$I3E&Tk}8>|TBrVMhw^b2#VKtBoUm&Y=q^ zIz%Y=B3O8(#e<4|Z`-)8f*yQ6)TcQ;j)hN2-W*X*BR6-U##Nvi#)1c(R~aCC_tt$A zgTiA$rEF}{a(Ud{{^PvyFFP)=zN9Vf=rppXJIYXv9(N~_TO)A2e*1gnjs-%MvcGM{fs zh>u5j58Nd^2sr-|&}Q7~F&90xEU5cZJ=rJc?fr%^SCk7w#Z&jaofvGA+qHvSI2ymd}D0lRurmq$F#+t)I~X~WGwQ==-ufAA+2#bgTA%Nn5puFP!u8`suN zJ0Cm3nn$L9ulRcWsFqiYH{OLm%^*+ZmYWI3atORhf$}iIi2Jlg5{&qK|0-E!PlU~F z(&WUYQU|JglzKtq>e^~SloX`YtQJ{1QgcR7?nl0)+nu#B`{7tG?AX@!+cQ?KJ_@=> zG3TmmyWL>pT1~9H#@1%9Wr`dY{1vG8f*^N#m9FQCjM;@$HLLPw#Er@Ij&BcP=bh?S zJBF%Ui##LooVpVUqSfg{7d4}819L+c%O0y!>MkU`{1W-|PR}IzEr=`!fAdTVnwjq0 zWh|=%Q6e_GUamtkwA`?pgS{4KVIg{Ln|luTw+X4wW0uOQnE4vqWa0Y9xC-6UR^u+$ znKmZF^q(w1*;)AUZM2!nsJh+i!Nx=S_YhrK+ULZf?=(DRl@zm}m-WVH_l+0IpMQ!O zVw>p#2GZNFo?Yq9|K3K13UUsj{uo(|*mT3KFW?@)s*g55e%dyN*6mWs!6^51YkO1U zyA)W5c|Mh%d4fOh!DMq9{d%SzX+3$sNO0Ml_Nye>-pr&X@YBfgs$DWV9^v{`^O(xx z?W3VVoZ8MzD3muXiLXpf#G-wKpEyU3kcvFI+_VrplU>RxOX1s*jIdnvC1#qUfuPm4 z%VEau!o&6`9JAlO;H-@pDTee2*fM0VCWND3-Puto%D$W#XQVT z#z?q$0H6VE%#6Zo7mYPco>!)^fU=q1E#*ne?yZ^KF+@cKM$%oO zn+eA)jd;OE8)v8pW{5TajyTl%m-16-+KseR)!iMXWk)qNoVSHT9EWRKIx3cJx zI_DYDyp_uJ!VMlA+R?cS=F?yAyS%uoBNwB&5yzoLr_Y!YSpk;@g33tj`cxN;K1Efi zK|U*f*OGqpCy4OVm*U+{P;V~t$p(tQ!e_b33)HKNOODBTsJ~Mi8E$(TCMw30yBnv~^X(0DQ5K0>^<{Rh4S>3!C=NCx8V{EAj7zd` z6YaKZy;EmgPtJBEJGSzvP~R0&XfYOdud8DB?gw!g9<+WUPpPM`LPFhi(Q*_)-+fZo zelmO4uY%n_zlClIst|jj`>Q+86uKU`Q*XU6n`&pW-zVRKKZVNlwQclan;tnhvXp$f zVhfkkt<{;XTg#-lSIW4zKqpJt)3!C|q#~xahd}D%o)K<{;t*O$zaspY(QHy;?O1)F z=v7|8VTu?#vK(8TLvQ+1&A}P_Ble7*Uwr^iFf5wI3#2pp6_#2?;iRdbr|lE12w@}o}T{MyA&8PRNWlwV0SKf~>yf6v)FF%QVSB=EIB$08>Vqc4 zZEw$jDI>u>j*Z*fO9N))tv1ixx&B}1=N;A;7`g7vPvB%BvYSNM8lg67VbkwZzOot} zu@USs6um*0{aDaP;4swgRzBEL%`gt4F!h75_O!3>*y01J1le?PpB+2tI|rHtVb=(r zT%WP2&KyXfn647K7~O1Pw4#9{E`K?kfY}5mGT>w}+sr|^&oB2e7vDfr8IV#*ljuLo zGg_OFmRb~Tl#K*M=F#jEa_cn9zB9D*;LyORQLpI9hejk;#8OZb{Eai!4egA$D)M>@ z2fA-U$Z}u)vb2$)WxA5Qf>a)%f)NEP&Uj%*gy$0ox$-B0Mkt_xT|2+b4W1}d@E7j! z%#qVzK21u%t+LIk=#=@NDMwkB+jdCp;mw|c93RBYagD}G<{1!$tUGCG=d7}iMS>m| z=?j4d_g7iA261Ka$LTjH;_BM?VbKQmLx9gy*Nd{2wcebD@6yb@zvd72n>8+VzQP%x8_bR^8Mot;ZxY7r#t48)t8CMS*c21j8;NTkn9DZkeem{7cIa zwdn6k%^TKmA=L$p*t&PT)Sqs#s^quCt$fzZlXMx<$`R==m)mu>UUN-eQOGhaLdBjFac&d^`Sop?i4w@DooH8)CP#}4nre@Ds2 z<2sK1428Oe`0S-L@GYFisx8KjK1Ti#T42Er@A2?Wlyk#qutTQ*>k5cYbZ@ zBdXM~nz&}n(O^74=xDMk=&esrQ%~WHPNs;%5xHroZ2;@(di7qw=3plYPLmOJ;;zT8 zQko|&en)>&%`O2&p7t0((y@3WeyM8TF(-zZlQ4@>)Gf3Q;KZB5t?Ppqn6-+2<(HT< z3SStDKnLCWoMbSAg`88m`WIRS`_p1AXOF7#2i#H#)+9c=$T{>j9*#BoLwp7vo*d2zUrEo8=VB+=X5=i$(yERmRSk$3j6|OScRcf2fN?ptMYDG#<-DgC zqn#D_Yts6!Zc_6`sQ6RitJvqn29@TbOc@z=SgBkM}uwwRNxCJb`{t5~{`A3h^P0 zWPhm+BKbP?kYPstYi3riO~b$n2IIJe^<4^M{vEIwW8|bRon*Y-rC?~=L9YZXuW5pc z?7q}&T)@H|C@GUNV(;Wvef6DEIrnLW$25c+SNahsiprwlVn^#ylJK=zc*Dd}8seA` zc}aGl^e+D^)(*n$v;z1i{k`=9QH8{seU8Ue&1cq`_}I7_Da)io;1*6oD|*LLf044x zv0#3wW_INt?5R4C5`VXaR&z=By84_so89Ib>wR-e=b z8NsFP)ifQR3a!{VDjnZd997FQHnLG-8>N?L)sxsE*Z2NXGa#1J0lS}QEc~U6f}CKw z$ctWc+B8$@R%6D47_?YL$#L$$V6m)W`OgI!#2WZ{Dx7}Q>et=#0d1`nRz6cRZR{we z{}q5%G<$C`Vj7$SLT?b8Qwfl>xCEu}Y)yzz(JRN*kFtpXJ5ssS7H}&hb*;nnwIq`WM!fK*fMtSJL{0 ziqhYBq(#F5=}t}2hwl!Fa^reuOv{L6XWh7eDBTgScx!zkcZ9WtjyoeZp6}<-j10MN zvFi&3v+XH&C(Tnr--N)THN8s~ff5?8n^}6+re5^*+%z}G69N9ltG^rM0o7k03V9sv z`%STn_wbI#bbPF_&iKLUN~a+l7;FO+EPESK%b|21++sVjf2Vf|^CyO#StGZ9vsO{} z4|_Pnd1?_THLiITm|0)l!dsK0y~J_Dkk8ksDs=zav1z+poTfJV-ecz_8T5Q~l>wr= z49IQfzGpjAnz+xhM_3(kiqk*X^}qLAQzDQET5QWjpTsjJ_i3Y+8VuyoO8ow>IT-aKeVN z#BMrdzW=pGLx>Z@Dn;EnYz=JC63q6<^sDKzb<7=j=jd~wW+oohsT`)U$>&d-L7B<|1JC@MDVB_=dO7|dn!R9otP9v3F+y&nVek(g}Tljbk~0Q~NKeB4uHJW=R2 zUlsMi@9+@8vbkYuk&sQKSXBIPtaDaZezJHM47xt!78R^_3Y#Ck#_o6YtLCYoOWVB@ z7{2`>ET_r)<4brlR>a*fKeFGtl5>x&=@I-mY$H{_JoMwIT@?qH#-=~{91UXS$@CSu zG|XA_1%U)RTMhU052C1c!{M=X-#h;8G5uYR6wA)q(JcV}@9$_Y{U-}(ONe9|Yi?xZ zUOxk?8HLhMKOGsIqGG%T-V-X5Z{+Gv!+B%b{{ef7_kR;G{LPck%Cc?*q@NQEN4!ad zEIu;{ij=&~0B>}?d6c-+;u9oF0RpAKW+`2P8-F^mY4}tujTw6fz=FCpYCzHuJv6~w zd~x!+h0)Ubps|sE2i=2~jlNT>g&p;oF_-t#Tr>B*^=oY9O8vPp8T%stb1&(cwp9+* zAt`BF(HMVCwIm5_&|zqnQmUa%VaBon&GLPJ;qBFcCp=YQv3oB@Mt`DdkO~RjEKjrV zHul))ENAfi>KL`xQ$2Fg?V`o1gI9;*J7)G;PB^XKtr@Sp*K~m2<(5NuFiCx}&#&Zz z`+${2?7~!-;eaoY96CMMY_m+$aSOX4@Bnwj5a!?Tr{v{wZk(IjGqpyTY}HA#zbCpibLb>?P!0v;m|E)d*Z~P%vOWGvb&*Y`u3u$ka*MqV|98vx zx5@AXv&o}78Ax^ia5$i9L|j%UeTUaHh##r_es8joyrmhxe3 z&3E62c?cE;h>V5{KxXw6C1zf9Ftk0RCmV!3Z8mx+K1}SE=yNOZaSA|nYNlS}jC9Z| z%w}n$KV)-Hs?;@e$%%uaduuJszcSKGQmQ&^j&Nwcjw`vo;b-9uh#uC(3^C%P{FWaP zG5d~=0pOkqr{Hn-C{RmI4rqij@0)qpQRC2trW*fe*r_N@S)mOwGVon#?%zpArSiKi z)`>G-wa!T2MpV*!dVRGQ-7w^Lo#&OGk^zd|uj@3f%U=K%O>#p-vQmB>(@h4B{tS4i zIm=}|Q3J{?JWI_oSD*93yR054_|R?@ni7yFDV5s}VFn&qu4@B%8`FzhI59stsL=A4 zKMCpI|$cL?`HxGr5W2+VAb-xD6md`X~TlQG}Oi!9ux>6;V7HE7Iwrf~OI2rv{^ zcnJ#pet`WVtAGW_7oczBepX?~B0!)SfCH)eqB6G~k;;u8af`D1A?SRtf#@%$bVc;j;-P@k z)DMP6DLmiNw*a-Hd?FOcXC)zT(U)BH4exr3JIZ2a*Jj%xY-aCm@?$(fw18G=m$ojf zOHcZ-X)#mZ+g8LehjpLT)6v8Iv`Q^WaM-Okv{sWIf$HrtJ2RbUc3t%B-Jc4}7%<7j(jqg^H z{9^F2nCj<@EV{D+=g|+lC2vN-v?uD-bm?!}ks}P8&gi|zH_uu>jXZ>G-f_f=E&b;> z!J{;1tR6OuXSI2vqHB|kA!v6h*d_EfOC{BbL>2KVDnlVvg>^$7fJ1cY$}XZl5s9cO z3AxNk%|@lb=9}jnuib`|w6`gayQ!Z}3b(mNOIZNW%r!c&{H1In2<;@MAM*iR_C*@ z==;_VKsApCh#?h|F|R|J=EZkQ9i&e;zVG)IU-*?@pBLgu2PBA{RGo2(Bwh2D+HQl5 z#c#{bn|)V$n&U@mG61eI;%lYvfpRMkSQV94&78B#XJ$*lR{+%MtgcsUyP;qS5s&%~ z$eb5616mI+4G;21J}RCc2V#I3R5gy<`t2i*s3s>01bl+|g5j$P?>Q#sAn7Mu1~G|{ujL{?IgFc;$Qoe&R_+{vR(HD zEyH%tz^*aV97QIM#*|&4sd zZl@&B_ReGyGY~1{F>KO=oZivH^)3OlQkSk2d}np!%mu?7h?_s{!xt>kj{Mk zk!=~`EvRGOumcGK50)Q>;g(caZjp{n*pHnZubL=tcHC{bH8bs7Tb2c#|6}woHkpp( zyOuI^{qM!v4mc01O0EXEN}m-UH<=JSUz}N!eD3~FvKhX%D4g_E&319=d6s~Cw7qLn z4G_9GgZ$JASdzzM*3qwA8MFSvyWW$ZB6DwM53}dFh`r=Jh4xE3UVNwlk1^)20AS97 zy|d09$k4RB=2vgK(KEC?k-}`$5U|HYKf(0&Ls}yN&sY$!}0vH`#Z)Qo+z-?+tAvvAAp-;#r`hf*l#<3_!ao? z+JPqE?=P1?5+KmCAHIL!w?luQ_r9!e+#7l_cY^gK*)dOKY4!WUp`p$_lf`dLs);0)!$?FpFgVi_bmDUca{jifcq7T lf9Z-X0N(8WuKOfFhcT)jb1t2R0Y^aBu3B6vyL|7_e*-PeGb#W8 literal 15271 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 QC!o8xjBi%lc=Y0b01DdCT>t<8 diff --git a/tests/E2E/playwright/theme-contrast-check.cjs b/tests/E2E/playwright/theme-contrast-check.cjs index fc7c077b..89c94404 100644 --- a/tests/E2E/playwright/theme-contrast-check.cjs +++ b/tests/E2E/playwright/theme-contrast-check.cjs @@ -13,11 +13,12 @@ const { chromium } = require('playwright'); await page.route('http://syslog-fixture.test/css/search.css', route => route.fulfill({ contentType: 'text/css', body: readFileSync(resolve(root, 'css/search.css'), 'utf8') })); await page.coverage.startCSSCoverage({ resetOnNavigation: false }); try { - for (const [mode, foreground, surface] of [['light', 'rgb(35, 54, 47)', 'rgb(255, 255, 255)'], ['dark', 'rgb(238, 238, 238)', 'rgb(32, 32, 32)']]) { + for (const [mode, foreground, surface] of [['light', 'rgb(35, 54, 47)', 'rgb(255, 255, 255)'], ['dark', 'rgb(238, 238, 238)', 'rgb(32, 32, 32)'], ['muted-dark', 'rgb(150, 155, 163)', 'rgb(0, 0, 0)']]) { await page.setContent(`
${[1, 2].map(id => ``).join('')}
edge-rtr-0${id}ifDown Gi1/0/24
`); From e0ed9443b90c500d01232c93e5db8ae375f98afd Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Mon, 26 Oct 2026 15:33:06 -0700 Subject: [PATCH 4/4] test: lock browser dependencies and use repository artifact paths Signed-off-by: Thomas Vincent --- .github/workflows/plugin-ci-workflow.yml | 2 +- .gitignore | 1 + docs/RESULT_THEME_TESTS.md | 2 +- tests/E2E/playwright/package-lock.json | 62 +++++++++++++++++++ tests/E2E/playwright/theme-contrast-check.cjs | 4 +- 5 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 tests/E2E/playwright/package-lock.json diff --git a/.github/workflows/plugin-ci-workflow.yml b/.github/workflows/plugin-ci-workflow.yml index e5d4fe5a..245d99ec 100644 --- a/.github/workflows/plugin-ci-workflow.yml +++ b/.github/workflows/plugin-ci-workflow.yml @@ -349,7 +349,7 @@ jobs: - name: Install existing browser tooling working-directory: tests/E2E/playwright run: | - npm install --ignore-scripts --package-lock=false + npm ci --ignore-scripts npx --no-install playwright install --with-deps chromium - name: Check result foregrounds across theme row states env: diff --git a/.gitignore b/.gitignore index fb03a808..ceac1c60 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,7 @@ composer.lock phpstan.neon phpstan-baseline.neon phpstan-stubs.php +/tests/E2E/playwright/artifacts/ graphify-out/** locales/po/*.po locales/LC_MESSAGES/*.mo diff --git a/docs/RESULT_THEME_TESTS.md b/docs/RESULT_THEME_TESTS.md index 3963c9ee..d5e1874e 100644 --- a/docs/RESULT_THEME_TESTS.md +++ b/docs/RESULT_THEME_TESTS.md @@ -19,7 +19,7 @@ node tests/E2E/playwright/theme-contrast-check.cjs Set `CHROMIUM_EXECUTABLE` to use an existing Chromium installation and `THEME_TEST_OUTPUT` to choose the artifact directory (default -`/tmp/syslog-theme-contrast`). The test checks both palettes in resting, +`tests/E2E/playwright/artifacts/theme-contrast`). The test checks light, dark, and muted dark palettes in resting, hovered, selected, and selected-hovered rows. It also writes Chromium CSS coverage to `changed-coverage.json`. diff --git a/tests/E2E/playwright/package-lock.json b/tests/E2E/playwright/package-lock.json new file mode 100644 index 00000000..4bc385c3 --- /dev/null +++ b/tests/E2E/playwright/package-lock.json @@ -0,0 +1,62 @@ +{ + "name": "plugin-syslog-e2e", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "plugin-syslog-e2e", + "version": "1.0.0", + "devDependencies": { + "playwright": "1.55.1" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright": { + "version": "1.55.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.1.tgz", + "integrity": "sha512-cJW4Xd/G3v5ovXtJJ52MAOclqeac9S/aGGgRzLabuF8TnIb6xHvMzKIa6JmrRzUkeXJgfL1MhukP0NK6l39h3A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.55.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.55.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.1.tgz", + "integrity": "sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + } + } +} diff --git a/tests/E2E/playwright/theme-contrast-check.cjs b/tests/E2E/playwright/theme-contrast-check.cjs index 89c94404..c6fefae1 100644 --- a/tests/E2E/playwright/theme-contrast-check.cjs +++ b/tests/E2E/playwright/theme-contrast-check.cjs @@ -6,7 +6,7 @@ const { chromium } = require('playwright'); (async () => { const root = resolve(__dirname, '../../..'); - const output = resolve(process.env.THEME_TEST_OUTPUT || '/tmp/syslog-theme-contrast'); + const output = resolve(process.env.THEME_TEST_OUTPUT || resolve(__dirname, 'artifacts/theme-contrast')); mkdirSync(output, { recursive: true }); const browser = await chromium.launch({ executablePath: process.env.CHROMIUM_EXECUTABLE || undefined }); const page = await browser.newPage(); @@ -35,7 +35,7 @@ const { chromium } = require('playwright'); await page.screenshot({ path: resolve(output, `syslog-${mode}.png`), fullPage: true }); } writeFileSync(resolve(output, 'changed-coverage.json'), JSON.stringify({ css: await page.coverage.stopCSSCoverage(), js: [] })); - console.log('PASS: light/dark result links and severity buttons in four row states'); + console.log('PASS: three palettes of result links and severity buttons in four row states'); } finally { await browser.close(); }