import { describe, expect, it } from 'vitest'
import { getMarkdown, parseMarkdownToStructure } from '../src'
const REAL_WORLD_MULTILINE_INPUT = `$2.897771955 times 10^{-3}text{m·K}$^[1]^
测试[3]。
$x$^[1]^
$x$ ^[1]^
测试^[1]^
$2.897771955 \\times 10^{-3}\\text{m·K}$^[1]^
[1]
测试[12]结束
A[3]B
$x$^[1]^
测试^[1]^
[3]
测试[12]结束`
function parse(markdown: string, final = true) {
const md = getMarkdown('issue-386')
return parseMarkdownToStructure(markdown, md, { final }) as any[]
}
function collectByType(value: any, targetType: string): any[] {
const out: any[] = []
const walk = (node: any) => {
if (!node)
return
if (Array.isArray(node)) {
node.forEach(walk)
return
}
if (node.type === targetType)
out.push(node)
if (Array.isArray(node.children))
node.children.forEach(walk)
if (Array.isArray(node.items))
node.items.forEach(walk)
}
walk(value)
return out
}
describe('issue #386 inline math suffix regressions', () => {
it.each([true, false])('keeps superscript syntax immediately after inline math when final=%s', (final) => {
const nodes = parse('$2.897771955 times 10^{-3}text{m·K}$^[1]^', final)
const paragraph = nodes[0] as any
expect(paragraph?.type).toBe('paragraph')
expect(paragraph?.children?.map((child: any) => child.type)).toEqual([
'math_inline',
'superscript',
])
expect(paragraph?.children?.[1]?.children?.map((child: any) => child.content).join('')).toBe('[1]')
const textNodes = collectByType(paragraph, 'text')
expect(textNodes.some((node: any) => node.content === '^[1]^')).toBe(false)
})
it.each([true, false])('keeps footnote references immediately after inline math when final=%s', (final) => {
const nodes = parse('$x$[^1]\n\n[^1]: note', final)
const paragraph = nodes[0] as any
expect(paragraph?.type).toBe('paragraph')
expect(paragraph?.children?.map((child: any) => child.type)).toEqual([
'math_inline',
'footnote_reference',
])
expect(paragraph?.children?.[1]?.id).toBe('1')
const footnotes = collectByType(nodes, 'footnote')
expect(footnotes).toHaveLength(1)
expect(footnotes[0]?.id).toBe('1')
expect(collectByType(footnotes[0], 'text').some((node: any) => node.content === 'note')).toBe(true)
})
it.each([true, false])('preserves brackets inside sup html content when final=%s', (final) => {
const nodes = parse('测试[3]。', final)
const paragraph = nodes[0] as any
const htmlInline = collectByType(paragraph, 'html_inline')[0] as any
expect(htmlInline).toBeDefined()
expect(htmlInline.content).toBe('[3]')
expect(htmlInline.children).toEqual([
{
type: 'text',
content: '[3]',
raw: '[3]',
},
])
})
it.each([true, false])('preserves brackets inside generic inline html content when final=%s', (final) => {
const nodes = parse('[3]', final)
const paragraph = nodes[0] as any
const htmlInline = collectByType(paragraph, 'html_inline')[0] as any
expect(htmlInline).toBeDefined()
expect(htmlInline.content).toBe('[3]')
expect(htmlInline.children).toEqual([
{
type: 'text',
content: '[3]',
raw: '[3]',
},
])
})
it.each([true, false])('parses the real multiline issue-386 input without leaking raw superscript syntax when final=%s', (final) => {
const nodes = parse(REAL_WORLD_MULTILINE_INPUT, final)
const paragraph = nodes[0] as any
const superscripts = collectByType(paragraph, 'superscript')
const htmlInlineNodes = collectByType(paragraph, 'html_inline')
const textNodes = collectByType(paragraph, 'text')
expect(paragraph?.type).toBe('paragraph')
expect(superscripts).toHaveLength(7)
expect(superscripts.map((node: any) => node.children?.map((child: any) => child.content).join(''))).toEqual([
'[1]',
'[1]',
'[1]',
'[1]',
'[1]',
'[1]',
'[1]',
])
expect(textNodes.some((node: any) => String(node.content).includes('^[1]^'))).toBe(false)
expect(htmlInlineNodes.map((node: any) => node.children?.map((child: any) => child.content).join(''))).toEqual([
'[3]',
'[1]',
'[12]',
'[3]',
'[3]',
'[12]',
])
})
})