feature/test-coverage #27
@@ -0,0 +1,550 @@
|
|||||||
|
/** @vitest-environment jsdom */
|
||||||
|
import {
|
||||||
|
afterEach,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
it,
|
||||||
|
vi,
|
||||||
|
} from 'vitest';
|
||||||
|
import { createVirtualizer } from './createVirtualizer.svelte';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NOTE: Svelte 5 Runes Testing Limitations
|
||||||
|
*
|
||||||
|
* The createVirtualizer helper uses Svelte 5 runes ($state, $derived, $derived.by)
|
||||||
|
* which require a full Svelte runtime environment to work correctly. In unit tests
|
||||||
|
* with jsdom, these runes are stubbed and don't provide actual reactivity.
|
||||||
|
*
|
||||||
|
* These tests focus on:
|
||||||
|
* 1. API surface verification (methods, getters exist)
|
||||||
|
* 2. Initial state calculation
|
||||||
|
* 3. DOM integration (event listeners are attached)
|
||||||
|
* 4. Edge case handling
|
||||||
|
*
|
||||||
|
* For full reactivity testing, use browser-based tests with @vitest/browser-playwright
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Mock ResizeObserver globally since it's not available in jsdom
|
||||||
|
class MockResizeObserver {
|
||||||
|
observe = vi.fn();
|
||||||
|
unobserve = vi.fn();
|
||||||
|
disconnect = vi.fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.ResizeObserver = MockResizeObserver as any;
|
||||||
|
|
||||||
|
// Mock requestAnimationFrame
|
||||||
|
globalThis.requestAnimationFrame =
|
||||||
|
((cb: FrameRequestCallback) =>
|
||||||
|
setTimeout(() => cb(performance.now()), 16) as unknown) as typeof requestAnimationFrame;
|
||||||
|
globalThis.cancelAnimationFrame = vi.fn();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to create test data array
|
||||||
|
*/
|
||||||
|
function createTestData(count: number): string[] {
|
||||||
|
return Array.from({ length: count }, (_, i) => `Item ${i}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to create a mock scrollable container element
|
||||||
|
*/
|
||||||
|
function createMockContainer(height = 500, scrollTop = 0): any {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
Object.defineProperty(container, 'offsetHeight', {
|
||||||
|
value: height,
|
||||||
|
configurable: true,
|
||||||
|
writable: true,
|
||||||
|
});
|
||||||
|
Object.defineProperty(container, 'scrollTop', {
|
||||||
|
value: scrollTop,
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
// Add scrollTo method for testing
|
||||||
|
container.scrollTo = vi.fn();
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('createVirtualizer - Basic API and State', () => {
|
||||||
|
describe('Basic Initialization and API Surface', () => {
|
||||||
|
it('should initialize and return expected API surface', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 0,
|
||||||
|
data: [],
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Verify API surface exists
|
||||||
|
expect(virtualizer).toHaveProperty('items');
|
||||||
|
expect(virtualizer).toHaveProperty('totalSize');
|
||||||
|
expect(virtualizer).toHaveProperty('scrollOffset');
|
||||||
|
expect(virtualizer).toHaveProperty('containerHeight');
|
||||||
|
expect(virtualizer).toHaveProperty('container');
|
||||||
|
expect(virtualizer).toHaveProperty('measureElement');
|
||||||
|
expect(virtualizer).toHaveProperty('scrollToIndex');
|
||||||
|
expect(virtualizer).toHaveProperty('scrollToOffset');
|
||||||
|
|
||||||
|
// Verify initial values
|
||||||
|
expect(virtualizer.items).toEqual([]);
|
||||||
|
expect(virtualizer.totalSize).toBe(0);
|
||||||
|
expect(virtualizer.scrollOffset).toBe(0);
|
||||||
|
expect(virtualizer.containerHeight).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate correct totalSize for uniform item sizes', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 10,
|
||||||
|
data: createTestData(10),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 10 items * 50px each = 500px total
|
||||||
|
expect(virtualizer.totalSize).toBe(500);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should calculate correct totalSize for varying item sizes', () => {
|
||||||
|
const sizes = [50, 100, 150, 75, 125]; // Sum = 500
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 5,
|
||||||
|
data: createTestData(5),
|
||||||
|
estimateSize: (i: number) => sizes[i],
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(virtualizer.totalSize).toBe(500);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle empty list (count = 0)', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 0,
|
||||||
|
data: [],
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(virtualizer.totalSize).toBe(0);
|
||||||
|
expect(virtualizer.items).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle very large lists', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100000,
|
||||||
|
data: createTestData(100000),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(virtualizer.totalSize).toBe(5000000); // 100000 * 50
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle zero estimated size', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 10,
|
||||||
|
data: createTestData(10),
|
||||||
|
estimateSize: () => 0,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(virtualizer.totalSize).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Container Action', () => {
|
||||||
|
let cleanupHandlers: (() => void)[] = [];
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanupHandlers.forEach(cleanup => cleanup());
|
||||||
|
cleanupHandlers = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should attach container action and set up listeners', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const container = createMockContainer(500, 0);
|
||||||
|
const addEventListenerSpy = vi.spyOn(container, 'addEventListener');
|
||||||
|
|
||||||
|
const cleanup = virtualizer.container(container);
|
||||||
|
cleanupHandlers.push(() => cleanup?.destroy?.());
|
||||||
|
|
||||||
|
// Verify scroll listener was attached
|
||||||
|
expect(addEventListenerSpy).toHaveBeenCalledWith(
|
||||||
|
'scroll',
|
||||||
|
expect.any(Function),
|
||||||
|
{ passive: true },
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update containerHeight when container is attached', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const container = createMockContainer(500, 0);
|
||||||
|
const cleanup = virtualizer.container(container);
|
||||||
|
cleanupHandlers.push(() => cleanup?.destroy?.());
|
||||||
|
|
||||||
|
expect(virtualizer.containerHeight).toBe(500);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clean up listeners on destroy', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const container = createMockContainer(500, 0);
|
||||||
|
const removeEventListenerSpy = vi.spyOn(container, 'removeEventListener');
|
||||||
|
|
||||||
|
const cleanup = virtualizer.container(container);
|
||||||
|
cleanup?.destroy?.();
|
||||||
|
|
||||||
|
expect(removeEventListenerSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support window scrolling mode', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
useWindowScroll: true,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const container = createMockContainer(500, 0);
|
||||||
|
const windowAddSpy = vi.spyOn(window, 'addEventListener');
|
||||||
|
|
||||||
|
const cleanup = virtualizer.container(container);
|
||||||
|
cleanupHandlers.push(() => cleanup?.destroy?.());
|
||||||
|
|
||||||
|
// Should attach to window scroll
|
||||||
|
expect(windowAddSpy).toHaveBeenCalledWith('scroll', expect.any(Function), expect.any(Object));
|
||||||
|
expect(windowAddSpy).toHaveBeenCalledWith('resize', expect.any(Function));
|
||||||
|
|
||||||
|
windowAddSpy.mockRestore();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scrollToIndex Method', () => {
|
||||||
|
let cleanupHandlers: (() => void)[] = [];
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanupHandlers.forEach(cleanup => cleanup());
|
||||||
|
cleanupHandlers = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should have scrollToIndex method that does not throw without container', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Should not throw when container is not attached
|
||||||
|
expect(() => virtualizer.scrollToIndex(50)).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should scroll to specific index with container attached', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const container = createMockContainer(500, 0);
|
||||||
|
const scrollToSpy = vi.spyOn(container, 'scrollTo');
|
||||||
|
|
||||||
|
const cleanup = virtualizer.container(container);
|
||||||
|
cleanupHandlers.push(() => cleanup?.destroy?.());
|
||||||
|
|
||||||
|
virtualizer.scrollToIndex(10);
|
||||||
|
|
||||||
|
expect(scrollToSpy).toHaveBeenCalledWith({
|
||||||
|
top: expect.any(Number),
|
||||||
|
behavior: 'smooth',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle center alignment', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const container = createMockContainer(500, 0);
|
||||||
|
const scrollToSpy = vi.spyOn(container, 'scrollTo');
|
||||||
|
|
||||||
|
const cleanup = virtualizer.container(container);
|
||||||
|
cleanupHandlers.push(() => cleanup?.destroy?.());
|
||||||
|
|
||||||
|
virtualizer.scrollToIndex(10, 'center');
|
||||||
|
|
||||||
|
expect(scrollToSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle end alignment', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const container = createMockContainer(500, 0);
|
||||||
|
const scrollToSpy = vi.spyOn(container, 'scrollTo');
|
||||||
|
|
||||||
|
const cleanup = virtualizer.container(container);
|
||||||
|
cleanupHandlers.push(() => cleanup?.destroy?.());
|
||||||
|
|
||||||
|
virtualizer.scrollToIndex(10, 'end');
|
||||||
|
|
||||||
|
expect(scrollToSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not scroll for out of bounds indices', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const container = createMockContainer(500, 0);
|
||||||
|
const scrollToSpy = vi.spyOn(container, 'scrollTo');
|
||||||
|
|
||||||
|
const cleanup = virtualizer.container(container);
|
||||||
|
cleanupHandlers.push(() => cleanup?.destroy?.());
|
||||||
|
|
||||||
|
// Negative index
|
||||||
|
virtualizer.scrollToIndex(-1);
|
||||||
|
|
||||||
|
// Index >= count
|
||||||
|
virtualizer.scrollToIndex(100);
|
||||||
|
|
||||||
|
// Should not have been called
|
||||||
|
expect(scrollToSpy).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('scrollToOffset Method', () => {
|
||||||
|
let cleanupHandlers: (() => void)[] = [];
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanupHandlers.forEach(cleanup => cleanup());
|
||||||
|
cleanupHandlers = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should scroll to specific pixel offset', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const container = createMockContainer(500, 0);
|
||||||
|
const scrollToSpy = vi.spyOn(container, 'scrollTo');
|
||||||
|
|
||||||
|
const cleanup = virtualizer.container(container);
|
||||||
|
cleanupHandlers.push(() => cleanup?.destroy?.());
|
||||||
|
|
||||||
|
virtualizer.scrollToOffset(1000);
|
||||||
|
|
||||||
|
expect(scrollToSpy).toHaveBeenCalledWith({ top: 1000, behavior: 'auto' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support smooth behavior', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const container = createMockContainer(500, 0);
|
||||||
|
const scrollToSpy = vi.spyOn(container, 'scrollTo');
|
||||||
|
|
||||||
|
const cleanup = virtualizer.container(container);
|
||||||
|
cleanupHandlers.push(() => cleanup?.destroy?.());
|
||||||
|
|
||||||
|
virtualizer.scrollToOffset(1000, 'smooth');
|
||||||
|
|
||||||
|
expect(scrollToSpy).toHaveBeenCalledWith({ top: 1000, behavior: 'smooth' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('measureElement Action', () => {
|
||||||
|
it('should attach measureElement action to DOM element', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 10,
|
||||||
|
data: createTestData(10),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const element = document.createElement('div');
|
||||||
|
element.dataset.index = '0';
|
||||||
|
|
||||||
|
// Should not throw when attaching measureElement
|
||||||
|
expect(() => {
|
||||||
|
const cleanup = virtualizer.measureElement(element);
|
||||||
|
cleanup?.destroy?.();
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clean up observer on destroy', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 10,
|
||||||
|
data: createTestData(10),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const element = document.createElement('div');
|
||||||
|
element.dataset.index = '0';
|
||||||
|
|
||||||
|
const cleanup = virtualizer.measureElement(element);
|
||||||
|
|
||||||
|
// Should not throw when destroying
|
||||||
|
expect(() => cleanup?.destroy?.()).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle multiple elements being measured', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 10,
|
||||||
|
data: createTestData(10),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const elements = Array.from({ length: 5 }, (_, i) => {
|
||||||
|
const el = document.createElement('div');
|
||||||
|
el.dataset.index = String(i);
|
||||||
|
return el;
|
||||||
|
});
|
||||||
|
|
||||||
|
const cleanups = elements.map(el => virtualizer.measureElement(el));
|
||||||
|
|
||||||
|
// Should not throw when measuring multiple elements
|
||||||
|
expect(() => {
|
||||||
|
cleanups.forEach(cleanup => cleanup?.destroy?.());
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Options Handling', () => {
|
||||||
|
it('should use default overscan of 5', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Options with default overscan should work
|
||||||
|
expect(virtualizer).toHaveProperty('items');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use custom overscan value', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
overscan: 10,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(virtualizer).toHaveProperty('items');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use index as default key when getItemKey is not provided', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 10,
|
||||||
|
data: createTestData(10),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(virtualizer).toHaveProperty('items');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use custom getItemKey when provided', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 10,
|
||||||
|
data: createTestData(10),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
getItemKey: (i: number) => `custom-key-${i}`,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(virtualizer).toHaveProperty('items');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use custom scrollMargin when provided', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
scrollMargin: 100,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(virtualizer).toHaveProperty('items');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Edge Cases', () => {
|
||||||
|
it('should handle single item list', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 1,
|
||||||
|
data: ['Item 0'],
|
||||||
|
estimateSize: () => 100,
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(virtualizer.totalSize).toBe(100);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle items larger than viewport', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 5,
|
||||||
|
data: createTestData(5),
|
||||||
|
estimateSize: () => 200, // Each item is 200px
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Total size should still be calculated correctly
|
||||||
|
expect(virtualizer.totalSize).toBe(1000); // 5 * 200
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle overscan larger than viewport', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 10,
|
||||||
|
data: createTestData(10),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
overscan: 100, // Very large overscan
|
||||||
|
}));
|
||||||
|
|
||||||
|
expect(virtualizer).toHaveProperty('items');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle negative estimated size (graceful degradation)', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 10,
|
||||||
|
data: createTestData(10),
|
||||||
|
estimateSize: () => -10,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Should calculate total size (may be negative, but shouldn't crash)
|
||||||
|
expect(virtualizer.totalSize).toBeLessThanOrEqual(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Virtual Item Structure', () => {
|
||||||
|
it('should return items with correct structure when container is attached', () => {
|
||||||
|
const virtualizer = createVirtualizer(() => ({
|
||||||
|
count: 100,
|
||||||
|
data: createTestData(100),
|
||||||
|
estimateSize: () => 50,
|
||||||
|
}));
|
||||||
|
|
||||||
|
const container = createMockContainer(500, 0);
|
||||||
|
const cleanup = virtualizer.container(container);
|
||||||
|
|
||||||
|
// Items may be empty in test environment due to reactivity limitations
|
||||||
|
// but we verify the structure exists
|
||||||
|
expect(Array.isArray(virtualizer.items)).toBe(true);
|
||||||
|
|
||||||
|
cleanup?.destroy?.();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user