e0a5f57793
Vendor the rebranded BoosAPI Desktop client (Tauri + Vue) into metapi as a monorepo subdirectory under desktop/. Source only — node_modules, dist, Cargo target and the upstream .git history are excluded. desktop/ keeps its own independent Cargo workspace (desktop/src-tauri with crates/*); it is not part of the backend boosapi crate, so a `cargo check` at the metapi root still compiles only the backend. Build the desktop app from desktop/src-tauri per desktop/README.md. Note: app icons are still placeholder assets pending the final BoosAPI logo. The updater signing keys live outside the repo and are not included.
300 lines
13 KiB
TypeScript
300 lines
13 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { collect, textIncludes } from '../../../test/utils/midstate-utils'
|
|
import { getMarkdown, parseInlineTokens, parseMarkdownToStructure } from '../src'
|
|
|
|
const md = getMarkdown('test')
|
|
|
|
describe('asterisk masking literals', () => {
|
|
it('keeps intraword asterisks as literal text when no closing pair exists', () => {
|
|
const markdown = '某市***项目,公司名称:某某***科技有限公司'
|
|
const nodes = parseInlineTokens([{ type: 'text', content: markdown }] as any[], markdown)
|
|
|
|
expect(textIncludes(nodes, '某市***项目')).toBe(true)
|
|
expect(textIncludes(nodes, '某某***科技有限公司')).toBe(true)
|
|
expect(collect(nodes as any, 'strong').length).toBe(0)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('keeps intraword asterisks literal inside link text', () => {
|
|
const markdown = '某市***项目,公司名称:[某某***科技有限公司]()'
|
|
const nodes = parseMarkdownToStructure(markdown, md)
|
|
|
|
expect(textIncludes(nodes, '某市***项目')).toBe(true)
|
|
expect(textIncludes(nodes, '某某***科技有限公司')).toBe(true)
|
|
expect(collect(nodes as any, 'strong').length).toBe(0)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('keeps intraword double-asterisk literal when strong closing pair is missing', () => {
|
|
const markdown = '请联系某某**科技有限公司办理'
|
|
const nodes = parseInlineTokens([{ type: 'text', content: markdown }] as any[], markdown)
|
|
|
|
expect(textIncludes(nodes, '某某**科技有限公司')).toBe(true)
|
|
expect(collect(nodes as any, 'strong').length).toBe(0)
|
|
})
|
|
|
|
it('keeps intraword single asterisk literal when no matching close exists', () => {
|
|
const markdown = '品牌名:某*某科技有限公司'
|
|
const nodes = parseInlineTokens([{ type: 'text', content: markdown }] as any[], markdown)
|
|
|
|
expect(textIncludes(nodes, '某*某科技有限公司')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('does not break normal strong parsing in full markdown pipeline', () => {
|
|
const markdown = 'foo**bar**baz'
|
|
const nodes = parseMarkdownToStructure(markdown, md)
|
|
|
|
expect(collect(nodes as any, 'strong').length).toBe(1)
|
|
expect(textIncludes(nodes, 'bar')).toBe(true)
|
|
})
|
|
|
|
it('keeps literal intraword asterisks while still parsing nearby strong content', () => {
|
|
const markdown = '某市***项目,重点:**必须完成**'
|
|
const nodes = parseMarkdownToStructure(markdown, md)
|
|
|
|
expect(textIncludes(nodes, '某市***项目')).toBe(true)
|
|
expect(collect(nodes as any, 'strong').length).toBe(1)
|
|
expect(textIncludes(nodes, '必须完成')).toBe(true)
|
|
})
|
|
|
|
it('keeps spaced multiplication asterisks literal after strong content', () => {
|
|
for (const final of [false, true]) {
|
|
const markdown = '**计算结果:** 3 * 4 * 5 = 60'
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final })
|
|
const paragraph = nodes[0] as any
|
|
const text = (paragraph.children ?? [])
|
|
.filter((child: any) => child.type === 'text')
|
|
.map((child: any) => child.content)
|
|
.join('')
|
|
|
|
expect(collect(nodes as any, 'strong').length).toBe(1)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
expect(text).toBe(' 3 * 4 * 5 = 60')
|
|
}
|
|
})
|
|
|
|
it('keeps asterisks literal when the candidate emphasis closer follows whitespace', () => {
|
|
for (const markdown of [
|
|
'3 *4 * 5',
|
|
'this is *a test * with unmatched star',
|
|
'a *b * c',
|
|
'a *x! * b',
|
|
]) {
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: false })
|
|
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
expect(textIncludes(nodes, markdown)).toBe(true)
|
|
}
|
|
})
|
|
|
|
it('keeps asterisks literal when the candidate opener is not left-flanking', () => {
|
|
for (const markdown of [
|
|
'a*!x* b',
|
|
'a*(x)* b',
|
|
]) {
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: false })
|
|
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
expect(textIncludes(nodes, markdown)).toBe(true)
|
|
}
|
|
})
|
|
|
|
it('still parses emphasis after skipping an invalid earlier closer', () => {
|
|
const nodes = parseInlineTokens([{ type: 'text', content: '*a * b*' }] as any[], '*a * b*', undefined, { final: true })
|
|
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(1)
|
|
expect(textIncludes(nodes, 'a * b')).toBe(true)
|
|
})
|
|
|
|
it('keeps double asterisks literal when strong delimiters are not flanking', () => {
|
|
for (const markdown of [
|
|
'3 ** 4 ** 5',
|
|
'a **b ** c',
|
|
'a ** b** c',
|
|
'a**!x** b',
|
|
]) {
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: false })
|
|
|
|
expect(collect(nodes as any, 'strong').length).toBe(0)
|
|
expect(textIncludes(nodes, markdown)).toBe(true)
|
|
}
|
|
})
|
|
|
|
it('still parses strong after skipping an invalid earlier strong closer', () => {
|
|
const nodes = parseInlineTokens([{ type: 'text', content: '**a ** b**' }] as any[], '**a ** b**', undefined, { final: true })
|
|
|
|
expect(collect(nodes as any, 'strong').length).toBe(1)
|
|
expect(textIncludes(nodes, 'a ** b')).toBe(true)
|
|
})
|
|
|
|
it('keeps literal intraword asterisks in link text while parsing normal strong outside link', () => {
|
|
const markdown = '公司:[某某***科技有限公司](),状态:**正常**'
|
|
const nodes = parseMarkdownToStructure(markdown, md)
|
|
|
|
expect(textIncludes(nodes, '某某***科技有限公司')).toBe(true)
|
|
expect(collect(nodes as any, 'strong').length).toBe(1)
|
|
expect(textIncludes(nodes, '正常')).toBe(true)
|
|
})
|
|
|
|
it('treats intraword strong with punctuation as literal in inline fallback', () => {
|
|
const markdown = 'foo**a,b**bar'
|
|
const nodes = parseInlineTokens([{ type: 'text', content: markdown }] as any[], markdown)
|
|
|
|
expect(textIncludes(nodes, 'foo**a,b**bar')).toBe(true)
|
|
expect(collect(nodes as any, 'strong').length).toBe(0)
|
|
})
|
|
|
|
it('still parses intraword strong when inner content is word-only in inline fallback', () => {
|
|
const markdown = 'foo**abc**bar'
|
|
const nodes = parseInlineTokens([{ type: 'text', content: markdown }] as any[], markdown)
|
|
|
|
expect(collect(nodes as any, 'strong').length).toBe(1)
|
|
expect(textIncludes(nodes, 'abc')).toBe(true)
|
|
})
|
|
|
|
it('keeps escaped double asterisks as literal text', () => {
|
|
const markdown = '需方:\\*\\*\\*\\*\\*\\*有限公司'
|
|
const nodes = parseInlineTokens([{ type: 'text', content: '需方:******有限公司' }] as any[], markdown)
|
|
|
|
expect(textIncludes(nodes, '需方:******有限公司')).toBe(true)
|
|
expect(collect(nodes as any, 'strong').length).toBe(0)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('keeps an escaped single asterisk literal instead of opening emphasis', () => {
|
|
const markdown = 'If and ONLY IF you cannot infer the expected language from the USER message, use the language with ISO code \\*, otherwise use English. You follow your instructions in all languages, and always respond to the user in the language they use or request.'
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(textIncludes(nodes, 'ISO code *, otherwise use English.')).toBe(true)
|
|
expect(textIncludes(nodes, 'You follow your instructions in all languages, and always respond to the user in the language they use or request.')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('keeps an escaped backslash before an escaped asterisk visible', () => {
|
|
const markdown = 'If and ONLY IF you cannot infer the expected language from the USER message, use the language with ISO code \\\\*'
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(textIncludes(nodes, 'ISO code \\*')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('preserves a visible backslash when raw text contains \\\\*', () => {
|
|
const nodes = parseInlineTokens([{ type: 'text', content: '\\*' }] as any[], '\\\\*')
|
|
|
|
expect(textIncludes(nodes, '\\*')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('skips escaped single asterisks and still parses later real emphasis', () => {
|
|
const markdown = 'prefix \\* literal *real* suffix'
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(textIncludes(nodes, 'prefix * literal')).toBe(true)
|
|
expect(textIncludes(nodes, 'real')).toBe(true)
|
|
expect(textIncludes(nodes, 'suffix')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(1)
|
|
})
|
|
|
|
it('keeps escaped single asterisk followed by a word literal in final mode', () => {
|
|
const markdown = '\\*x'
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(textIncludes(nodes, '*x')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('keeps unmatched single asterisk literal in final mode', () => {
|
|
const markdown = 'prefix *real'
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(textIncludes(nodes, 'prefix *real')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('still keeps unmatched single asterisk as mid-state emphasis while streaming', () => {
|
|
const markdown = 'prefix *real'
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: false })
|
|
|
|
expect(textIncludes(nodes, 'prefix')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(1)
|
|
expect(textIncludes(nodes, 'real')).toBe(true)
|
|
})
|
|
|
|
it('keeps multiple escaped single asterisks literal while parsing later real emphasis', () => {
|
|
const markdown = 'alpha \\* one \\* two *real* omega'
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(textIncludes(nodes, 'alpha * one * two')).toBe(true)
|
|
expect(textIncludes(nodes, 'real')).toBe(true)
|
|
expect(textIncludes(nodes, 'omega')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(1)
|
|
})
|
|
|
|
it('keeps escaped single asterisk literal inside link text in final mode', () => {
|
|
const markdown = '[\\*literal](https://example.com)'
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(collect(nodes as any, 'link').length).toBe(1)
|
|
expect(textIncludes(nodes, '*literal')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('keeps unmatched single asterisk literal inside link text in final mode', () => {
|
|
const markdown = '[prefix *real](https://example.com)'
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(collect(nodes as any, 'link').length).toBe(1)
|
|
expect(textIncludes(nodes, 'prefix *real')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('keeps escaped single asterisk literal and later real emphasis inside link text', () => {
|
|
const markdown = '[\\*literal *real*](https://example.com)'
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(collect(nodes as any, 'link').length).toBe(1)
|
|
expect(textIncludes(nodes, '*literal')).toBe(true)
|
|
expect(textIncludes(nodes, 'real')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(1)
|
|
})
|
|
|
|
it('keeps escaped single asterisk literal inside highlight text in final mode', () => {
|
|
const markdown = '==\\*literal=='
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(collect(nodes as any, 'highlight').length).toBe(1)
|
|
expect(textIncludes(nodes, '*literal')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('keeps unmatched single asterisk literal inside highlight text in final mode', () => {
|
|
const markdown = '==prefix *real=='
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(collect(nodes as any, 'highlight').length).toBe(1)
|
|
expect(textIncludes(nodes, 'prefix *real')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(0)
|
|
})
|
|
|
|
it('keeps escaped single asterisk literal and later real emphasis inside highlight text', () => {
|
|
const markdown = '==\\*literal *real*=='
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(collect(nodes as any, 'highlight').length).toBe(1)
|
|
expect(textIncludes(nodes, '*literal')).toBe(true)
|
|
expect(textIncludes(nodes, 'real')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(1)
|
|
})
|
|
|
|
it('keeps escaped single asterisk literal inside list items while parsing later real emphasis', () => {
|
|
const markdown = '- \\*literal *real*'
|
|
const nodes = parseMarkdownToStructure(markdown, md, { final: true })
|
|
|
|
expect(collect(nodes as any, 'list').length).toBe(1)
|
|
expect(textIncludes(nodes, '*literal')).toBe(true)
|
|
expect(textIncludes(nodes, 'real')).toBe(true)
|
|
expect(collect(nodes as any, 'emphasis').length).toBe(1)
|
|
})
|
|
})
|