aboutsummaryrefslogtreecommitdiff
path: root/Tests/NorgKitTests/TreeFolderTests.swift
diff options
context:
space:
mode:
authorRuben Beltran del Rio <jj@r.bdr.sh>2026-06-16 12:08:21 +0200
committerRuben Beltran del Rio <jj@r.bdr.sh>2026-06-16 12:22:47 +0200
commit501fdce29e4d2c46c4708ad7f44056878e0db3fa (patch)
treedb923b77037db9f5b37600a6e34c3bb2e8a971be /Tests/NorgKitTests/TreeFolderTests.swift
Initial extraction from Norganize1.0.0
Diffstat (limited to 'Tests/NorgKitTests/TreeFolderTests.swift')
-rw-r--r--Tests/NorgKitTests/TreeFolderTests.swift145
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)
+ }
+}