// @vitest-environment jsdom // // Each write reaches the lists that are never refetched once the server has it — and not before: // a refused write leaves them as they were. import { afterEach, describe, expect, it, vi } from "vitest"; import { QueryClient } from "@tanstack/react-query"; import { api } from "../../lib/api"; import { qk } from "../../lib/queryKeys"; import { addPlaylistItem, movePlaylistItem, removePlaylistItem, resetVideoState, writeVideoState, writeWatchLater, } from "./videoWrites"; afterEach(() => vi.restoreAllMocks()); // The marks' values, without when each was confirmed. const marks = (qc: QueryClient) => { const m = qc.getQueryData>>(qk.videoMarks()); return m ? Object.fromEntries( Object.entries(m).map(([id, f]) => [ id, Object.fromEntries(Object.entries(f).map(([k, x]) => [k, x.value])), ]), ) : undefined; }; const WATCH_LATER = { id: 1, kind: "watch_later" as const }; const MINE = { id: 2, kind: "user" as const }; describe("videoWrites", () => { it("marks a state written", async () => { vi.spyOn(api, "setState").mockResolvedValue({ ok: true } as never); const qc = new QueryClient(); await writeVideoState(qc, "X", "hidden"); expect(marks(qc)).toEqual({ X: { status: "hidden" } }); }); it("marks a reset as new, from the start", async () => { vi.spyOn(api, "clearState").mockResolvedValue({ ok: true } as never); const qc = new QueryClient(); await resetVideoState(qc, "X"); expect(marks(qc)).toEqual({ X: { status: "new", position_seconds: 0 } }); }); it("marks a Watch-later save and its removal", async () => { vi.spyOn(api, "watchLaterAdd").mockResolvedValue({ ok: true } as never); vi.spyOn(api, "watchLaterRemove").mockResolvedValue({ ok: true } as never); const qc = new QueryClient(); await writeWatchLater(qc, "X", true); expect(marks(qc)).toEqual({ X: { saved: true } }); await writeWatchLater(qc, "X", false); expect(marks(qc)).toEqual({ X: { saved: false } }); }); it("marks an item of the Watch later list as the bookmark it is", async () => { vi.spyOn(api, "addToPlaylist").mockResolvedValue({ ok: true } as never); vi.spyOn(api, "removeFromPlaylist").mockResolvedValue({ ok: true } as never); const qc = new QueryClient(); await addPlaylistItem(qc, WATCH_LATER, "X"); expect(marks(qc)).toEqual({ X: { saved: true } }); await removePlaylistItem(qc, WATCH_LATER, "X"); expect(marks(qc)).toEqual({ X: { saved: false } }); }); it("marks nothing for an item of any other list", async () => { vi.spyOn(api, "addToPlaylist").mockResolvedValue({ ok: true } as never); vi.spyOn(api, "removeFromPlaylist").mockResolvedValue({ ok: true } as never); const qc = new QueryClient(); await addPlaylistItem(qc, MINE, "X"); await removePlaylistItem(qc, { id: 3, kind: undefined }, "X"); expect(marks(qc)).toBeUndefined(); }); it("marks a move into and out of the Watch later list", async () => { vi.spyOn(api, "moveToPlaylist").mockResolvedValue({ ok: true } as never); const qc = new QueryClient(); await movePlaylistItem(qc, MINE, "X", WATCH_LATER); expect(marks(qc)).toEqual({ X: { saved: true } }); await movePlaylistItem(qc, WATCH_LATER, "X", MINE); expect(marks(qc)).toEqual({ X: { saved: false } }); }); it("marks nothing the server refused", async () => { vi.spyOn(api, "setState").mockRejectedValue(new Error("offline")); const qc = new QueryClient(); await expect(writeVideoState(qc, "X", "hidden")).rejects.toThrow("offline"); expect(marks(qc)).toBeUndefined(); }); });