krz/orgstar

A native macOS editor for org-mode files. editor org-mode swift

Tests/OrgCoreTests/CitationTests.swift

c1295824f55c7bba681fca8338bf08167e110105
orgstar/Tests/OrgCoreTests/CitationTests.swift history · blame · raw

141 lines · 7262 bytes

  1import Foundation
  2import Testing
  3@testable import OrgCore
  4
  5/// Citations against `oc-basic` through ox-md: each paragraph and the bibliography.
  6struct CitationTests {
  7    static let oracle = ProcessInfo.processInfo.environment["ORGSTAR_SKIP_ORACLE"] == nil
  8
  9    static let bib = #"""
 10        @string{jos = "Journal of Stuff"}
 11        @article{doe2020,
 12          author = {Doe, Jane and Roe, Richard},
 13          title = {A Study of {Things}},
 14          journal = jos,
 15          year = {2020},
 16          pages = {1--10}
 17        }
 18        @book{smith1999,
 19          author = "Smith, John",
 20          title = {The Book},
 21          publisher = {Pub House},
 22          year = 1999
 23        }
 24        @misc{lee,
 25          author = {Lee, Kim and Park, Min and Choi, Su},
 26          title = {Three Authors},
 27          year = {2021}
 28        }
 29        @article{doe2020b,
 30          author = {Doe, Jane and Roe, Richard},
 31          title = {Another Study},
 32          journal = {Other Journal},
 33          year = {2020}
 34        }
 35        @report{org,
 36          editor = {{Org Team}},
 37          title = {Manual},
 38          institution = {GNU},
 39          date = {2022-03-04}
 40        }
 41        @comment{ignored}
 42        """#
 43
 44    static let json = #"""
 45        [{"id": "csl1", "author": [{"family": "Zed", "given": "Ann"}], "title": "From JSON", "issued": {"date-parts": [[2018, 5]]}, "publisher": "Pubs"}]
 46        """#
 47
 48    static let body = """
 49        Plain [cite:@doe2020] and [cite:@smith1999 p. 5] and [cite/t:@lee].
 50
 51        Several [cite:see @doe2020;@smith1999] and common [cite:pre; @lee; post].
 52
 53        Same year [cite:@doe2020b] then [cite/t:@doe2020] and [cite/text/c:@lee].
 54
 55        Noauthor [cite/na:@doe2020], bare [cite/na/b:@smith1999], author [cite/a:@lee] [cite/author/caps:@lee].
 56
 57        Bare default [cite//b:@smith1999] and text bare [cite/t/b:@org], missing [cite:@nope], json [cite:@csl1].
 58
 59        Nocite here [cite/n:@lee] and there [cite/nocite:@smith1999].
 60
 61        Markup [cite:/see/ @doe2020 *p. 3*].
 62
 63        #+print_bibliography:
 64        """
 65
 66    func compare(_ text: String) throws {
 67        let folder = FileManager.default.temporaryDirectory.appendingPathComponent("orgstar-cite-\(UUID().uuidString)")
 68        try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
 69        defer { try? FileManager.default.removeItem(at: folder) }
 70        try Self.bib.write(to: folder.appendingPathComponent("refs.bib"), atomically: true, encoding: .utf8)
 71        try Self.json.write(to: folder.appendingPathComponent("more.json"), atomically: true, encoding: .utf8)
 72        let path = folder.appendingPathComponent("doc.org").path
 73        try text.write(toFile: path, atomically: true, encoding: .utf8)
 74        let text = "#+OPTIONS: toc:nil\n" + text
 75        try text.write(toFile: path, atomically: true, encoding: .utf8)
 76        let emacs = try EmacsOracle.evaluate("", """
 77            (with-current-buffer (find-file-noselect \(lispString(path)))
 78              (require 'ox-md) (require 'oc-basic)
 79              (list (org-export-as 'md nil nil t)))
 80            """)[0]
 81        let ours = MarkdownExport.document(text, path: path)
 82        func paragraphs(_ s: String) -> [String] {
 83            s.components(separatedBy: "\n\n").map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }.filter { !$0.isEmpty }
 84        }
 85        #expect(paragraphs(ours) == paragraphs(emacs), "ours:\n\(ours)\nemacs:\n\(emacs)")
 86    }
 87
 88    @Test func htmlUsesTheSameCitations() throws {
 89        let folder = FileManager.default.temporaryDirectory.appendingPathComponent("orgstar-cite-\(UUID().uuidString)")
 90        try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
 91        defer { try? FileManager.default.removeItem(at: folder) }
 92        try Self.bib.write(to: folder.appendingPathComponent("refs.bib"), atomically: true, encoding: .utf8)
 93        let page = HTMLExport.page("#+bibliography: refs.bib\nSee [cite:@doe2020 *p. 3*] [cite/n:@lee].\n\n#+print_bibliography:\n", fallbackTitle: "t",
 94                                   path: folder.appendingPathComponent("doc.org").path)
 95        #expect(page.contains("<p>See (Doe, Jane and Roe, Richard, 2020 <strong>p. 3</strong>).</p>"))
 96        #expect(page.contains("<p>Doe, Jane and Roe, Richard (2020). <i>A Study of Things</i>, Journal of Stuff.</p>"))
 97        #expect(page.contains("<p>Lee, Kim and Park, Min and Choi, Su (2021). <i>Three Authors</i>.</p>"))
 98        #expect(!page.contains("[cite"))
 99    }
100
101    @Test func aTitleCitationLeavesTheBodysAlone() throws {
102        let folder = FileManager.default.temporaryDirectory.appendingPathComponent("orgstar-cite-\(UUID().uuidString)")
103        try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
104        defer { try? FileManager.default.removeItem(at: folder) }
105        try Self.bib.write(to: folder.appendingPathComponent("refs.bib"), atomically: true, encoding: .utf8)
106        let page = HTMLExport.page("#+bibliography: refs.bib\n#+TITLE: On [cite:@doe2020]\nSee [cite:@doe2020].\n", fallbackTitle: "t",
107                                   path: folder.appendingPathComponent("doc.org").path)
108        #expect(page.contains("<h1 class=\"title\">On</h1>"))
109        #expect(page.contains("<p>See (Doe, Jane and Roe, Richard, 2020).</p>"))
110    }
111
112    /// As ox-md exports them: footnotes after the punctuation, holding text-style citations.
113    @Test func noteCitationsBecomeFootnotes() throws {
114        let folder = FileManager.default.temporaryDirectory.appendingPathComponent("orgstar-cite-\(UUID().uuidString)")
115        try FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true)
116        defer { try? FileManager.default.removeItem(at: folder) }
117        try Self.bib.write(to: folder.appendingPathComponent("refs.bib"), atomically: true, encoding: .utf8)
118        let text = "#+bibliography: refs.bib\nFirst claim [cite/ft:@doe2020]. Second [cite/note:see @smith1999 p. 5], and third [cite/ft:@lee].\n\nEnd [cite/ft/c:@lee]\n"
119        let markdown = MarkdownExport.document(text, path: folder.appendingPathComponent("doc.org").path)
120        #expect(markdown.contains("First claim.[^1] Second,[^2] and third.[^3]"))
121        #expect(markdown.contains("End[^4]"))
122        #expect(markdown.contains("[^1]: Doe, Jane and Roe, Richard (2020)"))
123        #expect(markdown.contains("[^2]: see Smith, John (1999) p. 5"))
124        #expect(markdown.contains("[^4]: Lee, Kim And Park, Min And Choi, Su (2021)"))
125    }
126
127    @Test(.enabled(if: oracle))
128    func authorYearMatchesOcBasic() throws {
129        try compare("#+bibliography: refs.bib\n#+bibliography: more.json\n" + Self.body)
130    }
131
132    @Test(.enabled(if: oracle))
133    func numericMatchesOcBasic() throws {
134        try compare("#+bibliography: refs.bib\n#+cite_export: basic numeric numeric\n" + "Cites [cite:@smith1999;@doe2020;@lee] and [cite:@doe2020b] and [cite:see @lee p. 2] and [cite:pre; @lee;@org; post].\n\n#+print_bibliography:\n")
135    }
136
137    @Test(.enabled(if: oracle))
138    func plainBibliographyMatchesOcBasic() throws {
139        try compare("#+bibliography: refs.bib\n#+cite_export: basic plain\n" + "A [cite:@lee] [cite:@doe2020] [cite:@org].\n\n#+print_bibliography:\n")
140    }
141}