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