Files
Outpost/Vendor/swift-markdown-engine/Tests/MarkdownEngineTests/BlockParserTests.swift
T
Puranjay Savar Mattas 6e1ffb96fd chore: vendor swift-markdown-engine as plain tracked source
Convert from git submodule to plain vendored copy. No push access to
upstream nodes-app/swift-markdown-engine meant our local fix commit
(05c1720) was stranded and unreachable from any remote on fresh
clones/CI. Vendoring as plain files folds it into normal repo history
instead.
2026-08-20 13:26:13 +01:00

106 lines
3.8 KiB
Swift

//
// BlockParserTests.swift
// MarkdownEngineTests
//
// Phase 1 — test-first specification of the block-structure pass. Each test
// pins an exact, tiling block decomposition (every UTF-16 unit covered once).
//
import Foundation
import Testing
@testable import MarkdownEngine
@Suite("Phase 1 — block parser")
struct BlockParserTests {
private func b(_ kind: BlockKind, _ location: Int, _ length: Int) -> Block {
Block(kind: kind, range: NSRange(location: location, length: length))
}
/// Sanity: the parsed blocks must tile `text` with no gaps or overlaps.
private func assertTiles(_ text: String) {
let blocks = BlockParser.parse(text)
let total = (text as NSString).length
guard total > 0 else { return }
var cursor = 0
for block in blocks {
#expect(block.range.location == cursor, "gap/overlap before \(block)")
cursor = NSMaxRange(block.range)
}
#expect(cursor == total, "blocks do not cover the whole string")
}
@Test("single line is one paragraph")
func singleParagraph() {
#expect(BlockParser.parse("hello world") == [b(.paragraph, 0, 11)])
}
@Test("blank line separates blocks and the result tiles the whole string")
func blankSeparates() {
let text = "a\n\nb"
#expect(BlockParser.parse(text) == [b(.paragraph, 0, 2), b(.blank, 2, 1), b(.paragraph, 3, 1)])
assertTiles(text)
}
@Test("consecutive plain lines merge into one paragraph")
func mergedParagraph() {
#expect(BlockParser.parse("a\nb\nc") == [b(.paragraph, 0, 5)])
}
@Test("ATX heading is its own block")
func heading() {
let text = "# Title\n\nbody"
#expect(BlockParser.parse(text) == [b(.heading, 0, 8), b(.blank, 8, 1), b(.paragraph, 9, 4)])
assertTiles(text)
}
@Test("thematic break is its own block")
func thematicBreak() {
let text = "a\n\n---\n\nb"
#expect(BlockParser.parse(text) == [
b(.paragraph, 0, 2), b(.blank, 2, 1), b(.thematicBreak, 3, 4), b(.blank, 7, 1), b(.paragraph, 8, 1),
])
assertTiles(text)
}
@Test("fenced code block is a single opaque block")
func fencedCode() {
#expect(BlockParser.parse("```\ncode\n```\n") == [b(.fencedCode, 0, 13)])
}
@Test("an unclosed fence stays literal text — blocks below are not swallowed")
func unclosedFenceDoesNotSwallow() {
// ```\n then a table, a thematic break, block LaTeX, and a link
// paragraph — none of them may end up inside a code block.
let text = "```\n\n|a|b|\n|-|-|\n|1|2|\n\n---\n\n$$x$$\n\n[l](u)"
let blocks = BlockParser.parse(text)
#expect(!blocks.contains { $0.kind == .fencedCode })
#expect(blocks.contains { $0.kind == .table })
#expect(blocks.contains { $0.kind == .thematicBreak })
#expect(blocks.contains { $0.kind == .blockLatex })
assertTiles(text)
}
@Test("an unclosed fence merges into the paragraph it starts")
func unclosedFenceIsParagraph() {
#expect(BlockParser.parse("```\n[l](u)") == [b(.paragraph, 0, 10)])
#expect(BlockParser.parse("a\n```swift") == [b(.paragraph, 0, 10)])
}
@Test("a closed fence below an unclosed opener pairs with it")
func fencePairing() {
// The first ``` pairs with the next fence line — CommonMark pairing —
// so this is one code block followed by a paragraph.
let text = "```\ncode\n```\ntail"
#expect(BlockParser.parse(text) == [b(.fencedCode, 0, 13), b(.paragraph, 13, 4)])
assertTiles(text)
}
@Test("consecutive blockquote lines form one block, ended by a non-quote line")
func blockquote() {
let text = "> a\n> b\nc"
#expect(BlockParser.parse(text) == [b(.blockquote, 0, 8), b(.paragraph, 8, 1)])
assertTiles(text)
}
}