From ebb1f39daf8eedc9d2d9656a626f56ec76270d77 Mon Sep 17 00:00:00 2001 From: Roman Date: Sun, 13 Sep 2026 20:54:25 +0100 Subject: [PATCH] test: add solid-virtual tests --- .../solid-virtual/tests/filter-items.test.ts | 51 +++++++++++++++++++ .../solid-virtual/tests/reactivity.test.ts | 44 ++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 packages/solid-virtual/tests/filter-items.test.ts create mode 100644 packages/solid-virtual/tests/reactivity.test.ts diff --git a/packages/solid-virtual/tests/filter-items.test.ts b/packages/solid-virtual/tests/filter-items.test.ts new file mode 100644 index 000000000..2712c5f31 --- /dev/null +++ b/packages/solid-virtual/tests/filter-items.test.ts @@ -0,0 +1,51 @@ +import { expect, test } from 'vitest' +import { createRoot, createSignal } from 'solid-js' + +import { createVirtualizer } from '../src/index' + +test('virtual items correctly index into filtered data after reactive shrink', () => { + createRoot((dispose) => { + const all = ['apple', 'banana', 'cherry', 'date', 'fig', 'grape'] + const [query, setQuery] = createSignal('') + + const filtered = () => { + const q = query() + if (!q) return all + return all.filter(s => s.includes(q)) + } + + const virtualizer = createVirtualizer({ + get count() { + return filtered().length + }, + getScrollElement: () => null, + estimateSize: () => 60, + initialRect: { width: 800, height: 600 }, + }) + + expect(virtualizer.getVirtualItems().length).toBe(6) + expect(virtualizer.getVirtualItems().map(v => filtered()[v.index])) + .toEqual(['apple', 'banana', 'cherry', 'date', 'fig', 'grape']) + + setQuery('e') + + expect(virtualizer.getVirtualItems().length).toBe(4) + const items = virtualizer.getVirtualItems() + expect(items.map(v => filtered()[v.index])) + .toEqual(['apple', 'cherry', 'date', 'grape']) + + setQuery('zz') + + expect(virtualizer.getVirtualItems().length).toBe(0) + expect(virtualizer.getVirtualItems().map(v => filtered()[v.index])) + .toEqual([]) + + setQuery('cherry') + + expect(virtualizer.getVirtualItems().length).toBe(1) + expect(virtualizer.getVirtualItems().map(v => filtered()[v.index])) + .toEqual(['cherry']) + + dispose() + }) +}) diff --git a/packages/solid-virtual/tests/reactivity.test.ts b/packages/solid-virtual/tests/reactivity.test.ts new file mode 100644 index 000000000..d202e5afb --- /dev/null +++ b/packages/solid-virtual/tests/reactivity.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from 'vitest' +import { createRoot, createSignal } from 'solid-js' +import { unwrap } from 'solid-js/store' +import { createVirtualizer } from '../src/index' + +describe('reactivity: unaffected slots keep stable references', () => { + it('does not recreate VirtualItem objects for slots whose data did not change', () => { + createRoot((dispose) => { + const data = Array.from({ length: 50 }, (_, i) => `item-${i}`) + const [filtered, setFiltered] = createSignal(data) + + const virtualizer = createVirtualizer({ + get count() { + return filtered().length + }, + getScrollElement: () => document.createElement('div'), + estimateSize: () => 30, + overscan: 0, + }) + + const before = virtualizer.getVirtualItems() + const beforeRefs = before.map((item) => unwrap(item)) + + // Shrink the array; leaves the first visible rows' index/start/end/size untouched. + setFiltered(data.slice(0, 40)) + + const after = virtualizer.getVirtualItems() + const afterRefs = after.map((item) => unwrap(item)) + + const unaffectedCount = Math.min(beforeRefs.length, afterRefs.length) + for (let i = 0; i < unaffectedCount; i++) { + if ( + beforeRefs[i].start === afterRefs[i].start && + beforeRefs[i].end === afterRefs[i].end && + beforeRefs[i].index === afterRefs[i].index + ) { + expect(afterRefs[i]).toBe(beforeRefs[i]) + } + } + + dispose() + }) + }) +})