Files
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

128 lines
4.4 KiB
TypeScript

/**
* @vitest-environment happy-dom
*/
import { Document } from '@tiptap/extension-document'
import { HardBreak } from '@tiptap/extension-hard-break'
import { Heading } from '@tiptap/extension-heading'
import { Italic } from '@tiptap/extension-italic'
import { Paragraph } from '@tiptap/extension-paragraph'
import { Text } from '@tiptap/extension-text'
import { MarkdownManager } from '@tiptap/markdown'
import { beforeEach, describe, expect, it } from 'vite-plus/test'
describe('MarkdownManager Mixed Markdown + HTML', () => {
let manager: MarkdownManager
const basicExtensions = [Document, Paragraph, Text, Heading, Italic, HardBreak]
beforeEach(() => {
manager = new MarkdownManager({ extensions: basicExtensions })
})
it('parses heading with inline HTML <em> as italic', () => {
const md = '## hello <em>world</em>'
const doc = manager.parse(md)
expect(doc.type).toBe('doc')
expect(Array.isArray(doc.content)).toBe(true)
const heading = doc.content![0]
expect(heading.type).toBe('heading')
// Find the text node that contains 'world'
const textNodes = heading.content!.flatMap((n: any) =>
n.type === 'text' ? [n] : n.content || [],
)
const worldNode = textNodes.find((n: any) => n.text && n.text.includes('world'))
// Use a function-call assertion to avoid the "no-unused-expressions" lint error
expect(worldNode).not.toBe(undefined)
// The italic mark should be present
expect(Array.isArray(worldNode!.marks)).toBe(true)
const hasItalic = worldNode!.marks.some((m: any) => m.type === 'italic')
expect(hasItalic).toBe(true)
})
it('parses standalone inline HTML <em>world</em> as italic', () => {
const md = '<em>world</em>'
const doc = manager.parse(md)
expect(doc.type).toBe('doc')
// Inline HTML typically produces a paragraph wrapper
const paragraph = doc.content![0]
expect(paragraph.type).toBe('paragraph')
const textNode = paragraph.content![0]
expect(textNode.text).toBe('world')
expect(Array.isArray(textNode.marks)).toBe(true)
const hasItalic = (textNode.marks || []).some((m: any) => m.type === 'italic')
expect(hasItalic).toBe(true)
})
it('parses markdown italic next to HTML italic correctly', () => {
const md = '*a* <em>b</em> *c*'
const doc = manager.parse(md)
expect(doc.type).toBe('doc')
const para = doc.content![0]
expect(para.type).toBe('paragraph')
// Collect texts and their mark states
const runs = para.content!.map((n: any) => ({ text: n.text, marks: n.marks || [] }))
// Expect there to be runs containing a, b, c
const texts = runs.map(r => (r.text || '').trim())
expect(texts).toContain('a')
expect(texts).toContain('b')
expect(texts).toContain('c')
;['a', 'b', 'c'].forEach(letter => {
const node = runs.find(r => (r.text || '').trim() === letter)
expect(node).not.toBe(undefined)
const hasItalic = ((node as any).marks || []).some((m: any) => m.type === 'italic')
expect(hasItalic).toBe(true)
})
})
it('parses an unclosed inline HTML tag without nesting a paragraph', () => {
const md = '<b>123'
const doc = manager.parse(md)
expect(doc.content).toEqual([{ type: 'paragraph', content: [{ type: 'text', text: '123' }] }])
})
it('parses an unclosed inline HTML tag and keeps the surrounding text', () => {
const md = 'a <b> b'
const doc = manager.parse(md)
// The dropped tag leaves both of the spaces around it.
expect(doc.content).toEqual([{ type: 'paragraph', content: [{ type: 'text', text: 'a b' }] }])
})
it('parses an empty inline HTML element without nesting a paragraph', () => {
const md = 'a <span></span> b'
const doc = manager.parse(md)
expect(doc.content).toEqual([{ type: 'paragraph', content: [{ type: 'text', text: 'a b' }] }])
})
it('parses inline HTML for a block element as its inline content', () => {
const md = 'a <h1>title</h1> b'
const doc = manager.parse(md)
expect(doc.content).toEqual([
{ type: 'paragraph', content: [{ type: 'text', text: 'a title b' }] },
])
})
it('parses inline HTML for an inline node and keeps the node', () => {
const md = 'a <br> b'
const doc = manager.parse(md)
expect(doc.content).toEqual([
{
type: 'paragraph',
content: [
{ type: 'text', text: 'a ' },
{ type: 'hardBreak' },
{ type: 'text', text: ' b' },
],
},
])
})
})