carbon-tokenization / frontend /tests /frontmatter-store.test.ts
tfrere's picture
tfrere HF Staff
feat(frontend): editor refresh (embed studio, comment popover, shiki, top bar, hooks, styles)
76fc93a
Raw
History Blame Contribute Delete
5.65 kB
/**
* Frontmatter Store Tests
*
* Exercises the Yjs-backed frontmatter store:
* - scalar get/set with defaults
* - authors / affiliations / links arrays (order preservation, concurrent-safe)
* - setAll / getAll round-trip
* - observe callback
*
* Uses an in-memory Y.Doc, no network.
*/
import { describe, it, expect, beforeEach } from "vitest";
import * as Y from "yjs";
import { createFrontmatterStore, type FrontmatterStore } from "../src/editor/frontmatter/frontmatter-store";
function makeStore(): { ydoc: Y.Doc; store: FrontmatterStore } {
const ydoc = new Y.Doc();
const store = createFrontmatterStore(ydoc);
return { ydoc, store };
}
describe("frontmatter-store — scalars", () => {
let store: FrontmatterStore;
beforeEach(() => { store = makeStore().store; });
it("returns default values when unset", () => {
expect(store.get("title")).toBe("");
expect(store.get("template")).toBe("article");
expect(store.get("showPdf")).toBe(true);
expect(store.get("tableOfContentsAutoCollapse")).toBe(false);
});
it("persists scalar set/get", () => {
store.set("title", "Hello world");
store.set("doi", "10.1000/xyz");
store.set("showPdf", false);
expect(store.get("title")).toBe("Hello world");
expect(store.get("doi")).toBe("10.1000/xyz");
expect(store.get("showPdf")).toBe(false);
});
it("changing template between 'article' and 'paper' round-trips", () => {
store.set("template", "paper");
expect(store.get("template")).toBe("paper");
store.set("template", "article");
expect(store.get("template")).toBe("article");
});
});
describe("frontmatter-store — arrays", () => {
let store: FrontmatterStore;
beforeEach(() => { store = makeStore().store; });
it("adds authors in insertion order", () => {
store.addAuthor({ name: "Alice", affiliations: [1] });
store.addAuthor({ name: "Bob", affiliations: [1, 2] });
const authors = store.get("authors");
expect(authors.map((a) => a.name)).toEqual(["Alice", "Bob"]);
});
it("updateAuthor mutates the right index only", () => {
store.addAuthor({ name: "Alice", affiliations: [1] });
store.addAuthor({ name: "Bob", affiliations: [1] });
store.updateAuthor(1, { name: "Bobby", affiliations: [2] });
const authors = store.get("authors");
expect(authors[0].name).toBe("Alice");
expect(authors[1].name).toBe("Bobby");
expect(authors[1].affiliations).toEqual([2]);
});
it("removeAuthor removes at index and is a no-op for out-of-range", () => {
store.addAuthor({ name: "Alice", affiliations: [] });
store.addAuthor({ name: "Bob", affiliations: [] });
store.removeAuthor(99);
expect(store.get("authors")).toHaveLength(2);
store.removeAuthor(0);
expect(store.get("authors").map((a) => a.name)).toEqual(["Bob"]);
});
it("addAffiliation returns a 1-based index", () => {
const i1 = store.addAffiliation({ name: "HF" });
const i2 = store.addAffiliation({ name: "MIT" });
expect(i1).toBe(1);
expect(i2).toBe(2);
});
});
describe("frontmatter-store — setAll / getAll", () => {
it("round-trips a full snapshot", () => {
const { store } = makeStore();
store.setAll({
title: "Paper",
subtitle: "Sub",
authors: [{ name: "Alice", affiliations: [1] }],
affiliations: [{ name: "HF" }],
links: [{ label: "repo", url: "https://example.com" }],
doi: "10.1/2",
template: "paper",
});
const snap = store.getAll();
expect(snap.title).toBe("Paper");
expect(snap.subtitle).toBe("Sub");
expect(snap.authors).toEqual([{ name: "Alice", affiliations: [1] }]);
expect(snap.affiliations).toEqual([{ name: "HF" }]);
expect(snap.links).toEqual([{ label: "repo", url: "https://example.com" }]);
expect(snap.doi).toBe("10.1/2");
expect(snap.template).toBe("paper");
});
it("setAll skips undefined values", () => {
const { store } = makeStore();
store.set("title", "original");
store.setAll({ title: undefined, doi: "new-doi" });
expect(store.get("title")).toBe("original");
expect(store.get("doi")).toBe("new-doi");
});
});
describe("frontmatter-store — observe", () => {
it("calls back when scalars change", () => {
const { store } = makeStore();
let count = 0;
const unsub = store.observe(() => { count++; });
store.set("title", "a");
store.set("title", "b");
expect(count).toBeGreaterThanOrEqual(2);
unsub();
store.set("title", "c");
const after = count;
expect(after).toBe(count);
});
it("calls back when arrays change", () => {
const { store } = makeStore();
let count = 0;
store.observe(() => { count++; });
store.addAuthor({ name: "A", affiliations: [] });
store.addAffiliation({ name: "HF" });
expect(count).toBeGreaterThanOrEqual(2);
});
});
describe("frontmatter-store — collaboration (two docs)", () => {
it("concurrent author inserts both survive after sync", () => {
const docA = new Y.Doc();
const docB = new Y.Doc();
const storeA = createFrontmatterStore(docA);
const storeB = createFrontmatterStore(docB);
storeA.addAuthor({ name: "Alice", affiliations: [] });
storeB.addAuthor({ name: "Bob", affiliations: [] });
const updA = Y.encodeStateAsUpdate(docA);
const updB = Y.encodeStateAsUpdate(docB);
Y.applyUpdate(docA, updB);
Y.applyUpdate(docB, updA);
const finalA = storeA.get("authors").map((a) => a.name).sort();
const finalB = storeB.get("authors").map((a) => a.name).sort();
expect(finalA).toEqual(["Alice", "Bob"]);
expect(finalB).toEqual(["Alice", "Bob"]);
});
});