Skip to content

Commit 8d4a763

Browse files
committed
Add more tests
1 parent 8132f7a commit 8d4a763

File tree

6 files changed

+198
-26
lines changed

6 files changed

+198
-26
lines changed

lib/components/grid/Grid.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ import type { Align } from "../../types";
1313
import { arePropsEqual } from "../../utils/arePropsEqual";
1414
import type { GridProps } from "./types";
1515

16-
// TODO Handle scrollbar sizes (add width/height if necessary)
17-
1816
export function Grid<CellProps extends object>({
1917
cellComponent: CellComponentProp,
2018
cellProps: cellPropsUnstable,

lib/components/list/List.test.tsx

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { act, render, screen } from "@testing-library/react";
2-
import { createRef, useLayoutEffect, type CSSProperties } from "react";
2+
import { createRef, useLayoutEffect } from "react";
33
import { beforeEach, describe, expect, test, vi } from "vitest";
44
import { EMPTY_OBJECT } from "../../../src/constants";
55
import {
@@ -8,12 +8,10 @@ import {
88
} from "../../utils/test/mockResizeObserver";
99
import { List } from "./List";
1010
import { type ListImperativeAPI, type RowComponentProps } from "./types";
11+
import { useListCallbackRef } from "./useListCallbackRef";
1112

1213
describe("List", () => {
13-
const RowComponent = vi.fn(function Row(props: {
14-
index: number;
15-
style: CSSProperties;
16-
}) {
14+
const RowComponent = vi.fn(function Row(props: RowComponentProps<object>) {
1715
const { index, style } = props;
1816

1917
useLayoutEffect(() => {
@@ -361,11 +359,12 @@ describe("List", () => {
361359
expect(HTMLElement.prototype.scrollTo).not.toHaveBeenCalled();
362360

363361
listRef.current?.scrollToRow({ index: 8 });
362+
364363
expect(HTMLElement.prototype.scrollTo).toHaveBeenCalledTimes(1);
365-
expect(HTMLElement.prototype.scrollTo).not.toHaveBeenLastCalledWith(
366-
8,
367-
"auto"
368-
);
364+
expect(HTMLElement.prototype.scrollTo).toHaveBeenLastCalledWith({
365+
behavior: "auto",
366+
top: 125
367+
});
369368
});
370369
});
371370

@@ -421,7 +420,7 @@ describe("List", () => {
421420
});
422421

423422
describe("edge cases", () => {
424-
test("should restore scroll indicates if rowProps changes", () => {
423+
test("should restore scroll indices if rowProps changes", () => {
425424
const listRef = createRef<ListImperativeAPI>();
426425
const onRowsRendered = vi.fn();
427426

@@ -528,5 +527,30 @@ describe("List", () => {
528527
);
529528
expect(container.textContent).toEqual("AB");
530529
});
530+
531+
test("should not cause a cycle of List callback ref is passed in rowProps", () => {
532+
function RowComponentWithRowProps({
533+
index,
534+
style
535+
}: RowComponentProps<{ listRef: ListImperativeAPI | null }>) {
536+
return <div style={style}>{index}</div>;
537+
}
538+
539+
function Test() {
540+
const [listRef, setListRef] = useListCallbackRef(null);
541+
542+
return (
543+
<List
544+
listRef={setListRef}
545+
rowComponent={RowComponentWithRowProps}
546+
rowCount={10}
547+
rowHeight={25}
548+
rowProps={{ listRef }}
549+
/>
550+
);
551+
}
552+
553+
render(<Test />);
554+
});
531555
});
532556
});

lib/core/createCachedBounds.test.ts

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,69 @@
1-
import { describe, test } from "vitest";
1+
import { describe, expect, test, vi } from "vitest";
2+
import { createCachedBounds } from "./createCachedBounds";
23

34
describe("createCachedBounds", () => {
4-
test("have tests", () => {
5-
// TODO Write tests
5+
test("should lazily measure items before the requested index", () => {
6+
const itemSize = vi.fn((index: number) => 10 + index);
7+
const cachedBounds = createCachedBounds({
8+
itemCount: 10,
9+
itemProps: {},
10+
itemSize
11+
});
12+
13+
expect(itemSize).not.toHaveBeenCalled();
14+
expect(cachedBounds.size).toBe(0);
15+
16+
expect(cachedBounds.get(2)).toEqual({
17+
scrollOffset: 21,
18+
size: 12
19+
});
20+
expect(itemSize).toHaveBeenCalledTimes(3);
21+
expect(cachedBounds.size).toBe(3);
22+
23+
expect(cachedBounds.get(3)).toEqual({
24+
scrollOffset: 33,
25+
size: 13
26+
});
27+
expect(itemSize).toHaveBeenCalledTimes(4);
28+
expect(cachedBounds.size).toBe(4);
29+
});
30+
31+
test("should cached measured sizes", () => {
32+
const itemSize = vi.fn(() => 10);
33+
34+
const cachedBounds = createCachedBounds({
35+
itemCount: 10,
36+
itemProps: {},
37+
itemSize
38+
});
39+
40+
expect(itemSize).not.toHaveBeenCalled();
41+
expect(cachedBounds.size).toBe(0);
42+
43+
cachedBounds.get(9);
44+
45+
expect(itemSize).toHaveBeenCalledTimes(10);
46+
expect(cachedBounds.size).toBe(10);
47+
48+
for (let index = 0; index < 10; index++) {
49+
cachedBounds.get(index);
50+
}
51+
52+
expect(itemSize).toHaveBeenCalledTimes(10);
53+
expect(cachedBounds.size).toBe(10);
54+
});
55+
56+
test("should gracefully handle an empty cache", () => {
57+
const cachedBounds = createCachedBounds({
58+
itemCount: 0,
59+
itemProps: {},
60+
itemSize: 10
61+
});
62+
63+
expect(cachedBounds.size).toBe(0);
64+
65+
expect(() => {
66+
cachedBounds.get(1);
67+
}).toThrow("Invalid index 1");
668
});
769
});

lib/core/getOffsetForIndex.test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ describe("getOffsetForIndex", () => {
3232
function createTestHelper(align: Align) {
3333
return function testHelperAuto(
3434
index: number,
35-
expectedIndex: number,
35+
expectedOffset: number,
3636
containerScrollOffset: number = 0
3737
) {
3838
expect(
@@ -42,7 +42,7 @@ describe("getOffsetForIndex", () => {
4242
index,
4343
containerScrollOffset
4444
})
45-
).toBe(expectedIndex);
45+
).toBe(expectedOffset);
4646
};
4747
}
4848

@@ -112,8 +112,10 @@ describe("getOffsetForIndex", () => {
112112

113113
// Shouldn't scroll if already visible
114114
testHelper(0, 0);
115-
testHelper(2, 0);
116-
testHelper(7, 50, 100);
115+
testHelper(3, 0);
116+
testHelper(3, 30, 30);
117+
testHelper(7, 30, 30);
118+
testHelper(7, 50, 50);
117119
testHelper(9, 50, 100);
118120

119121
// Should center align if not visible

lib/core/getStartStopIndices.test.ts

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ describe("getStartStopIndices", () => {
77
containerScrollOffset,
88
containerSize,
99
itemCount,
10-
itemSize
10+
itemSize,
11+
overscanCount = 0
1112
}: {
1213
containerScrollOffset: number;
1314
containerSize: number;
1415
itemCount: number;
1516
itemSize: number;
17+
overscanCount?: number;
1618
}) {
1719
const cachedBounds = createCachedBounds({
1820
itemCount: itemCount,
@@ -25,7 +27,7 @@ describe("getStartStopIndices", () => {
2527
containerScrollOffset,
2628
containerSize,
2729
itemCount,
28-
overscanCount: 0
30+
overscanCount
2931
});
3032
}
3133

@@ -40,7 +42,7 @@ describe("getStartStopIndices", () => {
4042
).toEqual([0, -1]);
4143
});
4244

43-
test("not enough rows to fill available height", () => {
45+
test("edge case: not enough rows to fill available height", () => {
4446
expect(
4547
getIndices({
4648
containerScrollOffset: 0,
@@ -106,5 +108,53 @@ describe("getStartStopIndices", () => {
106108
).toEqual([8, 9]);
107109
});
108110

109-
// TODO Test overscanCount
111+
describe("with overscan", () => {
112+
test("edge case: not enough rows to fill available height", () => {
113+
expect(
114+
getIndices({
115+
containerScrollOffset: 0,
116+
containerSize: 100,
117+
itemCount: 2,
118+
itemSize: 25,
119+
overscanCount: 2
120+
})
121+
).toEqual([0, 1]);
122+
});
123+
124+
test("edge case: no rows before", () => {
125+
expect(
126+
getIndices({
127+
containerScrollOffset: 0,
128+
containerSize: 100,
129+
itemCount: 100,
130+
itemSize: 25,
131+
overscanCount: 2
132+
})
133+
).toEqual([0, 5]);
134+
});
135+
136+
test("edge case: no rows after", () => {
137+
expect(
138+
getIndices({
139+
containerScrollOffset: 2400,
140+
containerSize: 100,
141+
itemCount: 100,
142+
itemSize: 25,
143+
overscanCount: 2
144+
})
145+
).toEqual([94, 99]);
146+
});
147+
148+
test("rows before and after", () => {
149+
expect(
150+
getIndices({
151+
containerScrollOffset: 100,
152+
containerSize: 100,
153+
itemCount: 100,
154+
itemSize: 25,
155+
overscanCount: 2
156+
})
157+
).toEqual([2, 9]);
158+
});
159+
});
110160
});

lib/core/useCachedBounds.test.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,43 @@
1-
import { describe, test } from "vitest";
1+
import { renderHook } from "@testing-library/react";
2+
import { describe, expect, test } from "vitest";
3+
import { EMPTY_OBJECT } from "../../src/constants";
4+
import { useCachedBounds } from "./useCachedBounds";
25

36
describe("useCachedBounds", () => {
4-
test("have tests", () => {
5-
// TODO Write tests
7+
test("should cache the CachedBounds unless props change", () => {
8+
const { result, rerender } = renderHook(
9+
(props?: Partial<Parameters<typeof useCachedBounds>>[0]) =>
10+
useCachedBounds({
11+
itemCount: 1,
12+
itemProps: EMPTY_OBJECT,
13+
itemSize: 10,
14+
...props
15+
})
16+
);
17+
18+
const cachedBoundsA = result.current;
19+
20+
rerender({
21+
itemCount: 1,
22+
itemProps: EMPTY_OBJECT,
23+
itemSize: 10
24+
});
25+
expect(result.current).toBe(cachedBoundsA);
26+
27+
rerender({
28+
itemCount: 1,
29+
itemProps: EMPTY_OBJECT,
30+
itemSize: 5
31+
});
32+
expect(result.current).not.toBe(cachedBoundsA);
33+
34+
const cachedBoundsB = result.current;
35+
36+
rerender({
37+
itemCount: 1,
38+
itemProps: EMPTY_OBJECT,
39+
itemSize: 5
40+
});
41+
expect(result.current).toBe(cachedBoundsB);
642
});
743
});

0 commit comments

Comments
 (0)