node-views.test.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import { EditorState } from "prosemirror-state"
  2. import { EditorView } from "prosemirror-view"
  3. import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
  4. import { MathBlockView } from "@/composables/useMathBlockView"
  5. import { schema } from "@/lib/schema"
  6. let views: EditorView[] = []
  7. beforeEach(() => {
  8. views = []
  9. })
  10. afterEach(() => {
  11. for (const v of views) {
  12. v.destroy()
  13. }
  14. views = []
  15. })
  16. function makeDocWithMathBlock(content: string) {
  17. const textNode = content ? schema.text(content) : undefined
  18. const block = textNode
  19. ? schema.nodes.math_block.create(null, [textNode])
  20. : schema.nodes.math_block.create()
  21. return schema.node("doc", {}, [block])
  22. }
  23. function createView(content: string) {
  24. const doc = makeDocWithMathBlock(content)
  25. const state = EditorState.create({ doc, schema })
  26. const dom = document.createElement("div")
  27. const view = new EditorView(dom, { state })
  28. views.push(view)
  29. return view
  30. }
  31. describe("MathBlockView", () => {
  32. it("creates DOM structure with correct classes", () => {
  33. const node = schema.nodes.math_block.create(null, [schema.text("$$\nE = mc^2\n$$")])
  34. const view = createView("$$\nE = mc^2\n$$")
  35. const nodeView = new MathBlockView(node, view, () => 0)
  36. expect(nodeView.dom.className).toBe("math-block")
  37. expect(nodeView.dom.children).toHaveLength(2)
  38. const preview = nodeView.dom.children[0] as HTMLElement
  39. const source = nodeView.dom.children[1] as HTMLElement
  40. expect(preview.className).toBe("math-block-preview")
  41. expect(source.className).toBe("math-block-source")
  42. expect(nodeView.contentDOM).toBe(source)
  43. })
  44. it("returns false from update for non-math_block nodes", () => {
  45. const textNode = schema.text("$$")
  46. const mathNode = schema.nodes.math_block.create(null, [textNode])
  47. const view = createView("$$")
  48. const nodeView = new MathBlockView(mathNode, view, () => 0)
  49. const paraNode = schema.nodes.paragraph.create(null, [schema.text("hello")])
  50. expect(nodeView.update(paraNode)).toBe(false)
  51. })
  52. it("returns true from update for math_block nodes", () => {
  53. const textNode = schema.text("$$\nE = mc^2\n$$")
  54. const mathNode = schema.nodes.math_block.create(null, [textNode])
  55. const view = createView("$$\nE = mc^2\n$$")
  56. const nodeView = new MathBlockView(mathNode, view, () => 0)
  57. const updatedNode = schema.nodes.math_block.create(null, [schema.text("$$\nx^2\n$$")])
  58. expect(nodeView.update(updatedNode)).toBe(true)
  59. })
  60. it("stopEvent returns true", () => {
  61. const textNode = schema.text("$$")
  62. const mathNode = schema.nodes.math_block.create(null, [textNode])
  63. const view = createView("$$")
  64. const nodeView = new MathBlockView(mathNode, view, () => 0)
  65. expect(nodeView.stopEvent()).toBe(true)
  66. })
  67. })
  68. describe("CodeBlockView", () => {
  69. it("creates DOM with cm-code-block class", async () => {
  70. const { CodeBlockView } = await import("@/composables/useCodeBlockView")
  71. const textNode = schema.text("```js\nconst x = 1\n```")
  72. const node = schema.nodes.code_block.create({ language: "js" }, [textNode])
  73. const doc = schema.node("doc", {}, [node])
  74. const state = EditorState.create({ doc, schema })
  75. const dom = document.createElement("div")
  76. const view = new EditorView(dom, { state })
  77. views.push(view)
  78. const nodeView = new CodeBlockView(node, view, () => 0)
  79. expect(nodeView.dom.className).toBe("cm-code-block")
  80. expect(nodeView.dom.children.length).toBeGreaterThan(0)
  81. // Clean up CM6 view
  82. nodeView.destroy()
  83. })
  84. it("update returns false for non-code_block nodes", async () => {
  85. const { CodeBlockView } = await import("@/composables/useCodeBlockView")
  86. const textNode = schema.text("```\ncode\n```")
  87. const node = schema.nodes.code_block.create({ language: "" }, [textNode])
  88. const doc = schema.node("doc", {}, [node])
  89. const state = EditorState.create({ doc, schema })
  90. const dom = document.createElement("div")
  91. const view = new EditorView(dom, { state })
  92. views.push(view)
  93. const nodeView = new CodeBlockView(node, view, () => 0)
  94. const paraNode = schema.nodes.paragraph.create(null, [schema.text("hello")])
  95. expect(nodeView.update(paraNode)).toBe(false)
  96. nodeView.destroy()
  97. })
  98. it("update returns true for code_block nodes", async () => {
  99. const { CodeBlockView } = await import("@/composables/useCodeBlockView")
  100. const textNode = schema.text("```\ncode\n```")
  101. const node = schema.nodes.code_block.create({ language: "" }, [textNode])
  102. const doc = schema.node("doc", {}, [node])
  103. const state = EditorState.create({ doc, schema })
  104. const dom = document.createElement("div")
  105. const view = new EditorView(dom, { state })
  106. views.push(view)
  107. const nodeView = new CodeBlockView(node, view, () => 0)
  108. const updatedNode = schema.nodes.code_block.create({ language: "ts" }, [textNode])
  109. expect(nodeView.update(updatedNode)).toBe(true)
  110. nodeView.destroy()
  111. })
  112. })