mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-10 02:39:56 +00:00
128 lines
2.9 KiB
TypeScript
128 lines
2.9 KiB
TypeScript
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()
|
|
})
|
|
})
|