File
Blob: tests/client/components/editor/controllers/details-block.test.ts
| 1 | import { describe, expect, it } from "vitest"; |
| 2 | import { getSchema } from "@tiptap/core"; |
| 3 | import { StarterKit } from "@tiptap/starter-kit"; |
| 4 | import { EditorState, TextSelection } from "@tiptap/pm/state"; |
| 5 | import { |
| 6 | applyMoveToDetailsContent, |
| 7 | createDetailsBlockNode, |
| 8 | DEFAULT_DETAILS_SUMMARY, |
| 9 | DETAILS_SUMMARY_PLACEHOLDER, |
| 10 | } from "@/client/components/editor/controllers/details-block"; |
| 11 | import { DetailsBlockExtensions } from "@/client/components/editor/extensions/details-block"; |
| 12 | |
| 13 | const schema = getSchema([StarterKit.configure({ undoRedo: false }), ...DetailsBlockExtensions]); |
| 14 | |
| 15 | describe("details block helpers", () => { |
| 16 | it("leaves the summary empty by default so the placeholder can guide the title field", () => { |
| 17 | expect(createDetailsBlockNode()).toEqual({ |
| 18 | type: "details", |
| 19 | attrs: { open: true }, |
| 20 | content: [ |
| 21 | { |
| 22 | type: "detailsSummary", |
| 23 | }, |
| 24 | { |
| 25 | type: "detailsContent", |
| 26 | content: [{ type: "paragraph" }], |
| 27 | }, |
| 28 | ], |
| 29 | }); |
| 30 | }); |
| 31 | |
| 32 | it("still preserves an explicit summary when one is provided", () => { |
| 33 | expect(createDetailsBlockNode({ summary: "Specs" })).toEqual({ |
| 34 | type: "details", |
| 35 | attrs: { open: true }, |
| 36 | content: [ |
| 37 | { |
| 38 | type: "detailsSummary", |
| 39 | content: [{ type: "text", text: "Specs" }], |
| 40 | }, |
| 41 | { |
| 42 | type: "detailsContent", |
| 43 | content: [{ type: "paragraph" }], |
| 44 | }, |
| 45 | ], |
| 46 | }); |
| 47 | }); |
| 48 | |
| 49 | it("keeps the human-readable fallback label and a separate summary placeholder", () => { |
| 50 | expect(DEFAULT_DETAILS_SUMMARY).toBe("Details"); |
| 51 | expect(DETAILS_SUMMARY_PLACEHOLDER).toBe("Summary"); |
| 52 | }); |
| 53 | |
| 54 | it("moves an empty summary selection into the details content", () => { |
| 55 | const doc = schema.nodeFromJSON({ |
| 56 | type: "doc", |
| 57 | content: [createDetailsBlockNode()], |
| 58 | }); |
| 59 | const state = EditorState.create({ |
| 60 | schema, |
| 61 | doc, |
| 62 | selection: TextSelection.create(doc, 2), |
| 63 | }); |
| 64 | const tr = state.tr; |
| 65 | |
| 66 | expect(applyMoveToDetailsContent(tr)).toBe(true); |
| 67 | expect(tr.selection.$from.parent.type.name).toBe("paragraph"); |
| 68 | }); |
| 69 | |
| 70 | it("opens a closed details block before moving into its content", () => { |
| 71 | const doc = schema.nodeFromJSON({ |
| 72 | type: "doc", |
| 73 | content: [createDetailsBlockNode({ open: false })], |
| 74 | }); |
| 75 | const state = EditorState.create({ |
| 76 | schema, |
| 77 | doc, |
| 78 | selection: TextSelection.create(doc, 2), |
| 79 | }); |
| 80 | const tr = state.tr; |
| 81 | |
| 82 | expect(applyMoveToDetailsContent(tr)).toBe(true); |
| 83 | expect(tr.doc.firstChild?.attrs.open).toBe(true); |
| 84 | expect(tr.selection.$from.parent.type.name).toBe("paragraph"); |
| 85 | }); |
| 86 | }); |