eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1import XCTest
2@testable import LChatCore
3
4// The storage→model adapter (#890 stage 2): synthetic attributed strings in,
5// wire bytes out — headless, because .composerStyle is the style truth and
6// no UIKit/AppKit is needed to read it.
7final class ComposerAttributesTests: XCTestCase {
8 private func attr(_ parts: [(String, InlineStyle)]) -> NSAttributedString {
9 let out = NSMutableAttributedString()
10 for (text, style) in parts {
11 var attrs: [NSAttributedString.Key: Any] = [:]
12 if !style.isPlain { attrs[.composerStyle] = ComposerStyleAttr(style) }
13 out.append(NSAttributedString(string: text, attributes: attrs))
14 }
15 return out
16 }
17
18 private let bold = InlineStyle(bold: true)
19
20 func testPlainPassesVerbatim() {
21 for s in ["hello", "a\nb", "2*3 and ~x~", "", "líne ✨"] {
22 XCTAssertEqual(composerMarkdown(of: attr([(s, .plain)])), s)
23 }
24 }
25
26 func testMarksSerialize() {
27 XCTAssertEqual(composerMarkdown(of: attr([("b", bold)])), "**b**")
28 XCTAssertEqual(composerMarkdown(of: attr([("i", InlineStyle(italic: true))])), "*i*")
29 XCTAssertEqual(composerMarkdown(of: attr([("s", InlineStyle(strike: true))])), "~s~")
30 XCTAssertEqual(composerMarkdown(of: attr([("c", InlineStyle(code: true))])), "`c`")
31 XCTAssertEqual(
32 composerMarkdown(of: attr([("say ", .plain), ("it", InlineStyle(bold: true, italic: true)), ("!", .plain)])),
33 "say ***it***!")
34 }
35
36 func testTypedNewlineStaysPlain() {
37 // The marked-break contract: a typed Shift+Return inserts a PLAIN
38 // "\n" — the mark closes and reopens on the wire.
39 let a = attr([("a", bold), ("\n", .plain), ("b", bold)])
40 XCTAssertEqual(composerMarkdown(of: a), "**a**\n**b**")
41 }
42
43 func testStyledNewlineSpansTheMark() {
44 // Select-across-lines ⌘B styles the "\n" too — one marked span.
45 let a = attr([("a\nb", bold)])
46 XCTAssertEqual(composerMarkdown(of: a), "**a\nb**")
47 }
48
49 func testAdjacentSameStyleRunsCoalesce() {
50 // Rendering-only attribute churn fragments runs; the grammar must not
51 // see the seams (an in-mark escape proves the runs merged).
52 let a = attr([("a", bold), ("*", bold), ("b", bold)])
53 XCTAssertEqual(composerMarkdown(of: a), "**a\\*b**")
54 }
55
56 func testDisplayEqualsStorageString() {
57 let a = attr([("plain ", .plain), ("bold", bold), ("\n", .plain), ("tail", .plain)])
58 XCTAssertEqual(displayText([.paragraph(composerInlineNodes(of: a))]), a.string)
59 }
60
61 func testStyleAttrEquality() {
62 // Attribute-run coalescing depends on isEqual across separately
63 // created instances.
64 XCTAssertEqual(ComposerStyleAttr(bold), ComposerStyleAttr(InlineStyle(bold: true)))
65 XCTAssertNotEqual(ComposerStyleAttr(bold), ComposerStyleAttr(.plain))
66 }
67}
68
69// Opt-out escaping (format-as-you-type OFF): typed markers are literal text
70// and must RENDER literally; only WYSIWYG-applied marks keep their markers.
71// The cases mirror test/composer.test.mjs's escaped-mode block byte-for-byte.
72extension ComposerAttributesTests {
73 func testEscapedModeEscapesPlainMarkers() {
74 let a = NSMutableAttributedString(string: "lit *x* _y_ ")
75 a.append(NSAttributedString(string: "bold", attributes: [.composerStyle: ComposerStyleAttr(InlineStyle(bold: true))]))
76 XCTAssertEqual(composerMarkdown(of: a), "lit *x* _y_ **bold**")
77 XCTAssertEqual(composerMarkdown(of: a, escapeLiterals: true), "lit \\*x\\* \\_y\\_ **bold**")
78 }
79
80 func testEscapedModeGuardsLineLeadingBlocks() {
81 let a = NSAttributedString(string: "- a\n2. b\n> q")
82 XCTAssertEqual(composerMarkdown(of: a, escapeLiterals: true), "\\- a\n2\\. b\n\\> q")
83 }
84
85 func testEscapedModeBackslashAndBacktick() {
86 let a = NSAttributedString(string: "\\* and `tick`")
87 XCTAssertEqual(composerMarkdown(of: a, escapeLiterals: true), "\\\\\\* and \\`tick\\`")
88 }
89}
90