layout-scroll.test.ts 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. import { describe, expect, test } from "bun:test"
  2. import { createRoot } from "solid-js"
  3. import { createStore } from "solid-js/store"
  4. import { makePersisted, type SyncStorage } from "@solid-primitives/storage"
  5. import { createScrollPersistence } from "./layout-scroll"
  6. describe("createScrollPersistence", () => {
  7. test("debounces persisted scroll writes", async () => {
  8. const key = "layout-scroll.test"
  9. const data = new Map<string, string>()
  10. const writes: string[] = []
  11. const stats = { flushes: 0 }
  12. const storage = {
  13. getItem: (k: string) => data.get(k) ?? null,
  14. setItem: (k: string, v: string) => {
  15. data.set(k, v)
  16. if (k === key) writes.push(v)
  17. },
  18. removeItem: (k: string) => {
  19. data.delete(k)
  20. },
  21. } as SyncStorage
  22. await new Promise<void>((resolve, reject) => {
  23. createRoot((dispose) => {
  24. const [raw, setRaw] = createStore({
  25. sessionView: {} as Record<string, { scroll: Record<string, { x: number; y: number }> }>,
  26. })
  27. const [store, setStore] = makePersisted([raw, setRaw], { name: key, storage })
  28. const scroll = createScrollPersistence({
  29. debounceMs: 30,
  30. getSnapshot: (sessionKey) => store.sessionView[sessionKey]?.scroll,
  31. onFlush: (sessionKey, next) => {
  32. stats.flushes += 1
  33. const current = store.sessionView[sessionKey]
  34. if (!current) {
  35. setStore("sessionView", sessionKey, { scroll: next })
  36. return
  37. }
  38. setStore("sessionView", sessionKey, "scroll", (prev) => ({ ...(prev ?? {}), ...next }))
  39. },
  40. })
  41. const run = async () => {
  42. await new Promise((r) => setTimeout(r, 0))
  43. writes.length = 0
  44. for (const i of Array.from({ length: 100 }, (_, n) => n)) {
  45. scroll.setScroll("session", "review", { x: 0, y: i })
  46. }
  47. await new Promise((r) => setTimeout(r, 120))
  48. expect(stats.flushes).toBeGreaterThanOrEqual(1)
  49. expect(writes.length).toBeGreaterThanOrEqual(1)
  50. expect(writes.length).toBeLessThanOrEqual(2)
  51. }
  52. void run()
  53. .then(resolve)
  54. .catch(reject)
  55. .finally(() => {
  56. scroll.dispose()
  57. dispose()
  58. })
  59. })
  60. })
  61. })
  62. })