diff options
| author | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-06-16 12:08:21 +0200 |
|---|---|---|
| committer | Ruben Beltran del Rio <jj@r.bdr.sh> | 2026-06-16 12:22:47 +0200 |
| commit | 501fdce29e4d2c46c4708ad7f44056878e0db3fa (patch) | |
| tree | db923b77037db9f5b37600a6e34c3bb2e8a971be /Tests/NorgKitTests/TreeFolderTests.swift | |
Initial extraction from Norganize1.0.0
Diffstat (limited to 'Tests/NorgKitTests/TreeFolderTests.swift')
| -rw-r--r-- | Tests/NorgKitTests/TreeFolderTests.swift | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/Tests/NorgKitTests/TreeFolderTests.swift b/Tests/NorgKitTests/TreeFolderTests.swift new file mode 100644 index 0000000..f3fe0a3 --- /dev/null +++ b/Tests/NorgKitTests/TreeFolderTests.swift @@ -0,0 +1,145 @@ +import Foundation +import Testing + +@testable import NorgKit + +struct TreeFolderTests { + + private func headingLevel(_ node: NorgNode?) -> Int? { + guard case .heading(let level, _, _, _)? = node?.block else { return nil } + return level + } + + private func plainText(_ node: NorgNode?) -> String? { + switch node?.block { + case .heading(_, _, let content, _), + .paragraph(let content, _), + .unorderedListItem(_, _, let content, _), + .quote(_, _, let content, _): + return content.plainText + default: + return nil + } + } + + @Test func headingOwnsFollowingParagraph() { + let tree = NorgParser.parseTree("* Title\nbody paragraph") + #expect(tree.count == 1) + #expect(headingLevel(tree.first) == 1) + #expect(tree.first?.children.count == 1) + #expect(plainText(tree.first?.children.first) == "body paragraph") + } + + @Test func subHeadingNestsUnderParent() { + let tree = NorgParser.parseTree("* H1\npara one\n** H2\npara two") + #expect(tree.count == 1) + let h1 = tree.first + #expect(headingLevel(h1) == 1) + #expect(h1?.children.count == 2) + #expect(plainText(h1?.children.first) == "para one") + let h2 = h1?.children.last + #expect(headingLevel(h2) == 2) + #expect(plainText(h2?.children.first) == "para two") + } + + @Test func sameOrHigherHeadingClosesScope() { + let tree = NorgParser.parseTree("* H1\nbody\n* H2") + #expect(tree.count == 2) + #expect(headingLevel(tree.first) == 1) + #expect(headingLevel(tree.last) == 1) + #expect(tree.first?.children.count == 1) + #expect(tree.last?.children.isEmpty == true) + } + + @Test func weakDelimiterClosesOneLevel() { + let tree = NorgParser.parseTree("* H1\n** H2\ninner\n*** H3\ninnerinner\n---\nback in h1") + #expect(tree.count == 1) + let h1 = tree.first + #expect(h1?.children.count == 1) + + let h2 = h1?.children.first + #expect(h2?.children.count == 3) + #expect(headingLevel(h2) == 2) + #expect(plainText(h2?.children.first) == "inner") + + let h3 = h2?.children[1] + #expect(headingLevel(h3) == 3) + #expect(plainText(h3?.children.first) == "innerinner") + #expect(h3?.children.count == 1) + + #expect(plainText(h2?.children.last) == "back in h1") + #expect( + h1?.children.allSatisfy { + if case .weakDelimiter = $0.block { return false } + return true + } == true) + } + + @Test func strongDelimiterClosesAllLevels() { + let tree = NorgParser.parseTree("* H1\n** H2\ninner\n*** H3\ninnerinner\n===\nat root") + #expect(tree.count == 2) + #expect(headingLevel(tree.first) == 1) + #expect(plainText(tree.last) == "at root") + } + + @Test func nestableItemsNestByLevelAndType() { + let tree = NorgParser.parseTree("- one\n-- nested\n--- deeper\n- two") + #expect(tree.count == 2) + let one = tree.first + #expect(plainText(one) == "one") + #expect(one?.children.count == 1) + let nested = one?.children.first + #expect(plainText(nested) == "nested") + #expect(plainText(nested?.children.first) == "deeper") + #expect(plainText(tree.last) == "two") + } + + @Test func differentNestableTypeDoesNotNest() { + let tree = NorgParser.parseTree("- item\n~ ordered") + #expect(tree.count == 2) + #expect(tree.first?.children.isEmpty == true) + } + + @Test func quotesNestByLevel() { + let tree = NorgParser.parseTree("> one\n>> nested\n> two") + #expect(tree.count == 2) + let one = tree.first + #expect(plainText(one) == "one") + #expect(one?.children.count == 1) + #expect(plainText(one?.children.first) == "nested") + #expect(plainText(tree.last) == "two") + } + + @Test func listNestsUnderHeading() { + let tree = NorgParser.parseTree("* Tasks\n- ( ) a\n-- ( ) sub") + let heading = tree.first + #expect(headingLevel(heading) == 1) + #expect(heading?.children.count == 1) + let item = heading?.children.first + #expect(plainText(item) == "a") + #expect(plainText(item?.children.first) == "sub") + } + + @Test func horizontalRuleStaysALeaf() { + let tree = NorgParser.parseTree("* H\n___\ntext") + let heading = tree.first + #expect(heading?.children.count == 2) + if case .horizontalRule = heading?.children.first?.block { + } else { + Issue.record("Expected horizontal rule leaf") + } + #expect(heading?.children.first?.children.isEmpty == true) + } + + @Test func parseTreeMatchesParseThenFold() { + let source = "* H1\npara\n** H2\n- list\n-- sub\n===\nroot para" + #expect(NorgParser.parseTree(source) == NorgParser.parse(source).tree()) + } + + @Test func treeRoundTripsThroughJSON() throws { + let tree = NorgParser.parseTree("* H1\nbody\n** H2\n- ( ) task") + let data = try JSONEncoder().encode(tree) + let decoded = try JSONDecoder().decode([NorgNode].self, from: data) + #expect(decoded == tree) + } +} |