aboutsummaryrefslogtreecommitdiff
path: root/tests/test-pearl-convert.el
blob: 4125e2292488368c87af8bd1f95d4ebe1502fc08 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
;;; test-pearl-convert.el --- Tests for the markdown->org conversion -*- lexical-binding: t; -*-

;; Copyright (C) 2026 Craig Jennings

;; Author: Craig Jennings <c@cjennings.net>

;; This program is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; Tests for `pearl--md-to-org' and `pearl--md-line-to-org' --
;; the pure-elisp markdown->org conversion tier.  Cover each supported
;; construct (links, inline code, bold, underscore italics, headings, bullets,
;; fenced code), the heading-safety guard, and literal pass-through.

;;; Code:

(require 'test-bootstrap (expand-file-name "test-bootstrap.el"))

;;; inline conversion

(ert-deftest test-pearl-convert-link ()
  "A markdown link becomes an org link with url and label swapped."
  (should (string= "see [[https://x.y][the docs]] now"
                   (pearl--md-line-to-org "see [the docs](https://x.y) now"))))

(ert-deftest test-pearl-convert-inline-code ()
  "Inline code backticks become org verbatim tildes."
  (should (string= "call ~foo()~ here"
                   (pearl--md-line-to-org "call `foo()` here"))))

(ert-deftest test-pearl-convert-bold ()
  "Markdown bold becomes org bold."
  (should (string= "a *strong* word" (pearl--md-line-to-org "a **strong** word"))))

(ert-deftest test-pearl-convert-italic-underscore ()
  "Underscore italics become org italics, but identifiers are left alone."
  (should (string= "an /emphatic/ point"
                   (pearl--md-line-to-org "an _emphatic_ point")))
  (should (string= "the foo_bar_baz name"
                   (pearl--md-line-to-org "the foo_bar_baz name"))))

;;; line / block conversion

(ert-deftest test-pearl-convert-heading-to-bold ()
  "A markdown heading becomes a bold line, never an org heading."
  (let ((out (pearl--md-to-org "## Big Heading")))
    (should (string= "*Big Heading*" out))
    (should-not (string-match-p "^\\*+ " out))))

(ert-deftest test-pearl-convert-bullets ()
  "Markdown `*' and `+' bullets become org `-' bullets."
  (should (string= "- one\n- two"
                   (pearl--md-to-org "* one\n+ two"))))

(ert-deftest test-pearl-convert-fenced-code ()
  "A fenced code block becomes a src block, verbatim inside."
  (should (string= "#+begin_src elisp\n(+ 1 2)\n#+end_src"
                   (pearl--md-to-org "```elisp\n(+ 1 2)\n```"))))

(ert-deftest test-pearl-convert-code-block-is-verbatim ()
  "Inline markup inside a fenced block is not converted."
  (let ((out (pearl--md-to-org "```\n**not bold** here\n```")))
    (should (string-match-p "\\*\\*not bold\\*\\*" out))))

(ert-deftest test-pearl-convert-guards-heading-line ()
  "A non-bullet line that Org would read as a heading is space-guarded."
  (let ((out (pearl--md-to-org "** looks like a heading")))
    (should-not (string-match-p "^\\*+ " out))
    (should (string-prefix-p " " out))))

(ert-deftest test-pearl-convert-passes-through-plain-and-tables ()
  "Plain text and unsupported constructs (tables) pass through unchanged."
  (should (string= "just some text" (pearl--md-to-org "just some text")))
  (should (string= "| a | b |\n|---|---|"
                   (pearl--md-to-org "| a | b |\n|---|---|"))))

(ert-deftest test-pearl-convert-empty ()
  "An empty or nil description converts to the empty string."
  (should (string= "" (pearl--md-to-org "")))
  (should (string= "" (pearl--md-to-org nil))))

;;; org -> markdown (the push direction)

(ert-deftest test-pearl-org-to-md-link ()
  "An org link becomes a markdown link with label and url swapped back."
  (should (string= "see [the docs](https://x.y) now"
                   (pearl--org-line-to-md "see [[https://x.y][the docs]] now"))))

(ert-deftest test-pearl-org-to-md-bare-link ()
  "An org link with no description becomes the bare url."
  (should (string= "visit https://x.y"
                   (pearl--org-line-to-md "visit [[https://x.y]]"))))

(ert-deftest test-pearl-org-to-md-inline-code ()
  "Org verbatim tildes become markdown backticks."
  (should (string= "call `foo()` here"
                   (pearl--org-line-to-md "call ~foo()~ here"))))

(ert-deftest test-pearl-org-to-md-bold ()
  "Org bold becomes markdown bold."
  (should (string= "a **strong** word" (pearl--org-line-to-md "a *strong* word"))))

(ert-deftest test-pearl-org-to-md-italic ()
  "Org italics become underscore italics, but paths are left alone."
  (should (string= "an _emphatic_ point"
                   (pearl--org-line-to-md "an /emphatic/ point")))
  (should (string= "the /usr/local/bin path"
                   (pearl--org-line-to-md "the /usr/local/bin path"))))

(ert-deftest test-pearl-org-to-md-fenced-code ()
  "An org src block becomes a fenced code block, language preserved."
  (should (string= "```elisp\n(+ 1 2)\n```"
                   (pearl--org-to-md "#+begin_src elisp\n(+ 1 2)\n#+end_src"))))

(ert-deftest test-pearl-org-to-md-code-block-is-verbatim ()
  "Org markup inside a src block is not converted back."
  (let ((out (pearl--org-to-md "#+begin_src\n*not bold* here\n#+end_src")))
    (should (string-match-p "\\*not bold\\* here" out))))

(ert-deftest test-pearl-org-to-md-quote-block ()
  "An org quote block becomes markdown blockquote lines."
  (should (string= "> a quote\n> second line"
                   (pearl--org-to-md
                    "#+begin_quote\na quote\nsecond line\n#+end_quote"))))

(ert-deftest test-pearl-org-to-md-checkbox-case ()
  "Org uppercase checkbox marks normalize to markdown lowercase."
  (should (string= "- [ ] todo\n- [x] done"
                   (pearl--org-to-md "- [ ] todo\n- [X] done"))))

(ert-deftest test-pearl-org-to-md-empty ()
  "An empty or nil body converts to the empty string."
  (should (string= "" (pearl--org-to-md "")))
  (should (string= "" (pearl--org-to-md nil))))

(ert-deftest test-pearl-org-to-md-passes-through-tables ()
  "Tables and unsupported constructs pass through unchanged."
  (should (string= "| a | b |\n|---|---|"
                   (pearl--org-to-md "| a | b |\n|---|---|"))))

;;; round-trip: org-to-md inverts md-to-org for the supported subset

(ert-deftest test-pearl-convert-roundtrip-identity ()
  "For the cleanly-supported constructs, org->md(md->org(x)) == x.
Markdown headings and single-asterisk italics are intentionally lossy (see
the conversion-tier docstring) and are excluded here."
  (dolist (md '("a **strong** word"
                "call `foo()` here"
                "an _emphatic_ point"
                "see [the docs](https://x.y) now"
                "- one\n- two\n- three"
                "1. first\n2. second"
                "- [ ] todo\n- [x] done"
                "```elisp\n(+ 1 2)\n```"
                "just some plain prose"
                "| a | b |\n|---|---|"))
    (should (string= md (pearl--org-to-md (pearl--md-to-org md))))))

(provide 'test-pearl-convert)
;;; test-pearl-convert.el ends here