package com.sptmobile.render import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test class MarkdownTest { // Distinct sentinel colors so span assertions are unambiguous. private val styles = MarkdownStyles( codeBg = Color(0xFF111111), codeFg = Color(0xFF222222), link = Color(0xFF333333), quote = Color(0xFF444444), xmlTag = Color(0xFF555555), xmlAttr = Color(0xFF666666), xmlValue = Color(0xFF777777), xmlComment = Color(0xFF888888), ) // [unit->REQ-RICH-RENDER] // Inline markers collapse to plain text; the emphasis rides as spans. @Test fun inlineEmphasisRendersToPlainTextWithSpans() { val a = renderMarkdown("a **b** _c_ `d`", styles) assertEquals("a b c d", a.text) assertTrue( "bold span over 'b'", a.spanStyles.any { it.item.fontWeight == FontWeight.Bold && a.text.substring(it.start, it.end) == "b" }, ) assertTrue( "italic span over 'c'", a.spanStyles.any { it.item.fontStyle == FontStyle.Italic && a.text.substring(it.start, it.end) == "c" }, ) assertTrue( "code span carries the code fg", a.spanStyles.any { it.item.color == styles.codeFg && a.text.substring(it.start, it.end) == "d" }, ) } // [unit->REQ-RICH-RENDER] // Unmatched markers are literal — arbitrary agent text never breaks. @Test fun unmatchedMarkersRenderLiterally() { assertEquals("a * b _ c", renderMarkdown("a * b _ c", styles).text) } // [unit->REQ-RICH-RENDER] // A link's [text] shows; the (url) is stripped and the text is colored. @Test fun linkTextShowsUrlStripped() { val a = renderMarkdown("see [docs](https://x.y)", styles) assertEquals("see docs", a.text) assertTrue(a.spanStyles.any { it.item.color == styles.link && a.text.substring(it.start, it.end) == "docs" }) } // [unit->REQ-RICH-RENDER] // Headings and list markers become readable prefixes/emphasis, not literals. @Test fun headingAndListPrefixes() { assertEquals("Title", renderMarkdown("## Title", styles).text) assertEquals(" • one\n • two", renderMarkdown("- one\n- two", styles).text) assertEquals(" 1. first", renderMarkdown("1. first", styles).text) } // [unit->REQ-RICH-RENDER] // A fenced ```xml block is emitted verbatim and colored by highlightXml. @Test fun fencedXmlBlockIsHighlighted() { val src = "```xml\nx\n```" val a = renderMarkdown(src, styles) assertEquals("x", a.text) assertTrue("tag punctuation/name colored", a.spanStyles.any { it.item.color == styles.xmlTag }) assertTrue("attr name colored", a.spanStyles.any { it.item.color == styles.xmlAttr }) assertTrue("attr value colored", a.spanStyles.any { it.item.color == styles.xmlValue }) } // [unit->REQ-RICH-RENDER] // highlightXml tokenizes tag / attr / value / comment; malformed is safe. @Test fun highlightXmlTokenizesAndToleratesMalformed() { val a = highlightXml("t", styles) assertEquals("t", a.text) assertTrue(a.spanStyles.any { it.item.color == styles.xmlComment && a.text.substring(it.start, it.end) == "" }) // An unclosed tag must not throw and must preserve the text. assertEquals("