krz/orgstar

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

Tests/OrgCoreTests/SetupFileTests.swift

b98a6509c5ae75e5172dd333d1b6105fd6ceee0e
orgstar/Tests/OrgCoreTests/SetupFileTests.swift history · blame · raw

96 lines · 5406 bytes

3 symbols in this file
 1import Foundation
 2import Testing
 3@testable import OrgCore
 4
 5/// `#+SETUPFILE` keywords, nested setup files included, as Org reads them.
 6struct SetupFileTests {
 7    static let oracle = ProcessInfo.processInfo.environment["ORGSTAR_SKIP_ORACLE"] == nil
 8
 9    static let common = """
10        #+TODO: NEXT WAIT | DONE CANCELLED
11        #+PRIORITIES: A E C
12        #+TAGS: work home
13        #+FILETAGS: :shared:
14        #+PROPERTY: owner alice
15        #+PROPERTY: header-args :results silent
16        #+TITLE: Setup Title
17        #+OPTIONS: toc:nil num:nil
18        #+MACRO: who Alice
19        #+SETUPFILE: ../deep.org
20        #+SETUPFILE: ../loop.org
21        """ + "\n"
22    static let deep = "#+TYP_TODO: BUG | FIXED\n#+SETUPFILE: setup/common.org\n"
23    static let main = """
24        #+SETUPFILE: setup/common.org
25
26        * NEXT [#E] Task :local:
27        {{{who}}}
28        * BUG Other
29        """ + "\n"
30
31    @Test func collectsNestedKeywordLines() {
32        let files = ["/w/setup/common.org": Self.common, "/w/deep.org": Self.deep]
33        let lines = SetupFile.keywordLines(in: Self.main, directory: "/w") { files[$0] }
34        #expect(lines.hasPrefix("#+TODO: NEXT WAIT | DONE CANCELLED\n"))
35        #expect(lines.contains("#+TYP_TODO: BUG | FIXED\n#+TITLE") == false)
36        #expect(lines.contains("#+TYP_TODO: BUG | FIXED\n"))
37        #expect(lines.components(separatedBy: "#+TODO:").count == 2)
38        #expect(SetupFile.keywordLines(in: "* x\n", directory: "/w") { files[$0] } == "")
39        // A file naming itself adds nothing; a setup file naming it back adds only its own.
40        let own = "#+SETUPFILE: a.org\n#+TODO: A B | C\n"
41        #expect(SetupFile.keywordLines(in: own, directory: "/w", path: "/w/a.org") { $0 == "/w/a.org" ? own : nil } == "")
42        let back = ["/w/a.org": "#+SETUPFILE: b.org\n#+TODO: A | B\n", "/w/b.org": "#+SETUPFILE: a.org\n#+TODO: X | Y\n"]
43        #expect(SetupFile.keywordLines(in: back["/w/a.org"]!, directory: "/w", path: "/w/a.org") { back[$0] } == "#+TODO: X | Y\n")
44        #expect(SetupFile.keywordLines(in: "#+SETUPFILE: https://example.com/a.org\n", directory: "/w") { _ in "#+TODO: X\n" } == "")
45    }
46
47    @Test(.enabled(if: oracle))
48    func matchesOrg() throws {
49        let folder = FileManager.default.temporaryDirectory.appendingPathComponent("orgstar-setup-\(UUID().uuidString)")
50        try FileManager.default.createDirectory(at: folder.appendingPathComponent("setup"), withIntermediateDirectories: true)
51        defer { try? FileManager.default.removeItem(at: folder) }
52        try Self.common.write(to: folder.appendingPathComponent("setup/common.org"), atomically: true, encoding: .utf8)
53        try Self.deep.write(to: folder.appendingPathComponent("deep.org"), atomically: true, encoding: .utf8)
54        let path = folder.appendingPathComponent("main.org").path
55        try Self.main.write(toFile: path, atomically: true, encoding: .utf8)
56
57        let emacs = try EmacsOracle.evaluate("", """
58            (with-current-buffer (find-file-noselect \(lispString(path)))
59              (require 'ox)
60              (goto-char (point-min))
61              (re-search-forward "^\\\\* NEXT")
62              (let ((env (org-export-get-environment)))
63                (list (mapconcat #'identity org-todo-keywords-1 " ")
64                      (format "%c %c %c" org-priority-highest org-priority-lowest org-priority-default)
65                      (mapconcat #'identity (org-get-tags) " ")
66                      (or (cdr (assoc "owner" org-keyword-properties)) "")
67                      (mapconcat #'identity (delq nil (mapcar (lambda (x) (and (stringp (car x)) (car x))) org-current-tag-alist)) " ")
68                      (org-element-interpret-data (plist-get env :title))
69                      (format "%s" (plist-get env :with-toc))
70                      (progn (org-macro-initialize-templates) (or (cdr (assoc "who" org-macro-templates)) "")))))
71            """)
72
73        let settings = OrgSettings.default.resolvingSetupFiles(in: Self.main, path: path, read: OrgSettings.readFile)
74        let tree = OrgParser.parse(Self.main, defaults: settings)
75        let model = DocumentModel(tree: tree)
76        let task = try #require(model.headings.firstIndex { $0.title.contains("Task") })
77        let priorities = tree.settings.priorities
78        let tagNames = (FastSelection.tagTable(settings.setupKeywords + Self.main) ?? []).compactMap { item -> String? in
79            if case .option(_, let name) = item { name } else { nil }
80        }
81        let options = ExportOptions.parse(settings.setupKeywords + Self.main)
82        let page = HTMLExport.page(Self.main, fallbackTitle: "main", settings: settings, path: path)
83        let ours = [
84            tree.settings.todoSequences.flatMap { ($0.active + $0.done).map(\.name) }.joined(separator: " "),
85            "\(priorities.highest) \(priorities.lowest) \(priorities.default)",
86            model.tags(of: task).map(\.value).joined(separator: " "),
87            model.property("owner", of: nil)?.value ?? "",
88            tagNames.joined(separator: " "),
89            HTMLExport.metadata(Self.main, settings: settings).title ?? "",
90            options.toc == nil ? "nil" : "\(options.toc!)",
91            page.contains("Alice") ? "Alice" : "",
92        ]
93        #expect(ours == emacs)
94        #expect(page.contains("<h1 class=\"title\">Setup Title</h1>") && !page.contains("id=\"table-of-contents\""))
95    }
96}