gitbayTests/MarkdownTests.swift
206 lines · 6727 bytes
28 symbols in this file
MarkdownTableTestsblocksheaderSeparatorAndBodyBecomeATablealignmentComesFromTheSeparatorRowouterPipesAreOptionalAndEscapedPipesStayInTheCellraggedRowsArePaddedToTheHeaderWidthaTableEndsAtABlankLineAndTheTextAfterItIsAParagraphaTableInterruptsThePrecedingParagraphaPipeInProseIsNotATableMarkdownLinkTestslinksrelativeTargetsResolveAgainstTheRepositoryabsoluteTargetsAreUntouchedwithoutOptionsTargetsAreLeftAloneMarkdownListTestsblocksindentedContinuationLinesStayInTheItemunindentedContinuationLinesStayInTheItemorderedItemsTakeContinuationLinesaListEndsAtABlankLineaHeadingEndsAListMarkdownHeadingTestsblockshashWithoutSpaceIsAParagraphhashReferenceDoesNotEndAParagraphhashReferenceContinuesAListItemheadingNeedsSpaceOrEndOfLinesevenHashesIsAParagraph
1import Foundation
2import OrgSwift
3import Testing
4@testable import gitbay
5
6@MainActor
7struct MarkdownTableTests {
8
9 private func blocks(_ markdown: String) -> [MarkdownView.Block] {
10 MarkdownView(markdown: markdown).blocks
11 }
12
13 @Test func headerSeparatorAndBodyBecomeATable() {
14 let parsed = blocks("""
15 | Add | Documented in |
16 |---|---|
17 | An RSS feed | [Collections](https://orgo.krz.sh/) |
18 """)
19 #expect(parsed == [.table(MarkdownView.Table(
20 header: ["Add", "Documented in"],
21 rows: [["An RSS feed", "[Collections](https://orgo.krz.sh/)"]],
22 alignments: [.leading, .leading]
23 ))])
24 }
25
26 @Test func alignmentComesFromTheSeparatorRow() {
27 let parsed = blocks("""
28 | l | c | r |
29 |:--|:-:|--:|
30 | a | b | c |
31 """)
32 guard case .table(let table) = parsed.first else {
33 Issue.record("expected a table, got \(parsed)")
34 return
35 }
36 #expect(table.alignments == [.leading, .center, .trailing])
37 }
38
39 @Test func outerPipesAreOptionalAndEscapedPipesStayInTheCell() {
40 let parsed = blocks("""
41 a | b
42 --- | ---
43 x \\| y | z
44 """)
45 guard case .table(let table) = parsed.first else {
46 Issue.record("expected a table, got \(parsed)")
47 return
48 }
49 #expect(table.header == ["a", "b"])
50 #expect(table.rows == [["x \\| y", "z"]])
51 }
52
53 @Test func raggedRowsArePaddedToTheHeaderWidth() {
54 let parsed = blocks("""
55 | a | b | c |
56 |---|---|---|
57 | 1 |
58 | 1 | 2 | 3 | 4 |
59 """)
60 guard case .table(let table) = parsed.first else {
61 Issue.record("expected a table, got \(parsed)")
62 return
63 }
64 #expect(table.rows == [["1", "", ""], ["1", "2", "3"]])
65 }
66
67 @Test func aTableEndsAtABlankLineAndTheTextAfterItIsAParagraph() {
68 let parsed = blocks("""
69 | a |
70 |---|
71 | 1 |
72
73 After the table.
74 """)
75 #expect(parsed.count == 2)
76 #expect(parsed.last == .paragraph("After the table."))
77 }
78
79 @Test func aTableInterruptsThePrecedingParagraph() {
80 let parsed = blocks("""
81 Each of these has a page in the guide:
82 | a |
83 |---|
84 | 1 |
85 """)
86 #expect(parsed.first == .paragraph("Each of these has a page in the guide:"))
87 #expect(parsed.count == 2)
88 }
89
90 @Test func aPipeInProseIsNotATable() {
91 let parsed = blocks("Run `a | b` to pipe.\nIt is not a table.")
92 #expect(parsed == [.paragraph("Run `a | b` to pipe. It is not a table.")])
93 }
94}
95
96@MainActor
97struct MarkdownLinkTests {
98
99 private let options = OrgRenderOptions(
100 host: "gitbay.org",
101 owner: "krz",
102 repositoryName: "org-swift",
103 ref: "HEAD",
104 readmePath: "README.md",
105 imagePathSegment: "raw",
106 linkPathSegment: "blob"
107 )
108
109 private func links(_ markdown: String, options: OrgRenderOptions?) -> [String] {
110 MarkdownView.attributed(markdown, linkOptions: options)
111 .runs.compactMap { $0.link?.absoluteString }
112 }
113
114 /// A relative target resolves to the repository's file page. Left alone it is a
115 /// scheme-less URL, which renders styled and does nothing when tapped.
116 @Test func relativeTargetsResolveAgainstTheRepository() {
117 #expect(links("See [ARCHITECTURE.md](ARCHITECTURE.md).", options: options)
118 == ["https://gitbay.org/krz/org-swift/blob/HEAD/ARCHITECTURE.md"])
119 #expect(links("[gaps](./GAPS.md)", options: options)
120 == ["https://gitbay.org/krz/org-swift/blob/HEAD/GAPS.md"])
121 #expect(links("[corpus](../org-conformance)", options: options)
122 == ["https://gitbay.org/krz/org-swift/blob/HEAD/org-conformance"])
123 #expect(links("[docs](/docs/x.md)", options: options)
124 == ["https://gitbay.org/krz/org-swift/blob/HEAD/docs/x.md"])
125 }
126
127 @Test func absoluteTargetsAreUntouched() {
128 #expect(links("[home](https://example.com/a)", options: options)
129 == ["https://example.com/a"])
130 }
131
132 /// A body with no file to be relative to keeps its targets as authored.
133 @Test func withoutOptionsTargetsAreLeftAlone() {
134 #expect(links("See [ARCHITECTURE.md](ARCHITECTURE.md).", options: nil)
135 == ["ARCHITECTURE.md"])
136 }
137}
138
139@MainActor
140struct MarkdownListTests {
141
142 private func blocks(_ markdown: String) -> [MarkdownView.Block] {
143 MarkdownView(markdown: markdown).blocks
144 }
145
146 @Test func indentedContinuationLinesStayInTheItem() {
147 let parsed = blocks("""
148 - Org source files in `content/`.
149 That directory is the URL root.
150 - HTML templates.
151 """)
152 #expect(parsed == [.bullet([
153 "Org source files in `content/`. That directory is the URL root.",
154 "HTML templates.",
155 ])])
156 }
157
158 @Test func unindentedContinuationLinesStayInTheItem() {
159 let parsed = blocks("- one\ncontinued\n- two")
160 #expect(parsed == [.bullet(["one continued", "two"])])
161 }
162
163 @Test func orderedItemsTakeContinuationLines() {
164 let parsed = blocks("1. first\n more\n2. second")
165 #expect(parsed == [.ordered(["first more", "second"])])
166 }
167
168 @Test func aListEndsAtABlankLine() {
169 let parsed = blocks("- one\n\nAfter the list.")
170 #expect(parsed == [.bullet(["one"]), .paragraph("After the list.")])
171 }
172
173 @Test func aHeadingEndsAList() {
174 let parsed = blocks("- one\n## Next")
175 #expect(parsed == [.bullet(["one"]), .heading(level: 2, text: "Next")])
176 }
177}
178
179@MainActor
180struct MarkdownHeadingTests {
181
182 private func blocks(_ markdown: String) -> [MarkdownView.Block] {
183 MarkdownView(markdown: markdown).blocks
184 }
185
186 @Test func hashWithoutSpaceIsAParagraph() {
187 #expect(blocks("#12 fixed this") == [.paragraph("#12 fixed this")])
188 }
189
190 @Test func hashReferenceDoesNotEndAParagraph() {
191 #expect(blocks("see\n#12 for context") == [.paragraph("see #12 for context")])
192 }
193
194 @Test func hashReferenceContinuesAListItem() {
195 #expect(blocks("- fixed in\n#12") == blocks("- fixed in #12"))
196 }
197
198 @Test func headingNeedsSpaceOrEndOfLine() {
199 #expect(blocks("## Usage") == [.heading(level: 2, text: "Usage")])
200 #expect(blocks("#") == [.heading(level: 1, text: "")])
201 }
202
203 @Test func sevenHashesIsAParagraph() {
204 #expect(blocks("####### x") == [.paragraph("####### x")])
205 }
206}