mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-09 14:34:04 +00:00
* feat(chat-ui): syntax-highlighted code blocks and native GFM tables in shared chat markdown (#100195) * chore(chat-ui): sync native i18n inventory * fix(chat-ui): harden native markdown rendering * refactor(chat-ui): split HTML block patterns * refactor(chat-ui): parse markdown blocks with swift-markdown * style(chat-ui): satisfy SwiftFormat lint * fix(ios): bundle markdown dependency licenses * style(ios): normalize markdown license file * fix(ios): keep native markdown cards legible
116 lines
2.9 KiB
Swift
116 lines
2.9 KiB
Swift
import Foundation
|
|
import Testing
|
|
@testable import OpenClawChatUI
|
|
|
|
struct ChatMarkdownDisplayPreprocessorTests {
|
|
@Test func `converts plain chat soft breaks to markdown hard breaks`() throws {
|
|
let markdown = """
|
|
alpha
|
|
beta
|
|
gamma
|
|
"""
|
|
|
|
let prepared = ChatMarkdownDisplayPreprocessor.preserveChatSoftBreaks(in: markdown)
|
|
|
|
#expect(
|
|
prepared == """
|
|
alpha
|
|
beta
|
|
gamma
|
|
""")
|
|
#expect(try self.renderedCharacters(prepared) == "alpha\nbeta\ngamma")
|
|
}
|
|
|
|
@Test func `keeps blank line paragraph boundaries`() {
|
|
let markdown = """
|
|
alpha
|
|
|
|
beta
|
|
"""
|
|
|
|
let prepared = ChatMarkdownDisplayPreprocessor.preserveChatSoftBreaks(in: markdown)
|
|
|
|
#expect(prepared == markdown)
|
|
}
|
|
|
|
@Test func `does not duplicate existing hard breaks`() {
|
|
let markdown = """
|
|
alpha
|
|
beta\\
|
|
gamma
|
|
"""
|
|
|
|
let prepared = ChatMarkdownDisplayPreprocessor.preserveChatSoftBreaks(in: markdown)
|
|
|
|
#expect(prepared == markdown)
|
|
}
|
|
|
|
// Fenced code and table handling moved to ChatMarkdownBlockSegmenter,
|
|
// which strips those blocks before prose reaches this preprocessor; see
|
|
// ChatMarkdownBlockSegmenterTests.
|
|
|
|
@Test func `preserves block markdown structure`() {
|
|
let markdown = """
|
|
Intro
|
|
- item one
|
|
- item two
|
|
|
|
# Heading
|
|
> quote
|
|
"""
|
|
|
|
let prepared = ChatMarkdownDisplayPreprocessor.preserveChatSoftBreaks(in: markdown)
|
|
|
|
#expect(prepared == markdown)
|
|
}
|
|
|
|
@Test func `does not alter fenced code kept on the prose path`() {
|
|
let markdown = """
|
|
[docs][d]
|
|
|
|
```swift
|
|
let value = 1
|
|
```
|
|
|
|
[d]: https://example.com
|
|
"""
|
|
|
|
#expect(ChatMarkdownDisplayPreprocessor.preserveChatSoftBreaks(in: markdown) == markdown)
|
|
}
|
|
|
|
@Test func `does not alter nested fenced code`() {
|
|
let markdown = """
|
|
- item
|
|
```swift
|
|
let value = 1
|
|
```
|
|
continuation
|
|
"""
|
|
|
|
#expect(ChatMarkdownDisplayPreprocessor.preserveChatSoftBreaks(in: markdown) == markdown)
|
|
}
|
|
|
|
@Test func `converts plain pipe prose soft breaks`() {
|
|
let markdown = """
|
|
Use foo | bar
|
|
then continue
|
|
"""
|
|
|
|
let prepared = ChatMarkdownDisplayPreprocessor.preserveChatSoftBreaks(in: markdown)
|
|
|
|
#expect(
|
|
prepared == """
|
|
Use foo | bar
|
|
then continue
|
|
""")
|
|
}
|
|
|
|
private func renderedCharacters(_ markdown: String) throws -> String {
|
|
let options = AttributedString.MarkdownParsingOptions(
|
|
interpretedSyntax: .full,
|
|
failurePolicy: .returnPartiallyParsedIfPossible)
|
|
let attributed = try AttributedString(markdown: markdown, options: options)
|
|
return String(attributed.characters)
|
|
}
|
|
}
|