/** * Embed Store Tests * * Covers the Yjs-backed embed store: * - get/set/has/remove * - rename (atomic via transact) * - patch (search-replace) * - keys / getAll * - observe / observeKey */ import { describe, it, expect } from "vitest"; import * as Y from "yjs"; import { createEmbedStore } from "../src/editor/embeds/embed-store"; function makeStore() { const ydoc = new Y.Doc(); return { ydoc, store: createEmbedStore(ydoc) }; } describe("embed-store — basic CRUD", () => { it("get returns empty string when key missing", () => { const { store } = makeStore(); expect(store.get("nope.html")).toBe(""); expect(store.has("nope.html")).toBe(false); }); it("set + get round-trips html content", () => { const { store } = makeStore(); store.set("chart.html", "
hi
"); expect(store.get("chart.html")).toBe("
hi
"); expect(store.has("chart.html")).toBe(true); }); it("remove deletes the entry", () => { const { store } = makeStore(); store.set("chart.html", "
"); store.remove("chart.html"); expect(store.has("chart.html")).toBe(false); expect(store.get("chart.html")).toBe(""); }); it("keys returns all inserted keys", () => { const { store } = makeStore(); store.set("a.html", "A"); store.set("b.html", "B"); expect(store.keys().sort()).toEqual(["a.html", "b.html"]); }); it("getAll returns a plain object snapshot", () => { const { store } = makeStore(); store.set("a.html", "A"); store.set("b.html", "B"); expect(store.getAll()).toEqual({ "a.html": "A", "b.html": "B" }); }); }); describe("embed-store — rename", () => { it("moves content to the new key and deletes the old one", () => { const { store } = makeStore(); store.set("old.html", ""); store.rename("old.html", "new.html"); expect(store.has("old.html")).toBe(false); expect(store.get("new.html")).toBe(""); }); it("is a no-op when the source key does not exist", () => { const { store } = makeStore(); store.rename("missing.html", "target.html"); expect(store.has("target.html")).toBe(false); }); }); describe("embed-store — patch", () => { it("returns false and does nothing when search text is missing", () => { const { store } = makeStore(); store.set("chart.html", "
hello
"); expect(store.patch("chart.html", "notfound", "bye")).toBe(false); expect(store.get("chart.html")).toBe("
hello
"); }); it("replaces first occurrence when search text is present", () => { const { store } = makeStore(); store.set("chart.html", "
hello hello
"); const ok = store.patch("chart.html", "hello", "bye"); expect(ok).toBe(true); expect(store.get("chart.html")).toBe("
bye hello
"); }); it("returns false for missing keys", () => { const { store } = makeStore(); expect(store.patch("missing.html", "a", "b")).toBe(false); }); }); describe("embed-store — observe", () => { it("observe() fires on any change", () => { const { store } = makeStore(); let count = 0; const unsub = store.observe(() => { count++; }); store.set("a.html", "A"); store.set("b.html", "B"); expect(count).toBe(2); unsub(); store.set("c.html", "C"); expect(count).toBe(2); }); it("observeKey() only fires for the matching key", () => { const { store } = makeStore(); const aCalls: string[] = []; const unsub = store.observeKey("a.html", (html) => { aCalls.push(html); }); store.set("a.html", "A1"); store.set("b.html", "B1"); store.set("a.html", "A2"); expect(aCalls).toEqual(["A1", "A2"]); unsub(); }); }); describe("embed-store — collaboration", () => { it("concurrent sets on different keys merge correctly", () => { const docA = new Y.Doc(); const docB = new Y.Doc(); const storeA = createEmbedStore(docA); const storeB = createEmbedStore(docB); storeA.set("a.html", ""); storeB.set("b.html", ""); Y.applyUpdate(docA, Y.encodeStateAsUpdate(docB)); Y.applyUpdate(docB, Y.encodeStateAsUpdate(docA)); expect(storeA.getAll()).toEqual({ "a.html": "", "b.html": "" }); expect(storeB.getAll()).toEqual({ "a.html": "", "b.html": "" }); }); });