import Foundation import OrgSwift import Testing @testable import gitbay /// `#N`, `!N`, `owner/name#N` and `owner/name!N`, found the way the web's /// autolink package finds them: `#` is an issue, `!` a merge request. struct BodyReferenceTests { private func refs(_ text: String, repo: String = "krz/gitbay") -> [BodyReference] { bodyReferences(in: text, repo: repo) } @Test func bareHashIsAnIssueInThisRepo() { #expect(refs("see #12 please") == [ BodyReference(range: 4..<7, destination: .issue(repo: "krz/gitbay", number: 12)), ]) } @Test func bangIsAMergeRequest() { #expect(refs("!3") == [ BodyReference(range: 0..<2, destination: .mr(repo: "krz/gitbay", number: 3)), ]) } @Test func crossRepoReferenceNamesItsRepo() { #expect(refs("fixed in krz/solar#7 and krz/solar!8.") == [ BodyReference(range: 9..<20, destination: .issue(repo: "krz/solar", number: 7)), BodyReference(range: 25..<36, destination: .mr(repo: "krz/solar", number: 8)), ]) } @Test func aBareReferenceNeedsABoundaryBefore() { #expect(refs("a1b2#3").isEmpty) #expect(refs("(#4)").map(\.range) == [1..<3]) #expect(refs("[#5]").map(\.range) == [1..<3]) #expect(refs("line\n#6").map(\.range) == [5..<7]) } @Test func digitsMustEndAtAWordBoundary() { #expect(refs("#12abc").isEmpty) #expect(refs("#12, #13").count == 2) } @Test func aCrossReferenceIsNotAlsoReadAsABareOne() { #expect(refs("krz/solar#7").count == 1) } @Test func offsetsCountCharactersNotBytes() { #expect(refs("é #1").map(\.range) == [2..<4]) } @Test func theURLRoundTrips() throws { for destination in [NotificationDestination.issue(repo: "krz/gitbay", number: 12), .mr(repo: "krz/solar", number: 3)] { let url = try #require(referenceURL(destination)) #expect(referenceDestination(url) == destination) } #expect(referenceDestination(URL(string: "https://gitbay.org/krz/gitbay/issues/1")!) == nil) } } @MainActor struct BodyReferenceMarkdownTests { private func links(_ markdown: String) -> [String: URL] { let attributed = MarkdownView.attributed(markdown, referenceRepo: "krz/gitbay") var found: [String: URL] = [:] for run in attributed.runs { if let link = run.link { found[String(attributed[run.range].characters)] = link } } return found } @Test func referencesBecomeLinks() { let found = links("closes #12, see krz/solar!3") #expect(found["#12"] == referenceURL(.issue(repo: "krz/gitbay", number: 12))) #expect(found["krz/solar!3"] == referenceURL(.mr(repo: "krz/solar", number: 3))) } @Test func codeSpansAndExistingLinksAreLeftAlone() { let found = links("`#12` and [#13](https://example.com)") #expect(found.count == 1) #expect(found["#13"] == URL(string: "https://example.com")) } @Test func noRepoMeansNoReferenceLinks() { #expect(MarkdownView.attributed("#12").runs.allSatisfy { $0.link == nil }) } } /// Org bodies reach the transform as `OrgSwiftUI` hands it prose: verbatim /// and code carry the `.code` intent, links carry `.link`. struct BodyReferenceOrgTests { private func links(_ org: String) -> [String: URL] { guard case .paragraph(let objects) = OrgParser.parse(org).elements.first else { return [:] } var attributed = OrgAttributedStringRenderer().inline(objects) linkReferences(in: &attributed, repo: "krz/gitbay") var found: [String: URL] = [:] for run in attributed.runs { if let link = run.link { found[String(attributed[run.range].characters)] = link } } return found } @Test func referencesBecomeLinks() { let found = links("closes #12, see krz/solar!3") #expect(found["#12"] == referenceURL(.issue(repo: "krz/gitbay", number: 12))) #expect(found["krz/solar!3"] == referenceURL(.mr(repo: "krz/solar", number: 3))) } @Test func verbatimCodeAndExistingLinksAreLeftAlone() { let found = links("=#12= ~!4~ and [[https://example.com][#13]] but *#14*") #expect(found.count == 2) #expect(found["#13"] == URL(string: "https://example.com")) #expect(found["#14"] == referenceURL(.issue(repo: "krz/gitbay", number: 14))) } }