@blocknote/core
Version:
A "Notion-style" block-based extensible text editor built on top of Prosemirror and Tiptap.
86 lines (76 loc) • 2.47 kB
text/typescript
/**
* @vitest-environment jsdom
*/
import {
relativePositionToAbsolutePosition,
ySyncPluginKey,
} from "y-prosemirror";
import { afterEach, describe, expect, it, vi } from "vite-plus/test";
import * as Y from "yjs";
import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
import { DefaultThreadStoreAuth } from "../../comments/threadstore/DefaultThreadStoreAuth.js";
import { withCollaboration } from "../extensions/index.js";
import { RESTYjsThreadStore } from "./RESTYjsThreadStore.js";
function createCollabEditor() {
const doc = new Y.Doc();
const fragment = doc.getXmlFragment("doc");
const editor = BlockNoteEditor.create(
withCollaboration({
collaboration: {
fragment,
user: { name: "Test User", color: "#FF0000" },
provider: undefined,
},
trailingBlock: false,
}),
);
editor.mount(document.createElement("div"));
editor.replaceBlocks(editor.document, [
{ type: "paragraph", content: "Hello World" },
]);
return { editor, doc, fragment };
}
afterEach(() => {
vi.restoreAllMocks();
});
describe("RESTYjsThreadStore", () => {
it("sends resolvable yjs positions along with the thread", async () => {
const { editor, doc, fragment } = createCollabEditor();
const requests: any[] = [];
vi.spyOn(globalThis, "fetch").mockImplementation((async (
_url: any,
init: any,
) => {
requests.push(JSON.parse(init.body));
return new Response("{}", {
status: 200,
headers: { "Content-Type": "application/json" },
});
}) as any);
const store = new RESTYjsThreadStore(
"https://example.com/threads",
{},
doc.getMap("threads"),
new DefaultThreadStoreAuth("user-1", "editor"),
);
await store.addThreadToDocument({
threadId: "thread-1",
selection: { anchor: 3, head: 8 },
editor,
});
expect(requests).toHaveLength(1);
const { yjs } = requests[0].selection;
expect(yjs).toBeDefined();
// the relative positions must resolve back to the positions we passed in
const state = ySyncPluginKey.getState(editor.prosemirrorState) as any;
const resolve = (relPos: any) =>
relativePositionToAbsolutePosition(
fragment.doc!,
state.binding.type,
Y.createRelativePositionFromJSON(relPos),
state.binding.mapping,
);
expect(resolve(yjs.anchor)).toBe(3);
expect(resolve(yjs.head)).toBe(8);
});
});