org-export: Add tests
[org-mode.git] / testing / lisp / test-org-export.el
blob8407c2fb58868573f0afc1f29cac2330ff0a7548
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"))
19 (defmacro org-test-with-backend (backend &rest body)
20 "Execute body with an export back-end defined.
22 BACKEND is the name of the back-end. BODY is the body to
23 execute. The defined back-end simply returns parsed data as Org
24 syntax."
25 (declare (debug (form body)) (indent 1))
26 `(let ((,(intern (format "org-%s-translate-alist" backend))
27 ',(let (transcode-table)
28 (dolist (type (append org-element-all-elements
29 org-element-all-objects)
30 transcode-table)
31 (push (cons type (intern (format "org-%s-%s" backend type)))
32 transcode-table)))))
33 (flet ,(let (transcoders)
34 (dolist (type (append org-element-all-elements
35 org-element-all-objects)
36 transcoders)
37 (push `(,(intern (format "org-%s-%s" backend type))
38 (obj contents info)
39 (,(intern (format "org-element-%s-interpreter" type))
40 obj contents))
41 transcoders)))
42 ,@body)))
44 (defmacro org-test-with-parsed-data (data &rest body)
45 "Execute body with parsed data available.
47 DATA is a string containing the data to be parsed. BODY is the
48 body to execute. Parse tree is available under the `tree'
49 variable, and communication channel under `info'.
51 This function calls `org-export-collect-tree-properties'. As
52 such, `:ignore-list' (for `org-element-map') and
53 `:parse-tree' (for `org-export-get-genealogy') properties are
54 already filled in `info'."
55 (declare (debug (form body)) (indent 1))
56 `(org-test-with-temp-text ,data
57 (let* ((tree (org-element-parse-buffer))
58 (info (org-export-collect-tree-properties
59 tree (org-export-get-environment))))
60 ,@body)))
64 ;;; Tests
66 (ert-deftest test-org-export/parse-option-keyword ()
67 "Test reading all standard #+OPTIONS: items."
68 (should
69 (equal
70 (org-export-parse-option-keyword
71 "H:1 num:t \\n:t timestamp:t arch:t author:t creator:t d:t email:t
72 *:t e:t ::t f:t pri:t -:t ^:t toc:t |:t tags:t tasks:t <:t todo:t")
73 '(:headline-levels
74 1 :preserve-breaks t :section-numbers t :time-stamp-file t
75 :with-archived-trees t :with-author t :with-creator t :with-drawers t
76 :with-email t :with-emphasize t :with-entities t :with-fixed-width t
77 :with-footnotes t :with-priority t :with-special-strings t
78 :with-sub-superscript t :with-toc t :with-tables t :with-tags t
79 :with-tasks t :with-timestamps t :with-todo-keywords t)))
80 ;; Test some special values.
81 (should
82 (equal
83 (org-export-parse-option-keyword
84 "arch:headline creator:comment d:(\"TEST\")
85 ^:{} toc:1 tags:not-in-toc tasks:todo num:2 <:active")
86 '( :section-numbers
88 :with-archived-trees headline :with-creator comment
89 :with-drawers ("TEST") :with-sub-superscript {} :with-toc 1
90 :with-tags not-in-toc :with-tasks todo :with-timestamps active))))
92 (ert-deftest test-org-export/get-inbuffer-options ()
93 "Test reading all standard export keywords."
94 (should
95 (equal
96 (org-test-with-temp-text "#+AUTHOR: Me, Myself and I
97 #+CREATOR: Idem
98 #+DATE: Today
99 #+DESCRIPTION: Testing
100 #+DESCRIPTION: with two lines
101 #+EMAIL: some@email.org
102 #+EXPORT_EXCLUDE_TAGS: noexport invisible
103 #+KEYWORDS: test
104 #+LANGUAGE: en
105 #+EXPORT_SELECT_TAGS: export
106 #+TITLE: Some title
107 #+TITLE: with spaces"
108 (org-export-get-inbuffer-options))
109 '(:author
110 ("Me, Myself and I") :creator "Idem" :date ("Today")
111 :description "Testing\nwith two lines" :email "some@email.org"
112 :exclude-tags ("noexport" "invisible") :keywords "test" :language "en"
113 :select-tags ("export") :title ("Some title with spaces")))))
115 (ert-deftest test-org-export/handle-options ()
116 "Test if export options have an impact on output."
117 ;; Test exclude tags.
118 (org-test-with-temp-text "* Head1 :noexport:"
119 (org-test-with-backend test
120 (should
121 (equal (org-export-as 'test nil nil nil '(:exclude-tags ("noexport")))
122 ""))))
123 ;; Test include tags.
124 (org-test-with-temp-text "
125 * Head1
126 * Head2
127 ** Sub-Head2.1 :export:
128 *** Sub-Head2.1.1
129 * Head2"
130 (org-test-with-backend test
131 (should
132 (equal
133 "* Head2\n** Sub-Head2.1 :export:\n*** Sub-Head2.1.1\n"
134 (let ((org-tags-column 0))
135 (org-export-as 'test nil nil nil '(:select-tags ("export"))))))))
136 ;; Test mixing include tags and exclude tags.
137 (org-test-with-temp-text "
138 * Head1 :export:
139 ** Sub-Head1 :noexport:
140 ** Sub-Head2
141 * Head2 :noexport:
142 ** Sub-Head1 :export:"
143 (org-test-with-backend test
144 (should
145 (string-match
146 "\\* Head1[ \t]+:export:\n\\*\\* Sub-Head2\n"
147 (org-export-as
148 'test nil nil nil
149 '(:select-tags ("export") :exclude-tags ("noexport")))))))
150 ;; Ignore tasks.
151 (let ((org-todo-keywords '((sequence "TODO" "DONE"))))
152 (org-test-with-temp-text "* TODO Head1"
153 (org-test-with-backend test
154 (should (equal (org-export-as 'test nil nil nil '(:with-tasks nil))
155 "")))))
156 (let ((org-todo-keywords '((sequence "TODO" "DONE"))))
157 (org-test-with-temp-text "* TODO Head1"
158 (org-test-with-backend test
159 (should (equal (org-export-as 'test nil nil nil '(:with-tasks t))
160 "* TODO Head1\n")))))
161 ;; Archived tree.
162 (org-test-with-temp-text "* Head1 :archive:"
163 (let ((org-archive-tag "archive"))
164 (org-test-with-backend test
165 (should
166 (equal (org-export-as 'test nil nil nil '(:with-archived-trees nil))
167 "")))))
168 (org-test-with-temp-text "* Head1 :archive:\nbody\n** Sub-head 2"
169 (let ((org-archive-tag "archive"))
170 (org-test-with-backend test
171 (should
172 (string-match
173 "\\* Head1[ \t]+:archive:"
174 (org-export-as 'test nil nil nil
175 '(:with-archived-trees headline)))))))
176 (org-test-with-temp-text "* Head1 :archive:"
177 (let ((org-archive-tag "archive"))
178 (org-test-with-backend test
179 (should
180 (string-match
181 "\\`\\* Head1[ \t]+:archive:\n\\'"
182 (org-export-as 'test nil nil nil '(:with-archived-trees t)))))))
183 ;; Drawers.
184 (let ((org-drawers '("TEST")))
185 (org-test-with-temp-text ":TEST:\ncontents\n:END:"
186 (org-test-with-backend test
187 (should (equal (org-export-as 'test nil nil nil '(:with-drawers nil))
188 ""))
189 (should (equal (org-export-as 'test nil nil nil '(:with-drawers t))
190 ":TEST:\ncontents\n:END:\n")))))
191 (let ((org-drawers '("FOO" "BAR")))
192 (org-test-with-temp-text ":FOO:\nkeep\n:END:\n:BAR:\nremove\n:END:"
193 (org-test-with-backend test
194 (should
195 (equal (org-export-as 'test nil nil nil '(:with-drawers ("FOO")))
196 ":FOO:\nkeep\n:END:\n")))))
197 ;; Timestamps.
198 (org-test-with-temp-text "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>"
199 (org-test-with-backend test
200 (should
201 (equal (org-export-as 'test nil nil nil '(:with-timestamps t))
202 "[2012-04-29 sun. 10:45]<2012-04-29 sun. 10:45>\n"))
203 (should
204 (equal (org-export-as 'test nil nil nil '(:with-timestamps nil)) ""))
205 (should
206 (equal (org-export-as 'test nil nil nil '(:with-timestamps active))
207 "<2012-04-29 sun. 10:45>\n"))
208 (should
209 (equal (org-export-as 'test nil nil nil '(:with-timestamps inactive))
210 "[2012-04-29 sun. 10:45]\n"))))
211 ;; Clocks.
212 (let ((org-clock-string "CLOCK:"))
213 (org-test-with-temp-text "CLOCK: [2012-04-29 sun. 10:45]"
214 (org-test-with-backend test
215 (should
216 (equal (org-export-as 'test nil nil nil '(:with-clocks t))
217 "CLOCK: [2012-04-29 sun. 10:45]\n"))
218 (should
219 (equal (org-export-as 'test nil nil nil '(:with-clocks nil)) "")))))
220 ;; Plannings.
221 (let ((org-closed-string "CLOSED:"))
222 (org-test-with-temp-text "CLOSED: [2012-04-29 sun. 10:45]"
223 (org-test-with-backend test
224 (should
225 (equal (org-export-as 'test nil nil nil '(:with-plannings t))
226 "CLOSED: [2012-04-29 sun. 10:45]\n"))
227 (should
228 (equal (org-export-as 'test nil nil nil '(:with-plannings nil))
229 ""))))))
231 (ert-deftest test-org-export/comment-tree ()
232 "Test if export process ignores commented trees."
233 (let ((org-comment-string "COMMENT"))
234 (org-test-with-temp-text "* COMMENT Head1"
235 (org-test-with-backend test
236 (should (equal (org-export-as 'test) ""))))))
238 (ert-deftest test-org-export/export-scope ()
239 "Test all export scopes."
240 (org-test-with-temp-text "
241 * Head1
242 ** Head2
243 text
244 *** Head3"
245 (org-test-with-backend test
246 ;; Subtree.
247 (forward-line 3)
248 (should (equal (org-export-as 'test 'subtree) "text\n*** Head3\n"))
249 ;; Visible.
250 (goto-char (point-min))
251 (forward-line)
252 (org-cycle)
253 (should (equal (org-export-as 'test nil 'visible) "* Head1\n"))
254 ;; Body only.
255 (flet ((org-test-template (body info) (format "BEGIN\n%sEND" body)))
256 (push '(template . org-test-template) org-test-translate-alist)
257 (should (equal (org-export-as 'test nil nil 'body-only)
258 "* Head1\n** Head2\ntext\n*** Head3\n"))
259 (should (equal (org-export-as 'test)
260 "BEGIN\n* Head1\n** Head2\ntext\n*** Head3\nEND")))
261 ;; Region.
262 (goto-char (point-min))
263 (forward-line 3)
264 (transient-mark-mode 1)
265 (push-mark (point) t t)
266 (goto-char (point-at-eol))
267 (should (equal (org-export-as 'test) "text\n"))))
268 ;; Subtree with a code block calling another block outside.
269 (org-test-with-temp-text "
270 * Head1
271 #+BEGIN_SRC emacs-lisp :noweb yes :exports results
272 <<test>>
273 #+END_SRC
274 * Head2
275 #+NAME: test
276 #+BEGIN_SRC emacs-lisp
277 \(+ 1 2)
278 #+END_SRC"
279 (org-test-with-backend test
280 (forward-line 1)
281 (should (equal (org-export-as 'test 'subtree) ": 3\n"))))
282 ;; Subtree's EXPORT_TITLE property.
283 (should
284 (equal
285 (plist-get (org-test-with-temp-text "* Headline
286 :PROPERTIES:
287 :EXPORT_TITLE: subtree-title
288 :END:
289 Paragraph"
290 (org-export-get-environment nil t))
291 :title)
292 '("subtree-title")))
293 ;; Subtree's EXPORT_TITLE property.
294 (org-test-with-temp-text "#+OPTIONS: H:1
295 * Headline
296 :PROPERTIES:
297 :EXPORT_OPTIONS: H:2
298 :END:
299 Paragraph"
300 (forward-line)
301 (should
302 (= 2 (plist-get (org-export-get-environment nil t) :headline-levels)))))
304 (ert-deftest test-org-export/export-snippet ()
305 "Test export snippets transcoding."
306 (org-test-with-temp-text "@@test:A@@@@t:B@@"
307 (org-test-with-backend test
308 (flet ((org-test-export-snippet
309 (snippet contents info)
310 (when (eq (org-export-snippet-backend snippet) 'test)
311 (org-element-property :value snippet))))
312 (let ((org-export-snippet-translation-alist nil))
313 (should (equal (org-export-as 'test) "A\n")))
314 (let ((org-export-snippet-translation-alist '(("t" . "test"))))
315 (should (equal (org-export-as 'test) "AB\n")))))))
317 (ert-deftest test-org-export/expand-include ()
318 "Test file inclusion in an Org buffer."
319 ;; Full insertion with recursive inclusion.
320 (org-test-with-temp-text
321 (format "#+INCLUDE: \"%s/examples/include.org\"" org-test-dir)
322 (org-export-expand-include-keyword)
323 (should (equal (buffer-string)
324 "Small Org file with an include keyword.
326 #+BEGIN_SRC emacs-lisp :exports results\n(+ 2 1)\n#+END_SRC
328 Success!
330 * Heading
331 body\n")))
332 ;; Localized insertion.
333 (org-test-with-temp-text
334 (format "#+INCLUDE: \"%s/examples/include.org\" :lines \"1-2\""
335 org-test-dir)
336 (org-export-expand-include-keyword)
337 (should (equal (buffer-string)
338 "Small Org file with an include keyword.\n")))
339 ;; Insertion with constraints on headlines level.
340 (org-test-with-temp-text
341 (format
342 "* Top heading\n#+INCLUDE: \"%s/examples/include.org\" :lines \"9-\""
343 org-test-dir)
344 (org-export-expand-include-keyword)
345 (should (equal (buffer-string) "* Top heading\n** Heading\nbody\n")))
346 ;; Inclusion within an example block.
347 (org-test-with-temp-text
348 (format "#+INCLUDE: \"%s/examples/include.org\" :lines \"1-2\" example"
349 org-test-dir)
350 (org-export-expand-include-keyword)
351 (should
352 (equal
353 (buffer-string)
354 "#+BEGIN_EXAMPLE\nSmall Org file with an include keyword.\n#+END_EXAMPLE\n")))
355 ;; Inclusion within a src-block.
356 (org-test-with-temp-text
357 (format
358 "#+INCLUDE: \"%s/examples/include.org\" :lines \"4-5\" src emacs-lisp"
359 org-test-dir)
360 (org-export-expand-include-keyword)
361 (should (equal (buffer-string)
362 "#+BEGIN_SRC emacs-lisp\n(+ 2 1)\n#+END_SRC\n"))))
364 (ert-deftest test-org-export/user-ignore-list ()
365 "Test if `:ignore-list' accepts user input."
366 (org-test-with-backend test
367 (flet ((skip-note-head
368 (data backend info)
369 ;; Ignore headlines with the word "note" in their title.
370 (org-element-map
371 data 'headline
372 (lambda (headline)
373 (when (string-match "\\<note\\>"
374 (org-element-property :raw-value headline))
375 (org-export-ignore-element headline info)))
376 info)
377 data))
378 ;; Install function in parse tree filters.
379 (let ((org-export-filter-parse-tree-functions '(skip-note-head)))
380 (org-test-with-temp-text "* Head1\n* Head2 (note)\n"
381 (should (equal (org-export-as 'test) "* Head1\n")))))))
383 (ert-deftest test-org-export/before-parsing-hook ()
384 "Test `org-export-before-parsing-hook'."
385 (org-test-with-backend test
386 (org-test-with-temp-text "* Headline 1\nBody 1\n* Headline 2\nBody 2"
387 (let ((org-export-before-parsing-hook
388 '((lambda ()
389 (org-map-entries
390 (lambda ()
391 (delete-region (point) (progn (forward-line) (point)))))))))
392 (should (equal (org-export-as 'test) "Body 1\nBody 2\n"))))))
394 (ert-deftest test-org-export/set-element ()
395 "Test `org-export-set-element' specifications."
396 (org-test-with-parsed-data "* Headline\n*a*"
397 (org-export-set-element
398 (org-element-map tree 'bold 'identity nil t)
399 '(italic nil "b"))
400 ;; Check if object is correctly replaced.
401 (should (org-element-map tree 'italic 'identity))
402 (should-not (org-element-map tree 'bold 'identity))
403 ;; Check if new object's parent is correctly set.
404 (should
405 (equal
406 (org-element-property :parent
407 (org-element-map tree 'italic 'identity nil t))
408 (org-element-map tree 'paragraph 'identity nil t)))))
412 ;;; Affiliated Keywords
414 (ert-deftest test-org-export/read-attribute ()
415 "Test `org-export-read-attribute' specifications."
416 ;; Standard test.
417 (should
418 (equal
419 (org-export-read-attribute
420 :attr_html
421 (org-test-with-temp-text "#+ATTR_HTML: :a 1 :b 2\nParagraph"
422 (org-element-current-element)))
423 '(:a 1 :b 2)))
424 ;; Return nil on empty attribute.
425 (should-not
426 (org-export-read-attribute
427 :attr_html
428 (org-test-with-temp-text "Paragraph" (org-element-current-element)))))
431 ;;; Footnotes
433 (ert-deftest test-org-export/footnotes ()
434 "Test footnotes specifications."
435 (let ((org-footnote-section nil)
436 (org-export-with-footnotes t))
437 ;; 1. Read every type of footnote.
438 (org-test-with-parsed-data
439 "Text[fn:1] [1] [fn:label:C] [fn::D]\n\n[fn:1] A\n\n[1] B"
440 (should
441 (equal
442 '((1 . "A") (2 . "B") (3 . "C") (4 . "D"))
443 (org-element-map
444 tree 'footnote-reference
445 (lambda (ref)
446 (let ((def (org-export-get-footnote-definition ref info)))
447 (cons (org-export-get-footnote-number ref info)
448 (if (eq (org-element-property :type ref) 'inline) (car def)
449 (car (org-element-contents
450 (car (org-element-contents def))))))))
451 info))))
452 ;; 2. Test nested footnotes order.
453 (org-test-with-parsed-data
454 "Text[fn:1:A[fn:2]] [fn:3].\n\n[fn:2] B [fn:3] [fn::D].\n\n[fn:3] C."
455 (should
456 (equal
457 '((1 . "fn:1") (2 . "fn:2") (3 . "fn:3") (4))
458 (org-element-map
459 tree 'footnote-reference
460 (lambda (ref)
461 (when (org-export-footnote-first-reference-p ref info)
462 (cons (org-export-get-footnote-number ref info)
463 (org-element-property :label ref))))
464 info))))
465 ;; 3. Test nested footnote in invisible definitions.
466 (org-test-with-temp-text "Text[1]\n\n[1] B [2]\n\n[2] C."
467 ;; Hide definitions.
468 (narrow-to-region (point) (point-at-eol))
469 (let* ((tree (org-element-parse-buffer))
470 (info (org-combine-plists
471 `(:parse-tree ,tree)
472 (org-export-collect-tree-properties
473 tree (org-export-get-environment)))))
474 ;; Both footnotes should be seen.
475 (should
476 (= (length (org-export-collect-footnote-definitions tree info)) 2))))
477 ;; 4. Test footnotes definitions collection.
478 (org-test-with-parsed-data "Text[fn:1:A[fn:2]] [fn:3].
480 \[fn:2] B [fn:3] [fn::D].
482 \[fn:3] C."
483 (should (= (length (org-export-collect-footnote-definitions tree info))
484 4)))
485 ;; 5. Test export of footnotes defined outside parsing scope.
486 (org-test-with-temp-text "[fn:1] Out of scope
487 * Title
488 Paragraph[fn:1]"
489 (org-test-with-backend test
490 (flet ((org-test-footnote-reference
491 (fn-ref contents info)
492 (org-element-interpret-data
493 (org-export-get-footnote-definition fn-ref info))))
494 (forward-line)
495 (should (equal "ParagraphOut of scope\n"
496 (org-export-as 'test 'subtree))))))))
500 ;;; Headlines and Inlinetasks
502 (ert-deftest test-org-export/get-relative-level ()
503 "Test `org-export-get-relative-level' specifications."
504 ;; Standard test.
505 (should
506 (equal '(1 2)
507 (let ((org-odd-levels-only nil))
508 (org-test-with-parsed-data "* Headline 1\n** Headline 2"
509 (org-element-map
510 tree 'headline
511 (lambda (h) (org-export-get-relative-level h info))
512 info)))))
513 ;; Missing levels
514 (should
515 (equal '(1 3)
516 (let ((org-odd-levels-only nil))
517 (org-test-with-parsed-data "** Headline 1\n**** Headline 2"
518 (org-element-map
519 tree 'headline
520 (lambda (h) (org-export-get-relative-level h info))
521 info))))))
523 (ert-deftest test-org-export/low-level-p ()
524 "Test `org-export-low-level-p' specifications."
525 (should
526 (equal
527 '(no yes)
528 (let ((org-odd-levels-only nil))
529 (org-test-with-parsed-data "* Headline 1\n** Headline 2"
530 (org-element-map
531 tree 'headline
532 (lambda (h) (if (org-export-low-level-p h info) 'yes 'no))
533 (plist-put info :headline-levels 1)))))))
535 (ert-deftest test-org-export/get-headline-number ()
536 "Test `org-export-get-headline-number' specifications."
537 ;; Standard test.
538 (should
539 (equal
540 '((1) (1 1))
541 (let ((org-odd-levels-only nil))
542 (org-test-with-parsed-data "* Headline 1\n** Headline 2"
543 (org-element-map
544 tree 'headline
545 (lambda (h) (org-export-get-headline-number h info))
546 info)))))
547 ;; Missing levels are replaced with 0.
548 (should
549 (equal
550 '((1) (1 0 1))
551 (let ((org-odd-levels-only nil))
552 (org-test-with-parsed-data "* Headline 1\n*** Headline 2"
553 (org-element-map
554 tree 'headline
555 (lambda (h) (org-export-get-headline-number h info))
556 info))))))
558 (ert-deftest test-org-export/numbered-headline-p ()
559 "Test `org-export-numbered-headline-p' specifications."
560 ;; If `:section-numbers' is nil, never number headlines.
561 (should-not
562 (org-test-with-parsed-data "* Headline"
563 (org-element-map
564 tree 'headline
565 (lambda (h) (org-export-numbered-headline-p h info))
566 (plist-put info :section-numbers nil))))
567 ;; If `:section-numbers' is a number, only number headlines with
568 ;; a level greater that it.
569 (should
570 (equal
571 '(yes no)
572 (org-test-with-parsed-data "* Headline 1\n** Headline 2"
573 (org-element-map
574 tree 'headline
575 (lambda (h) (if (org-export-numbered-headline-p h info) 'yes 'no))
576 (plist-put info :section-numbers 1)))))
577 ;; Otherwise, headlines are always numbered.
578 (should
579 (org-test-with-parsed-data "* Headline"
580 (org-element-map
581 tree 'headline
582 (lambda (h) (org-export-numbered-headline-p h info))
583 (plist-put info :section-numbers t)))))
585 (ert-deftest test-org-export/number-to-roman ()
586 "Test `org-export-number-to-roman' specifications."
587 ;; If number is negative, return it as a string.
588 (should (equal (org-export-number-to-roman -1) "-1"))
589 ;; Otherwise, return it as a roman number.
590 (should (equal (org-export-number-to-roman 1449) "MCDXLIX")))
592 (ert-deftest test-org-export/get-tags ()
593 "Test `org-export-get-tags' specifications."
594 (let ((org-export-exclude-tags '("noexport"))
595 (org-export-select-tags '("export")))
596 ;; Standard test: tags which are not a select tag, an exclude tag,
597 ;; or specified as optional argument shouldn't be ignored.
598 (should
599 (org-test-with-parsed-data "* Headline :tag:"
600 (org-export-get-tags (org-element-map tree 'headline 'identity info t)
601 info)))
602 ;; Exclude tags are removed.
603 (should-not
604 (org-test-with-parsed-data "* Headline :noexport:"
605 (org-export-get-tags (org-element-map tree 'headline 'identity info t)
606 info)))
607 ;; Select tags are removed.
608 (should-not
609 (org-test-with-parsed-data "* Headline :export:"
610 (org-export-get-tags (org-element-map tree 'headline 'identity info t)
611 info)))
612 (should
613 (equal
614 '("tag")
615 (org-test-with-parsed-data "* Headline :tag:export:"
616 (org-export-get-tags (org-element-map tree 'headline 'identity info t)
617 info))))
618 ;; Tags provided in the optional argument are also ignored.
619 (should-not
620 (org-test-with-parsed-data "* Headline :ignore:"
621 (org-export-get-tags (org-element-map tree 'headline 'identity info t)
622 info '("ignore"))))))
624 (ert-deftest test-org-export/first-sibling-p ()
625 "Test `org-export-first-sibling-p' specifications."
626 (should
627 (equal
628 '(yes yes no)
629 (org-test-with-temp-text "* Headline\n** Headline 2\n** Headline 3"
630 (org-element-map
631 (org-element-parse-buffer) 'headline
632 (lambda (h) (if (org-export-first-sibling-p h) 'yes 'no)))))))
634 (ert-deftest test-org-export/last-sibling-p ()
635 "Test `org-export-last-sibling-p' specifications."
636 (should
637 (equal
638 '(yes no yes)
639 (org-test-with-temp-text "* Headline\n** Headline 2\n** Headline 3"
640 (org-element-map
641 (org-element-parse-buffer) 'headline
642 (lambda (h) (if (org-export-last-sibling-p h) 'yes 'no)))))))
646 ;;; Links
648 (ert-deftest test-org-export/get-coderef-format ()
649 "Test `org-export-get-coderef-format' specifications."
650 ;; A link without description returns "%s"
651 (should (equal (org-export-get-coderef-format "(ref:line)" nil)
652 "%s"))
653 ;; Return "%s" when path is matched within description.
654 (should (equal (org-export-get-coderef-format "path" "desc (path)")
655 "desc %s"))
656 ;; Otherwise return description.
657 (should (equal (org-export-get-coderef-format "path" "desc")
658 "desc")))
660 (ert-deftest test-org-export/inline-image-p ()
661 "Test `org-export-inline-image-p' specifications."
662 (should
663 (org-export-inline-image-p
664 (org-test-with-temp-text "[[#id]]"
665 (org-element-map
666 (org-element-parse-buffer) 'link 'identity nil t))
667 '(("custom-id" . "id")))))
669 (ert-deftest test-org-export/fuzzy-link ()
670 "Test fuzzy links specifications."
671 ;; 1. Links to invisible (keyword) targets should be ignored.
672 (org-test-with-parsed-data
673 "Paragraph.\n#+TARGET: Test\n[[Test]]"
674 (should-not
675 (org-element-map
676 tree 'link
677 (lambda (link)
678 (org-export-get-ordinal
679 (org-export-resolve-fuzzy-link link info) info)) info)))
680 ;; 2. Link to an headline should return headline's number.
681 (org-test-with-parsed-data
682 "Paragraph.\n* Head1\n* Head2\n* Head3\n[[Head2]]"
683 (should
684 ;; Note: Headline's number is in fact a list of numbers.
685 (equal '(2)
686 (org-element-map
687 tree 'link
688 (lambda (link)
689 (org-export-get-ordinal
690 (org-export-resolve-fuzzy-link link info) info)) info t))))
691 ;; 3. Link to a target in an item should return item's number.
692 (org-test-with-parsed-data
693 "- Item1\n - Item11\n - <<test>>Item12\n- Item2\n\n\n[[test]]"
694 (should
695 ;; Note: Item's number is in fact a list of numbers.
696 (equal '(1 2)
697 (org-element-map
698 tree 'link
699 (lambda (link)
700 (org-export-get-ordinal
701 (org-export-resolve-fuzzy-link link info) info)) info t))))
702 ;; 4. Link to a target in a footnote should return footnote's
703 ;; number.
704 (org-test-with-parsed-data "
705 Paragraph[1][2][fn:lbl3:C<<target>>][[test]][[target]]\n[1] A\n\n[2] <<test>>B"
706 (should
707 (equal '(2 3)
708 (org-element-map
709 tree 'link
710 (lambda (link)
711 (org-export-get-ordinal
712 (org-export-resolve-fuzzy-link link info) info)) info))))
713 ;; 5. Link to a named element should return sequence number of that
714 ;; element.
715 (org-test-with-parsed-data
716 "#+NAME: tbl1\n|1|2|\n#+NAME: tbl2\n|3|4|\n#+NAME: tbl3\n|5|6|\n[[tbl2]]"
717 (should
718 (= 2
719 (org-element-map
720 tree 'link
721 (lambda (link)
722 (org-export-get-ordinal
723 (org-export-resolve-fuzzy-link link info) info)) info t))))
724 ;; 6. Link to a target not within an item, a table, a footnote
725 ;; reference or definition should return section number.
726 (org-test-with-parsed-data
727 "* Head1\n* Head2\nParagraph<<target>>\n* Head3\n[[target]]"
728 (should
729 (equal '(2)
730 (org-element-map
731 tree 'link
732 (lambda (link)
733 (org-export-get-ordinal
734 (org-export-resolve-fuzzy-link link info) info)) info t)))))
736 (ert-deftest test-org-export/resolve-coderef ()
737 "Test `org-export-resolve-coderef' specifications."
738 (let ((org-coderef-label-format "(ref:%s)"))
739 ;; 1. A link to a "-n -k -r" block returns line number.
740 (org-test-with-parsed-data
741 "#+BEGIN_EXAMPLE -n -k -r\nText (ref:coderef)\n#+END_EXAMPLE"
742 (should (= (org-export-resolve-coderef "coderef" info) 1)))
743 (org-test-with-parsed-data
744 "#+BEGIN_SRC emacs-lisp -n -k -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
745 (should (= (org-export-resolve-coderef "coderef" info) 1)))
746 ;; 2. A link to a "-n -r" block returns line number.
747 (org-test-with-parsed-data
748 "#+BEGIN_EXAMPLE -n -r\nText (ref:coderef)\n#+END_EXAMPLE"
749 (should (= (org-export-resolve-coderef "coderef" info) 1)))
750 (org-test-with-parsed-data
751 "#+BEGIN_SRC emacs-lisp -n -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
752 (should (= (org-export-resolve-coderef "coderef" info) 1)))
753 ;; 3. A link to a "-n" block returns coderef.
754 (org-test-with-parsed-data
755 "#+BEGIN_SRC emacs-lisp -n\n(+ 1 1) (ref:coderef)\n#+END_SRC"
756 (should (equal (org-export-resolve-coderef "coderef" info) "coderef")))
757 (org-test-with-parsed-data
758 "#+BEGIN_EXAMPLE -n\nText (ref:coderef)\n#+END_EXAMPLE"
759 (should (equal (org-export-resolve-coderef "coderef" info) "coderef")))
760 ;; 4. A link to a "-r" block returns line number.
761 (org-test-with-parsed-data
762 "#+BEGIN_SRC emacs-lisp -r\n(+ 1 1) (ref:coderef)\n#+END_SRC"
763 (should (= (org-export-resolve-coderef "coderef" info) 1)))
764 (org-test-with-parsed-data
765 "#+BEGIN_EXAMPLE -r\nText (ref:coderef)\n#+END_EXAMPLE"
766 (should (= (org-export-resolve-coderef "coderef" info) 1)))
767 ;; 5. A link to a block without a switch returns coderef.
768 (org-test-with-parsed-data
769 "#+BEGIN_SRC emacs-lisp\n(+ 1 1) (ref:coderef)\n#+END_SRC"
770 (should (equal (org-export-resolve-coderef "coderef" info) "coderef")))
771 (org-test-with-parsed-data
772 "#+BEGIN_EXAMPLE\nText (ref:coderef)\n#+END_EXAMPLE"
773 (should (equal (org-export-resolve-coderef "coderef" info) "coderef")))
774 ;; 6. Correctly handle continued line numbers. A "+n" switch
775 ;; should resume numbering from previous block with numbered
776 ;; lines, ignoring blocks not numbering lines in the process.
777 ;; A "-n" switch resets count.
778 (org-test-with-parsed-data "
779 #+BEGIN_EXAMPLE -n
780 Text.
781 #+END_EXAMPLE
783 #+BEGIN_SRC emacs-lisp
784 \(- 1 1)
785 #+END_SRC
787 #+BEGIN_SRC emacs-lisp +n -r
788 \(+ 1 1) (ref:addition)
789 #+END_SRC
791 #+BEGIN_EXAMPLE -n -r
792 Another text. (ref:text)
793 #+END_EXAMPLE"
794 (should (= (org-export-resolve-coderef "addition" info) 2))
795 (should (= (org-export-resolve-coderef "text" info) 1)))
796 ;; 7. Recognize coderef with user-specified syntax.
797 (org-test-with-parsed-data
798 "#+BEGIN_EXAMPLE -l \"[ref:%s]\"\nText. [ref:text]\n#+END_EXAMPLE"
799 (should (equal (org-export-resolve-coderef "text" info) "text")))))
801 (ert-deftest test-org-export/resolve-fuzzy-link ()
802 "Test `org-export-resolve-fuzzy-link' specifications."
803 ;; 1. Match target objects.
804 (org-test-with-parsed-data "<<target>> [[target]]"
805 (should
806 (org-export-resolve-fuzzy-link
807 (org-element-map tree 'link 'identity info t) info)))
808 ;; 2. Match target elements.
809 (org-test-with-parsed-data "#+TARGET: target\n[[target]]"
810 (should
811 (org-export-resolve-fuzzy-link
812 (org-element-map tree 'link 'identity info t) info)))
813 ;; 3. Match named elements.
814 (org-test-with-parsed-data "#+NAME: target\nParagraph\n\n[[target]]"
815 (should
816 (org-export-resolve-fuzzy-link
817 (org-element-map tree 'link 'identity info t) info)))
818 ;; 4. Match exact headline's name.
819 (org-test-with-parsed-data "* My headline\n[[My headline]]"
820 (should
821 (org-export-resolve-fuzzy-link
822 (org-element-map tree 'link 'identity info t) info)))
823 ;; 5. Targets objects have priority over named elements and headline
824 ;; titles.
825 (org-test-with-parsed-data
826 "* target\n#+NAME: target\n<<target>>\n\n[[target]]"
827 (should
828 (eq 'target
829 (org-element-type
830 (org-export-resolve-fuzzy-link
831 (org-element-map tree 'link 'identity info t) info)))))
832 ;; 6. Named elements have priority over headline titles.
833 (org-test-with-parsed-data
834 "* target\n#+NAME: target\nParagraph\n\n[[target]]"
835 (should
836 (eq 'paragraph
837 (org-element-type
838 (org-export-resolve-fuzzy-link
839 (org-element-map tree 'link 'identity info t) info)))))
840 ;; 7. If link's path starts with a "*", only match headline titles,
841 ;; though.
842 (org-test-with-parsed-data
843 "* target\n#+NAME: target\n<<target>>\n\n[[*target]]"
844 (should
845 (eq 'headline
846 (org-element-type
847 (org-export-resolve-fuzzy-link
848 (org-element-map tree 'link 'identity info t) info)))))
849 ;; 8. Return nil if no match.
850 (org-test-with-parsed-data "[[target]]"
851 (should-not
852 (org-export-resolve-fuzzy-link
853 (org-element-map tree 'link 'identity info t) info))))
855 (ert-deftest test-org-export/resolve-id-link ()
856 "Test `org-export-resolve-id-link' specifications."
857 ;; 1. Regular test for custom-id link.
858 (org-test-with-parsed-data "* Headline1
859 :PROPERTIES:
860 :CUSTOM-ID: test
861 :END:
862 * Headline 2
863 \[[#test]]"
864 (should
865 (org-export-resolve-id-link
866 (org-element-map tree 'link 'identity info t) info)))
867 ;; 2. Failing test for custom-id link.
868 (org-test-with-parsed-data "* Headline1
869 :PROPERTIES:
870 :CUSTOM-ID: test
871 :END:
872 * Headline 2
873 \[[#no-match]]"
874 (should-not
875 (org-export-resolve-id-link
876 (org-element-map tree 'link 'identity info t) info)))
877 ;; 3. Test for internal id target.
878 (org-test-with-parsed-data "* Headline1
879 :PROPERTIES:
880 :ID: aaaa
881 :END:
882 * Headline 2
883 \[[id:aaaa]]"
884 (should
885 (org-export-resolve-id-link
886 (org-element-map tree 'link 'identity info t) info)))
887 ;; 4. Test for external id target.
888 (org-test-with-parsed-data "[[id:aaaa]]"
889 (should
890 (org-export-resolve-id-link
891 (org-element-map tree 'link 'identity info t)
892 (org-combine-plists info '(:id-alist (("aaaa" . "external-file"))))))))
894 (ert-deftest test-org-export/resolve-radio-link ()
895 "Test `org-export-resolve-radio-link' specifications."
896 ;; Standard test.
897 (org-test-with-temp-text "<<<radio>>> radio"
898 (org-update-radio-target-regexp)
899 (should
900 (let* ((tree (org-element-parse-buffer))
901 (info `(:parse-tree ,tree)))
902 (org-export-resolve-radio-link
903 (org-element-map tree 'link 'identity info t)
904 info))))
905 ;; Radio target with objects.
906 (org-test-with-temp-text "<<<radio \\alpha>>> radio \\alpha"
907 (org-update-radio-target-regexp)
908 (should
909 (let* ((tree (org-element-parse-buffer))
910 (info `(:parse-tree ,tree)))
911 (org-export-resolve-radio-link
912 (org-element-map tree 'link 'identity info t)
913 info)))))
917 ;;; Macro
919 (ert-deftest test-org-export/define-macro ()
920 "Try defining various Org macro using in-buffer #+MACRO: keyword."
921 ;; Parsed macro.
922 (should (equal (org-test-with-temp-text "#+MACRO: one 1"
923 (org-export-get-inbuffer-options))
924 '(:macro-one ("1"))))
925 ;; Evaled macro.
926 (should (equal (org-test-with-temp-text "#+MACRO: two (eval (+ 1 1))"
927 (org-export-get-inbuffer-options))
928 '(:macro-two ("(eval (+ 1 1))"))))
929 ;; Incomplete macro.
930 (should-not (org-test-with-temp-text "#+MACRO: three"
931 (org-export-get-inbuffer-options)))
932 ;; Macro with newline character.
933 (should (equal (org-test-with-temp-text "#+MACRO: four a\\nb"
934 (org-export-get-inbuffer-options))
935 '(:macro-four ("a\nb"))))
936 ;; Macro with protected newline character.
937 (should (equal (org-test-with-temp-text "#+MACRO: five a\\\\nb"
938 (org-export-get-inbuffer-options))
939 '(:macro-five ("a\\nb"))))
940 ;; Recursive macro.
941 (org-test-with-temp-text "#+MACRO: six 6\n#+MACRO: seven 1 + {{{six}}}"
942 (should
943 (equal
944 (org-export-get-inbuffer-options)
945 '(:macro-six
946 ("6")
947 :macro-seven
948 ("1 + " (macro (:key "six" :value "{{{six}}}" :args nil :begin 5 :end 14
949 :post-blank 0 :parent nil))))))))
951 (ert-deftest test-org-export/expand-macro ()
952 "Test `org-export-expand-macro' specifications."
953 ;; Standard test.
954 (should
955 (equal
956 "some text"
957 (org-test-with-parsed-data "#+MACRO: macro some text\n{{{macro}}}"
958 (org-export-expand-macro
959 (org-element-map tree 'macro 'identity info t) info))))
960 ;; Macro with arguments.
961 (should
962 (equal
963 "some text"
964 (org-test-with-parsed-data "#+MACRO: macro $1 $2\n{{{macro(some,text)}}}"
965 (org-export-expand-macro
966 (org-element-map tree 'macro 'identity info t) info))))
967 ;; Macro with "eval"
968 (should
969 (equal
971 (org-test-with-parsed-data "#+MACRO: add (eval (+ $1 $2))\n{{{add(1,2)}}}"
972 (org-export-expand-macro
973 (org-element-map tree 'macro 'identity info t) info))))
974 ;; Nested macros.
975 (should
976 (equal
977 "inner outer"
978 (org-test-with-parsed-data
979 "#+MACRO: in inner\n#+MACRO: out {{{in}}} outer\n{{{out}}}"
980 (flet ((translate-macro (macro contents info)
981 (org-export-expand-macro macro info)))
982 (org-export-expand-macro
983 (org-element-map tree 'macro 'identity info t)
984 (org-combine-plists
985 info `(:translate-alist ((macro . translate-macro))))))))))
989 ;;; Src-block and example-block
991 (ert-deftest test-org-export/unravel-code ()
992 "Test `org-export-unravel-code' function."
993 (let ((org-coderef-label-format "(ref:%s)"))
994 ;; 1. Code without reference.
995 (org-test-with-temp-text "#+BEGIN_EXAMPLE\n(+ 1 1)\n#+END_EXAMPLE"
996 (should (equal (org-export-unravel-code (org-element-current-element))
997 '("(+ 1 1)\n"))))
998 ;; 2. Code with reference.
999 (org-test-with-temp-text
1000 "#+BEGIN_EXAMPLE\n(+ 1 1) (ref:test)\n#+END_EXAMPLE"
1001 (should (equal (org-export-unravel-code (org-element-current-element))
1002 '("(+ 1 1)\n" (1 . "test")))))
1003 ;; 3. Code with user-defined reference.
1004 (org-test-with-temp-text
1005 "#+BEGIN_EXAMPLE -l \"[ref:%s]\"\n(+ 1 1) [ref:test]\n#+END_EXAMPLE"
1006 (should (equal (org-export-unravel-code (org-element-current-element))
1007 '("(+ 1 1)\n" (1 . "test")))))
1008 ;; 4. Code references keys are relative to the current block.
1009 (org-test-with-temp-text "
1010 #+BEGIN_EXAMPLE -n
1011 \(+ 1 1)
1012 #+END_EXAMPLE
1013 #+BEGIN_EXAMPLE +n
1014 \(+ 2 2)
1015 \(+ 3 3) (ref:one)
1016 #+END_EXAMPLE"
1017 (goto-line 5)
1018 (should (equal (org-export-unravel-code (org-element-current-element))
1019 '("(+ 2 2)\n(+ 3 3)\n" (2 . "one")))))
1020 ;; 5. Free up comma-protected lines.
1022 ;; 5.1. In an Org source block, every line is protected.
1023 (org-test-with-temp-text
1024 "#+BEGIN_SRC org\n,* Test\n,# comment\n,Text\n#+END_SRC"
1025 (should (equal (org-export-unravel-code (org-element-current-element))
1026 '("* Test\n# comment\nText\n"))))
1027 ;; 5.2. In other blocks, only headlines, comments and keywords are
1028 ;; protected.
1029 (org-test-with-temp-text
1030 "#+BEGIN_EXAMPLE\n,* Headline\n, * Not headline\n,Keep\n#+END_EXAMPLE"
1031 (should (equal (org-export-unravel-code (org-element-current-element))
1032 '("* Headline\n, * Not headline\n,Keep\n"))))))
1036 ;;; Tables
1038 (ert-deftest test-org-export/special-column ()
1039 "Test if the table's special column is properly recognized."
1040 ;; 1. First column is special if it contains only a special marking
1041 ;; characters or empty cells.
1042 (org-test-with-temp-text "
1043 | ! | 1 |
1044 | | 2 |"
1045 (should
1046 (org-export-table-has-special-column-p
1047 (org-element-map
1048 (org-element-parse-buffer) 'table 'identity nil 'first-match))))
1049 ;; 2. If the column contains anything else, it isn't special.
1050 (org-test-with-temp-text "
1051 | ! | 1 |
1052 | b | 2 |"
1053 (should-not
1054 (org-export-table-has-special-column-p
1055 (org-element-map
1056 (org-element-parse-buffer) 'table 'identity nil 'first-match))))
1057 ;; 3. Special marking characters are "#", "^", "*", "_", "/", "$"
1058 ;; and "!".
1059 (org-test-with-temp-text "
1060 | # | 1 |
1061 | ^ | 2 |
1062 | * | 3 |
1063 | _ | 4 |
1064 | / | 5 |
1065 | $ | 6 |
1066 | ! | 7 |"
1067 (should
1068 (org-export-table-has-special-column-p
1069 (org-element-map
1070 (org-element-parse-buffer) 'table 'identity nil 'first-match))))
1071 ;; 4. A first column with only empty cells isn't considered as
1072 ;; special.
1073 (org-test-with-temp-text "
1074 | | 1 |
1075 | | 2 |"
1076 (should-not
1077 (org-export-table-has-special-column-p
1078 (org-element-map
1079 (org-element-parse-buffer) 'table 'identity nil 'first-match)))))
1081 (ert-deftest test-org-export/table-row-is-special-p ()
1082 "Test `org-export-table-row-is-special-p' specifications."
1083 ;; 1. A row is special if it has a special marking character in the
1084 ;; special column.
1085 (org-test-with-parsed-data "| ! | 1 |"
1086 (should
1087 (org-export-table-row-is-special-p
1088 (org-element-map tree 'table-row 'identity nil 'first-match) info)))
1089 ;; 2. A row is special when its first field is "/"
1090 (org-test-with-parsed-data "
1091 | / | 1 |
1092 | a | b |"
1093 (should
1094 (org-export-table-row-is-special-p
1095 (org-element-map tree 'table-row 'identity nil 'first-match) info)))
1096 ;; 3. A row only containing alignment cookies is also considered as
1097 ;; special.
1098 (org-test-with-parsed-data "| <5> | | <l> | <l22> |"
1099 (should
1100 (org-export-table-row-is-special-p
1101 (org-element-map tree 'table-row 'identity nil 'first-match) info)))
1102 ;; 4. Everything else isn't considered as special.
1103 (org-test-with-parsed-data "| \alpha | | c |"
1104 (should-not
1105 (org-export-table-row-is-special-p
1106 (org-element-map tree 'table-row 'identity nil 'first-match) info)))
1107 ;; 5. Table's rules are never considered as special rows.
1108 (org-test-with-parsed-data "|---+---|"
1109 (should-not
1110 (org-export-table-row-is-special-p
1111 (org-element-map tree 'table-row 'identity nil 'first-match) info))))
1113 (ert-deftest test-org-export/has-header-p ()
1114 "Test `org-export-table-has-header-p' specifications."
1115 ;; 1. With an header.
1116 (org-test-with-parsed-data "
1117 | a | b |
1118 |---+---|
1119 | c | d |"
1120 (should
1121 (org-export-table-has-header-p
1122 (org-element-map tree 'table 'identity info 'first-match)
1123 info)))
1124 ;; 2. Without an header.
1125 (org-test-with-parsed-data "
1126 | a | b |
1127 | c | d |"
1128 (should-not
1129 (org-export-table-has-header-p
1130 (org-element-map tree 'table 'identity info 'first-match)
1131 info)))
1132 ;; 3. Don't get fooled with starting and ending rules.
1133 (org-test-with-parsed-data "
1134 |---+---|
1135 | a | b |
1136 | c | d |
1137 |---+---|"
1138 (should-not
1139 (org-export-table-has-header-p
1140 (org-element-map tree 'table 'identity info 'first-match)
1141 info))))
1143 (ert-deftest test-org-export/table-row-group ()
1144 "Test `org-export-table-row-group' specifications."
1145 ;; 1. A rule creates a new group.
1146 (org-test-with-parsed-data "
1147 | a | b |
1148 |---+---|
1149 | 1 | 2 |"
1150 (should
1151 (equal
1152 '(1 nil 2)
1153 (mapcar (lambda (row) (org-export-table-row-group row info))
1154 (org-element-map tree 'table-row 'identity)))))
1155 ;; 2. Special rows are ignored in count.
1156 (org-test-with-parsed-data "
1157 | / | < | > |
1158 |---|---+---|
1159 | | 1 | 2 |"
1160 (should
1161 (equal
1162 '(nil nil 1)
1163 (mapcar (lambda (row) (org-export-table-row-group row info))
1164 (org-element-map tree 'table-row 'identity)))))
1165 ;; 3. Double rules also are ignored in count.
1166 (org-test-with-parsed-data "
1167 | a | b |
1168 |---+---|
1169 |---+---|
1170 | 1 | 2 |"
1171 (should
1172 (equal
1173 '(1 nil nil 2)
1174 (mapcar (lambda (row) (org-export-table-row-group row info))
1175 (org-element-map tree 'table-row 'identity))))))
1177 (ert-deftest test-org-export/table-cell-width ()
1178 "Test `org-export-table-cell-width' specifications."
1179 ;; 1. Width is primarily determined by width cookies. If no cookie
1180 ;; is found, cell's width is nil.
1181 (org-test-with-parsed-data "
1182 | / | <l> | <6> | <l7> |
1183 | | a | b | c |"
1184 (should
1185 (equal
1186 '(nil 6 7)
1187 (mapcar (lambda (cell) (org-export-table-cell-width cell info))
1188 (org-element-map tree 'table-cell 'identity info)))))
1189 ;; 2. The last width cookie has precedence.
1190 (org-test-with-parsed-data "
1191 | <6> |
1192 | <7> |
1193 | a |"
1194 (should
1195 (equal
1196 '(7)
1197 (mapcar (lambda (cell) (org-export-table-cell-width cell info))
1198 (org-element-map tree 'table-cell 'identity info)))))
1199 ;; 3. Valid width cookies must have a specific row.
1200 (org-test-with-parsed-data "| <6> | cell |"
1201 (should
1202 (equal
1203 '(nil nil)
1204 (mapcar (lambda (cell) (org-export-table-cell-width cell info))
1205 (org-element-map tree 'table-cell 'identity))))))
1207 (ert-deftest test-org-export/table-cell-alignment ()
1208 "Test `org-export-table-cell-alignment' specifications."
1209 (let ((org-table-number-fraction 0.5)
1210 (org-table-number-regexp "^[0-9]+$"))
1211 ;; 1. Alignment is primarily determined by alignment cookies.
1212 (org-test-with-temp-text "| <l> | <c> | <r> |"
1213 (let* ((tree (org-element-parse-buffer))
1214 (info `(:parse-tree ,tree)))
1215 (should
1216 (equal
1217 '(left center right)
1218 (mapcar (lambda (cell) (org-export-table-cell-alignment cell info))
1219 (org-element-map tree 'table-cell 'identity))))))
1220 ;; 2. The last alignment cookie has precedence.
1221 (org-test-with-parsed-data "
1222 | <l8> |
1223 | cell |
1224 | <r9> |"
1225 (should
1226 (equal
1227 '(right right right)
1228 (mapcar (lambda (cell) (org-export-table-cell-alignment cell info))
1229 (org-element-map tree 'table-cell 'identity)))))
1230 ;; 3. If there's no cookie, cell's contents determine alignment.
1231 ;; A column mostly made of cells containing numbers will align
1232 ;; its cells to the right.
1233 (org-test-with-parsed-data "
1234 | 123 |
1235 | some text |
1236 | 12345 |"
1237 (should
1238 (equal
1239 '(right right right)
1240 (mapcar (lambda (cell)
1241 (org-export-table-cell-alignment cell info))
1242 (org-element-map tree 'table-cell 'identity)))))
1243 ;; 4. Otherwise, they will be aligned to the left.
1244 (org-test-with-parsed-data "
1245 | text |
1246 | some text |
1247 | \alpha |"
1248 (should
1249 (equal
1250 '(left left left)
1251 (mapcar (lambda (cell)
1252 (org-export-table-cell-alignment cell info))
1253 (org-element-map tree 'table-cell 'identity)))))))
1255 (ert-deftest test-org-export/table-cell-borders ()
1256 "Test `org-export-table-cell-borders' specifications."
1257 ;; 1. Recognize various column groups indicators.
1258 (org-test-with-parsed-data "| / | < | > | <> |"
1259 (should
1260 (equal
1261 '((right bottom top) (left bottom top) (right bottom top)
1262 (right left bottom top))
1263 (mapcar (lambda (cell)
1264 (org-export-table-cell-borders cell info))
1265 (org-element-map tree 'table-cell 'identity)))))
1266 ;; 2. Accept shortcuts to define column groups.
1267 (org-test-with-parsed-data "| / | < | < |"
1268 (should
1269 (equal
1270 '((right bottom top) (right left bottom top) (left bottom top))
1271 (mapcar (lambda (cell)
1272 (org-export-table-cell-borders cell info))
1273 (org-element-map tree 'table-cell 'identity)))))
1274 ;; 3. A valid column groups row must start with a "/".
1275 (org-test-with-parsed-data "
1276 | | < |
1277 | a | b |"
1278 (should
1279 (equal '((top) (top) (bottom) (bottom))
1280 (mapcar (lambda (cell)
1281 (org-export-table-cell-borders cell info))
1282 (org-element-map tree 'table-cell 'identity)))))
1283 ;; 4. Take table rules into consideration.
1284 (org-test-with-parsed-data "
1285 | 1 |
1286 |---|
1287 | 2 |"
1288 (should
1289 (equal '((below top) (bottom above))
1290 (mapcar (lambda (cell)
1291 (org-export-table-cell-borders cell info))
1292 (org-element-map tree 'table-cell 'identity)))))
1293 ;; 5. Top and (resp. bottom) rules induce both `top' and `above'
1294 ;; (resp. `bottom' and `below') borders. Any special row is
1295 ;; ignored.
1296 (org-test-with-parsed-data "
1297 |---+----|
1298 | / | |
1299 | | 1 |
1300 |---+----|"
1301 (should
1302 (equal '((bottom below top above))
1303 (last
1304 (mapcar (lambda (cell)
1305 (org-export-table-cell-borders cell info))
1306 (org-element-map tree 'table-cell 'identity)))))))
1308 (ert-deftest test-org-export/table-dimensions ()
1309 "Test `org-export-table-dimensions' specifications."
1310 ;; 1. Standard test.
1311 (org-test-with-parsed-data "
1312 | 1 | 2 | 3 |
1313 | 4 | 5 | 6 |"
1314 (should
1315 (equal '(2 . 3)
1316 (org-export-table-dimensions
1317 (org-element-map tree 'table 'identity info 'first-match) info))))
1318 ;; 2. Ignore horizontal rules and special columns.
1319 (org-test-with-parsed-data "
1320 | / | < | > |
1321 | 1 | 2 | 3 |
1322 |---+---+---|
1323 | 4 | 5 | 6 |"
1324 (should
1325 (equal '(2 . 3)
1326 (org-export-table-dimensions
1327 (org-element-map tree 'table 'identity info 'first-match) info)))))
1329 (ert-deftest test-org-export/table-cell-address ()
1330 "Test `org-export-table-cell-address' specifications."
1331 ;; 1. Standard test: index is 0-based.
1332 (org-test-with-parsed-data "| a | b |"
1333 (should
1334 (equal '((0 . 0) (0 . 1))
1335 (org-element-map
1336 tree 'table-cell
1337 (lambda (cell) (org-export-table-cell-address cell info))
1338 info))))
1339 ;; 2. Special column isn't counted, nor are special rows.
1340 (org-test-with-parsed-data "
1341 | / | <> |
1342 | | c |"
1343 (should
1344 (equal '(0 . 0)
1345 (org-export-table-cell-address
1346 (car (last (org-element-map tree 'table-cell 'identity info)))
1347 info))))
1348 ;; 3. Tables rules do not count either.
1349 (org-test-with-parsed-data "
1350 | a |
1351 |---|
1352 | b |
1353 |---|
1354 | c |"
1355 (should
1356 (equal '(2 . 0)
1357 (org-export-table-cell-address
1358 (car (last (org-element-map tree 'table-cell 'identity info)))
1359 info))))
1360 ;; 4. Return nil for special cells.
1361 (org-test-with-parsed-data "| / | a |"
1362 (should-not
1363 (org-export-table-cell-address
1364 (org-element-map tree 'table-cell 'identity nil 'first-match)
1365 info))))
1367 (ert-deftest test-org-export/get-table-cell-at ()
1368 "Test `org-export-get-table-cell-at' specifications."
1369 ;; 1. Address ignores special columns, special rows and rules.
1370 (org-test-with-parsed-data "
1371 | / | <> |
1372 | | a |
1373 |---+----|
1374 | | b |"
1375 (should
1376 (equal '("b")
1377 (org-element-contents
1378 (org-export-get-table-cell-at
1379 '(1 . 0)
1380 (org-element-map tree 'table 'identity info 'first-match)
1381 info)))))
1382 ;; 2. Return value for a non-existent address is nil.
1383 (org-test-with-parsed-data "| a |"
1384 (should-not
1385 (org-export-get-table-cell-at
1386 '(2 . 2)
1387 (org-element-map tree 'table 'identity info 'first-match)
1388 info)))
1389 (org-test-with-parsed-data "| / |"
1390 (should-not
1391 (org-export-get-table-cell-at
1392 '(0 . 0)
1393 (org-element-map tree 'table 'identity info 'first-match)
1394 info))))
1396 (ert-deftest test-org-export/table-cell-starts-colgroup-p ()
1397 "Test `org-export-table-cell-starts-colgroup-p' specifications."
1398 ;; 1. A cell at a beginning of a row always starts a column group.
1399 (org-test-with-parsed-data "| a |"
1400 (should
1401 (org-export-table-cell-starts-colgroup-p
1402 (org-element-map tree 'table-cell 'identity info 'first-match)
1403 info)))
1404 ;; 2. Special column should be ignored when determining the
1405 ;; beginning of the row.
1406 (org-test-with-parsed-data "
1407 | / | |
1408 | | a |"
1409 (should
1410 (org-export-table-cell-starts-colgroup-p
1411 (org-element-map tree 'table-cell 'identity info 'first-match)
1412 info)))
1413 ;; 2. Explicit column groups.
1414 (org-test-with-parsed-data "
1415 | / | | < |
1416 | a | b | c |"
1417 (should
1418 (equal
1419 '(yes no yes)
1420 (org-element-map
1421 tree 'table-cell
1422 (lambda (cell)
1423 (if (org-export-table-cell-starts-colgroup-p cell info) 'yes 'no))
1424 info)))))
1426 (ert-deftest test-org-export/table-cell-ends-colgroup-p ()
1427 "Test `org-export-table-cell-ends-colgroup-p' specifications."
1428 ;; 1. A cell at the end of a row always ends a column group.
1429 (org-test-with-parsed-data "| a |"
1430 (should
1431 (org-export-table-cell-ends-colgroup-p
1432 (org-element-map tree 'table-cell 'identity info 'first-match)
1433 info)))
1434 ;; 2. Special column should be ignored when determining the
1435 ;; beginning of the row.
1436 (org-test-with-parsed-data "
1437 | / | |
1438 | | a |"
1439 (should
1440 (org-export-table-cell-ends-colgroup-p
1441 (org-element-map tree 'table-cell 'identity info 'first-match)
1442 info)))
1443 ;; 3. Explicit column groups.
1444 (org-test-with-parsed-data "
1445 | / | < | |
1446 | a | b | c |"
1447 (should
1448 (equal
1449 '(yes no yes)
1450 (org-element-map
1451 tree 'table-cell
1452 (lambda (cell)
1453 (if (org-export-table-cell-ends-colgroup-p cell info) 'yes 'no))
1454 info)))))
1456 (ert-deftest test-org-export/table-row-starts-rowgroup-p ()
1457 "Test `org-export-table-row-starts-rowgroup-p' specifications."
1458 ;; 1. A row at the beginning of a table always starts a row group.
1459 ;; So does a row following a table rule.
1460 (org-test-with-parsed-data "
1461 | a |
1462 |---|
1463 | b |"
1464 (should
1465 (equal
1466 '(yes no yes)
1467 (org-element-map
1468 tree 'table-row
1469 (lambda (row)
1470 (if (org-export-table-row-starts-rowgroup-p row info) 'yes 'no))
1471 info))))
1472 ;; 2. Special rows should be ignored when determining the beginning
1473 ;; of the row.
1474 (org-test-with-parsed-data "
1475 | / | < |
1476 | | a |
1477 |---+---|
1478 | / | < |
1479 | | b |"
1480 (should
1481 (equal
1482 '(yes no yes)
1483 (org-element-map
1484 tree 'table-row
1485 (lambda (row)
1486 (if (org-export-table-row-starts-rowgroup-p row info) 'yes 'no))
1487 info)))))
1489 (ert-deftest test-org-export/table-row-ends-rowgroup-p ()
1490 "Test `org-export-table-row-ends-rowgroup-p' specifications."
1491 ;; 1. A row at the end of a table always ends a row group. So does
1492 ;; a row preceding a table rule.
1493 (org-test-with-parsed-data "
1494 | a |
1495 |---|
1496 | b |"
1497 (should
1498 (equal
1499 '(yes no yes)
1500 (org-element-map
1501 tree 'table-row
1502 (lambda (row)
1503 (if (org-export-table-row-ends-rowgroup-p row info) 'yes 'no))
1504 info))))
1505 ;; 2. Special rows should be ignored when determining the beginning
1506 ;; of the row.
1507 (org-test-with-parsed-data "
1508 | | a |
1509 | / | < |
1510 |---+---|
1511 | | b |
1512 | / | < |"
1513 (should
1514 (equal
1515 '(yes no yes)
1516 (org-element-map
1517 tree 'table-row
1518 (lambda (row)
1519 (if (org-export-table-row-ends-rowgroup-p row info) 'yes 'no))
1520 info)))))
1522 (ert-deftest test-org-export/table-row-starts-header-p ()
1523 "Test `org-export-table-row-starts-header-p' specifications."
1524 ;; 1. Only the row starting the first row group starts the table
1525 ;; header.
1526 (org-test-with-parsed-data "
1527 | a |
1528 | b |
1529 |---|
1530 | c |"
1531 (should
1532 (equal
1533 '(yes no no no)
1534 (org-element-map
1535 tree 'table-row
1536 (lambda (row)
1537 (if (org-export-table-row-starts-header-p row info) 'yes 'no))
1538 info))))
1539 ;; 2. A row cannot start an header if there's no header in the
1540 ;; table.
1541 (org-test-with-parsed-data "
1542 | a |
1543 |---|"
1544 (should-not
1545 (org-export-table-row-starts-header-p
1546 (org-element-map tree 'table-row 'identity info 'first-match)
1547 info))))
1549 (ert-deftest test-org-export/table-row-ends-header-p ()
1550 "Test `org-export-table-row-ends-header-p' specifications."
1551 ;; 1. Only the row starting the first row group starts the table
1552 ;; header.
1553 (org-test-with-parsed-data "
1554 | a |
1555 | b |
1556 |---|
1557 | c |"
1558 (should
1559 (equal
1560 '(no yes no no)
1561 (org-element-map
1562 tree 'table-row
1563 (lambda (row)
1564 (if (org-export-table-row-ends-header-p row info) 'yes 'no))
1565 info))))
1566 ;; 2. A row cannot start an header if there's no header in the
1567 ;; table.
1568 (org-test-with-parsed-data "
1569 | a |
1570 |---|"
1571 (should-not
1572 (org-export-table-row-ends-header-p
1573 (org-element-map tree 'table-row 'identity info 'first-match)
1574 info))))
1577 (provide 'test-org-export)
1578 ;;; test-org-export.el end here