From 0752fa3028db35849466aac9b0b6b4256ca428f2 Mon Sep 17 00:00:00 2001 From: EugeniyKiyashko Date: Thu, 22 Oct 2026 11:40:09 +0400 Subject: [PATCH 1/7] Visual tests: wait for faded scrollbars and async templates before screenshots - Gantt show resources/dependencies: the task-area thumb is rendered visible and fades out 1.5 s after the initial scroll; wait until it is hidden. - Scroll position before hide (useNative, simulated scrollbars): the thumbs hide 500 ms after the scroll and fade for 1.5 s; wait until they are hidden. The fluent-next etalons were shot with the thumbs, now they match fluent. - T1152498: isReady() ignores pending async cell templates; wait until the fixed cells are rendered and no virtual row is in the viewport. --- e2e/testcafe-devextreme/helpers/domUtils.ts | 10 ++++++++++ .../tests/common/gantt/common.ts | 10 +++++++--- .../tests/dataGrid/common/scrolling.ts | 10 +++++++++- ...mScrollbar=true (fluent-next.blue.light).png | Bin 332 -> 299 bytes ...mScrollbar=true (fluent-next.blue.light).png | Bin 333 -> 299 bytes .../tests/navigation/scrollable/visibility.ts | 6 +++++- 6 files changed, 31 insertions(+), 5 deletions(-) diff --git a/e2e/testcafe-devextreme/helpers/domUtils.ts b/e2e/testcafe-devextreme/helpers/domUtils.ts index af65b0fe4d56..d505ac3fb765 100644 --- a/e2e/testcafe-devextreme/helpers/domUtils.ts +++ b/e2e/testcafe-devextreme/helpers/domUtils.ts @@ -157,3 +157,13 @@ export const hasHorizontalScroll = ClientFunction((selector) => { return element.scrollWidth > element.clientWidth; }); + +export const hasVisibleScrollbarThumb = ClientFunction((selector: string) => Array + .from(document.querySelectorAll(`${selector} .dx-scrollable-scroll`)) + .some((thumb) => { + const content = thumb.querySelector('.dx-scrollable-scroll-content'); + + // a hoverable scrollbar keeps the thumb opaque and fades the thumb content out + return window.getComputedStyle(thumb).opacity !== '0' + && (!content || window.getComputedStyle(content).backgroundColor !== 'rgba(0, 0, 0, 0)'); + })); diff --git a/e2e/testcafe-devextreme/tests/common/gantt/common.ts b/e2e/testcafe-devextreme/tests/common/gantt/common.ts index 0a30228449e8..bffba6d08521 100644 --- a/e2e/testcafe-devextreme/tests/common/gantt/common.ts +++ b/e2e/testcafe-devextreme/tests/common/gantt/common.ts @@ -4,7 +4,7 @@ import { Selector } from 'testcafe'; import { testScreenshot } from '../../../helpers/themeUtils'; import url from '../../../helpers/getPageUrl'; import { createWidget } from '../../../helpers/createWidget'; -import { appendElementTo } from '../../../helpers/domUtils'; +import { appendElementTo, hasVisibleScrollbarThumb } from '../../../helpers/domUtils'; import { Themes } from '../../../helpers/themes'; const TOOLBAR_ITEM_BUTTON = '.dx-button'; @@ -122,7 +122,9 @@ test.meta({ themes: [Themes.materialBlue] })('Gantt - show resources button shou const { takeScreenshot, compareResults } = createScreenshotsComparer(t); await t - .click(Selector(TOOLBAR_ITEM_BUTTON)); + .click(Selector(TOOLBAR_ITEM_BUTTON)) + .expect(hasVisibleScrollbarThumb('#container')) + .notOk(); await testScreenshot(t, takeScreenshot, 'Gantt show resourced.png', { element: '#container' }); await t .expect(compareResults.isValid()) @@ -143,7 +145,9 @@ test('Gantt - show dependencies button should not have focus state (T1264485)', const { takeScreenshot, compareResults } = createScreenshotsComparer(t); await t - .click(Selector(TOOLBAR_ITEM_BUTTON)); + .click(Selector(TOOLBAR_ITEM_BUTTON)) + .expect(hasVisibleScrollbarThumb('#container')) + .notOk(); await testScreenshot(t, takeScreenshot, 'Gantt show dependencies.png', { element: '#container' }); await t .expect(compareResults.isValid()) diff --git a/e2e/testcafe-devextreme/tests/dataGrid/common/scrolling.ts b/e2e/testcafe-devextreme/tests/dataGrid/common/scrolling.ts index 2fdc489ba382..0b32227eb994 100644 --- a/e2e/testcafe-devextreme/tests/dataGrid/common/scrolling.ts +++ b/e2e/testcafe-devextreme/tests/dataGrid/common/scrolling.ts @@ -2063,7 +2063,15 @@ test('Last group should not disappear after collapsing another subgroup with vir // act await dataGrid.scrollTo(t, { top: 200 }); await dataGrid.scrollTo(t, { top: 600 }); - await t.expect(dataGrid.isReady()).ok(); + await t + .expect(dataGrid.isReady()) + .ok() + .expect(dataGrid.isVirtualRowIntersectViewport()) + .notOk() + .expect(dataGrid.getFixedDataCell(12, 0).element.textContent) + .eql('12') + .expect(dataGrid.getFixedDataCell(19, 0).element.textContent) + .eql('19'); // assert await testScreenshot(t, takeScreenshot, `grid-${scrollingMode}-scrolling-T1152498.png`, { element: '#container' }); diff --git a/e2e/testcafe-devextreme/tests/navigation/scrollable/etalons/Scroll position before hide, useNative=true,rtl=false,useSimScrollbar=true (fluent-next.blue.light).png b/e2e/testcafe-devextreme/tests/navigation/scrollable/etalons/Scroll position before hide, useNative=true,rtl=false,useSimScrollbar=true (fluent-next.blue.light).png index b3a746b5a9acd2a0780391421ff77e413f9128ed..97434201690064c1f65feff42b853e6e3d5d5ba1 100644 GIT binary patch literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^DImLk?!0^X?elMU6Soj-kndCA`PZ%C%38F7 z>_1n0{(Eq>;8beCX7hR1x$nQIssFGR;-PmdKI;Vst060Km AdH?_b diff --git a/e2e/testcafe-devextreme/tests/navigation/scrollable/etalons/Scroll position before hide, useNative=true,rtl=true,useSimScrollbar=true (fluent-next.blue.light).png b/e2e/testcafe-devextreme/tests/navigation/scrollable/etalons/Scroll position before hide, useNative=true,rtl=true,useSimScrollbar=true (fluent-next.blue.light).png index 6a69970ad5a36f68d7b161bda6273dc691ab4271..97434201690064c1f65feff42b853e6e3d5d5ba1 100644 GIT binary patch delta 70 zcmX@hw3=yxp*+JAPZ!6Kw+9^=8w?nZY)C4K;Y@>=d#Wzp$Py6@EeH$ literal 333 zcmeAS@N?(olHy`uVBq!ia0vp^DImGf~~yGi%>J{`~rPdq&R+Y*6O>@80m|UlsnR zyQI$KlU)k7egxZ!R}oWCVntxWl!$C-{6x4g-b{gQu&X%Q~lo FCIF33ZoB{h diff --git a/e2e/testcafe-devextreme/tests/navigation/scrollable/visibility.ts b/e2e/testcafe-devextreme/tests/navigation/scrollable/visibility.ts index afcf1abcc073..d0b6aa0eccc3 100644 --- a/e2e/testcafe-devextreme/tests/navigation/scrollable/visibility.ts +++ b/e2e/testcafe-devextreme/tests/navigation/scrollable/visibility.ts @@ -4,7 +4,7 @@ import Scrollable from 'devextreme-testcafe-models/scrollView/internal/scrollabl import { testScreenshot } from '../../../helpers/themeUtils'; import url from '../../../helpers/getPageUrl'; import { createWidget } from '../../../helpers/createWidget'; -import { appendElementTo } from '../../../helpers/domUtils'; +import { appendElementTo, hasVisibleScrollbarThumb } from '../../../helpers/domUtils'; fixture.disablePageReloads`Scrollable_visibility_integration` .page(url(__dirname, '../../container.html')); @@ -23,6 +23,10 @@ type ScrollableDirection = 'both' | 'horizontal' | 'vertical'; const expectedScrollOffsetValue = { left: 10, top: 20 }; await t.expect(await scrollable.scrollOffset()).eql(expectedScrollOffsetValue); + if (useNative && useSimulatedScrollbar) { + await t.expect(hasVisibleScrollbarThumb('#scrollable')).notOk(); + } + await testScreenshot(t, takeScreenshot, `Scroll position before hide, useNative=${useNative},rtl=${rtlEnabled},useSimScrollbar=${useSimulatedScrollbar}.png`, { element: Selector('#scrollable') }); await t From 613a889bb3866e02062dc7d24b1646b4d27b4cb0 Mon Sep 17 00:00:00 2001 From: EugeniyKiyashko Date: Thu, 22 Oct 2026 18:37:18 +0400 Subject: [PATCH 2/7] Fluent-next: re-shoot the Gantt etalons without the scrollbar thumb --- ... dependencies (fluent-next.blue.light).png | Bin 40746 -> 40685 bytes ...how resourced (fluent-next.blue.light).png | Bin 30301 -> 30242 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/common/gantt/etalons/Gantt show dependencies (fluent-next.blue.light).png b/e2e/testcafe-devextreme/tests/common/gantt/etalons/Gantt show dependencies (fluent-next.blue.light).png index f9a28ba61f19123105404285aa6d1d2d39b3518a..797461b71e73c5f901e3d9e4a65a048659bce27f 100644 GIT binary patch delta 2350 zcmbVOdpK0<8b2Z1R<4QM(l7|Qq*2)F?8dD%GNaIx#weGVlH1sB$trT$3{As?afZqa zcH~?-N7>D%_Y=45Mp4su$ zAdnMXfS4hAj?SeX^=Mjwkds~v#cQV{9Djrb8QuB$8~9iV zQib56D@=@ou!Wc)%?8SNHh@7uN!<3({TH`D$c^9T=G>7z)vExlfk06p!vQ5DM`xr& zwEFi5GzBOCCmz+CAuCd!Dl0;uX+m7bSUo&(Xm;OSb9tnqnn-F+_ zFuwJIKeVq;Fft8J^sGB<2s9d$F5U)5)t77IBOl9Ikb`Rk){0n6mc#y~d@6r!;q_|d z;-!?QEh2$bt!1srh?T@crMV`VR$O}n-qM@A`IU)AMc>q&WBT?}z2t>BepRi9j$i2_QaTa3>#->Tn51tX7{D+ zGu{Q1S8yI#T=Xdjj$`(#4S`FrS~(>n)ldr+^wX-E&Jw`|6jPtr^^;Z1myA}2m5jC& zIW2uey6b%<)lL^*f{|gllX6ff;(!*$j#Sir`rHM)zlk%Q$^QL0(x;)P=N9R2DxpK8 z^M?nL*@v-A7y9HCj_13G#I6NdAYE73IW>|nu@jqXUQnMiYG@$WOwsi{JN?y4Q&MBk zts-;Gth|zuewR}!dXT{D#<3f5dR&~#^`H_|nnJcaPJ24ue=tA%9OJB>{5@UUD@s@( z5~jL3L*M(GOR~+B^HKA+-se37o*|zT&rC~VQKD$HxOb|i97V^LR&Yl)f_g$QwFfHA zx5nrK6}~VphsQ1I)%+Q8J3A=_d$nRYBEGLLBYbmae`eu010PuNr0Nv0)W!zwQ#hCK zOI;Pmla1FLsBjKzw)DUe%5lc`u=zcD*2ykLD@{yIrbD-S8W~|BEiEkpGm(agefVsd zY9|VHp&q87+t^!;R~o|GEX3izBr(ot8iT0Ri^cO}+_w{13uB7&pBrRyqZ^pVwh{_e zSqRir#{-A;P4)h?kHt4b-v zUDRxD<*_NhNokO}T4W49Z|5GeUDnm_>OPMvD6i^#Qr*J!%vr^!w0Kf$+CX&pg(|204dlyR7}lCE0w4-8x@=&0j<9$UbTFg@uYR zkV?nLv)S@;^(HeFLt~eCh)FhS@(p-&{*9MuZU>q_=oiRQHxXC1zsQfI7%m1e0W&Vv zM^yUgi2wfGYn&(5jyJsS_LAOLc!eDs_ScY7&LicQtXsWQjHu!;RF+s3JATNyepE(0 znlSjLptih~&_WzKT&;Tj+1-SV`h%}328+AhJ$zU<+9y8U(q&<0y2iLIR85eg<4l1+ z5AN)Mj`3R|{gVd?)WN~_iueMY_mPV_Z|)9SmEQIlsi_buQg)q zaizG+&*~Z`w{TO#{`ZJO4?6nYa%K&7bz-$9Ncr@m*Xv#&i3t+>K8F#?=s~Qy256k9 z?dZXg6AZopHc$_)c-1`)S<%a5t|-_aUmelAvGR+eingYyxIO0lHO!G(6O5V2EA-$6 z53Q8(cTkG*wtqYSuf9tLrb=L2^V{nSwJWWoqIIqwp5T15YNyR9+9iMjDOE!tbgmTm zNl2L5s{^fWtj@i!bdv&TVsvyO5whHi(S_C}=d8=@gu}e{53acmAYb>#Y-my6)q@(A zbZLk)ZC%*=@Eq%5^h~JbA6LyXWFTL+l7!`ly|YfNKBp6*mg-k4RiYvxScC!xXPdME zq*`mVdaznL{6{D;hjCD-r!YXCYyR%ouisBa2Y_TaLlvONwjQoWtF^*`Sg83B1W1z^ zIA|Ko(*%#r(*TMXH0cC+NZA246fanfX!LzYG#6(f7e%=fRg2E=fg~KDd4t>PYxZ?= z0jv;&UX^@kX+mTBjvzGtuPefT3pJzqI`{ax#dZoPN0w05w{y&ZJ4mU@$|uDf?fwEi MfGygl6nQE3pQ?j)4*&oF delta 2411 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 diff --git a/e2e/testcafe-devextreme/tests/common/gantt/etalons/Gantt show resourced (fluent-next.blue.light).png b/e2e/testcafe-devextreme/tests/common/gantt/etalons/Gantt show resourced (fluent-next.blue.light).png index 127099e9c7ce2475c587d242aef9588810e3f7e5..a52a6397984367a14fdc2480ef49d089ac45f104 100644 GIT binary patch delta 4980 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 G&ffv>cAPx` delta 5078 zcmbVQ2{>D67oILtIvq;eiK3<2Xi-&5RAX&x&?r$Qlv<{-)lggP!JSq&+i0bBk&Y$7 zAP7ZmRoe)nrIy&0Q2S01_1_!+O#kP9X6B#g&yzg4_k8C&-#O==d*1i^Ql@wHPw&d( z!>S)qLVbVPxHov@?Qe@Uc_Y?U68!?g-n%2l&8t7UJ>r_4K(GRu} z_aq{_`+F!$o$BW`*7QX>N&;e)mIQy?;$6FiEK_K2R-CGR?s;%FuT#h&z~xc?(Ti$W zd|-rp;IlA-;=XkCvNui@!nR6Q9~3k$HhfwZcNws|S_g`4+G!M6qv$2=ncO8xF zc6s-K(As%_%wsBag>rHC&B?qoS6>R`aUCrj=h@cY@**RXhc`5SH@9G-zDOm*o?*2eRh~IRSb57fwS%s69vi zS_yzc`%A4;A1sHbVS`K}E80w$ID zjo4H8exYzU>XY9CzN=AtcanWw@yiWi-uXK~t&(r|f2R7s{CUd_Mykj1LmM7Zos4%# z9yWALh_Vv|-ENZ&eF{f8NHxi~Ay^Km_UF<~;C<914EThUab_{k$dUjifoyB`2$Uk4 zPr^|*#BI&A6UgP~^^FRoH9-?X-uyrmJX5|vdgK>`eq+J6P+C(@fKL!Z2m01U*64s< z!QvU`iVm$EZBY4!)mEwoD`Qjh=T{n|9U2=(p!Kxd0$`S~3vQROn_5;rDXY;ZldNgL$-}w8+ z;V}%%q!$t6veeHtcsrd*@ml#tt4ycp`7swOyW2(jHWL>H1LuL`CJBM&rqt_&cw^H@ zZ};e_O|O^>qVpA8iPA|x@U(eY@F7mFq|SYiG+EtvPl4SuGqd&f2oD((kAYDn4z5is zBXM@cEtg%B)>(Ei`_q|S4RHw-^qexE(jtT=G3#SHrP}!=eKCL#l8>^Fjfr#eL|*)O zSam6yBFINf##7a_yNWvMHb)oYKV)V#;i(=+8JZHz1!u=tC8_OyPe zW}?!hgohj>+O`tjJ~GVVIvaSkYI5v^~#TW z3~JQUy@IY^aOSD+GWdD4AxY)GFiHo0lp;y0nth1B@0+TKoUZfR9e z!JHwd&~3a!y#^}tep~b;>Yj~cN_P5fzB$>{?v<4_s6p0=sP|KM%hldGR)`OTiV#3$ z74R?TM%558xr4sJOV^4U6TGrg1}uKK>u@izU$dud?M{wB0E_W!68`gANm;%w4PE5f zTj*k8H{+I2)EgW3Bs13~u`}Y=?Bi&f_vWF)JGJBZmA3EX%Ej&yy1)`|dsDGlJSk0k zrFLb1!cr4rJa(d}D~{y)uE!EcLuzPao;RpuTQ&NozWmK8F$^&!hrx)<<@t+`MD(1b z-j!*~ZZKw+;%we`Q*b?8*r2q*)ZWMDGskv{l*w&SIzkk-Fw?Mew+XAu6zVYaDDTAv z7FT~*)H;m~%I=J_O&I`ZQubn{Uie&^GOynXN$PTC$is{*D}X)bUGlRAqvb)knEW_; zn6SW6suxrq=#D4%3fjs2IqgZ=hfhHzc*9FewdIw{k#+Tv34IxZj*OT%f3>kYo64rS zIr&9CSBtw?c@;H15tx@MJY@1@ZTYXg_OznvS@%Y~F0YSyp*OFjpMhU_leQDVJ9d`@ zvcxA?g#{c_GPM^BTSwW&Rj+d0m zH{EPDkSd$L`?9-lYO&G^AJ$nf(;z-4d63DxlkfF*L?GotBilK6bwg^cr@=FFt%T

+@ig@X3pSn&{+o``-D<^VqDa0_3=*g zk?__QS=b(mjx&{NNIvfY`Zv@Or)xc8Kle?S^~Sl}J)|Tn>`c)$y??(&j-vSap=uRW z)CpA0i3hJWQBf~w-(3MtpbjmMZcVnyL~c|~DX{ggaKlmev&0@;ih^ed{f`?FYwml0 z($oK@r9&u`9^R$Fh$JST0E>zTC<6@`OKwk+*n8@yL>C|1VnY? zQx*Z049!+{nFc6;K#NoOJIsUwB!36;e~a;;C5{0O$=NZ5cvE(T`)Zr)ez6xcfh(7S z1+EO$5U92Sz$CZWi!(M4e8f@r;XJ{7yAOgF3=;>~A=$CACx1I3T@LB5jpK(!T=RDmpK)bD3diqHdNYKw1LN=lLVBnz1Uc5Lv2jxvln zmRnOLzFAb&0fQ3ScS@B-AF(v2j`cD9!WgCQBv94;^N>z^-w;o#T zu!A|Ac>-kSEk4Z=?(*}(lNb|z^_27Z0Z#GE{G+Ivvz(AzL3c#x91fL94{~&OUvNrI zO;wHd#|O+r0}NohR>Ouh2n2hs&Mj6|7SVxEBpxd^n`$IH?CiDnX;LHbVZ>*|Hg?^5 zDS~=#p^WsXc5`!QSnWK?T4=H>T9)|+c6#63jzTyv!aHg!3pFcVilageW zl|w(TE~-+%s2V2;u{)Wp}vy&wTTHVo_NeA#V;hiPLf^|s2s?!VBW>O z6?O>ni+>o8np}}1+fzNJW-VRLwOpr2Z7e&=CguPIs)M#z=O|{n_?VC-FYl}8#we0H zk39_&GoGjyt}kC!SZk5mRDC%%Ds=KCvu{GSD9*$2$MB8rBe68C)4at@=e4JZ08T*3 z55TJGEUtVAR+=&9lxp3Fe8Jo?g@6|ZYcH-@B>ocgvw}d^nWJHm-7a?($~;0c{hKv` zg`0#h8{?}Hjq4Z>_3Nw3o@gh8kRmoKt$vg(g%p**A6KR*3PlLXbr^d`UYzWRPjKd2 zN-lA~cVI>~Nm=}R>?rzWwP3j$ie8E-WSZBk1v0En4vGP(fgvJl3uYY*AMQbvIl-qD1DuNKoQPEk#4%$ z{=5O)JN~;gL3Ww%f=}!(22|KXJ+4dBit27I35l))cDGNCN!Jl#ON;i^)x+wd?^D?? zTG#8pK1~@fSt0t#pgVmtreBs8cVgLZjw#s4xzVe=!(b#^7w`K=Kl*sz$vK`ctJYww zHWniFk4{g3KR-T6QN{W=kr=l&%h`QOe#RqT>oo^0FW3q}mXsK0n@%PCo>(3N1e}7LZMug3 z@5>;ZIRuFV<*HJDY}2(to|5TNAbm>T=)VCvszBPt-#@_~poKuN+03B=ii!H*&CaeI zy!gKlNWO3<#C_0?NUv$t1_oqD{sGj=4Jp#rw8Jxapy&4x Date: Fri, 23 Oct 2026 14:33:49 +0400 Subject: [PATCH 3/7] Gantt visual tests: hide the scrollbar thumb with CSS instead of waiting for it to fade --- e2e/testcafe-devextreme/tests/common/gantt/common.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/common/gantt/common.ts b/e2e/testcafe-devextreme/tests/common/gantt/common.ts index bffba6d08521..aff538f45377 100644 --- a/e2e/testcafe-devextreme/tests/common/gantt/common.ts +++ b/e2e/testcafe-devextreme/tests/common/gantt/common.ts @@ -4,7 +4,7 @@ import { Selector } from 'testcafe'; import { testScreenshot } from '../../../helpers/themeUtils'; import url from '../../../helpers/getPageUrl'; import { createWidget } from '../../../helpers/createWidget'; -import { appendElementTo, hasVisibleScrollbarThumb } from '../../../helpers/domUtils'; +import { appendElementTo, insertStylesheetRulesToPage } from '../../../helpers/domUtils'; import { Themes } from '../../../helpers/themes'; const TOOLBAR_ITEM_BUTTON = '.dx-button'; @@ -122,14 +122,13 @@ test.meta({ themes: [Themes.materialBlue] })('Gantt - show resources button shou const { takeScreenshot, compareResults } = createScreenshotsComparer(t); await t - .click(Selector(TOOLBAR_ITEM_BUTTON)) - .expect(hasVisibleScrollbarThumb('#container')) - .notOk(); + .click(Selector(TOOLBAR_ITEM_BUTTON)); await testScreenshot(t, takeScreenshot, 'Gantt show resourced.png', { element: '#container' }); await t .expect(compareResults.isValid()) .ok(compareResults.errorMessages()); }).before(async () => { + await insertStylesheetRulesToPage('.dx-gantt .dx-scrollable-scroll { visibility: hidden !important; }'); const id = `${new Guid()}`; await appendElementTo('#container', 'div', id, {}); await createWidget('dxGantt', { @@ -145,14 +144,13 @@ test('Gantt - show dependencies button should not have focus state (T1264485)', const { takeScreenshot, compareResults } = createScreenshotsComparer(t); await t - .click(Selector(TOOLBAR_ITEM_BUTTON)) - .expect(hasVisibleScrollbarThumb('#container')) - .notOk(); + .click(Selector(TOOLBAR_ITEM_BUTTON)); await testScreenshot(t, takeScreenshot, 'Gantt show dependencies.png', { element: '#container' }); await t .expect(compareResults.isValid()) .ok(compareResults.errorMessages()); }).before(async () => { + await insertStylesheetRulesToPage('.dx-gantt .dx-scrollable-scroll { visibility: hidden !important; }'); const id = `${new Guid()}`; await appendElementTo('#container', 'div', id, {}); await createWidget('dxGantt', { From 96e8e15111b678a6c630d6eb32d78d9d17faa076 Mon Sep 17 00:00:00 2001 From: EugeniyKiyashko Date: Mon, 26 Oct 2026 12:54:29 +0400 Subject: [PATCH 4/7] Gantt visual tests: keep the hidden scrollbar thumb rule in one const --- e2e/testcafe-devextreme/tests/common/gantt/common.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/common/gantt/common.ts b/e2e/testcafe-devextreme/tests/common/gantt/common.ts index aff538f45377..475d90d296cc 100644 --- a/e2e/testcafe-devextreme/tests/common/gantt/common.ts +++ b/e2e/testcafe-devextreme/tests/common/gantt/common.ts @@ -8,6 +8,7 @@ import { appendElementTo, insertStylesheetRulesToPage } from '../../../helpers/d import { Themes } from '../../../helpers/themes'; const TOOLBAR_ITEM_BUTTON = '.dx-button'; +const HIDDEN_SCROLLBAR_THUMB_STYLE = '.dx-gantt .dx-scrollable-scroll { visibility: hidden !important; }'; fixture`Gantt` .page(url(__dirname, '../../container-extended.html')); @@ -128,7 +129,7 @@ test.meta({ themes: [Themes.materialBlue] })('Gantt - show resources button shou .expect(compareResults.isValid()) .ok(compareResults.errorMessages()); }).before(async () => { - await insertStylesheetRulesToPage('.dx-gantt .dx-scrollable-scroll { visibility: hidden !important; }'); + await insertStylesheetRulesToPage(HIDDEN_SCROLLBAR_THUMB_STYLE); const id = `${new Guid()}`; await appendElementTo('#container', 'div', id, {}); await createWidget('dxGantt', { @@ -150,7 +151,7 @@ test('Gantt - show dependencies button should not have focus state (T1264485)', .expect(compareResults.isValid()) .ok(compareResults.errorMessages()); }).before(async () => { - await insertStylesheetRulesToPage('.dx-gantt .dx-scrollable-scroll { visibility: hidden !important; }'); + await insertStylesheetRulesToPage(HIDDEN_SCROLLBAR_THUMB_STYLE); const id = `${new Guid()}`; await appendElementTo('#container', 'div', id, {}); await createWidget('dxGantt', { From c080bcd2c1b1e129293a2a96981d45d54bc2c213 Mon Sep 17 00:00:00 2001 From: EugeniyKiyashko Date: Mon, 26 Oct 2026 13:17:00 +0400 Subject: [PATCH 5/7] Scrollable visual tests: pin the simulated scrollbar thumbs of the native strategy --- e2e/testcafe-devextreme/helpers/domUtils.ts | 10 ---------- ...mScrollbar=true (fluent-next.blue.light).png | Bin 299 -> 332 bytes ...mScrollbar=true (fluent-next.blue.light).png | Bin 299 -> 333 bytes .../tests/navigation/scrollable/visibility.ts | 12 +++++++----- 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/e2e/testcafe-devextreme/helpers/domUtils.ts b/e2e/testcafe-devextreme/helpers/domUtils.ts index d505ac3fb765..af65b0fe4d56 100644 --- a/e2e/testcafe-devextreme/helpers/domUtils.ts +++ b/e2e/testcafe-devextreme/helpers/domUtils.ts @@ -157,13 +157,3 @@ export const hasHorizontalScroll = ClientFunction((selector) => { return element.scrollWidth > element.clientWidth; }); - -export const hasVisibleScrollbarThumb = ClientFunction((selector: string) => Array - .from(document.querySelectorAll(`${selector} .dx-scrollable-scroll`)) - .some((thumb) => { - const content = thumb.querySelector('.dx-scrollable-scroll-content'); - - // a hoverable scrollbar keeps the thumb opaque and fades the thumb content out - return window.getComputedStyle(thumb).opacity !== '0' - && (!content || window.getComputedStyle(content).backgroundColor !== 'rgba(0, 0, 0, 0)'); - })); diff --git a/e2e/testcafe-devextreme/tests/navigation/scrollable/etalons/Scroll position before hide, useNative=true,rtl=false,useSimScrollbar=true (fluent-next.blue.light).png b/e2e/testcafe-devextreme/tests/navigation/scrollable/etalons/Scroll position before hide, useNative=true,rtl=false,useSimScrollbar=true (fluent-next.blue.light).png index 97434201690064c1f65feff42b853e6e3d5d5ba1..b3a746b5a9acd2a0780391421ff77e413f9128ed 100644 GIT binary patch literal 332 zcmeAS@N?(olHy`uVBq!ia0vp^DImLk?!0^X?elMU6Soj-kndCA`PZ%C%38F7 z>_1n0{(Eq>;8beCX7hR1x$nQIssFGR;-PmdKI;Vst060Km AdH?_b literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^DImGf~~yGi%>J{`~rPdq&R+Y*6O>@80m|UlsnR zyQI$KlU)k7egxZ!R}oWCVntxWl!$C-{6x4g-b{gQu&X%Q~lo FCIF33ZoB{h delta 70 zcmX@hw3=yxp*+JAPZ!6Kw+9^=8w?nZY)C4K;Y@>=d#Wzp$Py6@EeH$ diff --git a/e2e/testcafe-devextreme/tests/navigation/scrollable/visibility.ts b/e2e/testcafe-devextreme/tests/navigation/scrollable/visibility.ts index d0b6aa0eccc3..b827058719bf 100644 --- a/e2e/testcafe-devextreme/tests/navigation/scrollable/visibility.ts +++ b/e2e/testcafe-devextreme/tests/navigation/scrollable/visibility.ts @@ -4,12 +4,14 @@ import Scrollable from 'devextreme-testcafe-models/scrollView/internal/scrollabl import { testScreenshot } from '../../../helpers/themeUtils'; import url from '../../../helpers/getPageUrl'; import { createWidget } from '../../../helpers/createWidget'; -import { appendElementTo, hasVisibleScrollbarThumb } from '../../../helpers/domUtils'; +import { appendElementTo, insertStylesheetRulesToPage } from '../../../helpers/domUtils'; fixture.disablePageReloads`Scrollable_visibility_integration` .page(url(__dirname, '../../container.html')); type ScrollableDirection = 'both' | 'horizontal' | 'vertical'; +const PINNED_SCROLLBAR_THUMB_STYLE = '#scrollable .dx-scrollable-scroll { opacity: 1 !important; transition: none !important; }'; + (['both'] as ScrollableDirection[]).forEach((direction) => { [false, true].forEach((useNative) => { [false, true].forEach((rtlEnabled) => { @@ -23,10 +25,6 @@ type ScrollableDirection = 'both' | 'horizontal' | 'vertical'; const expectedScrollOffsetValue = { left: 10, top: 20 }; await t.expect(await scrollable.scrollOffset()).eql(expectedScrollOffsetValue); - if (useNative && useSimulatedScrollbar) { - await t.expect(hasVisibleScrollbarThumb('#scrollable')).notOk(); - } - await testScreenshot(t, takeScreenshot, `Scroll position before hide, useNative=${useNative},rtl=${rtlEnabled},useSimScrollbar=${useSimulatedScrollbar}.png`, { element: Selector('#scrollable') }); await t @@ -46,6 +44,10 @@ type ScrollableDirection = 'both' | 'horizontal' | 'vertical'; .expect(compareResults.isValid()) .ok(compareResults.errorMessages()); }).before(async () => { + if (useNative && useSimulatedScrollbar) { + await insertStylesheetRulesToPage(PINNED_SCROLLBAR_THUMB_STYLE); + } + await appendElementTo('#container', 'div', 'scrollable'); await appendElementTo('#scrollable', 'div', 'content', { From 4651d6a3158c26295eb011d1a0c7d317f3d9d1b7 Mon Sep 17 00:00:00 2001 From: EugeniyKiyashko Date: Mon, 26 Oct 2026 14:21:30 +0400 Subject: [PATCH 6/7] Scrollable visual tests: re-shoot the pinned-thumb etalons from CI --- ...mScrollbar=true (fluent-next.blue.light).png | Bin 299 -> 332 bytes ...mScrollbar=true (fluent-next.blue.light).png | Bin 299 -> 333 bytes ...useSimScrollbar=true (fluent.blue.light).png | Bin 299 -> 328 bytes ...useSimScrollbar=true (fluent.blue.light).png | Bin 299 -> 330 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/navigation/scrollable/etalons/Scroll position after show, useNative=true,rtl=false,useSimScrollbar=true (fluent-next.blue.light).png b/e2e/testcafe-devextreme/tests/navigation/scrollable/etalons/Scroll position after show, useNative=true,rtl=false,useSimScrollbar=true (fluent-next.blue.light).png index 97434201690064c1f65feff42b853e6e3d5d5ba1..bbefce70a5578e031f6c66563682f7a6990c94d7 100644 GIT binary patch literal 332 zcmeAS@N?(olHy`uVBq!ia0vp^DIm>nipC8-20e;oc?_gHrB_2zQZMMgmQiM#zBcVkbrvpO(*7(8A5T-G@y GGywqpc5!(C delta 70 zcmX@hw3=yxp*+JAPZ!6Kw+9^=8w?nZY)C4K;Y@>=d#Wzp$Py6@EeH$ diff --git a/e2e/testcafe-devextreme/tests/navigation/scrollable/etalons/Scroll position before hide, useNative=true,rtl=false,useSimScrollbar=true (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/navigation/scrollable/etalons/Scroll position before hide, useNative=true,rtl=false,useSimScrollbar=true (fluent.blue.light).png index 97434201690064c1f65feff42b853e6e3d5d5ba1..0b8dc30b378c06450cb36cfd2018c7bcbdac3e4d 100644 GIT binary patch literal 328 zcmeAS@N?(olHy`uVBq!ia0vp^DIm8hyVZp literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^DIm`9L=I_{rT;fs#P@ll598x4@5tm!{F)a=d#Wzp$Pyb CvXB-4 literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^DIm Date: Mon, 26 Oct 2026 15:06:25 +0400 Subject: [PATCH 7/7] Scrollable visual tests: re-shoot the fluent after-show etalons with pinned thumbs --- ...useSimScrollbar=true (fluent.blue.light).png | Bin 299 -> 328 bytes ...useSimScrollbar=true (fluent.blue.light).png | Bin 299 -> 330 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/e2e/testcafe-devextreme/tests/navigation/scrollable/etalons/Scroll position after show, useNative=true,rtl=false,useSimScrollbar=true (fluent.blue.light).png b/e2e/testcafe-devextreme/tests/navigation/scrollable/etalons/Scroll position after show, useNative=true,rtl=false,useSimScrollbar=true (fluent.blue.light).png index 97434201690064c1f65feff42b853e6e3d5d5ba1..0b8dc30b378c06450cb36cfd2018c7bcbdac3e4d 100644 GIT binary patch literal 328 zcmeAS@N?(olHy`uVBq!ia0vp^DIm8hyVZp literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^DIm`9L=I_{rT;fs#P@ll598x4@5tm!{F)a=d#Wzp$Pyb CvXB-4 literal 299 zcmeAS@N?(olHy`uVBq!ia0vp^DIm