org-export: Run a hook before parsing
[org-mode/org-mode-NeilSmithlineMods.git] / testing / lisp / test-org-export.el
blob90a257cdad11f8a2283a5d47ec10dd7b79270b67
1 ;;; test-org-export.el --- Tests for org-export.el
3 ;; Copyright (C) 2012 Nicolas Goaziou
5 ;; Author: Nicolas Goaziou <n.goaziou at gmail dot com>
7 ;; Released under the GNU General Public License version 3
8 ;; see: http://www.gnu.org/licenses/gpl-3.0.html
10 ;;;; Comments
14 ;;; Code:
16 (unless (featurep 'org-export)
17 (signal 'missing-test-dependency "org-export"))
21 ;;; Tests
23 (defmacro org-test-with-backend (backend &rest body)
24 "Execute body with an export back-end defined.
26 BACKEND is the name, as a string, of the back-end. BODY is the
27 body to execute. The defined back-end simply returns parsed data
28 as Org syntax."
29 (declare (debug (form body)) (indent 1))
30 `(flet ,(let (transcoders)
31 (dolist (type (append org-element-all-elements
32 org-element-all-objects)
33 transcoders)
34 (push `(,(intern (format "org-%s-%s" backend type))
35 (obj contents info)
36 (,(intern (format "org-element-%s-interpreter" type))
37 obj contents))
38 transcoders)))
39 ,@body))
41 (defmacro org-test-with-parsed-data (data &rest body)
42 "Execute body with parsed data available.
44 DATA is a string containing the data to be parsed. BODY is the
45 body to execute. Parse tree is available under the `tree'
46 variable, and communication channel under `info'.
48 This function calls `org-export-collect-tree-properties'. As
49 such, `:ignore-list' (for `org-element-map') and
50 `:parse-tree' (for `org-export-get-genealogy') properties are
51 already filled in `info'."
52 (declare (debug (form body)) (indent 1))
53 `(org-test-with-temp-text ,data
54 (let* ((tree (org-element-parse-buffer))
55 (info (org-export-collect-tree-properties tree nil nil)))
56 ,@body)))
58 (ert-deftest test-org-export/parse-option-keyword ()
59 "Test reading all standard #+OPTIONS: items."
60 (should
61 (equal
62 (org-export-parse-option-keyword
63 "H:1 num:t \\n:t timestamp:t arch:t author:t creator:t d:t email:t
64 *:t e:t ::t f:t pri:t -:t ^:t toc:t |:t tags:t tasks:t <:t todo:t")
65 '(:headline-levels
66 1 :preserve-breaks t :section-numbers t :time-stamp-file t
67 :with-archived-trees t :with-author t :with-creator t :with-drawers t
68 :with-email t :with-emphasize t :with-entities t :with-fixed-width t
69 :with-footnotes t :with-priority t :with-special-strings t
70 :with-sub-superscript t :with-toc t :with-tables t :with-tags t
71 :with-tasks t :with-timestamps t :with-todo-keywords t)))
72 ;; Test some special values.
73 (should
74 (equal
75 (org-export-parse-option-keyword
76 "arch:headline creator:comment d:(\"TEST\")
77 ^:{} toc:1 tags:not-in-toc tasks:todo num:2")
78 '( :section-numbers
80 :with-archived-trees headline :with-creator comment
81 :with-drawers ("TEST") :with-sub-superscript {} :with-toc 1
82 :with-tags not-in-toc :with-tasks todo))))
84 (ert-deftest test-org-export/get-inbuffer-options ()
85 "Test reading all standard export keywords."
86 (should
87 (equal
88 (org-test-with-temp-text "#+AUTHOR: Me, Myself and I
89 #+CREATOR: Idem
90 #+DATE: Today
91 #+DESCRIPTION: Testing
92 #+DESCRIPTION: with two lines
93 #+EMAIL: some@email.org
94 #+EXPORT_EXCLUDE_TAGS: noexport invisible
95 #+KEYWORDS: test
96 #+LANGUAGE: en
97 #+EXPORT_SELECT_TAGS: export
98 #+TITLE: Some title
99 #+TITLE: with spaces"
100 (org-export-get-inbuffer-options))
101 '(:author
102 ("Me, Myself and I") :creator "Idem" :date "Today"
103 :description "Testing\nwith two lines" :email "some@email.org"
104 :exclude-tags ("noexport" "invisible") :keywords "test" :language "en"
105 :select-tags ("export") :title ("Some title with spaces")))))
107 (ert-deftest test-org-export/define-macro ()
108 "Try defining various Org macro using in-buffer #+MACRO: keyword."
109 ;; Parsed macro.
110 (should (equal (org-test-with-temp-text "#+MACRO: one 1"
111 (org-export-get-inbuffer-options))
112 '(:macro-one ("1"))))
113 ;; Evaled macro.
114 (should (equal (org-test-with-temp-text "#+MACRO: two (eval (+ 1 1))"
115 (org-export-get-inbuffer-options))
116 '(:macro-two "(eval (+ 1 1))")))
117 ;; Incomplete macro.
118 (should-not (org-test-with-temp-text "#+MACRO: three"
119 (org-export-get-inbuffer-options)))
120 ;; Macro with newline character.
121 (should (equal (org-test-with-temp-text "#+MACRO: four a\\nb"
122 (org-export-get-inbuffer-options))
123 '(:macro-four ("a\nb"))))
124 ;; Macro with protected newline character.
125 (should (equal (org-test-with-temp-text "#+MACRO: five a\\\\nb"
126 (org-export-get-inbuffer-options))
127 '(:macro-five ("a\\nb"))))
128 ;; Recursive macro.
129 (org-test-with-temp-text "#+MACRO: six 6\n#+MACRO: seven 1 + {{{six}}}"
130 (should
131 (equal
132 (org-export-get-inbuffer-options)
133 '(:macro-six
134 ("6")
135 :macro-seven
136 ("1 + " (macro (:key "six" :value "{{{six}}}" :args nil :begin 5 :end 14
137 :post-blank 0))))))))
139 (ert-deftest test-org-export/handle-options ()
140 "Test if export options have an impact on output."
141 ;; Test exclude tags.
142 (org-test-with-temp-text "* Head1 :noexport:"
143 (org-test-with-backend "test"
144 (should
145 (equal (org-export-as 'test nil nil nil '(:exclude-tags ("noexport")))
146 ""))))
147 ;; Test include tags.
148 (org-test-with-temp-text "
149 * Head1
150 ** Sub-Head1.1 :export:
151 *** Sub-Head1.1.1
152 * Head2"
153 (org-test-with-backend "test"
154 (should
155 (string-match
156 "\\* Head1\n\\*\\* Sub-Head1.1[ \t]+:export:\n\\*\\*\\* Sub-Head1.1.1\n"
157 (org-export-as 'test nil nil nil '(:select-tags ("export")))))))
158 ;; Test mixing include tags and exclude tags.
159 (org-test-with-temp-text "
160 * Head1 :export:
161 ** Sub-Head1 :noexport:
162 ** Sub-Head2
163 * Head2 :noexport:
164 ** Sub-Head1 :export:"
165 (org-test-with-backend "test"
166 (should
167 (string-match
168 "\\* Head1[ \t]+:export:\n\\*\\* Sub-Head2\n"
169 (org-export-as
170 'test nil nil nil
171 '(:select-tags ("export") :exclude-tags ("noexport")))))))
172 ;; Ignore tasks.
173 (let ((org-todo-keywords '((sequence "TODO" "DONE"))))
174 (org-test-with-temp-text "* TODO Head1"
175 (org-test-with-backend "test"
176 (should (equal (org-export-as 'test nil nil nil '(:with-tasks nil))
177 "")))))
178 (let ((org-todo-keywords '((sequence "TODO" "DONE"))))
179 (org-test-with-temp-text "* TODO Head1"
180 (org-test-with-backend "test"
181 (should (equal (org-export-as 'test nil nil nil '(:with-tasks t))
182 "* TODO Head1\n")))))
183 ;; Archived tree.
184 (org-test-with-temp-text "* Head1 :archive:"
185 (let ((org-archive-tag "archive"))
186 (org-test-with-backend "test"
187 (should
188 (equal (org-export-as 'test nil nil nil '(:with-archived-trees nil))
189 "")))))
190 (org-test-with-temp-text "* Head1 :archive:\nbody\n** Sub-head 2"
191 (let ((org-archive-tag "archive"))
192 (org-test-with-backend "test"
193 (should
194 (string-match
195 "\\* Head1[ \t]+:archive:"
196 (org-export-as 'test nil nil nil
197 '(:with-archived-trees headline)))))))
198 (org-test-with-temp-text "* Head1 :archive:"
199 (let ((org-archive-tag "archive"))
200 (org-test-with-backend "test"
201 (should
202 (string-match
203 "\\`\\* Head1[ \t]+:archive:\n\\'"
204 (org-export-as 'test nil nil nil '(:with-archived-trees t)))))))
205 ;; Drawers.
206 (let ((org-drawers '("TEST")))
207 (org-test-with-temp-text ":TEST:\ncontents\n:END:"
208 (org-test-with-backend "test"
209 (should (equal (org-export-as 'test nil nil nil '(:with-drawers nil))
210 "")))))
211 (let ((org-drawers '("TEST")))
212 (org-test-with-temp-text ":TEST:\ncontents\n:END:"
213 (org-test-with-backend "test"
214 (should (equal (org-export-as 'test nil nil nil '(:with-drawers t))
215 ":TEST:\ncontents\n:END:\n"))))))
217 (ert-deftest test-org-export/comment-tree ()
218 "Test if export process ignores commented trees."
219 (let ((org-comment-string "COMMENT"))
220 (org-test-with-temp-text "* COMMENT Head1"
221 (org-test-with-backend "test"
222 (should (equal (org-export-as 'test) ""))))))
224 (ert-deftest test-org-export/export-scope ()
225 "Test all export scopes."
226 (org-test-with-temp-text "
227 * Head1
228 ** Head2
229 text
230 *** Head3"
231 (org-test-with-backend "test"
232 ;; Subtree.
233 (forward-line 3)
234 (should (equal (org-export-as 'test 'subtree) "text\n*** Head3\n"))
235 ;; Visible.
236 (goto-char (point-min))
237 (forward-line)
238 (org-cycle)
239 (should (equal (org-export-as 'test nil 'visible) "* Head1\n"))
240 ;; Body only.
241 (flet ((org-test-template (body info) (format "BEGIN\n%sEND" body)))
242 (should (equal (org-export-as 'test nil nil 'body-only)
243 "* Head1\n** Head2\ntext\n*** Head3\n"))
244 (should (equal (org-export-as 'test)
245 "BEGIN\n* Head1\n** Head2\ntext\n*** Head3\nEND")))
246 ;; Region.
247 (goto-char (point-min))
248 (forward-line 3)
249 (transient-mark-mode 1)
250 (push-mark (point) t t)
251 (goto-char (point-at-eol))
252 (should (equal (org-export-as 'test) "text\n"))))
253 ;; Subtree with a code block calling another block outside.
254 (org-test-with-temp-text "
255 * Head1
256 #+BEGIN_SRC emacs-lisp :noweb yes :exports results
257 <<test>>
258 #+END_SRC
259 * Head2
260 #+NAME: test
261 #+BEGIN_SRC emacs-lisp
262 \(+ 1 2)
263 #+END_SRC"
264 (org-test-with-backend "test"
265 (forward-line 1)
266 (should (equal (org-export-as 'test 'subtree) ": 3\n")))))
268 (ert-deftest test-org-export/export-snippet ()
269 "Test export snippets transcoding."
270 (org-test-with-temp-text "@test{A}@t{B}"
271 (org-test-with-backend "test"
272 (flet ((org-test-export-snippet
273 (snippet contents info)
274 (when (eq (org-export-snippet-backend snippet) 'test)
275 (org-element-property :value snippet))))
276 (let ((org-export-snippet-translation-alist nil))
277 (should (equal (org-export-as 'test) "A\n")))
278 (let ((org-export-snippet-translation-alist '(("t" . "test"))))
279 (should (equal (org-export-as 'test) "AB\n")))))))
281 (ert-deftest test-org-export/expand-include ()
282 "Test file inclusion in an Org buffer."
283 ;; Full insertion with recursive inclusion.
284 (org-test-with-temp-text
285 (format "#+INCLUDE: \"%s/examples/include.org\"" org-test-dir)
286 (org-export-expand-include-keyword)
287 (should (equal (buffer-string)
288 "Small Org file with an include keyword.
290 #+BEGIN_SRC emacs-lisp :exports results\n(+ 2 1)\n#+END_SRC
292 Success!
294 * Heading
295 body\n")))
296 ;; Localized insertion.
297 (org-test-with-temp-text
298 (format "#+INCLUDE: \"%s/examples/include.org\" :lines \"1-2\""
299 org-test-dir)
300 (org-export-expand-include-keyword)
301 (should (equal (buffer-string)
302 "Small Org file with an include keyword.\n")))
303 ;; Insertion with constraints on headlines level.
304 (org-test-with-temp-text
305 (format
306 "* Top heading\n#+INCLUDE: \"%s/examples/include.org\" :lines \"9-\""
307 org-test-dir)
308 (org-export-expand-include-keyword)
309 (should (equal (buffer-string) "* Top heading\n** Heading\nbody\n")))
310 ;; Inclusion within an example block.
311 (org-test-with-temp-text
312 (format "#+INCLUDE: \"%s/examples/include.org\" :lines \"1-2\" example"
313 org-test-dir)
314 (org-export-expand-include-keyword)
315 (should
316 (equal
317 (buffer-string)
318 "#+BEGIN_EXAMPLE\nSmall Org file with an include keyword.\n#+END_EXAMPLE\n")))
319 ;; Inclusion within a src-block.
320 (org-test-with-temp-text
321 (format
322 "#+INCLUDE: \"%s/examples/include.org\" :lines \"4-5\" src emacs-lisp"
323 org-test-dir)
324 (org-export-expand-include-keyword)
325 (should (equal (buffer-string)
326 "#+BEGIN_SRC emacs-lisp\n(+ 2 1)\n#+END_SRC\n"))))
328 (ert-deftest test-org-export/user-ignore-list ()
329 "Test if `:ignore-list' accepts user input."
330 (org-test-with-backend "test"
331 (flet ((skip-note-head
332 (data backend info)
333 ;; Ignore headlines with the word "note" in their title.
334 (org-element-map
335 data 'headline
336 (lambda (headline)
337 (when (string-match "\\<note\\>"
338 (org-element-property :raw-value headline))
339 (org-export-ignore-element headline info)))
340 info)
341 data))
342 ;; Install function in parse tree filters.
343 (let ((org-export-filter-parse-tree-functions '(skip-note-head)))
344 (org-test-with-temp-text "* Head1\n* Head2 (note)\n"
345 (should (equal (org-export-as 'test) "* Head1\n")))))))
347 (ert-deftest test-org-export/before-parsing-hook ()
348 "Test `org-export-before-parsing-hook'."
349 (org-test-with-backend "test"
350 (org-test-with-temp-text "* Headline 1\nBody 1\n* Headline 2\nBody 2"
351 (let ((org-export-before-parsing-hook
352 ((lambda ()
353 (org-map-entries
354 (lambda ()
355 (delete-region (point) (progn (forward-line) (point)))))))))
356 (should (equal (org-export-as 'test) "Body 1\nBody 2\n"))))))
360 ;;; Footnotes
362 (ert-deftest test-org-export/footnotes ()
363 "Test footnotes specifications."
364 (let ((org-footnote-section nil))
365 ;; 1. Read every type of footnote.
366 (org-test-with-temp-text
367 "Text[fn:1] [1] [fn:label:C] [fn::D]\n\n[fn:1] A\n\n[1] B"
368 (let* ((tree (org-element-parse-buffer))
369 (info (org-export-store-footnote-definitions
370 `(:parse-tree ,tree :with-footnotes t))))
371 (should
372 (equal
373 '((1 . "A") (2 . "B") (3 . "C") (4 . "D"))
374 (org-element-map
375 tree 'footnote-reference
376 (lambda (ref)
377 (let ((def (org-export-get-footnote-definition ref info)))
378 (cons (org-export-get-footnote-number ref info)
379 (if (eq (org-element-property :type ref) 'inline) (car def)
380 (car (org-element-contents
381 (car (org-element-contents def))))))))
382 info)))))
383 ;; 2. Test nested footnotes order.
384 (org-test-with-temp-text
385 "Text[fn:1:A[fn:2]] [fn:3].\n\n[fn:2] B [fn:3] [fn::D].\n\n[fn:3] C."
386 (let* ((tree (org-element-parse-buffer))
387 (info (org-export-store-footnote-definitions
388 `(:parse-tree ,tree :with-footnotes t))))
389 (should
390 (equal
391 '((1 . "fn:1") (2 . "fn:2") (3 . "fn:3") (4))
392 (org-element-map
393 tree 'footnote-reference
394 (lambda (ref)
395 (when (org-export-footnote-first-reference-p ref info)
396 (cons (org-export-get-footnote-number ref info)
397 (org-element-property :label ref))))
398 info)))))
399 ;; 3. Test nested footnote in invisible definitions.
400 (org-test-with-temp-text "Text[1]\n\n[1] B [2]\n\n[2] C."
401 ;; Hide definitions.
402 (narrow-to-region (point) (point-at-eol))
403 (let* ((tree (org-element-parse-buffer))
404 (info (org-export-store-footnote-definitions
405 `(:parse-tree ,tree :with-footnotes t))))
406 ;; Both footnotes should be seen.
407 (should
408 (= (length (org-export-collect-footnote-definitions tree info)) 2))))
409 ;; 4. Test footnotes definitions collection.
410 (org-test-with-temp-text "Text[fn:1:A[fn:2]] [fn:3].
412 \[fn:2] B [fn:3] [fn::D].
414 \[fn:3] C."
415 (let* ((tree (org-element-parse-buffer))
416 (info (org-export-store-footnote-definitions
417 `(:parse-tree ,tree :with-footnotes t))))
418 (should (= (length (org-export-collect-footnote-definitions tree info))
419 4))))
420 ;; 5. Test export of footnotes defined outside parsing scope.
421 (org-test-with-temp-text "[fn:1] Out of scope
422 * Title
423 Paragraph[fn:1]"
424 (org-test-with-backend "test"
425 (flet ((org-test-footnote-reference
426 (fn-ref contents info)
427 (org-element-interpret-data
428 (org-export-get-footnote-definition fn-ref info))))
429 (forward-line)
430 (should (equal "ParagraphOut of scope\n"
431 (org-export-as 'test 'subtree))))))))
435 ;;; Links
437 (ert-deftest test-org-export/fuzzy-links ()
438 "Test fuzzy link export specifications."
439 ;; 1. Links to invisible (keyword) targets should be ignored.
440 (org-test-with-temp-text
441 "Paragraph.\n#+TARGET: Test\n[[Test]]"
442 (let* ((tree (org-element-parse-buffer))
443 (info (org-export-collect-tree-properties tree nil 'test)))
444 (should-not
445 (org-element-map
446 tree 'link
447 (lambda (link)
448 (org-export-get-ordinal
449 (org-export-resolve-fuzzy-link link info) info)) info))))
450 ;; 2. Link to an headline should return headline's number.
451 (org-test-with-temp-text
452 "Paragraph.\n* Head1\n* Head2\n* Head3\n[[Head2]]"
453 (let* ((tree (org-element-parse-buffer))
454 (info (org-export-collect-tree-properties tree nil 'test)))
455 (should
456 ;; Note: Headline's number is in fact a list of numbers.
457 (equal '(2)
458 (org-element-map
459 tree 'link
460 (lambda (link)
461 (org-export-get-ordinal
462 (org-export-resolve-fuzzy-link link info) info)) info t)))))
463 ;; 3. Link to a target in an item should return item's number.
464 (org-test-with-temp-text
465 "- Item1\n - Item11\n - <<test>>Item12\n- Item2\n\n\n[[test]]"
466 (let* ((tree (org-element-parse-buffer))
467 (info (org-export-collect-tree-properties tree nil 'test)))
468 (should
469 ;; Note: Item's number is in fact a list of numbers.
470 (equal '(1 2)
471 (org-element-map
472 tree 'link
473 (lambda (link)
474 (org-export-get-ordinal
475 (org-export-resolve-fuzzy-link link info) info)) info t)))))
476 ;; 4. Link to a target in a footnote should return footnote's
477 ;; number.
478 (org-test-with-temp-text
479 "Paragraph[1][2][fn:lbl3:C<<target>>][[test]][[target]]\n[1] A\n\n[2] <<test>>B"
480 (let* ((tree (org-element-parse-buffer))
481 (info (org-export-collect-tree-properties tree nil 'test)))
482 (should
483 (equal '(2 3)
484 (org-element-map
485 tree 'link
486 (lambda (link)
487 (org-export-get-ordinal
488 (org-export-resolve-fuzzy-link link info) info)) info)))))
489 ;; 5. Link to a named element should return sequence number of that
490 ;; element.
491 (org-test-with-temp-text
492 "#+NAME: tbl1\n|1|2|\n#+NAME: tbl2\n|3|4|\n#+NAME: tbl3\n|5|6|\n[[tbl2]]"
493 (let* ((tree (org-element-parse-buffer))
494 (info (org-export-collect-tree-properties tree nil 'test)))
495 (should
496 (= 2
497 (org-element-map
498 tree 'link
499 (lambda (link)
500 (org-export-get-ordinal
501 (org-export-resolve-fuzzy-link link info) info)) info t)))))
502 ;; 6. Link to a target not within an item, a table, a footnote
503 ;; reference or definition should return section number.
504 (org-test-with-temp-text
505 "* Head1\n* Head2\nParagraph<<target>>\n* Head3\n[[target]]"
506 (let* ((tree (org-element-parse-buffer))
507 (info (org-export-collect-tree-properties tree nil 'test)))
508 (should
509 (equal '(2)
510 (org-element-map
511 tree 'link
512 (lambda (link)
513 (org-export-get-ordinal
514 (org-export-resolve-fuzzy-link link info) info)) info t))))))
516 (defun test-org-export/resolve-coderef ()
517 "Test `org-export-resolve-coderef' specifications."
518 (let ((org-coderef-label-format "(ref:%s)"))
519 ;; 1. A link to a "-n -k -r" block returns line number.
520 (org-test-with-temp-text
521 "#+BEGIN_EXAMPLE -n -k -r\nText (ref:coderef)\n#+END_EXAMPLE"
522 (let ((tree (org-element-parse-buffer)))
523 (should
524 (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
525 (org-test-with-temp-text
526 "#+BEGIN_SRC emacs-lisp -n -k -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
527 (let ((tree (org-element-parse-buffer)))
528 (should
529 (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
530 ;; 2. A link to a "-n -r" block returns line number.
531 (org-test-with-temp-text
532 "#+BEGIN_EXAMPLE -n -r\nText (ref:coderef)\n#+END_EXAMPLE"
533 (let ((tree (org-element-parse-buffer)))
534 (should
535 (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
536 (org-test-with-temp-text
537 "#+BEGIN_SRC emacs-lisp -n -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
538 (let ((tree (org-element-parse-buffer)))
539 (should
540 (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
541 ;; 3. A link to a "-n" block returns coderef.
542 (org-test-with-temp-text
543 "#+BEGIN_SRC emacs-lisp -n\n(+ 1 1) (ref:coderef)\n#+END_SRC"
544 (let ((tree (org-element-parse-buffer)))
545 (should
546 (equal (org-export-resolve-coderef "coderef" `(:parse-tree ,tree))
547 "coderef"))))
548 (org-test-with-temp-text
549 "#+BEGIN_EXAMPLE -n\nText (ref:coderef)\n#+END_EXAMPLE"
550 (let ((tree (org-element-parse-buffer)))
551 (should
552 (equal (org-export-resolve-coderef "coderef" `(:parse-tree ,tree))
553 "coderef"))))
554 ;; 4. A link to a "-r" block returns line number.
555 (org-test-with-temp-text
556 "#+BEGIN_SRC emacs-lisp -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
557 (let ((tree (org-element-parse-buffer)))
558 (should
559 (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
560 (org-test-with-temp-text
561 "#+BEGIN_EXAMPLE -r\nText (ref:coderef)\n#+END_EXAMPLE"
562 (let ((tree (org-element-parse-buffer)))
563 (should
564 (= (org-export-resolve-coderef "coderef" `(:parse-tree ,tree)) 1))))
565 ;; 5. A link to a block without a switch returns coderef.
566 (org-test-with-temp-text
567 "#+BEGIN_SRC emacs-lisp\n(+ 1 1) (ref:coderef)\n#+END_SRC"
568 (let ((tree (org-element-parse-buffer)))
569 (should
570 (equal (org-export-resolve-coderef "coderef" `(:parse-tree ,tree))
571 "coderef"))))
572 (org-test-with-temp-text
573 "#+BEGIN_EXAMPLE\nText (ref:coderef)\n#+END_EXAMPLE"
574 (let ((tree (org-element-parse-buffer)))
575 (should
576 (equal (org-export-resolve-coderef "coderef" `(:parse-tree ,tree))
577 "coderef"))))
578 ;; 6. Correctly handle continued line numbers. A "+n" switch
579 ;; should resume numbering from previous block with numbered
580 ;; lines, ignoring blocks not numbering lines in the process.
581 ;; A "-n" switch resets count.
582 (org-test-with-temp-text "
583 #+BEGIN_EXAMPLE -n
584 Text.
585 #+END_EXAMPLE
587 #+BEGIN_SRC emacs-lisp
588 \(- 1 1)
589 #+END_SRC
591 #+BEGIN_SRC emacs-lisp +n -r
592 \(+ 1 1) (ref:addition)
593 #+END_SRC
595 #+BEGIN_EXAMPLE -n -r
596 Another text. (ref:text)
597 #+END_EXAMPLE"
598 (let* ((tree (org-element-parse-buffer))
599 (info `(:parse-tree ,tree)))
600 (should (= (org-export-resolve-coderef "addition" info) 2))
601 (should (= (org-export-resolve-coderef "text" info) 1))))
602 ;; 7. Recognize coderef with user-specified syntax.
603 (org-test-with-temp-text
604 "#+BEGIN_EXAMPLE -l \"[ref:%s]\"\nText. [ref:text]\n#+END_EXAMPLE"
605 (let ((tree (org-element-parse-buffer)))
606 (should (equal (org-export-resolve-coderef "text" `(:parse-tree ,tree))
607 "text"))))))
611 ;;; Src-block and example-block
613 (ert-deftest test-org-export/unravel-code ()
614 "Test `org-export-unravel-code' function."
615 (let ((org-coderef-label-format "(ref:%s)"))
616 ;; 1. Code without reference.
617 (org-test-with-temp-text "#+BEGIN_EXAMPLE\n(+ 1 1)\n#+END_EXAMPLE"
618 (should (equal (org-export-unravel-code (org-element-current-element))
619 '("(+ 1 1)\n"))))
620 ;; 2. Code with reference.
621 (org-test-with-temp-text
622 "#+BEGIN_EXAMPLE\n(+ 1 1) (ref:test)\n#+END_EXAMPLE"
623 (should (equal (org-export-unravel-code (org-element-current-element))
624 '("(+ 1 1)\n" (1 . "test")))))
625 ;; 3. Code with user-defined reference.
626 (org-test-with-temp-text
627 "#+BEGIN_EXAMPLE -l \"[ref:%s]\"\n(+ 1 1) [ref:test]\n#+END_EXAMPLE"
628 (should (equal (org-export-unravel-code (org-element-current-element))
629 '("(+ 1 1)\n" (1 . "test")))))
630 ;; 4. Code references keys are relative to the current block.
631 (org-test-with-temp-text "
632 #+BEGIN_EXAMPLE -n
633 \(+ 1 1)
634 #+END_EXAMPLE
635 #+BEGIN_EXAMPLE +n
636 \(+ 2 2)
637 \(+ 3 3) (ref:one)
638 #+END_EXAMPLE"
639 (goto-line 5)
640 (should (equal (org-export-unravel-code (org-element-current-element))
641 '("(+ 2 2)\n(+ 3 3)\n" (2 . "one")))))
642 ;; 5. Free up comma-protected lines.
644 ;; 5.1. In an Org source block, every line is protected.
645 (org-test-with-temp-text
646 "#+BEGIN_SRC org\n,* Test\n,# comment\n,Text\n#+END_SRC"
647 (should (equal (org-export-unravel-code (org-element-current-element))
648 '("* Test\n# comment\nText\n"))))
649 ;; 5.2. In other blocks, only headlines, comments and keywords are
650 ;; protected.
651 (org-test-with-temp-text
652 "#+BEGIN_EXAMPLE\n,* Headline\n, * Not headline\n,Keep\n#+END_EXAMPLE"
653 (should (equal (org-export-unravel-code (org-element-current-element))
654 '("* Headline\n, * Not headline\n,Keep\n"))))))
658 ;;; Tables
660 (ert-deftest test-org-export/special-column ()
661 "Test if the table's special column is properly recognized."
662 ;; 1. First column is special if it contains only a special marking
663 ;; characters or empty cells.
664 (org-test-with-temp-text "
665 | ! | 1 |
666 | | 2 |"
667 (should
668 (org-export-table-has-special-column-p
669 (org-element-map
670 (org-element-parse-buffer) 'table 'identity nil 'first-match))))
671 ;; 2. If the column contains anything else, it isn't special.
672 (org-test-with-temp-text "
673 | ! | 1 |
674 | b | 2 |"
675 (should-not
676 (org-export-table-has-special-column-p
677 (org-element-map
678 (org-element-parse-buffer) 'table 'identity nil 'first-match))))
679 ;; 3. Special marking characters are "#", "^", "*", "_", "/", "$"
680 ;; and "!".
681 (org-test-with-temp-text "
682 | # | 1 |
683 | ^ | 2 |
684 | * | 3 |
685 | _ | 4 |
686 | / | 5 |
687 | $ | 6 |
688 | ! | 7 |"
689 (should
690 (org-export-table-has-special-column-p
691 (org-element-map
692 (org-element-parse-buffer) 'table 'identity nil 'first-match))))
693 ;; 4. A first column with only empty cells isn't considered as
694 ;; special.
695 (org-test-with-temp-text "
696 | | 1 |
697 | | 2 |"
698 (should-not
699 (org-export-table-has-special-column-p
700 (org-element-map
701 (org-element-parse-buffer) 'table 'identity nil 'first-match)))))
703 (ert-deftest test-org-export/special-row ()
704 "Test if special rows in a table are properly recognized."
705 ;; 1. A row is special if it has a special marking character in the
706 ;; special column.
707 (org-test-with-parsed-data "| ! | 1 |"
708 (should
709 (org-export-table-row-is-special-p
710 (org-element-map tree 'table-row 'identity nil 'first-match) info)))
711 ;; 2. A row is special when its first field is "/"
712 (org-test-with-parsed-data "
713 | / | 1 |
714 | a | b |"
715 (should
716 (org-export-table-row-is-special-p
717 (org-element-map tree 'table-row 'identity nil 'first-match) info)))
718 ;; 3. A row only containing alignment cookies is also considered as
719 ;; special.
720 (org-test-with-parsed-data "| <5> | | <l> | <l22> |"
721 (should
722 (org-export-table-row-is-special-p
723 (org-element-map tree 'table-row 'identity nil 'first-match) info)))
724 ;; 4. Everything else isn't considered as special.
725 (org-test-with-parsed-data "| a | | c |"
726 (should-not
727 (org-export-table-row-is-special-p
728 (org-element-map tree 'table-row 'identity nil 'first-match) info)))
729 ;; 5. Table's rules are never considered as special rows.
730 (org-test-with-parsed-data "|---+---|"
731 (should-not
732 (org-export-table-row-is-special-p
733 (org-element-map tree 'table-row 'identity nil 'first-match) info))))
735 (ert-deftest test-org-export/has-header-p ()
736 "Test `org-export-table-has-header-p' specifications."
737 ;; 1. With an header.
738 (org-test-with-parsed-data "
739 | a | b |
740 |---+---|
741 | c | d |"
742 (should
743 (org-export-table-has-header-p
744 (org-element-map tree 'table 'identity info 'first-match)
745 info)))
746 ;; 2. Without an header.
747 (org-test-with-parsed-data "
748 | a | b |
749 | c | d |"
750 (should-not
751 (org-export-table-has-header-p
752 (org-element-map tree 'table 'identity info 'first-match)
753 info)))
754 ;; 3. Don't get fooled with starting and ending rules.
755 (org-test-with-parsed-data "
756 |---+---|
757 | a | b |
758 | c | d |
759 |---+---|"
760 (should-not
761 (org-export-table-has-header-p
762 (org-element-map tree 'table 'identity info 'first-match)
763 info))))
765 (ert-deftest test-org-export/table-row-group ()
766 "Test `org-export-table-row-group' specifications."
767 ;; 1. A rule creates a new group.
768 (org-test-with-parsed-data "
769 | a | b |
770 |---+---|
771 | 1 | 2 |"
772 (should
773 (equal
774 '(1 nil 2)
775 (mapcar (lambda (row) (org-export-table-row-group row info))
776 (org-element-map tree 'table-row 'identity)))))
777 ;; 2. Special rows are ignored in count.
778 (org-test-with-parsed-data "
779 | / | < | > |
780 |---|---+---|
781 | | 1 | 2 |"
782 (should
783 (equal
784 '(nil nil 1)
785 (mapcar (lambda (row) (org-export-table-row-group row info))
786 (org-element-map tree 'table-row 'identity)))))
787 ;; 3. Double rules also are ignored in count.
788 (org-test-with-parsed-data "
789 | a | b |
790 |---+---|
791 |---+---|
792 | 1 | 2 |"
793 (should
794 (equal
795 '(1 nil nil 2)
796 (mapcar (lambda (row) (org-export-table-row-group row info))
797 (org-element-map tree 'table-row 'identity))))))
799 (ert-deftest test-org-export/table-cell-width ()
800 "Test `org-export-table-cell-width' specifications."
801 ;; 1. Width is primarily determined by width cookies. If no cookie
802 ;; is found, cell's width is nil.
803 (org-test-with-parsed-data "
804 | / | <l> | <6> | <l7> |
805 | | a | b | c |"
806 (should
807 (equal
808 '(nil 6 7)
809 (mapcar (lambda (cell) (org-export-table-cell-width cell info))
810 (org-element-map tree 'table-cell 'identity info)))))
811 ;; 2. The last width cookie has precedence.
812 (org-test-with-parsed-data "
813 | <6> |
814 | <7> |
815 | a |"
816 (should
817 (equal
818 '(7)
819 (mapcar (lambda (cell) (org-export-table-cell-width cell info))
820 (org-element-map tree 'table-cell 'identity info)))))
821 ;; 3. Valid width cookies must have a specific row.
822 (org-test-with-parsed-data "| <6> | cell |"
823 (should
824 (equal
825 '(nil nil)
826 (mapcar (lambda (cell) (org-export-table-cell-width cell info))
827 (org-element-map tree 'table-cell 'identity))))))
829 (ert-deftest test-org-export/table-cell-alignment ()
830 "Test `org-export-table-cell-alignment' specifications."
831 (let ((org-table-number-fraction 0.5)
832 (org-table-number-regexp "^[0-9]+$"))
833 ;; 1. Alignment is primarily determined by alignment cookies.
834 (org-test-with-temp-text "| <l> | <c> | <r> |"
835 (let* ((tree (org-element-parse-buffer))
836 (info `(:parse-tree ,tree)))
837 (should
838 (equal
839 '(left center right)
840 (mapcar (lambda (cell) (org-export-table-cell-alignment cell info))
841 (org-element-map tree 'table-cell 'identity))))))
842 ;; 2. The last alignment cookie has precedence.
843 (org-test-with-temp-text "
844 | <l8> |
845 | cell |
846 | <r9> |"
847 (let* ((tree (org-element-parse-buffer))
848 (info `(:parse-tree ,tree)))
849 (should
850 (equal
851 '(right right right)
852 (mapcar (lambda (cell) (org-export-table-cell-alignment cell info))
853 (org-element-map tree 'table-cell 'identity))))))
854 ;; 3. If there's no cookie, cell's contents determine alignment.
855 ;; A column mostly made of cells containing numbers will align
856 ;; its cells to the right.
857 (org-test-with-temp-text "
858 | 123 |
859 | some text |
860 | 12345 |"
861 (let* ((tree (org-element-parse-buffer))
862 (info `(:parse-tree ,tree)))
863 (should
864 (equal
865 '(right right right)
866 (mapcar (lambda (cell)
867 (org-export-table-cell-alignment cell info))
868 (org-element-map tree 'table-cell 'identity))))))
869 ;; 5. Otherwise, they will be aligned to the left.
870 (org-test-with-temp-text "
871 | text |
872 | some text |
873 | 12345 |"
874 (let* ((tree (org-element-parse-buffer))
875 (info `(:parse-tree ,tree)))
876 (should
877 (equal
878 '(left left left)
879 (mapcar (lambda (cell)
880 (org-export-table-cell-alignment cell info))
881 (org-element-map tree 'table-cell 'identity))))))))
883 (ert-deftest test-org-export/table-cell-borders ()
884 "Test `org-export-table-cell-borders' specifications."
885 ;; 1. Recognize various column groups indicators.
886 (org-test-with-parsed-data "| / | < | > | <> |"
887 (should
888 (equal
889 '((right bottom top) (left bottom top) (right bottom top)
890 (right left bottom top))
891 (mapcar (lambda (cell)
892 (org-export-table-cell-borders cell info))
893 (org-element-map tree 'table-cell 'identity)))))
894 ;; 2. Accept shortcuts to define column groups.
895 (org-test-with-parsed-data "| / | < | < |"
896 (should
897 (equal
898 '((right bottom top) (right left bottom top) (left bottom top))
899 (mapcar (lambda (cell)
900 (org-export-table-cell-borders cell info))
901 (org-element-map tree 'table-cell 'identity)))))
902 ;; 3. A valid column groups row must start with a "/".
903 (org-test-with-parsed-data "
904 | | < |
905 | a | b |"
906 (should
907 (equal '((top) (top) (bottom) (bottom))
908 (mapcar (lambda (cell)
909 (org-export-table-cell-borders cell info))
910 (org-element-map tree 'table-cell 'identity)))))
911 ;; 4. Take table rules into consideration.
912 (org-test-with-parsed-data "
913 | 1 |
914 |---|
915 | 2 |"
916 (should
917 (equal '((below top) (bottom above))
918 (mapcar (lambda (cell)
919 (org-export-table-cell-borders cell info))
920 (org-element-map tree 'table-cell 'identity)))))
921 ;; 5. Top and (resp. bottom) rules induce both `top' and `above'
922 ;; (resp. `bottom' and `below') borders. Any special row is
923 ;; ignored.
924 (org-test-with-parsed-data "
925 |---+----|
926 | / | |
927 | | 1 |
928 |---+----|"
929 (should
930 (equal '((bottom below top above))
931 (last
932 (mapcar (lambda (cell)
933 (org-export-table-cell-borders cell info))
934 (org-element-map tree 'table-cell 'identity)))))))
936 (ert-deftest test-org-export/table-dimensions ()
937 "Test `org-export-table-dimensions' specifications."
938 ;; 1. Standard test.
939 (org-test-with-parsed-data "
940 | 1 | 2 | 3 |
941 | 4 | 5 | 6 |"
942 (should
943 (equal '(2 . 3)
944 (org-export-table-dimensions
945 (org-element-map tree 'table 'identity info 'first-match) info))))
946 ;; 2. Ignore horizontal rules and special columns.
947 (org-test-with-parsed-data "
948 | / | < | > |
949 | 1 | 2 | 3 |
950 |---+---+---|
951 | 4 | 5 | 6 |"
952 (should
953 (equal '(2 . 3)
954 (org-export-table-dimensions
955 (org-element-map tree 'table 'identity info 'first-match) info)))))
957 (ert-deftest test-org-export/table-cell-address ()
958 "Test `org-export-table-cell-address' specifications."
959 ;; 1. Standard test: index is 0-based.
960 (org-test-with-parsed-data "| a | b |"
961 (should
962 (equal '((0 . 0) (0 . 1))
963 (org-element-map
964 tree 'table-cell
965 (lambda (cell) (org-export-table-cell-address cell info))
966 info))))
967 ;; 2. Special column isn't counted, nor are special rows.
968 (org-test-with-parsed-data "
969 | / | <> |
970 | | c |"
971 (should
972 (equal '(0 . 0)
973 (org-export-table-cell-address
974 (car (last (org-element-map tree 'table-cell 'identity info)))
975 info))))
976 ;; 3. Tables rules do not count either.
977 (org-test-with-parsed-data "
978 | a |
979 |---|
980 | b |
981 |---|
982 | c |"
983 (should
984 (equal '(2 . 0)
985 (org-export-table-cell-address
986 (car (last (org-element-map tree 'table-cell 'identity info)))
987 info))))
988 ;; 4. Return nil for special cells.
989 (org-test-with-parsed-data "| / | a |"
990 (should-not
991 (org-export-table-cell-address
992 (org-element-map tree 'table-cell 'identity nil 'first-match)
993 info))))
995 (ert-deftest test-org-export/get-table-cell-at ()
996 "Test `org-export-get-table-cell-at' specifications."
997 ;; 1. Address ignores special columns, special rows and rules.
998 (org-test-with-parsed-data "
999 | / | <> |
1000 | | a |
1001 |---+----|
1002 | | b |"
1003 (should
1004 (equal '("b")
1005 (org-element-contents
1006 (org-export-get-table-cell-at
1007 '(1 . 0)
1008 (org-element-map tree 'table 'identity info 'first-match)
1009 info)))))
1010 ;; 2. Return value for a non-existent address is nil.
1011 (org-test-with-parsed-data "| a |"
1012 (should-not
1013 (org-export-get-table-cell-at
1014 '(2 . 2)
1015 (org-element-map tree 'table 'identity info 'first-match)
1016 info)))
1017 (org-test-with-parsed-data "| / |"
1018 (should-not
1019 (org-export-get-table-cell-at
1020 '(0 . 0)
1021 (org-element-map tree 'table 'identity info 'first-match)
1022 info))))
1024 (ert-deftest test-org-export/table-cell-starts-colgroup-p ()
1025 "Test `org-export-table-cell-starts-colgroup-p' specifications."
1026 ;; 1. A cell at a beginning of a row always starts a column group.
1027 (org-test-with-parsed-data "| a |"
1028 (should
1029 (org-export-table-cell-starts-colgroup-p
1030 (org-element-map tree 'table-cell 'identity info 'first-match)
1031 info)))
1032 ;; 2. Special column should be ignored when determining the
1033 ;; beginning of the row.
1034 (org-test-with-parsed-data "
1035 | / | |
1036 | | a |"
1037 (should
1038 (org-export-table-cell-starts-colgroup-p
1039 (org-element-map tree 'table-cell 'identity info 'first-match)
1040 info)))
1041 ;; 2. Explicit column groups.
1042 (org-test-with-parsed-data "
1043 | / | | < |
1044 | a | b | c |"
1045 (should
1046 (equal
1047 '(yes no yes)
1048 (org-element-map
1049 tree 'table-cell
1050 (lambda (cell)
1051 (if (org-export-table-cell-starts-colgroup-p cell info) 'yes 'no))
1052 info)))))
1054 (ert-deftest test-org-export/table-cell-ends-colgroup-p ()
1055 "Test `org-export-table-cell-ends-colgroup-p' specifications."
1056 ;; 1. A cell at the end of a row always ends a column group.
1057 (org-test-with-parsed-data "| a |"
1058 (should
1059 (org-export-table-cell-ends-colgroup-p
1060 (org-element-map tree 'table-cell 'identity info 'first-match)
1061 info)))
1062 ;; 2. Special column should be ignored when determining the
1063 ;; beginning of the row.
1064 (org-test-with-parsed-data "
1065 | / | |
1066 | | a |"
1067 (should
1068 (org-export-table-cell-ends-colgroup-p
1069 (org-element-map tree 'table-cell 'identity info 'first-match)
1070 info)))
1071 ;; 3. Explicit column groups.
1072 (org-test-with-parsed-data "
1073 | / | < | |
1074 | a | b | c |"
1075 (should
1076 (equal
1077 '(yes no yes)
1078 (org-element-map
1079 tree 'table-cell
1080 (lambda (cell)
1081 (if (org-export-table-cell-ends-colgroup-p cell info) 'yes 'no))
1082 info)))))
1084 (ert-deftest test-org-export/table-row-starts-rowgroup-p ()
1085 "Test `org-export-table-row-starts-rowgroup-p' specifications."
1086 ;; 1. A row at the beginning of a table always starts a row group.
1087 ;; So does a row following a table rule.
1088 (org-test-with-parsed-data "
1089 | a |
1090 |---|
1091 | b |"
1092 (should
1093 (equal
1094 '(yes no yes)
1095 (org-element-map
1096 tree 'table-row
1097 (lambda (row)
1098 (if (org-export-table-row-starts-rowgroup-p row info) 'yes 'no))
1099 info))))
1100 ;; 2. Special rows should be ignored when determining the beginning
1101 ;; of the row.
1102 (org-test-with-parsed-data "
1103 | / | < |
1104 | | a |
1105 |---+---|
1106 | / | < |
1107 | | b |"
1108 (should
1109 (equal
1110 '(yes no yes)
1111 (org-element-map
1112 tree 'table-row
1113 (lambda (row)
1114 (if (org-export-table-row-starts-rowgroup-p row info) 'yes 'no))
1115 info)))))
1117 (ert-deftest test-org-export/table-row-ends-rowgroup-p ()
1118 "Test `org-export-table-row-ends-rowgroup-p' specifications."
1119 ;; 1. A row at the end of a table always ends a row group. So does
1120 ;; a row preceding a table rule.
1121 (org-test-with-parsed-data "
1122 | a |
1123 |---|
1124 | b |"
1125 (should
1126 (equal
1127 '(yes no yes)
1128 (org-element-map
1129 tree 'table-row
1130 (lambda (row)
1131 (if (org-export-table-row-ends-rowgroup-p row info) 'yes 'no))
1132 info))))
1133 ;; 2. Special rows should be ignored when determining the beginning
1134 ;; of the row.
1135 (org-test-with-parsed-data "
1136 | | a |
1137 | / | < |
1138 |---+---|
1139 | | b |
1140 | / | < |"
1141 (should
1142 (equal
1143 '(yes no yes)
1144 (org-element-map
1145 tree 'table-row
1146 (lambda (row)
1147 (if (org-export-table-row-ends-rowgroup-p row info) 'yes 'no))
1148 info)))))
1150 (ert-deftest test-org-export/table-row-starts-header-p ()
1151 "Test `org-export-table-row-starts-header-p' specifications."
1152 ;; 1. Only the row starting the first row group starts the table
1153 ;; header.
1154 (org-test-with-parsed-data "
1155 | a |
1156 | b |
1157 |---|
1158 | c |"
1159 (should
1160 (equal
1161 '(yes no no no)
1162 (org-element-map
1163 tree 'table-row
1164 (lambda (row)
1165 (if (org-export-table-row-starts-header-p row info) 'yes 'no))
1166 info))))
1167 ;; 2. A row cannot start an header if there's no header in the
1168 ;; table.
1169 (org-test-with-parsed-data "
1170 | a |
1171 |---|"
1172 (should-not
1173 (org-export-table-row-starts-header-p
1174 (org-element-map tree 'table-row 'identity info 'first-match)
1175 info))))
1177 (ert-deftest test-org-export/table-row-ends-header-p ()
1178 "Test `org-export-table-row-ends-header-p' specifications."
1179 ;; 1. Only the row starting the first row group starts the table
1180 ;; header.
1181 (org-test-with-parsed-data "
1182 | a |
1183 | b |
1184 |---|
1185 | c |"
1186 (should
1187 (equal
1188 '(no yes no no)
1189 (org-element-map
1190 tree 'table-row
1191 (lambda (row)
1192 (if (org-export-table-row-ends-header-p row info) 'yes 'no))
1193 info))))
1194 ;; 2. A row cannot start an header if there's no header in the
1195 ;; table.
1196 (org-test-with-parsed-data "
1197 | a |
1198 |---|"
1199 (should-not
1200 (org-export-table-row-ends-header-p
1201 (org-element-map tree 'table-row 'identity info 'first-match)
1202 info))))
1205 (provide 'test-org-export)
1206 ;;; test-org-export.el end here