File
Blob: tests/client/components/editor/extensions/page-mention-paste-guard.test.ts
| 1 | import { describe, expect, it } from "vitest"; |
| 2 | import { getSchema } from "@tiptap/core"; |
| 3 | import { StarterKit } from "@tiptap/starter-kit"; |
| 4 | import { Slice } from "@tiptap/pm/model"; |
| 5 | import { PageMentionNode } from "@/client/components/editor/extensions/page-mention/node"; |
| 6 | import { stripPageMentions } from "@/client/components/editor/extensions/page-mention/node"; |
| 7 | |
| 8 | const schema = getSchema([StarterKit.configure({ undoRedo: false }), PageMentionNode]); |
| 9 | |
| 10 | function paragraphSlice(content: Array<{ type: string; text?: string; attrs?: Record<string, unknown> }>): Slice { |
| 11 | const doc = schema.nodeFromJSON({ |
| 12 | type: "doc", |
| 13 | content: [{ type: "paragraph", content }], |
| 14 | }); |
| 15 | return doc.slice(0, doc.content.size); |
| 16 | } |
| 17 | |
| 18 | function collectMentionPageIds(slice: Slice): string[] { |
| 19 | const ids: string[] = []; |
| 20 | slice.content.descendants((node) => { |
| 21 | if (node.type.name === "pageMention") { |
| 22 | const id = (node.attrs as { pageId?: string }).pageId; |
| 23 | if (id) ids.push(id); |
| 24 | } |
| 25 | return true; |
| 26 | }); |
| 27 | return ids; |
| 28 | } |
| 29 | |
| 30 | describe("stripPageMentions", () => { |
| 31 | it("drops pageMention nodes and preserves surrounding inline content", () => { |
| 32 | const slice = paragraphSlice([ |
| 33 | { type: "text", text: "hello " }, |
| 34 | { type: "pageMention", attrs: { pageId: "page-123" } }, |
| 35 | { type: "text", text: " world" }, |
| 36 | ]); |
| 37 | |
| 38 | expect(collectMentionPageIds(slice)).toEqual(["page-123"]); |
| 39 | |
| 40 | const stripped = stripPageMentions(slice); |
| 41 | |
| 42 | expect(collectMentionPageIds(stripped)).toEqual([]); |
| 43 | expect(stripped.content.firstChild?.textContent).toBe("hello world"); |
| 44 | }); |
| 45 | |
| 46 | it("returns the original slice when no mentions are present", () => { |
| 47 | const slice = paragraphSlice([{ type: "text", text: "no mentions here" }]); |
| 48 | expect(stripPageMentions(slice)).toBe(slice); |
| 49 | }); |
| 50 | |
| 51 | it("strips mentions nested inside other inline structures", () => { |
| 52 | const slice = paragraphSlice([ |
| 53 | { type: "text", text: "outer " }, |
| 54 | { type: "pageMention", attrs: { pageId: "p1" } }, |
| 55 | { type: "text", text: " between " }, |
| 56 | { type: "pageMention", attrs: { pageId: "p2" } }, |
| 57 | { type: "text", text: " end" }, |
| 58 | ]); |
| 59 | |
| 60 | const stripped = stripPageMentions(slice); |
| 61 | |
| 62 | expect(collectMentionPageIds(stripped)).toEqual([]); |
| 63 | expect(stripped.content.firstChild?.textContent).toBe("outer between end"); |
| 64 | }); |
| 65 | }); |