Files
upstream-tiptap/packages/markdown/tests/integration/server-side-parsing.test.ts
upstream-mirror 083a46e93b
Some checks failed
Verify / Install node dependencies (push) Failing after 2s
Verify / Build packages (push) Has been skipped
Verify / Check linting & formatting (push) Has been skipped
Verify / Run unit tests (push) Has been skipped
Verify / Run e2e tests (shard 1/4) (push) Has been skipped
Verify / Run e2e tests (shard 2/4) (push) Has been skipped
Verify / Run e2e tests (shard 3/4) (push) Has been skipped
Verify / Run e2e tests (shard 4/4) (push) Has been skipped
Publish / Resolve publish config (push) Failing after 2s
Publish / Send notification to Slack (push) Has been skipped
Publish / Send failure notification to Slack (push) Failing after 8s
Verify / Merge e2e reports (push) Has been skipped
Publish / Build (push) Has been skipped
Publish / Bump Versions OR Release to Registry (push) Has been skipped
snapshot of https://github.com/ueberdosis/tiptap.git (main, 2026-09-15; blobs > 131072B omitted)
2026-09-15 21:11:02 +05:00

213 lines
7.2 KiB
TypeScript

/**
* @vitest-environment node
*/
import { Code } from '@tiptap/extension-code'
import { Document } from '@tiptap/extension-document'
import { Heading } from '@tiptap/extension-heading'
import { Paragraph } from '@tiptap/extension-paragraph'
import { Strike } from '@tiptap/extension-strike'
import { Text } from '@tiptap/extension-text'
import { MarkdownManager } from '@tiptap/markdown'
import { beforeEach, describe, expect, it } from 'vite-plus/test'
describe('MarkdownManager Server-side Parsing', () => {
let manager: MarkdownManager
const basicExtensions = [Document, Paragraph, Text, Strike, Code, Heading]
beforeEach(() => {
manager = new MarkdownManager({ extensions: basicExtensions })
})
it('parses inline code with HTML-like tags and strike marks without error', () => {
const md = '`<h1>`~`<h6>`'
const doc = manager.parse(md)
expect(doc.type).toBe('doc')
expect(doc.content).toBeDefined()
const paragraph = doc.content![0]
expect(paragraph.type).toBe('paragraph')
expect(paragraph.content).toBeDefined()
// Find text nodes with code marks (inline code)
const codeTextNodes = paragraph.content!.filter(
(n: any) => n.type === 'text' && n.marks?.some((m: any) => m.type === 'code'),
)
expect(codeTextNodes.length).toBeGreaterThan(0)
// At least one code segment should contain an HTML-like tag
const hasHtmlTag = codeTextNodes.some(
(n: any) => n.text && (n.text.includes('<h1>') || n.text.includes('<h6>')),
)
expect(hasHtmlTag).toBe(true)
// All text content combined should include both HTML tags and the tilde
const allTextNodes = paragraph.content!.filter((n: any) => n.type === 'text')
const allText = allTextNodes.map((n: any) => n.text).join('')
expect(allText).toContain('h1')
expect(allText).toContain('h6')
expect(allText).toMatch(/~/)
})
it('parses inline code with HTML-like tags and double strike marks without error', () => {
const md = '`<h1>`~~`<h6>`'
const doc = manager.parse(md)
expect(doc.type).toBe('doc')
expect(doc.content).toBeDefined()
const paragraph = doc.content![0]
expect(paragraph.type).toBe('paragraph')
// Find text nodes with code marks
const codeTextNodes = paragraph.content!.filter(
(n: any) => n.type === 'text' && n.marks?.some((m: any) => m.type === 'code'),
)
expect(codeTextNodes.length).toBeGreaterThan(0)
// At least one code segment should contain an HTML-like tag
const hasHtmlTag = codeTextNodes.some(
(n: any) => n.text && (n.text.includes('<h1>') || n.text.includes('<h6>')),
)
expect(hasHtmlTag).toBe(true)
// All text should contain both HTML tags
const allTextNodes = paragraph.content!.filter((n: any) => n.type === 'text')
const allText = allTextNodes.map((n: any) => n.text).join('')
expect(allText).toContain('h1')
expect(allText).toContain('h6')
})
it('parses strike marks in normal text', () => {
const md = 'a~b'
const doc = manager.parse(md)
expect(doc.type).toBe('doc')
expect(Array.isArray(doc.content)).toBe(true)
})
it('parses HTML heading tags with strike marks', () => {
const md = '<h1>~~test~~</h1>'
const doc = manager.parse(md)
expect(doc.type).toBe('doc')
expect(doc.content).toBeDefined()
const paragraph = doc.content![0]
expect(paragraph.type).toBe('paragraph')
expect(paragraph.content).toBeDefined()
// In Node.js environment, HTML tags should be treated as literal text
const allText = paragraph
.content!.map((n: any) =>
n.text ? n.text : n.content?.map((c: any) => c.text).join('') || '',
)
.join('')
// Should contain the HTML tags as literal text
expect(allText).toContain('<h1>')
expect(allText).toContain('</h1>')
// Should contain 'test' text
expect(allText).toContain('test')
})
it('parses multiple inline code segments with HTML-like content and strike', () => {
const md = 'text `<h1>` more ~~text~~ and `<h2>`'
const doc = manager.parse(md)
expect(doc.type).toBe('doc')
expect(doc.content).toBeDefined()
const paragraph = doc.content![0]
expect(paragraph.type).toBe('paragraph')
// Find text nodes with code marks (inline code)
const codeTextNodes = paragraph.content!.filter(
(n: any) => n.type === 'text' && n.marks?.some((m: any) => m.type === 'code'),
)
expect(codeTextNodes.length).toBeGreaterThanOrEqual(2)
// Code text should contain literal HTML-like tags
expect(codeTextNodes[0].text).toBe('<h1>')
expect(codeTextNodes[1].text).toBe('<h2>')
// Find text nodes with strike marks
const textNodes = paragraph.content!.filter((n: any) => n.type === 'text')
const strikeTextNode = textNodes.find(
(n: any) =>
n.text && n.text.includes('text') && n.marks?.some((m: any) => m.type === 'strike'),
)
expect(strikeTextNode).toBeDefined()
})
it('preserves unknown angle-bracket placeholders as literal text in server environment', () => {
const md = '<enter existing CID here if available>\n\nother text after'
const doc = manager.parse(md)
expect(doc.type).toBe('doc')
expect(Array.isArray(doc.content)).toBe(true)
expect(doc.content).toHaveLength(2)
// angle-bracket placeholder preserved as literal text
const firstPara = doc.content![0]
expect(firstPara.type).toBe('paragraph')
expect(firstPara.content).toHaveLength(1)
expect(firstPara.content![0].type).toBe('text')
expect(firstPara.content![0].text).toBe('<enter existing CID here if available>')
// trailing text after the blank line
const secondPara = doc.content![1]
expect(secondPara.type).toBe('paragraph')
expect(secondPara.content).toHaveLength(1)
expect(secondPara.content![0].type).toBe('text')
expect(secondPara.content![0].text).toBe('other text after')
})
it('parses HTML tags as plain text in server environment', () => {
const md = '<p>Hello</p>'
const doc = manager.parse(md)
expect(doc.type).toBe('doc')
expect(doc.content).toBeDefined()
expect(Array.isArray(doc.content)).toBe(true)
// The document should have a paragraph
const paragraph = doc.content![0]
expect(paragraph.type).toBe('paragraph')
expect(paragraph.content).toBeDefined()
// The HTML should be preserved as literal text (not parsed)
const textContent = paragraph.content!.map((n: any) => n.text).join('')
expect(textContent).toContain('<p>Hello</p>')
})
it('still parses markdown syntax correctly alongside HTML', () => {
const md = '<div>test</div> ~~world~~'
const doc = manager.parse(md)
expect(doc.type).toBe('doc')
expect(doc.content).toBeDefined()
// Find all text nodes
const textNodes = doc.content!.flatMap((node: any) =>
node.content ? node.content.filter((n: any) => n.type === 'text') : [],
)
// Should have HTML as literal text
const hasHtmlText = textNodes.some((n: any) => n.text && n.text.includes('<div>test</div>'))
expect(hasHtmlText).toBe(true)
// Should have 'world' text (with or without strike mark depending on parsing)
const hasWorldText = textNodes.some((n: any) => n.text && n.text.includes('world'))
expect(hasWorldText).toBe(true)
})
})