import { describe, expect, test } from "vitest"; import { act, renderHook } from "@testing-library/react"; import { type ReactNode, useState } from "react"; import { defaultWSValue, WSContext, type WSContextValue, } from "../../../contexts/ws/WSContext.ts"; import type { Dump } from "../../../model.ts"; import { useDumpListSync } from "../../../hooks/dump/useDumpListSync.ts"; import { makeDump } from "../../fixtures.ts"; /** A WS context whose value a test can push new events into. */ function liveWS() { let push: (patch: Partial) => void = () => {}; const base = defaultWSValue; function Wrapper({ children }: { children: ReactNode }) { const [value, setValue] = useState(base); push = (patch) => setValue((v) => ({ ...v, ...patch })); return {children}; } return { Wrapper, emit: (patch: Partial) => act(() => push(patch)), }; } function useList(initial: T[]) { const [items, setItems] = useState(initial); return { items, setItems }; } const ids = (xs: { id: string }[]) => xs.map((x) => x.id); describe("useDumpListSync", () => { function setup( initial: Dump[], opts?: Parameters[1], ) { const ws = liveWS(); const hook = renderHook(() => { const list = useList(initial); useDumpListSync(list.setItems, opts); return list; }, { wrapper: ws.Wrapper }); return { ...ws, hook }; } test("drops deleted dumps", () => { const { hook, emit } = setup([ makeDump({ id: "a" }), makeDump({ id: "b" }), ]); emit({ deletedDumpIds: new Set(["a"]) }); expect(ids(hook.result.current.items)).toEqual(["b"]); }); test("updates a dump in place", () => { const { hook, emit } = setup([makeDump({ id: "a", title: "old" })]); emit({ lastDumpEvent: makeDump({ id: "a", title: "new" }) }); expect(hook.result.current.items[0].title).toBe("new"); }); test("a dump gone private leaves others' lists but not the owner's", () => { const a = makeDump({ id: "a" }); const viewer = setup([a]); viewer.emit({ lastDumpEvent: { ...a, isPrivate: true } }); expect(viewer.hook.result.current.items).toEqual([]); const owner = setup([a], { isOwner: true }); owner.emit({ lastDumpEvent: { ...a, isPrivate: true } }); expect(owner.hook.result.current.items[0].isPrivate).toBe(true); }); test("re-inserts only the owner's dumps, and only when asked", () => { const mine = makeDump({ id: "m", userId: "me" }); const theirs = makeDump({ id: "t", userId: "them" }); const none = setup([]); none.emit({ lastDumpEvent: mine }); expect(none.hook.result.current.items).toEqual([]); const owned = setup([], { ownerId: "me" }); owned.emit({ lastDumpEvent: theirs }); expect(owned.hook.result.current.items).toEqual([]); owned.emit({ lastDumpEvent: mine }); expect(ids(owned.hook.result.current.items)).toEqual(["m"]); const skip = setup([], { ownerId: "me", skipReinsert: true }); skip.emit({ lastDumpEvent: mine }); expect(skip.hook.result.current.items).toEqual([]); const privateOne = setup([], { ownerId: "me" }); privateOne.emit({ lastDumpEvent: { ...mine, isPrivate: true } }); expect(privateOne.hook.result.current.items).toEqual([]); }); test("comment events move the count, never below zero; edits don't", () => { const { hook, emit } = setup([makeDump({ id: "a", commentCount: 0 })]); emit({ lastCommentEvent: { type: "created", dumpId: "a" } }); expect(hook.result.current.items[0].commentCount).toBe(1); emit({ lastCommentEvent: { type: "updated", dumpId: "a" } }); expect(hook.result.current.items[0].commentCount).toBe(1); emit({ lastCommentEvent: { type: "deleted", dumpId: "a" } }); emit({ lastCommentEvent: { type: "deleted", dumpId: "a" } }); expect(hook.result.current.items[0].commentCount).toBe(0); emit({ lastCommentEvent: { type: "created", dumpId: "zzz" } }); expect(hook.result.current.items).toHaveLength(1); }); });