import Foundation import OrgSwift import Testing @testable import gitbay @MainActor struct MarkdownTableTests { private func blocks(_ markdown: String) -> [MarkdownView.Block] { MarkdownView(markdown: markdown).blocks } @Test func headerSeparatorAndBodyBecomeATable() { let parsed = blocks(""" | Add | Documented in | |---|---| | An RSS feed | [Collections](https://orgo.krz.sh/) | """) #expect(parsed == [.table(MarkdownView.Table( header: ["Add", "Documented in"], rows: [["An RSS feed", "[Collections](https://orgo.krz.sh/)"]], alignments: [.leading, .leading] ))]) } @Test func alignmentComesFromTheSeparatorRow() { let parsed = blocks(""" | l | c | r | |:--|:-:|--:| | a | b | c | """) guard case .table(let table) = parsed.first else { Issue.record("expected a table, got \(parsed)") return } #expect(table.alignments == [.leading, .center, .trailing]) } @Test func outerPipesAreOptionalAndEscapedPipesStayInTheCell() { let parsed = blocks(""" a | b --- | --- x \\| y | z """) guard case .table(let table) = parsed.first else { Issue.record("expected a table, got \(parsed)") return } #expect(table.header == ["a", "b"]) #expect(table.rows == [["x \\| y", "z"]]) } @Test func raggedRowsArePaddedToTheHeaderWidth() { let parsed = blocks(""" | a | b | c | |---|---|---| | 1 | | 1 | 2 | 3 | 4 | """) guard case .table(let table) = parsed.first else { Issue.record("expected a table, got \(parsed)") return } #expect(table.rows == [["1", "", ""], ["1", "2", "3"]]) } @Test func aTableEndsAtABlankLineAndTheTextAfterItIsAParagraph() { let parsed = blocks(""" | a | |---| | 1 | After the table. """) #expect(parsed.count == 2) #expect(parsed.last == .paragraph("After the table.")) } @Test func aTableInterruptsThePrecedingParagraph() { let parsed = blocks(""" Each of these has a page in the guide: | a | |---| | 1 | """) #expect(parsed.first == .paragraph("Each of these has a page in the guide:")) #expect(parsed.count == 2) } @Test func aPipeInProseIsNotATable() { let parsed = blocks("Run `a | b` to pipe.\nIt is not a table.") #expect(parsed == [.paragraph("Run `a | b` to pipe. It is not a table.")]) } } @MainActor struct MarkdownLinkTests { private let options = OrgRenderOptions( host: "gitbay.org", owner: "krz", repositoryName: "org-swift", ref: "HEAD", readmePath: "README.md", imagePathSegment: "raw", linkPathSegment: "blob" ) private func links(_ markdown: String, options: OrgRenderOptions?) -> [String] { MarkdownView.attributed(markdown, linkOptions: options) .runs.compactMap { $0.link?.absoluteString } } /// A relative target resolves to the repository's file page. Left alone it is a /// scheme-less URL, which renders styled and does nothing when tapped. @Test func relativeTargetsResolveAgainstTheRepository() { #expect(links("See [ARCHITECTURE.md](ARCHITECTURE.md).", options: options) == ["https://gitbay.org/krz/org-swift/blob/HEAD/ARCHITECTURE.md"]) #expect(links("[gaps](./GAPS.md)", options: options) == ["https://gitbay.org/krz/org-swift/blob/HEAD/GAPS.md"]) #expect(links("[corpus](../org-conformance)", options: options) == ["https://gitbay.org/krz/org-swift/blob/HEAD/org-conformance"]) #expect(links("[docs](/docs/x.md)", options: options) == ["https://gitbay.org/krz/org-swift/blob/HEAD/docs/x.md"]) } @Test func absoluteTargetsAreUntouched() { #expect(links("[home](https://example.com/a)", options: options) == ["https://example.com/a"]) } /// A body with no file to be relative to keeps its targets as authored. @Test func withoutOptionsTargetsAreLeftAlone() { #expect(links("See [ARCHITECTURE.md](ARCHITECTURE.md).", options: nil) == ["ARCHITECTURE.md"]) } } @MainActor struct MarkdownListTests { private func blocks(_ markdown: String) -> [MarkdownView.Block] { MarkdownView(markdown: markdown).blocks } @Test func indentedContinuationLinesStayInTheItem() { let parsed = blocks(""" - Org source files in `content/`. That directory is the URL root. - HTML templates. """) #expect(parsed == [.bullet([ "Org source files in `content/`. That directory is the URL root.", "HTML templates.", ])]) } @Test func unindentedContinuationLinesStayInTheItem() { let parsed = blocks("- one\ncontinued\n- two") #expect(parsed == [.bullet(["one continued", "two"])]) } @Test func orderedItemsTakeContinuationLines() { let parsed = blocks("1. first\n more\n2. second") #expect(parsed == [.ordered(["first more", "second"])]) } @Test func aListEndsAtABlankLine() { let parsed = blocks("- one\n\nAfter the list.") #expect(parsed == [.bullet(["one"]), .paragraph("After the list.")]) } @Test func aHeadingEndsAList() { let parsed = blocks("- one\n## Next") #expect(parsed == [.bullet(["one"]), .heading(level: 2, text: "Next")]) } }