XDMarkupTests.swift (3386B)
1 import Foundation 2 import Testing 3 4 @testable import Crossmate 5 6 @Suite("XDMarkup") 7 struct XDMarkupTests { 8 9 private struct Run: Equatable { 10 let text: String 11 let intent: InlinePresentationIntent? 12 } 13 14 /// The styled substrings of an attributed string, paired with the emphasis 15 /// intent applied to each, matching `AttributedString.runs`. 16 private func runs(_ attributed: AttributedString) -> [Run] { 17 attributed.runs.map { run in 18 Run(text: String(attributed[run.range].characters), intent: run.inlinePresentationIntent) 19 } 20 } 21 22 @Test("Plain clue text round-trips with no emphasis") 23 func plainTextHasNoEmphasis() { 24 let attributed = XDMarkup.attributed("Capital of France") 25 #expect(runs(attributed) == [Run(text: "Capital of France", intent: nil)]) 26 #expect(XDMarkup.stripped("Capital of France") == "Capital of France") 27 } 28 29 @Test("Italic span renders emphasized and strips its braces") 30 func italicSpan() { 31 let source = "{/Why Keanu is such a generous tipper?/}" 32 let attributed = XDMarkup.attributed(source) 33 #expect(runs(attributed) == [Run(text: "Why Keanu is such a generous tipper?", intent: .emphasized)]) 34 #expect(XDMarkup.stripped(source) == "Why Keanu is such a generous tipper?") 35 } 36 37 @Test("Mid-clue italics leave surrounding text plain") 38 func partialItalics() { 39 let attributed = XDMarkup.attributed("As seen in {/Hamlet/}, e.g.") 40 #expect(runs(attributed) == [ 41 Run(text: "As seen in ", intent: nil), 42 Run(text: "Hamlet", intent: .emphasized), 43 Run(text: ", e.g.", intent: nil) 44 ]) 45 } 46 47 @Test("Bold maps to strong emphasis") 48 func boldSpan() { 49 let attributed = XDMarkup.attributed("{*Note*}: read carefully") 50 #expect(runs(attributed) == [ 51 Run(text: "Note", intent: .stronglyEmphasized), 52 Run(text: ": read carefully", intent: nil) 53 ]) 54 } 55 56 @Test("Strikethrough span carries the strikethrough intent") 57 func strikethroughSpan() { 58 let attributed = XDMarkup.attributed("{-gone-}") 59 #expect(runs(attributed) == [Run(text: "gone", intent: .strikethrough)]) 60 } 61 62 @Test("Strikethrough delimiter only closes at a brace, not an internal hyphen") 63 func strikethroughWithInternalHyphen() { 64 #expect(XDMarkup.stripped("{-strike-thru-}") == "strike-thru") 65 } 66 67 @Test("A literal asterisk in plain prose is left untouched") 68 func literalAsteriskUntouched() { 69 // Starred theme clues carry a bare '*' that must not be read as markup. 70 let source = "*Sphere of influence" 71 #expect(runs(XDMarkup.attributed(source)) == [Run(text: source, intent: nil)]) 72 #expect(XDMarkup.stripped(source) == source) 73 } 74 75 @Test("An unterminated span stays literal") 76 func unterminatedSpanStaysLiteral() { 77 let source = "Set in {/the past" 78 #expect(runs(XDMarkup.attributed(source)) == [Run(text: source, intent: nil)]) 79 #expect(XDMarkup.stripped(source) == source) 80 } 81 82 @Test("A brace with no valid marker stays literal") 83 func braceWithoutMarkerStaysLiteral() { 84 let source = "Score of {0}" 85 #expect(runs(XDMarkup.attributed(source)) == [Run(text: source, intent: nil)]) 86 #expect(XDMarkup.stripped(source) == source) 87 } 88 }