summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/pages/session/scroll-spy.test.ts
diff options
context:
space:
mode:
authorAdam <[email protected]>2026-02-06 09:37:49 -0600
committerGitHub <[email protected]>2026-02-06 09:37:49 -0600
commita4bc883595df9ea0f752079519081bc602408553 (patch)
tree583f21642f431899abe1dfb1f6bd9b2c01dc0206 /packages/app/src/pages/session/scroll-spy.test.ts
parentc07077f96c0019b2e18e0e8e1e0383deda08b3e6 (diff)
downloadopencode-a4bc883595df9ea0f752079519081bc602408553.tar.gz
opencode-a4bc883595df9ea0f752079519081bc602408553.zip
chore: refactoring and tests (#12468)
Diffstat (limited to 'packages/app/src/pages/session/scroll-spy.test.ts')
-rw-r--r--packages/app/src/pages/session/scroll-spy.test.ts127
1 files changed, 127 insertions, 0 deletions
diff --git a/packages/app/src/pages/session/scroll-spy.test.ts b/packages/app/src/pages/session/scroll-spy.test.ts
new file mode 100644
index 000000000..f3e6775cb
--- /dev/null
+++ b/packages/app/src/pages/session/scroll-spy.test.ts
@@ -0,0 +1,127 @@
+import { describe, expect, test } from "bun:test"
+import { createScrollSpy, pickOffsetId, pickVisibleId } from "./scroll-spy"
+
+const rect = (top: number, height = 80): DOMRect =>
+ ({
+ x: 0,
+ y: top,
+ top,
+ left: 0,
+ right: 800,
+ bottom: top + height,
+ width: 800,
+ height,
+ toJSON: () => ({}),
+ }) as DOMRect
+
+const setRect = (el: Element, top: number, height = 80) => {
+ Object.defineProperty(el, "getBoundingClientRect", {
+ configurable: true,
+ value: () => rect(top, height),
+ })
+}
+
+describe("pickVisibleId", () => {
+ test("prefers higher intersection ratio", () => {
+ const id = pickVisibleId(
+ [
+ { id: "a", ratio: 0.2, top: 100 },
+ { id: "b", ratio: 0.8, top: 300 },
+ ],
+ 120,
+ )
+
+ expect(id).toBe("b")
+ })
+
+ test("breaks ratio ties by nearest line", () => {
+ const id = pickVisibleId(
+ [
+ { id: "a", ratio: 0.5, top: 90 },
+ { id: "b", ratio: 0.5, top: 140 },
+ ],
+ 130,
+ )
+
+ expect(id).toBe("b")
+ })
+})
+
+describe("pickOffsetId", () => {
+ test("uses binary search cutoff", () => {
+ const id = pickOffsetId(
+ [
+ { id: "a", top: 0 },
+ { id: "b", top: 200 },
+ { id: "c", top: 400 },
+ ],
+ 350,
+ )
+
+ expect(id).toBe("b")
+ })
+})
+
+describe("createScrollSpy fallback", () => {
+ test("tracks active id from offsets and dirty refresh", () => {
+ const active: string[] = []
+ const root = document.createElement("div") as HTMLDivElement
+ const one = document.createElement("div")
+ const two = document.createElement("div")
+ const three = document.createElement("div")
+
+ root.append(one, two, three)
+ document.body.append(root)
+
+ Object.defineProperty(root, "scrollTop", { configurable: true, writable: true, value: 250 })
+ setRect(root, 0, 800)
+ setRect(one, -250)
+ setRect(two, -50)
+ setRect(three, 150)
+
+ const queue: FrameRequestCallback[] = []
+ const flush = () => {
+ const run = [...queue]
+ queue.length = 0
+ for (const cb of run) cb(0)
+ }
+
+ const spy = createScrollSpy({
+ onActive: (id) => active.push(id),
+ raf: (cb) => (queue.push(cb), queue.length),
+ caf: () => {},
+ IntersectionObserver: undefined,
+ ResizeObserver: undefined,
+ MutationObserver: undefined,
+ })
+
+ spy.setContainer(root)
+ spy.register(one, "a")
+ spy.register(two, "b")
+ spy.register(three, "c")
+ spy.onScroll()
+ flush()
+
+ expect(spy.getActiveId()).toBe("b")
+ expect(active.at(-1)).toBe("b")
+
+ root.scrollTop = 450
+ setRect(one, -450)
+ setRect(two, -250)
+ setRect(three, -50)
+ spy.onScroll()
+ flush()
+ expect(spy.getActiveId()).toBe("c")
+
+ root.scrollTop = 250
+ setRect(one, -250)
+ setRect(two, 250)
+ setRect(three, 150)
+ spy.markDirty()
+ spy.onScroll()
+ flush()
+ expect(spy.getActiveId()).toBe("a")
+
+ spy.destroy()
+ })
+})