summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/context/layout-scroll.test.ts
blob: b7962c411cd6f49384f3f166424171df95c80a99 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import { describe, expect, test } from "bun:test"
import { createRoot } from "solid-js"
import { createStore } from "solid-js/store"
import { makePersisted, type SyncStorage } from "@solid-primitives/storage"
import { createScrollPersistence } from "./layout-scroll"

describe("createScrollPersistence", () => {
  test("debounces persisted scroll writes", async () => {
    const key = "layout-scroll.test"
    const data = new Map<string, string>()
    const writes: string[] = []
    const stats = { flushes: 0 }

    const storage = {
      getItem: (k: string) => data.get(k) ?? null,
      setItem: (k: string, v: string) => {
        data.set(k, v)
        if (k === key) writes.push(v)
      },
      removeItem: (k: string) => {
        data.delete(k)
      },
    } as SyncStorage

    await new Promise<void>((resolve, reject) => {
      createRoot((dispose) => {
        const [raw, setRaw] = createStore({
          sessionView: {} as Record<string, { scroll: Record<string, { x: number; y: number }> }>,
        })

        const [store, setStore] = makePersisted([raw, setRaw], { name: key, storage })

        const scroll = createScrollPersistence({
          debounceMs: 30,
          getSnapshot: (sessionKey) => store.sessionView[sessionKey]?.scroll,
          onFlush: (sessionKey, next) => {
            stats.flushes += 1

            const current = store.sessionView[sessionKey]
            if (!current) {
              setStore("sessionView", sessionKey, { scroll: next })
              return
            }
            setStore("sessionView", sessionKey, "scroll", (prev) => ({ ...(prev ?? {}), ...next }))
          },
        })

        const run = async () => {
          await new Promise((r) => setTimeout(r, 0))
          writes.length = 0

          for (const i of Array.from({ length: 100 }, (_, n) => n)) {
            scroll.setScroll("session", "review", { x: 0, y: i })
          }

          await new Promise((r) => setTimeout(r, 120))

          expect(stats.flushes).toBeGreaterThanOrEqual(1)
          expect(writes.length).toBeGreaterThanOrEqual(1)
          expect(writes.length).toBeLessThanOrEqual(2)
        }

        void run()
          .then(resolve)
          .catch(reject)
          .finally(() => {
            scroll.dispose()
            dispose()
          })
      })
    })
  })
})