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) } }