88 lines
3.8 KiB
Python
88 lines
3.8 KiB
Python
|
|
"""Regression test for #1669 follow-up — sidebar scroll jump fix.
|
||
|
|
|
||
|
|
The original PR #1669 added DOM virtualization to renderSessionListFromCache,
|
||
|
|
which:
|
||
|
|
|
||
|
|
1. Attached an unconditional scroll listener to the session list
|
||
|
|
2. The scroll listener triggers renderSessionListFromCache() on every rAF
|
||
|
|
3. The render rebuilds the list DOM via list.innerHTML='' / appendChild loop
|
||
|
|
4. After the rebuild, scrollTop was only restored when virtualWindow.virtualized
|
||
|
|
was true (i.e. total > 80 rows)
|
||
|
|
5. For lists ≤ 80 rows, the scrollTop reset to 0 on every scroll event,
|
||
|
|
producing a "scroll keeps jumping back" feel.
|
||
|
|
|
||
|
|
This test pins:
|
||
|
|
- The non-virtualized branch always restores scrollTop after a rebuild
|
||
|
|
- The scroll handler short-circuits when total <= threshold (prevents the
|
||
|
|
rebuild churn entirely on small lists)
|
||
|
|
"""
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
SESSIONS_JS = Path(__file__).parent.parent / "static" / "sessions.js"
|
||
|
|
|
||
|
|
|
||
|
|
def _read_source():
|
||
|
|
return SESSIONS_JS.read_text()
|
||
|
|
|
||
|
|
|
||
|
|
def test_render_restores_scroll_top_for_non_virtualized_lists():
|
||
|
|
"""The bug: virtualWindow.virtualized=false skipped the scrollTop restore.
|
||
|
|
|
||
|
|
The fix: restore scrollTop whenever listScrollTopBeforeRender > 0,
|
||
|
|
regardless of virtualized flag. Otherwise small lists (≤80 rows) reset
|
||
|
|
to scrollTop=0 on every render.
|
||
|
|
"""
|
||
|
|
src = _read_source()
|
||
|
|
# The new branch must include listScrollTopBeforeRender>0 as the guard
|
||
|
|
# rather than virtualWindow.virtualized
|
||
|
|
assert "}else if(listScrollTopBeforeRender>0){" in src, (
|
||
|
|
"Expected the scrollTop-restore guard to use listScrollTopBeforeRender>0, "
|
||
|
|
"not virtualWindow.virtualized — without this fix, small lists drop "
|
||
|
|
"scrollTop to 0 on every scroll event."
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def test_scroll_handler_short_circuits_below_virtualization_threshold():
|
||
|
|
"""The bug: the rAF re-render fired on every scroll event regardless of
|
||
|
|
whether virtualization was actually needed. For ≤80-row lists this caused
|
||
|
|
full DOM rebuild on every scroll tick.
|
||
|
|
|
||
|
|
The fix: _scheduleSessionVirtualizedRender skips the rebuild when
|
||
|
|
total <= SESSION_VIRTUAL_THRESHOLD_ROWS — there's no virtual window to
|
||
|
|
recompute on small lists, and the rebuild was wasteful (and bug-prone).
|
||
|
|
"""
|
||
|
|
src = _read_source()
|
||
|
|
# Locate the function body
|
||
|
|
start = src.find("function _scheduleSessionVirtualizedRender()")
|
||
|
|
end = src.find("function _ensureSessionVirtualScrollHandler", start)
|
||
|
|
body = src[start:end]
|
||
|
|
# The fix introduces an early-return when total <= SESSION_VIRTUAL_THRESHOLD_ROWS
|
||
|
|
assert "SESSION_VIRTUAL_THRESHOLD_ROWS" in body, (
|
||
|
|
"Expected _scheduleSessionVirtualizedRender to read the threshold; "
|
||
|
|
"without this guard, the rAF re-render fires on every scroll event "
|
||
|
|
"even when there's nothing to virtualize."
|
||
|
|
)
|
||
|
|
assert "total<=SESSION_VIRTUAL_THRESHOLD_ROWS" in body or "total <= SESSION_VIRTUAL_THRESHOLD_ROWS" in body, (
|
||
|
|
"Expected explicit total<=THRESHOLD comparison to short-circuit the re-render."
|
||
|
|
)
|
||
|
|
# The early return must be BEFORE the rAF schedule (else it's dead code)
|
||
|
|
early_return_idx = body.find("return")
|
||
|
|
raf_idx = body.find("requestAnimationFrame")
|
||
|
|
assert early_return_idx > 0 and early_return_idx < raf_idx, (
|
||
|
|
"The total<=THRESHOLD short-circuit must return BEFORE scheduling the rAF."
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def test_virtualization_still_active_for_large_lists():
|
||
|
|
"""Regression: ensure the threshold + virtualWindow logic is still in place
|
||
|
|
for large lists. The fix must not break the original virtualization path.
|
||
|
|
"""
|
||
|
|
src = _read_source()
|
||
|
|
assert "SESSION_VIRTUAL_THRESHOLD_ROWS = 80" in src, (
|
||
|
|
"Threshold constant must remain at 80 rows."
|
||
|
|
)
|
||
|
|
# _sessionVirtualWindow function still defined
|
||
|
|
assert "function _sessionVirtualWindow" in src
|
||
|
|
# virtualWindow.virtualized branch still drives spacer rendering
|
||
|
|
assert "virtualWindow.virtualized" in src
|