/**
* 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": "" });
});
});