test(sidebar): restore the clamping model for the scroll fixture

The element is pinned again with a getter and a no-op setter, which is what an
unscrollable list does: the browser clamps the write and the element never moves.
That immovability is the defect's defining property — it is why the virtualizer's
remembered offset can drift away and never resync, since nothing fires a scroll
event to correct it.

Dropping it was my call and it was wrong. The proof that it was inert was correct
about the current assertions and not a licence to remove it: a fixture whose
element CAN move is the one least likely to catch the half of this defect that is
still open, where the range start is still chosen from the drifted offset.

The comment now says what the fixture models AND that the assertions do not yet
observe it. The original mistake was claiming the browser-clamping story as the
reason it was needed, rather than as the thing it represents.
This commit is contained in:
Merge Sim
2026-09-07 15:02:49 -07:00
parent 91ecdb26d3
commit f09dd69e76
@@ -49,9 +49,20 @@ const DRIFTED_OFFSET = 70
function createScrollElement(scrollTop: number): HTMLDivElement {
const element = document.createElement('div')
document.body.append(element)
// The scroll-anchor listener writes this once from a layout effect, after the render that
// computes the sticky index, so it cannot reach the assertion.
element.scrollTop = scrollTop
// Models the defect's defining property: the list is too short to scroll, so the browser clamps
// any write and the element never moves. That immovability is precisely why the virtualizer's
// remembered offset can drift away and never resync — nothing fires a scroll event to correct it.
//
// Honest about its reach: the current assertions do not observe this. The scroll-anchor listener
// writes scrollTop once from a layout effect, after the render that computes the sticky index,
// so a plain writable property passes these tests identically. It is kept because a fixture
// whose element CAN move is the one least likely to catch the half of this defect that is still
// open — the range start is still chosen from the drifted offset.
Object.defineProperty(element, 'scrollTop', {
configurable: true,
get: () => scrollTop,
set: () => undefined
})
return element
}