1import { describe, expect, it } from "vite-plus/test";
2
3import { resolveWorkGroupScrollAnchor } from "./scrollAnchor.js";
4
5describe("tool-group scroll anchors", () => {
6 const data = Array.from({ length: 18 }, (_, index) => ({ id: `call-${index}` }));
7 const state = { data, positionAtIndex: (index: number) => index * 33 };
8
9 it("returns to the top after a fast flick even when the cached start is five rows behind", () => {
10 const snapshot = { ...state, scroll: 0, start: 5 };
11 expect(resolveWorkGroupScrollAnchor(snapshot)).toEqual({
12 rowId: "call-0",
13 offsetWithinRow: 0,
14 scrollOffset: 0,
15 });
16 });
17
18 it.each([
19 { scroll: 302, start: 0, rowId: "call-9", offsetWithinRow: 5 },
20 { scroll: 40, start: 12, rowId: "call-1", offsetWithinRow: 7 },
21 { scroll: 165, start: 4, rowId: "call-5", offsetWithinRow: 0 },
22 ])("uses the measured position at $scroll rather than cached row $start", (example) => {
23 expect(resolveWorkGroupScrollAnchor({ ...state, ...example })).toEqual({
24 rowId: example.rowId,
25 offsetWithinRow: example.offsetWithinRow,
26 scrollOffset: example.scroll,
27 });
28 });
29
30 it("preserves an offset within expanded output", () => {
31 const positions = [0, 33, 600];
32 expect(
33 resolveWorkGroupScrollAnchor({
34 data: [{ id: "first" }, { id: "output" }, { id: "last" }],
35 scroll: 153,
36 positionAtIndex: (index) => positions[index],
37 }),
38 ).toEqual({ rowId: "output", offsetWithinRow: 120, scrollOffset: 153 });
39 });
40
41 it("does not preserve overscroll before the first row", () => {
42 expect(resolveWorkGroupScrollAnchor({ ...state, scroll: -20 })).toEqual({
43 rowId: "call-0",
44 offsetWithinRow: 0,
45 scrollOffset: 0,
46 });
47 });
48
49 it("skips incomplete layout snapshots instead of saving an invalid anchor", () => {
50 expect(resolveWorkGroupScrollAnchor({ ...state, data: [], scroll: 0 })).toBeUndefined();
51 expect(resolveWorkGroupScrollAnchor({ ...state, scroll: Number.NaN })).toBeUndefined();
52 expect(
53 resolveWorkGroupScrollAnchor({ ...state, scroll: 165, positionAtIndex: () => undefined }),
54 ).toBeUndefined();
55 expect(
56 resolveWorkGroupScrollAnchor({
57 ...state,
58 scroll: 165,
59 positionAtIndex: (index) => (index === 0 ? 0 : Number.NaN),
60 }),
61 ).toBeUndefined();
62 });
63});