fix(#4346): suppress footer jitter during virtual scroll measurement re-renders

This commit is contained in:
Rod Boev authored and nesquena-hermes committed 2026-06-23 05:48:36 +00:00
1 parent 0953e7ca8e
commit 028fb61f3a
3 files changed
+54 -1

No files matched your search

@@ -0,0 +1,50 @@
"""Static source assertions for issue #4346 virtual-scroll footer jitter fix."""
import pathlib
import re
ROOT = pathlib.Path(__file__).parent.parent
CSS = (ROOT / 'static' / 'style.css').read_text(encoding='utf-8')
JS = (ROOT / 'static' / 'ui.js').read_text(encoding='utf-8')
def test_css_vscroll_measuring_guard():
"""style.css suppresses opacity transitions on .msg-foot and .msg-actions
while .vscroll-measuring is present on the scroll container."""
assert 'vscroll-measuring' in CSS
assert re.search(
r'\.vscroll-measuring\s+\.msg-foot.*transition\s*:\s*none\s*!important',
CSS, re.DOTALL
), "missing transition:none !important for .vscroll-measuring .msg-foot"
assert re.search(
r'\.vscroll-measuring\s+\.msg-actions.*transition\s*:\s*none\s*!important',
CSS, re.DOTALL
), "missing transition:none !important for .vscroll-measuring .msg-actions"
def test_js_compensate_adds_vscroll_measuring():
"""_compensateScrollForMeasurementDelta adds and removes the vscroll-measuring
class around the render callback."""
fn_match = re.search(
r'function _compensateScrollForMeasurementDelta\(renderFn\)\{(.+?)^(?=function )',
JS, re.DOTALL | re.MULTILINE
)
assert fn_match, "_compensateScrollForMeasurementDelta not found"
body = fn_match.group(1)
assert "classList.add('vscroll-measuring')" in body
assert "classList.remove('vscroll-measuring')" in body
def test_js_try_finally_guards_class_removal():
"""The classList.remove is in a finally block so the class is always cleared."""
fn_match = re.search(
r'function _compensateScrollForMeasurementDelta\(renderFn\)\{(.+?)^(?=function )',
JS, re.DOTALL | re.MULTILINE
)
assert fn_match
body = fn_match.group(1)
add_pos = body.find("classList.add('vscroll-measuring')")
try_pos = body.find('try{', add_pos) if add_pos != -1 else -1
finally_pos = body.find('finally{', try_pos) if try_pos != -1 else -1
remove_pos = body.find("classList.remove('vscroll-measuring')", finally_pos) if finally_pos != -1 else -1
assert remove_pos != -1, "classList.remove must be inside a finally block after classList.add"