gitbayTests/BodyReferenceTests.swift
123 lines · 4562 bytes
19 symbols in this file
BodyReferenceTestsrefsbareHashIsAnIssueInThisRepobangIsAMergeRequestcrossRepoReferenceNamesItsRepoaBareReferenceNeedsABoundaryBeforedigitsMustEndAtAWordBoundaryaCrossReferenceIsNotAlsoReadAsABareOneoffsetsCountCharactersNotBytestheURLRoundTripsBodyReferenceMarkdownTestslinksreferencesBecomeLinkscodeSpansAndExistingLinksAreLeftAlonenoRepoMeansNoReferenceLinksBodyReferenceOrgTestslinksreferencesBecomeLinksverbatimCodeAndExistingLinksAreLeftAlone
1import Foundation
2import OrgSwift
3import Testing
4@testable import gitbay
5
6/// `#N`, `!N`, `owner/name#N` and `owner/name!N`, found the way the web's
7/// autolink package finds them: `#` is an issue, `!` a merge request.
8struct BodyReferenceTests {
9
10 private func refs(_ text: String, repo: String = "krz/gitbay") -> [BodyReference] {
11 bodyReferences(in: text, repo: repo)
12 }
13
14 @Test func bareHashIsAnIssueInThisRepo() {
15 #expect(refs("see #12 please") == [
16 BodyReference(range: 4..<7, destination: .issue(repo: "krz/gitbay", number: 12)),
17 ])
18 }
19
20 @Test func bangIsAMergeRequest() {
21 #expect(refs("!3") == [
22 BodyReference(range: 0..<2, destination: .mr(repo: "krz/gitbay", number: 3)),
23 ])
24 }
25
26 @Test func crossRepoReferenceNamesItsRepo() {
27 #expect(refs("fixed in krz/solar#7 and krz/solar!8.") == [
28 BodyReference(range: 9..<20, destination: .issue(repo: "krz/solar", number: 7)),
29 BodyReference(range: 25..<36, destination: .mr(repo: "krz/solar", number: 8)),
30 ])
31 }
32
33 @Test func aBareReferenceNeedsABoundaryBefore() {
34 #expect(refs("a1b2#3").isEmpty)
35 #expect(refs("(#4)").map(\.range) == [1..<3])
36 #expect(refs("[#5]").map(\.range) == [1..<3])
37 #expect(refs("line\n#6").map(\.range) == [5..<7])
38 }
39
40 @Test func digitsMustEndAtAWordBoundary() {
41 #expect(refs("#12abc").isEmpty)
42 #expect(refs("#12, #13").count == 2)
43 }
44
45 @Test func aCrossReferenceIsNotAlsoReadAsABareOne() {
46 #expect(refs("krz/solar#7").count == 1)
47 }
48
49 @Test func offsetsCountCharactersNotBytes() {
50 #expect(refs("é #1").map(\.range) == [2..<4])
51 }
52
53 @Test func theURLRoundTrips() throws {
54 for destination in [NotificationDestination.issue(repo: "krz/gitbay", number: 12),
55 .mr(repo: "krz/solar", number: 3)] {
56 let url = try #require(referenceURL(destination))
57 #expect(referenceDestination(url) == destination)
58 }
59 #expect(referenceDestination(URL(string: "https://gitbay.org/krz/gitbay/issues/1")!) == nil)
60 }
61}
62
63@MainActor
64struct BodyReferenceMarkdownTests {
65
66 private func links(_ markdown: String) -> [String: URL] {
67 let attributed = MarkdownView.attributed(markdown, referenceRepo: "krz/gitbay")
68 var found: [String: URL] = [:]
69 for run in attributed.runs {
70 if let link = run.link {
71 found[String(attributed[run.range].characters)] = link
72 }
73 }
74 return found
75 }
76
77 @Test func referencesBecomeLinks() {
78 let found = links("closes #12, see krz/solar!3")
79 #expect(found["#12"] == referenceURL(.issue(repo: "krz/gitbay", number: 12)))
80 #expect(found["krz/solar!3"] == referenceURL(.mr(repo: "krz/solar", number: 3)))
81 }
82
83 @Test func codeSpansAndExistingLinksAreLeftAlone() {
84 let found = links("`#12` and [#13](https://example.com)")
85 #expect(found.count == 1)
86 #expect(found["#13"] == URL(string: "https://example.com"))
87 }
88
89 @Test func noRepoMeansNoReferenceLinks() {
90 #expect(MarkdownView.attributed("#12").runs.allSatisfy { $0.link == nil })
91 }
92}
93
94/// Org bodies reach the transform as `OrgSwiftUI` hands it prose: verbatim
95/// and code carry the `.code` intent, links carry `.link`.
96struct BodyReferenceOrgTests {
97
98 private func links(_ org: String) -> [String: URL] {
99 guard case .paragraph(let objects) = OrgParser.parse(org).elements.first else { return [:] }
100 var attributed = OrgAttributedStringRenderer().inline(objects)
101 linkReferences(in: &attributed, repo: "krz/gitbay")
102 var found: [String: URL] = [:]
103 for run in attributed.runs {
104 if let link = run.link {
105 found[String(attributed[run.range].characters)] = link
106 }
107 }
108 return found
109 }
110
111 @Test func referencesBecomeLinks() {
112 let found = links("closes #12, see krz/solar!3")
113 #expect(found["#12"] == referenceURL(.issue(repo: "krz/gitbay", number: 12)))
114 #expect(found["krz/solar!3"] == referenceURL(.mr(repo: "krz/solar", number: 3)))
115 }
116
117 @Test func verbatimCodeAndExistingLinksAreLeftAlone() {
118 let found = links("=#12= ~!4~ and [[https://example.com][#13]] but *#14*")
119 #expect(found.count == 2)
120 #expect(found["#13"] == URL(string: "https://example.com"))
121 #expect(found["#14"] == referenceURL(.issue(repo: "krz/gitbay", number: 14)))
122 }
123}