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
128 lines
4.4 KiB
TypeScript
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' },
|
|
],
|
|
},
|
|
])
|
|
})
|
|
})
|