import Testing
import Foundation
@testable import EdmundCore
// String-assertion tests for the HTML renderer: parse markdown → render → assert
// HTML. Pure logic, no AppKit/window needed.
@Suite("HTMLRenderer — core GFM")
struct HTMLRendererCoreTests {
private func html(_ md: String) -> String { HTMLRenderer.render(markdown: md) }
@Test("Headings render h1…h6")
func headings() {
#expect(html("# Title") == "
Title ")
#expect(html("### Sub") == "Sub ")
#expect(html("###### Six") == "Six ")
}
@Test("Setext headings render h1/h2")
func setextHeadings() {
#expect(html("Title\n===") == "Title ")
#expect(html("Title\n---") == "Title ")
}
@Test("Paragraph wraps in ")
func paragraph() {
#expect(html("hello world") == "
hello world
")
}
@Test("Emphasis, strong, strikethrough, inline code")
func inlineMarks() {
#expect(html("*i*") == "i
")
#expect(html("**b**") == "b
")
#expect(html("~~s~~") == "s
")
#expect(html("`x`") == "x
")
}
@Test("Fenced code block keeps language class and escapes content")
func codeBlock() {
let out = html("```swift\nlet x = a < b && c > d\n```")
#expect(out.contains(""))
#expect(out.contains("a < b && c > d"))
#expect(!out.contains("a < b"))
}
@Test("Unordered, ordered, and task lists")
func lists() {
// A tight list (GFM §5.3): no wrapper inside items.
#expect(html("- a\n- b") == "
")
#expect(html("1. a").hasPrefix(""))
#expect(html("3. a\n4. b").hasPrefix(""))
let task = html("- [ ] todo\n- [x] done")
#expect(task.contains(" wrappers")
func looseList() {
let out = html("- a\n\n- b")
#expect(out.contains("a
"))
#expect(out.contains("b
"))
}
@Test("A multi-block item with a blank gap makes the whole list loose")
func looseByMultiBlockItem() {
let out = html("- a\n\n second\n- b")
#expect(out.contains("a
"))
#expect(out.contains("second
"))
#expect(out.contains("b
"))
}
@Test("Nested loose list gets ; the tight outer list doesn't")
func mixedNesting() {
let out = html("- a\n - x\n\n - y\n- b")
#expect(out.contains("
x
"))
#expect(out.contains("y
"))
#expect(!out.contains("a
"))
#expect(out.contains("b "))
}
@Test("Link title renders as an escaped title attribute")
func linkTitle() {
#expect(html("[x](https://example.com \"hi there\")")
== "x
")
#expect(html("[x](https://example.com \"a & b\")").contains("title=\"a & b\""))
let internalLink = html("[o](other.md \"note\")")
#expect(internalLink.contains(""))
#expect(out.contains("
"))
#expect(out.contains("a "))
#expect(out.contains("b "))
#expect(out.contains("c "))
#expect(out.contains("1 "))
}
@Test("Thematic break → ")
func thematicBreak() {
#expect(html("---").contains(" "))
}
@Test("External links keep their real href")
func links() {
#expect(html("[text](https://example.com)") == "text
")
#expect(html("[m](mailto:a@b.com)").contains(""))
}
@Test("In-page anchor links keep their fragment href")
func anchorLink() {
#expect(html("[go](#section)").contains(" go "))
}
@Test("Relative/internal links route through the private link scheme")
func internalLink() {
let out = html("[other](notes/other.md)")
#expect(out.contains("let"))
#expect(out.contains("1 "))
#expect(out.contains(""))
}
@Test("Code token spans still escape special characters")
func codeTokensEscape() {
let out = html("```\na < b && c\n```")
#expect(out.contains("a < b && c"))
#expect(!out.contains("a < b"))
}
@Test("Image emits a placeholder carrying the raw source for the asset pass")
func image() {
let out = html("")
#expect(out.contains(" "))
}
@Test("Wikilink renders as a private-scheme anchor with encoded target")
func wikilink() {
let out = html("see [[My Note#Heading]] here")
#expect(out.contains(" My Note#Heading "))
}
@Test("Wikilink alias shows the alias as display text")
func wikilinkAlias() {
let out = html("[[Target|shown]]")
#expect(out.contains(">shown"))
}
@Test("Plain block quote stays a blockquote")
func blockQuote() {
#expect(html("> quoted") == "quoted
")
}
}
@Suite("HTMLRenderer — escaping & security")
struct HTMLRendererEscapingTests {
private func html(_ md: String) -> String { HTMLRenderer.render(markdown: md) }
@Test("Inline & b")
#expect(!out.contains("")
#expect(!out.contains("