Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions packages/cli/src/ui/components/shared/VirtualizedList.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,82 @@ describe('<VirtualizedList />', () => {
expect(listRef!.getScrollIndex()).toBe(19);
});

it('collapses bottom-stuck viewport to measured rows when estimates are too tall', async () => {
const tallEstimate = () => 4;

const { lastFrame, rerender } = render(
<VirtualizedList<Item>
data={makeItems(20)}
renderItem={renderItem}
estimatedItemHeight={tallEstimate}
keyExtractor={keyExtractor}
initialScrollIndex={SCROLL_TO_ITEM_END}
containerHeight={20}
width={40}
showScrollbar={false}
/>,
);

for (let i = 0; i < 2; i++) {
rerender(
<VirtualizedList<Item>
data={makeItems(20)}
renderItem={renderItem}
estimatedItemHeight={tallEstimate}
keyExtractor={keyExtractor}
initialScrollIndex={SCROLL_TO_ITEM_END}
containerHeight={20}
width={40}
showScrollbar={false}
/>,
);
await act(async () => {});
}

const frame = lastFrame() ?? '';
expect(frame).toContain('item-0');
expect(frame).toContain('item-19');
expect(frame.endsWith('item-19')).toBe(true);
Comment on lines +128 to +130

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] The regression test passes without the PR's source changes — verified by running the full test suite against the pre-PR VirtualizedList.tsx. With 20 items, tallEstimate = () => 4, containerHeight = 20, and 10 rerenders, the natural iterative measurement convergence completes fully (~5–6 iterations), so all items render regardless of whether the renderStartIndex/measuredRenderedHeight collapse mechanism exists.

Concrete cost: a future regression in the collapse logic (e.g., an off-by-one in i - 1, or a wrong early-return condition) would ship undetected because this test provides no regression safety net for the code paths it claims to exercise.

Consider reducing the rerender count to 2–3 and asserting frame.endsWith('item-19') at that point (which requires the collapse mechanism since convergence hasn't finished), or adding a separate test with 50+ items where the virtualization window never renders all items and the collapse path is the only way to pass.

— qwen3.7-max via Qwen Code /review

});

it('collapses a short bottom-stuck list below the container height', async () => {
const { lastFrame, rerender } = render(
<VirtualizedList<Item>
data={makeItems(5)}
renderItem={renderItem}
estimatedItemHeight={estimatedItemHeight}
keyExtractor={keyExtractor}
initialScrollIndex={SCROLL_TO_ITEM_END}
containerHeight={20}
width={40}
showScrollbar={false}
/>,
);

rerender(
<VirtualizedList<Item>
data={makeItems(5)}
renderItem={renderItem}
estimatedItemHeight={estimatedItemHeight}
keyExtractor={keyExtractor}
initialScrollIndex={SCROLL_TO_ITEM_END}
containerHeight={20}
width={40}
showScrollbar={false}
/>,
);
await act(async () => {});

const frame = lastFrame() ?? '';
expect(frame.split('\n')).toEqual([
'item-0',
'item-1',
'item-2',
'item-3',
'item-4',
]);
});

it('targetScrollIndex anchors to that index on first usable render', () => {
type RefShape = VirtualizedListRef<Item>;
let listRef: RefShape | null = null;
Expand Down
Loading