;;; test-org.el --- tests for org.el -*- lexical-binding: t -*-
;; Copyright (c) David Maus
;; Authors: David Maus
;; This file is not part of GNU Emacs.
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see .
;; Template test file for Org tests
;;; Code:
(eval-and-compile (require 'cl-lib))
(eval-when-compile (require 'org-macs)) ;For `org-with-gensyms'.
(require 'org)
(require 'org-inlinetask)
(require 'org-refile)
(require 'org-agenda)
;;; Helpers
(defmacro org-test-with-timezone (tz &rest body)
"Evaluate BODY with TZ environment temporary set to the passed value."
(declare (indent 1))
(org-with-gensyms (tz-saved)
`(let ((,tz-saved (getenv "TZ")))
(unwind-protect
(progn
(setenv "TZ" ,tz)
,@body)
(setenv "TZ" ,tz-saved)))))
(defmacro org-test-with-result (result &rest body)
"Evaluate BODY, and return buffer content based on RESULT.
RESULT is an sexp, and is processed according to the following
rules.
If RESULT is the quoted symbol `buffer', return buffer string.
If RESULT is the quoted symbol `buffer-no-properties', return
buffer string with no properties.
If RESULT is a positive integer, return the RESULT-th line of the
buffer.
Otherwise, evaluate RESULT as an sexp and return its result."
(declare (indent 1))
`(progn ,@body
,(pcase result
(''buffer-no-properties
'(buffer-substring-no-properties
(point-min) (point-max)))
(''buffer '(buffer-string))
((and (pred numberp) (pred cl-plusp))
`(buffer-substring (line-beginning-position ,result)
(line-end-position ,result)))
(_ result))))
(defmacro org-test-without-dow (&rest body)
"Eval BODY skipping day-of-week in timestamps."
(declare (debug (body)))
`(let ((org-time-stamp-formats '("%Y-%m-%d" . "%Y-%m-%d %H:%M")))
,@body))
;;; Comments
(ert-deftest test-org/toggle-comment ()
"Test `org-toggle-comment' specifications."
;; Simple headline.
(should
(equal "* Test"
(org-test-with-temp-text "* COMMENT Test"
(org-toggle-comment)
(buffer-string))))
(should
(equal "* COMMENT Test"
(org-test-with-temp-text "* Test"
(org-toggle-comment)
(buffer-string))))
;; Headline with a regular keyword.
(should
(equal "* TODO Test"
(org-test-with-temp-text "* TODO COMMENT Test"
(org-toggle-comment)
(buffer-string))))
(should
(equal "* TODO COMMENT Test"
(org-test-with-temp-text "* TODO Test"
(org-toggle-comment)
(buffer-string))))
;; Empty headline.
(should
(equal "* "
(org-test-with-temp-text "* COMMENT"
(org-toggle-comment)
(buffer-string))))
(should
(equal "* COMMENT"
(org-test-with-temp-text "* "
(org-toggle-comment)
(buffer-string))))
;; Headline with a single keyword.
(should
(equal "* TODO "
(org-test-with-temp-text "* TODO COMMENT"
(org-toggle-comment)
(buffer-string))))
(should
(equal "* TODO COMMENT"
(org-test-with-temp-text "* TODO"
(org-toggle-comment)
(buffer-string))))
;; Headline with a keyword, a priority cookie and contents.
(should
(equal "* TODO [#A] Headline"
(org-test-with-temp-text "* TODO [#A] COMMENT Headline"
(org-toggle-comment)
(buffer-string))))
(should
(equal "* TODO [#A] COMMENT Headline"
(org-test-with-temp-text "* TODO [#A] Headline"
(org-toggle-comment)
(buffer-string)))))
(ert-deftest test-org/comment-dwim ()
"Test `comment-dwim' behaviour in an Org buffer."
;; No region selected, no comment on current line and line not
;; empty: insert comment on line above.
(should
(equal "# \nComment"
(org-test-with-temp-text "Comment"
(call-interactively #'org-comment-dwim)
(buffer-string))))
;; No region selected, no comment on current line and line empty:
;; insert comment on this line.
(should
(equal "# \nParagraph"
(org-test-with-temp-text "\nParagraph"
(call-interactively #'org-comment-dwim)
(buffer-string))))
;; No region selected, and a comment on this line: indent it.
(should
(equal "* Headline\n # Comment"
(org-test-with-temp-text "* Headline\n# Comment"
(let ((org-adapt-indentation t))
(call-interactively #'org-comment-dwim))
(buffer-string))))
;; Also recognize single # at column 0 as comments.
(should
(equal "# Comment"
(org-test-with-temp-text "# Comment"
(call-interactively #'org-comment-dwim)
(buffer-string))))
;; Region selected and only comments and blank lines within it:
;; un-comment all commented lines.
(should
(equal "Comment 1\n\nComment 2"
(org-test-with-temp-text "# Comment 1\n\n# Comment 2"
(transient-mark-mode 1)
(push-mark (point) t t)
(goto-char (point-max))
(call-interactively #'org-comment-dwim)
(buffer-string))))
;; Region selected without comments: comment all lines if
;; `comment-empty-lines' is non-nil, only non-blank lines otherwise.
(should
(equal "# Comment 1\n\n# Comment 2"
(org-test-with-temp-text "Comment 1\n\nComment 2"
(transient-mark-mode 1)
(push-mark (point) t t)
(goto-char (point-max))
(let ((comment-empty-lines nil))
(call-interactively #'org-comment-dwim))
(buffer-string))))
(should
(equal "# Comment 1\n# \n# Comment 2"
(org-test-with-temp-text "Comment 1\n\nComment 2"
(transient-mark-mode 1)
(push-mark (point) t t)
(goto-char (point-max))
(let ((comment-empty-lines t))
(call-interactively #'org-comment-dwim))
(buffer-string))))
;; In front of a keyword without region, insert a new comment.
(should
(equal "# \n#+KEYWORD: value"
(org-test-with-temp-text "#+KEYWORD: value"
(call-interactively #'org-comment-dwim)
(buffer-string))))
;; Comment a heading
(should
(equal "* COMMENT Test"
(org-test-with-temp-text "* Test"
(call-interactively #'org-comment-dwim)
(buffer-string))))
;; Uncomment a heading
(should
(equal "* Test"
(org-test-with-temp-text "* COMMENT Test"
(call-interactively #'org-comment-dwim)
(buffer-string))))
;; Comment an inlinetask
(should
(equal "*** COMMENT Test"
(let ((org-inlinetask-min-level 3))
(org-test-with-temp-text "*** Test"
(call-interactively #'org-comment-dwim)
(buffer-string)))))
;; Uncomment an inlinetask
(should
(equal "*** Test"
(let ((org-inlinetask-min-level 3))
(org-test-with-temp-text "*** COMMENT Test"
(call-interactively #'org-comment-dwim)
(buffer-string)))))
;; In a source block, use appropriate syntax.
(should
(equal " ;; "
(org-test-with-temp-text "#+BEGIN_SRC emacs-lisp\n\n#+END_SRC"
(let ((org-edit-src-content-indentation 2))
(call-interactively #'org-comment-dwim))
(buffer-substring-no-properties (line-beginning-position)
(point)))))
(should
(equal "#+BEGIN_SRC emacs-lisp\n ;; a\n ;; b\n#+END_SRC"
(org-test-with-temp-text
"#+BEGIN_SRC emacs-lisp\na\nb\n#+END_SRC"
(transient-mark-mode 1)
(push-mark (point) t t)
(forward-line 2)
(let ((org-edit-src-content-indentation 2))
(call-interactively #'org-comment-dwim))
(buffer-string)))))
;;; Date and time analysis
(ert-deftest test-org/org-encode-time ()
"Test various ways to call `org-encode-time'"
(org-test-with-timezone "UTC"
;; list as the sole argument
(should (string-equal
"2022-03-24 23:30:01"
(format-time-string
"%F %T"
(org-encode-time '(1 30 23 24 3 2022 nil -1 nil)))))
;; SECOND...YEAR
(should (string-equal
"2022-03-24 23:30:02"
(format-time-string
"%F %T"
(org-encode-time 2 30 23 24 3 2022))))
;; SECOND...YEAR IGNORED DST ZONE
(should (string-equal
"2022-03-24 23:30:03"
(format-time-string
"%F %T"
(org-encode-time 3 30 23 24 3 2022 nil -1 nil))))
;; function call
(should (string-equal
"2022-03-24 23:30:04"
(format-time-string
"%F %T"
(org-encode-time (apply #'list 4 30 23 '(24 3 2022 nil -1 nil))))))
;; wrong number of arguments
(if (not (version< emacs-version "27.1"))
(should-error (string-equal
"2022-03-24 23:30:05"
(format-time-string
"%F %T"
(org-encode-time 5 30 23 24 3 2022 nil))))))
;; daylight saving time
(if (not (version< emacs-version "27.1"))
;; DST value is not ignored for multiple arguments unlike for `encode-time'
(should (string-equal
"2022-04-01 00:30:06 +0200 CEST"
(format-time-string
"%F %T %z %Z"
(org-encode-time 6 30 23 31 3 2022 nil nil "Europe/Madrid")
"Europe/Madrid")))
(should (string-equal
"2022-03-31 23:30:07 +0200 CEST"
(format-time-string
"%F %T %z %Z"
(org-encode-time 7 30 23 31 3 2022 nil t "Europe/Madrid")
"Europe/Madrid"))))
(org-test-with-timezone "Europe/Madrid"
;; Standard time is not forced when DST is not specified
(should (string-equal
"2022-03-31 23:30:08"
(format-time-string
"%F %T"
(org-encode-time 8 30 23 31 3 2022))))))
(ert-deftest test-org/org-time-string-to-time ()
"Test `org-time-string-to-time' around DST transition."
(org-test-with-timezone "UTC"
(should (string-equal
"2022-03-31 23:31:00"
(format-time-string
"%F %T"
(org-time-string-to-time "2022-03-31 23:31")))))
(org-test-with-timezone "Europe/Madrid"
(should (string-equal
"2022-03-24 23:32:00 +0100 CET"
(format-time-string
"%F %T %z %Z"
(org-time-string-to-time "2022-03-24 23:32"))))
(should (string-equal
"2022-03-31 23:33:00 +0200 CEST"
(format-time-string
"%F %T %z %Z"
(org-time-string-to-time "2022-03-31 23:33"))))))
(ert-deftest test-org/org-read-date ()
"Test `org-read-date' specifications."
(defvar org-time-was-given) ; dynamically scoped parameter
;; Parse ISO date with abbreviated year and month.
(should (equal "2012-03-29 16:40"
(let ((org-time-was-given t))
(org-read-date t nil "12-3-29 16:40"))))
;; Parse Europeans dates.
(should (equal "2012-03-29 16:40"
(let ((org-time-was-given t))
(org-read-date t nil "29.03.2012 16:40"))))
;; Parse Europeans dates without year.
(should (string-match "2[0-9]\\{3\\}-03-29 16:40"
(let ((org-time-was-given t))
(org-read-date t nil "29.03. 16:40"))))
;; Relative date applied to current time if there is single
;; plus/minus, or to default date when there are two of them.
(should
(equal
"2015-03-04"
(org-test-at-time "2014-03-04"
(org-read-date
t nil "+1y" nil
(org-time-string-to-time "2012-03-29")))))
(should
(equal
"2013-03-29"
(org-test-at-time "2014-03-04"
(org-read-date
t nil "++1y" nil
(org-time-string-to-time "2012-03-29")))))
;; When `org-read-date-prefer-future' is non-nil, prefer future
;; dates (relatively to now) when incomplete. Otherwise, use
;; default date.
(should
(equal
"2014-04-01"
(org-test-at-time "2014-03-04"
(let ((org-read-date-prefer-future t))
(org-read-date t nil "1")))))
(should
(equal
"2013-03-04"
(org-test-at-time "2012-03-29"
(let ((org-read-date-prefer-future t))
(org-read-date t nil "3-4")))))
(should
(equal
"2012-03-04"
(org-test-at-time "2012-03-29"
(let ((org-read-date-prefer-future nil))
(org-read-date t nil "3-4")))))
;; When set to `org-read-date-prefer-future' is set to `time', read
;; day is moved to tomorrow if specified hour is before current
;; time. However, it only happens in no other part of the date is
;; specified.
(should
(equal
"2012-03-30"
(org-test-at-time "2012-03-29 16:40"
(let ((org-read-date-prefer-future 'time))
(org-read-date t nil "00:40" nil)))))
(should-not
(equal
"2012-03-30"
(org-test-at-time "2012-03-29 16:40"
(let ((org-read-date-prefer-future 'time))
(org-read-date t nil "29 00:40" nil)))))
;; Caveat: `org-read-date-prefer-future' always refers to current
;; time, not default time, when they differ.
(should
(equal
"2014-04-01"
(org-test-at-time "2014-03-04"
(let ((org-read-date-prefer-future t))
(org-read-date
t nil "1" nil
(org-time-string-to-time "2012-03-29"))))))
(should
(equal
"2014-03-25"
(org-test-at-time "2014-03-04"
(let ((org-read-date-prefer-future t))
(org-read-date
t nil "25" nil
(org-time-string-to-time "2012-03-29")))))))
(ert-deftest test-org/org-parse-time-string ()
"Test `org-parse-time-string'."
(should (equal (org-parse-time-string "2012-03-29 16:40")
'(0 40 16 29 3 2012 nil -1 nil)))
(should (equal (org-parse-time-string "[2012-03-29 16:40]")
'(0 40 16 29 3 2012 nil -1 nil)))
(should (equal (org-parse-time-string "<2012-03-29 16:40>")
'(0 40 16 29 3 2012 nil -1 nil)))
(should (equal (org-parse-time-string "<2012-03-29>")
'(0 0 0 29 3 2012 nil -1 nil)))
(should (equal (org-parse-time-string "<2012-03-29>" t)
'(0 nil nil 29 3 2012 nil -1 nil))))
(ert-deftest test-org/closest-date ()
"Test `org-closest-date' specifications."
(require 'calendar)
;; Time stamps without a repeater are returned unchanged.
(should
(equal
'(3 29 2012)
(calendar-gregorian-from-absolute
(org-closest-date "<2012-03-29>" "<2014-03-04>" nil))))
;; Time stamps with a null repeater are returned unchanged.
(should
(equal
'(3 29 2012)
(calendar-gregorian-from-absolute
(org-closest-date "<2012-03-29 +0d>" "<2014-03-04>" nil))))
;; if PREFER is set to `past' always return a date before, or equal
;; to CURRENT.
(should
(equal
'(3 1 2014)
(calendar-gregorian-from-absolute
(org-closest-date "<2012-03-29 +1m>" "<2014-03-04>" 'past))))
(should
(equal
'(3 4 2014)
(calendar-gregorian-from-absolute
(org-closest-date "<2012-03-04 +1m>" "<2014-03-04>" 'past))))
;; if PREFER is set to `future' always return a date before, or equal
;; to CURRENT.
(should
(equal
'(3 29 2014)
(calendar-gregorian-from-absolute
(org-closest-date "<2012-03-29 +1m>" "<2014-03-04>" 'future))))
(should
(equal
'(3 4 2014)
(calendar-gregorian-from-absolute
(org-closest-date "<2012-03-04 +1m>" "<2014-03-04>" 'future))))
;; If PREFER is neither `past' nor `future', select closest date.
(should
(equal
'(3 1 2014)
(calendar-gregorian-from-absolute
(org-closest-date "<2012-03-29 +1m>" "<2014-03-04>" nil))))
(should
(equal
'(5 4 2014)
(calendar-gregorian-from-absolute
(org-closest-date "<2012-03-04 +1m>" "<2014-04-28>" nil))))
;; Test "day" repeater.
(should
(equal '(3 8 2014)
(calendar-gregorian-from-absolute
(org-closest-date "<2014-03-04 +2d>" "<2014-03-09>" 'past))))
(should
(equal '(3 10 2014)
(calendar-gregorian-from-absolute
(org-closest-date "<2014-03-04 +2d>" "<2014-03-09>" 'future))))
;; Test "month" repeater.
(should
(equal '(1 5 2015)
(calendar-gregorian-from-absolute
(org-closest-date "<2014-03-05 +2m>" "<2015-02-04>" 'past))))
(should
(equal '(3 29 2014)
(calendar-gregorian-from-absolute
(org-closest-date "<2012-03-29 +2m>" "<2014-03-04>" 'future))))
;; Test "year" repeater.
(should
(equal '(3 5 2014)
(calendar-gregorian-from-absolute
(org-closest-date "<2014-03-05 +2y>" "<2015-02-04>" 'past))))
(should
(equal '(3 29 2014)
(calendar-gregorian-from-absolute
(org-closest-date "<2012-03-29 +2y>" "<2014-03-04>" 'future)))))
(ert-deftest test-org/deadline-close-p ()
"Test `org-deadline-close-p' specifications."
(org-test-at-time "2016-06-03 Fri 01:43"
;; Timestamps are close if they are within `ndays' of lead time.
(org-test-with-temp-text "* Heading"
(should (org-deadline-close-p "2016-06-03 Fri" 0))
(should (org-deadline-close-p "2016-06-02 Thu" 0))
(should-not (org-deadline-close-p "2016-06-04 Sat" 0))
(should (org-deadline-close-p "2016-06-04 Sat" 1))
(should (org-deadline-close-p "2016-06-03 Fri 12:00" 0)))
;; Read `ndays' from timestamp if argument not given.
(org-test-with-temp-text "* H"
(should (org-deadline-close-p "2016-06-04 Sat -1d"))
(should-not (org-deadline-close-p "2016-06-04 Sat -0d"))
(should (org-deadline-close-p "2016-06-10 Fri -1w"))
(should-not (org-deadline-close-p "2016-06-11 Sat -1w")))
;; Prefer `ndays' argument over lead time in timestamp.
(org-test-with-temp-text "* H"
(should (org-deadline-close-p "2016-06-04 Sat -0d" 1))
(should-not (org-deadline-close-p "2016-06-04 Sat -0d" 0)))
;; Completed tasks are never close.
(let ((org-todo-keywords '(("TODO" "|" "DONE"))))
(org-test-with-temp-text "* TODO Heading"
(should (org-deadline-close-p "2016-06-03")))
(org-test-with-temp-text "* DONE Heading"
(should-not (org-deadline-close-p "2016-06-03"))))))
;;; Drawers
(ert-deftest test-org/at-property-p ()
"Test `org-at-property-p' specifications."
(should
(equal 't
(org-test-with-temp-text "* H\n:PROPERTIES:\n:PROP: t\n:END:\n"
(org-at-property-p))))
(should
(equal 't
(org-test-with-temp-text ":PROPERTIES:\n:PROP: t\n:END:\n"
(org-at-property-p)))))
(ert-deftest test-org/at-property-drawer-p ()
"Test `org-at-property-drawer-p' specifications."
(should
(org-test-with-temp-text "* H\n:PROPERTIES:\n:PROP: t\n:END:\n"
(org-at-property-drawer-p)))
(should
(org-test-with-temp-text ":PROPERTIES:\n:PROP: t\n:END:\n"
(org-at-property-drawer-p)))
;; The function only returns t if point is at the first line of
;; a property block.
(should-not
(org-test-with-temp-text ":PROPERTIES:\n:PROP: t\n:END:\n"
(org-at-property-drawer-p)))
;; The function ignores incomplete drawers.
(should-not
(org-test-with-temp-text ":PROPERTIES:\n:PROP: t\n"
(org-at-property-drawer-p)))
;; tab separating the value.
(should
(org-test-with-temp-text ":PROPERTIES:\n:PROP: t\n:END:\n"
(org-at-property-drawer-p))))
(ert-deftest test-org/get-property-block ()
"Test `org-get-property-block' specifications."
(should
(equal '(14 . 14)
(org-test-with-temp-text ":PROPERTIES:\n:END:\n* H\n"
(org-get-property-block))))
(should
(equal '(14 . 14)
(org-test-with-temp-text ":PROPERTIES:\n:END:\n"
(org-get-property-block))))
;; Comments above a document property block is ok.
(should
(equal '(18 . 18)
(org-test-with-temp-text "# C\n:PROPERTIES:\n:END:\n"
(org-get-property-block))))
;; Keywords above a document property block is ok.
(should
(equal '(22 . 22)
(org-test-with-temp-text "# C\n# C\n:PROPERTIES:\n:END:\n"
(org-get-property-block))))
;; Comments and keywords are allowed before a document property block.
(should
(equal '(18 . 27)
(org-test-with-temp-text "# C\n:PROPERTIES:\n:KEY: V:\n:END:\n"
(org-get-property-block))))
;; A document property block will not be valid if there are lines
;; with whitespace above it
(should-not
(org-test-with-temp-text "\n:PROPERTIES:\n:END:\n"
(org-get-property-block)))
(should
(equal '(18 . 18)
(org-test-with-temp-text "* H\n:PROPERTIES:\n:END:\n"
(org-get-property-block))))
(should
(equal "* H\n:PROPERTIES:\n:END:\n"
(org-test-with-temp-text "* H"
(let ((org-adapt-indentation nil))
(org-get-property-block nil 'force))
(buffer-string))))
(should
(equal ":PROPERTIES:\n:END:\n"
(org-test-with-temp-text ""
(org-get-property-block nil 'force)
(buffer-string))))
(should
(equal "* H1\n :PROPERTIES:\n :END:\n* H2"
(org-test-with-temp-text "* H1\n* H2"
(let ((org-adapt-indentation t))
(org-get-property-block nil 'force))
(buffer-string)))))
(ert-deftest test-org/insert-property-drawer ()
"Test `org-insert-property-drawer' specifications."
;; Insert drawer in empty buffer
(should
(equal ":PROPERTIES:\n:END:\n"
(org-test-with-temp-text ""
(let ((org-adapt-indentation nil)) (org-insert-property-drawer))
(buffer-string))))
;; Insert drawer in document header with existing comment and
;; keyword.
(should
(equal "# C\n:PROPERTIES:\n:END:\n#+TITLE: T"
(org-test-with-temp-text "# C\n#+TITLE: T"
(let ((org-adapt-indentation nil)) (org-insert-property-drawer))
(buffer-string))))
;; Insert drawer in document header with existing keyword.
(should
(equal ":PROPERTIES:\n:END:\n#+TITLE: T"
(org-test-with-temp-text "#+TITLE: T"
(let ((org-adapt-indentation nil)) (org-insert-property-drawer))
(buffer-string))))
(should
(equal ":PROPERTIES:\n:END:"
(org-test-with-temp-text ":PROPERTIES:\n:END:"
(let ((org-adapt-indentation nil)) (org-insert-property-drawer))
(buffer-string))))
;; Insert drawer in document header with one existing heading in buffer.
(should
(equal ":PROPERTIES:\n:END:\n\n* T\n"
(org-test-with-temp-text "\n* T\n"
(let ((org-adapt-indentation nil)) (org-insert-property-drawer))
(buffer-string))))
;; Insert drawer right after headline if there is no planning line,
;; or after it otherwise.
(should
(equal "* H\n:PROPERTIES:\n:END:\nParagraph"
(org-test-with-temp-text "* H\nParagraph"
(let ((org-adapt-indentation nil)) (org-insert-property-drawer))
(buffer-string))))
(should
(equal "* H\nDEADLINE: <2014-03-04 tue.>\n:PROPERTIES:\n:END:\nParagraph"
(org-test-with-temp-text
"* H\nDEADLINE: <2014-03-04 tue.>\nParagraph"
(let ((org-adapt-indentation nil)) (org-insert-property-drawer))
(buffer-string))))
;; Indent inserted drawer.
(should
(equal "* H\n :PROPERTIES:\n :END:\nParagraph"
(org-test-with-temp-text "* H\nParagraph"
(let ((org-adapt-indentation t)) (org-insert-property-drawer))
(buffer-string))))
;; Handle insertion at eob.
(should
(equal "* H\n:PROPERTIES:\n:END:\n"
(org-test-with-temp-text "* H"
(let ((org-adapt-indentation nil)) (org-insert-property-drawer))
(buffer-string))))
;; Skip inlinetasks before point.
(when (featurep 'org-inlinetask)
(should
(equal "* H\n:PROPERTIES:\n:END:\n*************** I\n*************** END\nP"
(org-test-with-temp-text
"* H\n*************** I\n*************** END\nP"
(let ((org-adapt-indentation nil)
(org-inlinetask-min-level 15))
(org-insert-property-drawer))
(buffer-string)))))
;; Correctly set drawer in an inlinetask.
(when (featurep 'org-inlinetask)
(should
(equal "* H\n*************** I\n:PROPERTIES:\n:END:\nP\n*************** END"
(org-test-with-temp-text
"* H\n*************** I\nP\n*************** END"
(let ((org-adapt-indentation nil)
(org-inlinetask-min-level 15))
(org-insert-property-drawer))
(buffer-string))))))
;;; Filling
(ert-deftest test-org/fill-element ()
"Test `org-fill-element' specifications."
;; At an Org table, align it.
(should
(equal "| a |\n"
(org-test-with-temp-text "|a|"
(org-fill-element)
(buffer-string))))
(should
(equal "#+name: table\n| a |\n"
(org-test-with-temp-text "#+name: table\n| a |\n"
(org-fill-element)
(buffer-string))))
;; At a paragraph, preserve line breaks.
(org-test-with-temp-text "some \\\\\nlong\ntext"
(let ((fill-column 20))
(org-fill-element)
(should (equal (buffer-string) "some \\\\\nlong text"))))
;; Correctly fill a paragraph when point is at its very end.
(should
(equal "A B"
(org-test-with-temp-text "A\nB"
(let ((fill-column 20))
(goto-char (point-max))
(org-fill-element)
(buffer-string)))))
;; Correctly fill the last paragraph of a greater element.
(should
(equal "#+BEGIN_CENTER\n- 012345\n 789\n#+END_CENTER"
(org-test-with-temp-text "#+BEGIN_CENTER\n- 012345 789\n#+END_CENTER"
(let ((fill-column 8))
(forward-line)
(end-of-line)
(org-fill-element)
(buffer-string)))))
;; Correctly fill an element in a narrowed buffer.
(should
(equal "01234\n6"
(org-test-with-temp-text "01234 6789"
(let ((fill-column 5))
(narrow-to-region 1 8)
(org-fill-element)
(buffer-string)))))
;; Handle `adaptive-fill-regexp' in paragraphs.
(should
(equal "> a b"
(org-test-with-temp-text "> a\n> b"
(let ((fill-column 5)
(adaptive-fill-regexp "[ \t]*>+[ \t]*"))
(org-fill-element)
(buffer-string)))))
;; Special case: Fill first paragraph when point is at an item or
;; a plain-list or a footnote reference.
(should
(equal "- A B"
(org-test-with-temp-text "- A\n B"
(let ((fill-column 20))
(org-fill-element)
(buffer-string)))))
(should
(equal "[fn:1] A B"
(org-test-with-temp-text "[fn:1] A\nB"
(let ((fill-column 20))
(org-fill-element)
(buffer-string)))))
(org-test-with-temp-text "#+BEGIN_VERSE\nSome \\\\\nlong\ntext\n#+END_VERSE"
(let ((fill-column 20))
(org-fill-element)
(should (equal (buffer-string)
"#+BEGIN_VERSE\nSome \\\\\nlong\ntext\n#+END_VERSE"))))
;; Fill contents of `comment-block' elements.
(should
(equal
(org-test-with-temp-text "#+BEGIN_COMMENT\nSome\ntext\n#+END_COMMENT"
(let ((fill-column 20))
(forward-line)
(org-fill-element)
(buffer-string)))
"#+BEGIN_COMMENT\nSome text\n#+END_COMMENT"))
;; Fill `comment' elements.
(should
(equal " # A B"
(org-test-with-temp-text " # A\n # B"
(let ((fill-column 20))
(org-fill-element)
(buffer-string)))))
;; Do not mix consecutive comments when filling one of them.
(should
(equal "# A B\n\n# C"
(org-test-with-temp-text "# A\n# B\n\n# C"
(let ((fill-column 20))
(org-fill-element)
(buffer-string)))))
;; Use commented empty lines as separators when filling comments.
(should
(equal "# A B\n#\n# C"
(org-test-with-temp-text "# A\n# B\n#\n# C"
(let ((fill-column 20))
(org-fill-element)
(buffer-string)))))
;; Handle `adaptive-fill-regexp' in comments.
(should
(equal "# > a b"
(org-test-with-temp-text "# > a\n# > b"
(let ((fill-column 20)
(adaptive-fill-regexp "[ \t]*>+[ \t]*"))
(org-fill-element)
(buffer-string)))))
;; Do nothing at affiliated keywords.
(should
(equal "#+NAME: para\nSome\ntext."
(org-test-with-temp-text "#+NAME: para\nSome\ntext."
(let ((fill-column 20))
(org-fill-element)
(buffer-string)))))
;; Do not move point after table when filling a table.
(should-not
(org-test-with-temp-text "| a | b |\n| c | d |\n"
(forward-char)
(org-fill-element)
(eobp)))
;; Do not fill "n" macro, with or without arguments, followed by
;; a dot or a closing parenthesis since it could be confused with
;; a numbered bullet.
(should-not
(equal "123456789\n{{{n}}}."
(org-test-with-temp-text "123456789 {{{n}}}."
(let ((fill-column 10))
(org-fill-element)
(buffer-string)))))
(should-not
(equal "123456789\n{{{n}}}\)"
(org-test-with-temp-text "123456789 {{{n}}}\)"
(let ((fill-column 10))
(org-fill-element)
(buffer-string)))))
(should-not
(equal "123456789\n{{{n()}}}."
(org-test-with-temp-text "123456789 {{{n()}}}."
(let ((fill-column 10))
(org-fill-element)
(buffer-string)))))
(should-not
(equal "123456789\n{{{n(counter)}}}."
(org-test-with-temp-text "123456789 {{{n(counter)}}}."
(let ((fill-column 10))
(org-fill-element)
(buffer-string))))))
(ert-deftest test-org/fill-paragraph ()
"Test `org-fill-paragraph' specifications."
;; Regular test.
(should
(equal "012345678\n9"
(org-test-with-temp-text "012345678 9"
(let ((fill-column 10))
(org-fill-paragraph)
(buffer-string)))))
;; Fill paragraph even at end of buffer.
(should
(equal "012345678\n9\n"
(org-test-with-temp-text "012345678 9\n"
(let ((fill-column 10))
(org-fill-paragraph)
(buffer-string)))))
;; Between two paragraphs, fill the next one.
(should
(equal "012345678 9\n\n012345678\n9"
(org-test-with-temp-text "012345678 9\n\n012345678 9"
(let ((fill-column 10))
(org-fill-paragraph)
(buffer-string)))))
(should
(equal "012345678\n9\n\n012345678 9"
(org-test-with-temp-text "012345678 9\n\n012345678 9"
(let ((fill-column 10))
(org-fill-paragraph)
(buffer-string)))))
;; Fill paragraph in a comment block.
(should
(equal "#+begin_comment\n012345678\n9\n#+end_comment"
(org-test-with-temp-text
"#+begin_comment\n012345678 9\n#+end_comment"
(let ((fill-column 10))
(org-fill-paragraph)
(buffer-string)))))
;; When a region is selected, fill every paragraph in the region.
(should
(equal "012345678\n9\n\n012345678\n9"
(org-test-with-temp-text "012345678 9\n\n012345678 9"
(let ((fill-column 10))
(transient-mark-mode 1)
(push-mark (point-min) t t)
(goto-char (point-max))
(call-interactively #'org-fill-paragraph)
(buffer-string)))))
(should
(equal "012345678\n9\n\n012345678 9"
(org-test-with-temp-text "012345678 9\n\n012345678 9"
(let ((fill-column 10))
(transient-mark-mode 1)
(push-mark (point) t t)
(goto-char (point-min))
(call-interactively #'org-fill-paragraph)
(buffer-string)))))
(should
(equal "012345678 9\n\n012345678\n9"
(org-test-with-temp-text "012345678 9\n\n012345678 9"
(let ((fill-column 10))
(transient-mark-mode 1)
(push-mark (point) t t)
(goto-char (point-max))
(call-interactively #'org-fill-paragraph)
(buffer-string)))))
;; Fill every list item in a region
(should
(equal "\n- 2345678\n 9\n- 2345678\n 9"
(org-test-with-temp-text "\n- 2345678 9\n- 2345678 9"
(let ((fill-column 10))
(transient-mark-mode 1)
(push-mark (point-min) t t)
(goto-char (point-max))
(call-interactively #'org-fill-paragraph)
(buffer-string)))))
(should
(equal "\n- 2345678\n 9\n- 2345678"
(org-test-with-temp-text "\n- 2345678 9\n- 2345678"
(let ((fill-column 10))
(transient-mark-mode 1)
(push-mark (point-min) t t)
(goto-char (point-max))
(call-interactively #'org-fill-paragraph)
(buffer-string))))))
(ert-deftest test-org/fill-region ()
"Test `fill-region' behaviour."
;; fill-region should fill every item of a list
(should
(equal "\n- 2345678\n 9\n- 2345678\n 9"
(org-test-with-temp-text "\n- 2345678 9\n- 2345678 9"
(let ((fill-column 10))
(transient-mark-mode 1)
(push-mark (point-min) t t)
(goto-char (point-max))
(call-interactively #'fill-region)
(buffer-string)))))
(should
(equal "\n- 1 2\n- 1 2"
(org-test-with-temp-text "\n- 1\n 2\n- 1\n 2"
(let ((fill-column 10))
(transient-mark-mode 1)
(push-mark (point-min) t t)
(goto-char (point-max))
(call-interactively #'fill-region)
(buffer-string))))) )
(ert-deftest test-org/auto-fill-function ()
"Test auto-filling features."
;; Auto fill paragraph.
(should
(equal "12345\n7890"
(org-test-with-temp-text "12345 7890"
(let ((fill-column 5))
(end-of-line)
(org-auto-fill-function)
(buffer-string)))))
;; Auto fill first paragraph in an item.
(should
(equal "- 12345\n 7890"
(org-test-with-temp-text "- 12345 7890"
(let ((fill-column 7))
(end-of-line)
(org-auto-fill-function)
(buffer-string)))))
;; Auto fill paragraph when `adaptive-fill-regexp' matches.
(should
(equal "> 12345\n 7890"
(org-test-with-temp-text "> 12345 7890"
(let ((fill-column 10)
(adaptive-fill-regexp "[ \t]*>+[ \t]*")
(adaptive-fill-first-line-regexp "\\`[ ]*\\'"))
(end-of-line)
(org-auto-fill-function)
(buffer-string)))))
(should
(equal "> 12345\n> 12345\n> 7890"
(org-test-with-temp-text "> 12345\n> 12345 7890"
(let ((fill-column 10)
(adaptive-fill-regexp "[ \t]*>+[ \t]*"))
(goto-char (point-max))
(org-auto-fill-function)
(buffer-string)))))
(should-not
(equal " 12345\n *12345\n *12345"
(org-test-with-temp-text " 12345\n *12345 12345"
(let ((fill-column 10)
(adaptive-fill-regexp "[ \t]*>+[ \t]*"))
(goto-char (point-max))
(org-auto-fill-function)
(buffer-string)))))
;; Auto fill comments.
(should
(equal " # 12345\n # 7890"
(org-test-with-temp-text " # 12345 7890"
(let ((fill-column 10))
(end-of-line)
(org-auto-fill-function)
(buffer-string)))))
;; A hash within a line isn't a comment.
(should-not
(equal "12345 # 7890\n# 1"
(org-test-with-temp-text "12345 # 7890 1"
(let ((fill-column 12))
(end-of-line)
(org-auto-fill-function)
(buffer-string)))))
;; Correctly interpret empty prefix.
(should-not
(equal "# a\n# b\nRegular\n# paragraph"
(org-test-with-temp-text "# a\n# b\nRegular paragraph"
(let ((fill-column 12))
(end-of-line 3)
(org-auto-fill-function)
(buffer-string)))))
;; Do not fill VERSE blocks.
(should-not
(equal "#+BEGIN_VERSE\n12345\n7890\n#+END_VERSE"
(org-test-with-temp-text "#+BEGIN_VERSE\n12345 7890\n#+END_VERSE"
(let ((fill-column 5))
(forward-line)
(end-of-line)
(org-auto-fill-function)
(buffer-string)))))
;; Comment block: auto fill contents.
(should
(equal "#+BEGIN_COMMENT\n12345\n7890\n#+END_COMMENT"
(org-test-with-temp-text "#+BEGIN_COMMENT\n12345 7890\n#+END_COMMENT"
(let ((fill-column 5))
(forward-line)
(end-of-line)
(org-auto-fill-function)
(buffer-string)))))
;; Do not fill if a new item could be created.
(should-not
(equal "12345\n- 90"
(org-test-with-temp-text "12345 - 90"
(let ((fill-column 5))
(end-of-line)
(org-auto-fill-function)
(buffer-string)))))
;; Do not fill if a line break could be introduced.
(should-not
(equal "123\\\\\n7890"
(org-test-with-temp-text "123\\\\ 7890"
(let ((fill-column 6))
(end-of-line)
(org-auto-fill-function)
(buffer-string)))))
;; Do not fill affiliated keywords.
(should-not
(equal "#+ATTR_LATEX: ABC\nDEFGHIJKL"
(org-test-with-temp-text "#+ATTR_LATEX: ABC DEFGHIJKL"
(let ((fill-column 20))
(end-of-line)
(org-auto-fill-function)
(buffer-string))))))
;;; Indentation
(ert-deftest test-org/indent-line ()
"Test `org-indent-line' specifications."
;; Do not indent diary sexps, footnote definitions or headlines.
(should
(zerop
(org-test-with-temp-text "%%(org-calendar-holiday)"
(org-indent-line)
(org-get-indentation))))
(should
(zerop
(org-test-with-temp-text "[fn:1] fn"
(let ((org-adapt-indentation t)) (org-indent-line))
(org-get-indentation))))
(should
(zerop
(org-test-with-temp-text "* H"
(org-indent-line)
(org-get-indentation))))
;; Do not indent before first headline.
(should
(zerop
(org-test-with-temp-text ""
(org-indent-line)
(org-get-indentation))))
;; Indent according to headline level otherwise, unless
;; `org-adapt-indentation' is nil.
(should
(= 2
(org-test-with-temp-text "* H\nA"
(let ((org-adapt-indentation t)) (org-indent-line))
(org-get-indentation))))
(should
(= 2
(org-test-with-temp-text "* H\n\nA"
(let ((org-adapt-indentation t)) (org-indent-line))
(org-get-indentation))))
(should
(zerop
(org-test-with-temp-text "* H\nA"
(let ((org-adapt-indentation nil)) (org-indent-line))
(org-get-indentation))))
;; Indenting preserves point position.
(should
(org-test-with-temp-text "* H\nAB"
(let ((org-adapt-indentation t)) (org-indent-line))
(looking-at "B")))
;; Do not change indentation at an item or a LaTeX environment.
(should
(= 1
(org-test-with-temp-text "* H\n - A"
(let ((org-adapt-indentation t)) (org-indent-line))
(org-get-indentation))))
(should
(= 1
(org-test-with-temp-text
"\\begin{equation}\n 1+1=2\n\\end{equation}"
(org-indent-line)
(org-get-indentation))))
;; On blank lines at the end of a list, indent like last element
;; within it if the line is still in the list. If the last element
;; is an item, indent like its contents. Otherwise, indent like the
;; whole list.
(should
(= 4
(org-test-with-temp-text "* H\n- A\n - AA\n"
(let ((org-adapt-indentation t)) (org-indent-line))
(org-get-indentation))))
(should
(= 4
(org-test-with-temp-text "* H\n- A\n -\n\n"
(let ((org-adapt-indentation t)) (org-indent-line))
(org-get-indentation))))
(should
(zerop
(org-test-with-temp-text "* H\n- A\n - AA\n\n\n\n"
(let ((org-adapt-indentation t)) (org-indent-line))
(org-get-indentation))))
(should
(= 4
(org-test-with-temp-text "* H\n- A\n - \n"
(let ((org-adapt-indentation t)) (org-indent-line))
(org-get-indentation))))
(should
(= 4
(org-test-with-temp-text
"* H\n - \n #+BEGIN_SRC emacs-lisp\n t\n #+END_SRC\n"
(let ((org-adapt-indentation t)) (org-indent-line))
(org-get-indentation))))
(should
(= 2
(org-test-with-temp-text "- A\n B\n\n"
(let ((org-adapt-indentation nil)) (org-indent-line))
(org-get-indentation))))
(should
(= 2
(org-test-with-temp-text
"- A\n \begin{cases} 1 + 1\n \end{cases}\n\n"
(let ((org-adapt-indentation nil)) (org-indent-line))
(org-get-indentation))))
;; Likewise, on a blank line at the end of a footnote definition,
;; indent at column 0 if line belongs to the definition. Otherwise,
;; indent like the definition itself.
(should
(zerop
(org-test-with-temp-text "* H\n[fn:1] Definition\n"
(let ((org-adapt-indentation t)) (org-indent-line))
(org-get-indentation))))
(should
(zerop
(org-test-with-temp-text "* H\n[fn:1] Definition\n\n\n\n"
(let ((org-adapt-indentation t)) (org-indent-line))
(org-get-indentation))))
;; After the end of the contents of a greater element, indent like
;; the beginning of the element.
(should
(= 1
(org-test-with-temp-text
" #+BEGIN_CENTER\n Contents\n#+END_CENTER"
(org-indent-line)
(org-get-indentation))))
;; On blank lines after a paragraph, indent like its last non-empty
;; line.
(should
(= 1
(org-test-with-temp-text " Paragraph\n\n"
(org-indent-line)
(org-get-indentation))))
;; At the first line of an element, indent like previous element's
;; first line, ignoring footnotes definitions and inline tasks, or
;; according to parent.
(let ((org-adapt-indentation t))
(should
(= 2
(org-test-with-temp-text "A\n\n B\n\nC"
(org-indent-line)
(org-get-indentation))))
(should
(= 1
(org-test-with-temp-text " A\n\n[fn:1] B\n\n\nC"
(org-indent-line)
(org-get-indentation))))
(should
(= 1
(org-test-with-temp-text
" #+BEGIN_CENTER\n Contents\n#+END_CENTER"
(org-indent-line)
(org-get-indentation)))))
;; Within code part of a source block, use language major mode if
;; `org-src-tab-acts-natively' is non-nil. Otherwise, indent
;; according to line above.
(should
(= 6
(org-test-with-temp-text
"#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC"
(let ((org-src-tab-acts-natively t)
(org-edit-src-content-indentation 0))
(org-indent-line))
(org-get-indentation))))
(should
(= 1
(org-test-with-temp-text
"#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC"
(let ((org-src-tab-acts-natively nil)
(org-edit-src-content-indentation 0))
(org-indent-line))
(org-get-indentation))))
;; Otherwise, indent like the first non-blank line above.
(should
(zerop
(org-test-with-temp-text
"#+BEGIN_CENTER\nline1\n\n line2\n#+END_CENTER"
(org-indent-line)
(org-get-indentation))))
;; Align node properties according to `org-property-format'. Handle
;; nicely empty values.
(should
(equal "* H\n:PROPERTIES:\n:key: value\n:END:"
(org-test-with-temp-text
"* H\n:PROPERTIES:\n:key: value\n:END:"
(let ((org-property-format "%-10s %s")) (org-indent-line))
(buffer-string))))
(should
(equal "* H\n:PROPERTIES:\n:key:\n:END:"
(org-test-with-temp-text "* H\n:PROPERTIES:\n:key:\n:END:"
(let ((org-property-format "%-10s %s")) (org-indent-line))
(buffer-string)))))
(ert-deftest test-org/indent-region ()
"Test `org-indent-region' specifications."
;; Indent paragraph.
(let ((org-adapt-indentation t))
(should
(equal "A\nB\nC"
(org-test-with-temp-text " A\nB\n C"
(org-indent-region (point-min) (point-max))
(buffer-string)))))
;; Indent greater elements along with their contents.
(should
(equal "#+BEGIN_CENTER\nA\nB\n#+END_CENTER"
(org-test-with-temp-text "#+BEGIN_CENTER\n A\n B\n#+END_CENTER"
(org-indent-region (point-min) (point-max))
(buffer-string))))
;; Ignore contents of verse blocks. Only indent block delimiters.
(should
(equal "#+BEGIN_VERSE\n A\n B\n#+END_VERSE"
(org-test-with-temp-text "#+BEGIN_VERSE\n A\n B\n#+END_VERSE"
(org-indent-region (point-min) (point-max))
(buffer-string))))
(let ((org-adapt-indentation t))
(should
(equal "#+BEGIN_VERSE\n A\n B\n#+END_VERSE"
(org-test-with-temp-text " #+BEGIN_VERSE\n A\n B\n #+END_VERSE"
(org-indent-region (point-min) (point-max))
(buffer-string)))))
;; Indent example blocks as a single block, unless indentation
;; should be preserved. In this case only indent the block markers.
(should
(equal "#+BEGIN_EXAMPLE\n A\n B\n#+END_EXAMPLE"
(org-test-with-temp-text "#+BEGIN_EXAMPLE\n A\n B\n#+END_EXAMPLE"
(org-indent-region (point-min) (point-max))
(buffer-string))))
(let ((org-adapt-indentation t))
(should
(equal "#+BEGIN_EXAMPLE\n A\n B\n#+END_EXAMPLE"
(org-test-with-temp-text " #+BEGIN_EXAMPLE\n A\n B\n #+END_EXAMPLE"
(org-indent-region (point-min) (point-max))
(buffer-string))))
(should
(equal "#+BEGIN_EXAMPLE -i\n A\n B\n#+END_EXAMPLE"
(org-test-with-temp-text
" #+BEGIN_EXAMPLE -i\n A\n B\n #+END_EXAMPLE"
(org-indent-region (point-min) (point-max))
(buffer-string))))
(should
(equal "#+BEGIN_EXAMPLE\n A\n B\n#+END_EXAMPLE"
(org-test-with-temp-text
" #+BEGIN_EXAMPLE\n A\n B\n #+END_EXAMPLE"
(let ((org-src-preserve-indentation t))
(org-indent-region (point-min) (point-max)))
(buffer-string)))))
;; Treat export blocks as a whole.
(should
(equal "#+BEGIN_EXPORT latex\n A\n B\n#+END_EXPORT"
(org-test-with-temp-text "#+BEGIN_EXPORT latex\n A\n B\n#+END_EXPORT"
(org-indent-region (point-min) (point-max))
(buffer-string))))
(let ((org-adapt-indentation t))
(should
(equal "#+BEGIN_EXPORT latex\n A\n B\n#+END_EXPORT"
(org-test-with-temp-text
" #+BEGIN_EXPORT latex\n A\n B\n #+END_EXPORT"
(org-indent-region (point-min) (point-max))
(buffer-string)))))
;; Indent according to mode if `org-src-tab-acts-natively' is
;; non-nil. Otherwise, do not indent code at all.
(should
(equal "#+BEGIN_SRC emacs-lisp\n(and A\n B)\n#+END_SRC"
(org-test-with-temp-text
"#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC"
(let ((org-src-tab-acts-natively t)
(org-edit-src-content-indentation 0))
(org-indent-region (point-min) (point-max)))
(buffer-string))))
(should
(equal "#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC"
(org-test-with-temp-text
"#+BEGIN_SRC emacs-lisp\n (and A\nB)\n#+END_SRC"
(let ((org-src-tab-acts-natively nil)
(org-edit-src-content-indentation 0))
(org-indent-region (point-min) (point-max)))
(buffer-string))))
;; Align node properties according to `org-property-format'. Handle
;; nicely empty values.
(should
(equal "* H\n:PROPERTIES:\n:key: value\n:END:"
(org-test-with-temp-text "* H\n:PROPERTIES:\n:key: value\n:END:"
(let ((org-property-format "%-10s %s")
(org-adapt-indentation nil))
(org-indent-region (point) (point-max)))
(buffer-string))))
(should
(equal "* H\n:PROPERTIES:\n:key:\n:END:"
(org-test-with-temp-text "* H\n:PROPERTIES:\n:key:\n:END:"
(let ((org-property-format "%-10s %s")
(org-adapt-indentation nil))
(org-indent-region (point) (point-max)))
(buffer-string))))
;; Indent property drawers according to `org-adapt-indentation'.
(let ((org-adapt-indentation 'headline-data))
(should
(equal "* H\n :PROPERTIES:\n :key:\n :END:"
(org-test-with-temp-text "* H\n:PROPERTIES:\n:key:\n:END:"
(org-indent-region (point-min) (point-max))
(buffer-string)))))
;; Indent planning according to `org-adapt-indentation'.
(let ((org-adapt-indentation 'headline-data))
(should
(equal "* H\n SCHEDULED: <2022-11-03>"
(org-test-with-temp-text "* H\nSCHEDULED: <2022-11-03>"
(org-indent-region (point-min) (point-max))
(buffer-string)))))
;; Indent LOGBOOK according to `org-adapt-indentation'.
(let ((org-adapt-indentation 'headline-data))
(should
(equal "* H\n :LOGBOOK:
CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46
:END:"
(org-test-with-temp-text "* H\n:LOGBOOK:
CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46
:END:"
(org-indent-region (point-min) (point-max))
(buffer-string)))))
;; Indent clock lines according to `org-adapt-indentation'.
(let ((org-adapt-indentation 'headline-data))
(should
(equal "* H
CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46"
(org-test-with-temp-text "* H
CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46"
(org-indent-region (point-min) (point-max))
(buffer-string)))))
;; Do not indent beyond headline data.
(let ((org-adapt-indentation 'headline-data))
(should
(equal "* H\n SCHEDULED: <2022-11-03>\nParagraph"
(org-test-with-temp-text "* H\nSCHEDULED: <2022-11-03>\nParagraph"
(org-indent-region (point-min) (point-max))
(buffer-string)))))
;; Do not indent empty lines after heading, when no headline data.
(let ((org-adapt-indentation 'headline-data))
(should
(equal "* H1\n\n* H2\n"
(org-test-with-temp-text "* H1\n\n* H2\n"
(org-indent-line)
(buffer-string)))))
(let ((org-adapt-indentation 'headline-data)
(org-log-into-drawer t))
(should
(equal "* TODO A task
:PROPERTIES:
:CAPTURED: [2022-09-11 dim. 21:25]
:END:
:LOGBOOK:
CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46
:END:
Paragraph"
(org-test-with-temp-text "* TODO A task
:PROPERTIES:
:CAPTURED: [2022-09-11 dim. 21:25]
:END:
:LOGBOOK:
CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46
:END:
Paragraph"
(org-indent-region (point-min) (point-max))
(buffer-string)))))
(let ((org-adapt-indentation 'headline-data))
(should
(equal "* TODO A task
CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46
Paragraph
CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46"
(org-test-with-temp-text "* TODO A task
CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46
Paragraph
CLOCK: [2022-09-17 sam. 11:00]--[2022-09-17 sam. 11:46] => 0:46"
(org-indent-region (point-min) (point-max))
(buffer-string)))))
;; Indent plain lists.
(let ((org-adapt-indentation t))
(should
(equal "- A\n B\n - C\n\n D"
(org-test-with-temp-text "- A\n B\n - C\n\n D"
(org-indent-region (point-min) (point-max))
(buffer-string))))
(should
(equal "- A\n\n- B"
(org-test-with-temp-text " - A\n\n - B"
(org-indent-region (point-min) (point-max))
(buffer-string)))))
;; Indent footnote definitions.
(should
(equal "[fn:1] Definition\n\nDefinition"
(org-test-with-temp-text "[fn:1] Definition\n\n Definition"
(org-indent-region (point-min) (point-max))
(buffer-string))))
;; Special case: Start indenting on a blank line.
(should
(equal "\nParagraph"
(org-test-with-temp-text "\n Paragraph"
(org-indent-region (point-min) (point-max))
(buffer-string)))))
(ert-deftest test-org/default-indent-new-line ()
"Test behavior of default binding `M-j'."
;; Calling `M-j' when point is not in an Org comment:
(should
(equal "* Some heading\n"
(org-test-with-temp-text "* Some heading"
(call-interactively #'default-indent-new-line)
(buffer-string))))
;; Calling `M-j' when point is in an Org comment:
(should
(equal "# Some Org comment\n# "
(org-test-with-temp-text "# Some Org comment"
(call-interactively #'default-indent-new-line)
(buffer-string))))
(should
(equal "# Some Org\n# comment"
(org-test-with-temp-text "# Some Org comment"
(call-interactively #'default-indent-new-line)
(buffer-string)))))
;;; Editing
(ert-deftest test-org/delete-indentation ()
"Test `org-delete-indentation' specifications."
;; Regular test.
(should (equal "foo bar"
(org-test-with-temp-text
"foo \n bar"
(org-delete-indentation)
(buffer-string))))
;; With optional argument.
(should (equal "foo bar"
(org-test-with-temp-text
"foo \n bar"
(org-delete-indentation t)
(buffer-string))))
;; At headline text should be appended to the headline text.
(should
(equal"* foo bar :tag:"
(let (org-auto-align-tags)
(org-test-with-temp-text
"* foo :tag:\n bar"
(org-delete-indentation)
(buffer-string)))))
(should
(equal "* foo bar :tag:"
(let (org-auto-align-tags)
(org-test-with-temp-text
"* foo :tag:\n bar"
(org-delete-indentation t)
(buffer-string))))))
(ert-deftest test-org/return ()
"Test `org-return' specifications."
;; Regular test.
(should
(equal "Para\ngraph"
(org-test-with-temp-text "Paragraph"
(org-return)
(buffer-string))))
;; With optional argument, indent line.
(should
(equal " Para\n graph"
(org-test-with-temp-text " Paragraph"
(org-return t)
(buffer-string))))
;; On a table, call `org-table-next-row'.
(should
(org-test-with-temp-text "| a |\n| b |"
(org-return)
(looking-at-p "b")))
;; Open link or timestamp under point when `org-return-follows-link'
;; is non-nil.
(should
(org-test-with-temp-text "Link [[target]] <>"
(let ((org-return-follows-link t)
(org-link-search-must-match-exact-headline nil))
(org-return))
(looking-at-p "<>")))
(should-not
(org-test-with-temp-text "Link [[target]] <>"
(let ((org-return-follows-link nil)) (org-return))
(looking-at-p "<>")))
(should
(org-test-with-temp-text "* [[b][a]]\n* b"
(let ((org-return-follows-link t)) (org-return))
(looking-at-p "* b")))
(should
(org-test-with-temp-text "Link [[target][/desciption/]] <>"
(let ((org-return-follows-link t)
(org-link-search-must-match-exact-headline nil))
(org-return))
(looking-at-p "<>")))
(should-not
(org-test-with-temp-text "Link [[target]] <>"
(let ((org-return-follows-link t)
(org-link-search-must-match-exact-headline nil))
(org-return))
(looking-at-p "<>")))
;; When `org-return-follows-link' is non-nil, tolerate links and
;; timestamps in comments, node properties, etc.
(should
(org-test-with-temp-text "# Comment [[target]]\n <>"
(let ((org-return-follows-link t)
(org-link-search-must-match-exact-headline nil))
(org-return))
(looking-at-p "<>")))
(should-not
(org-test-with-temp-text "# Comment [[target]]\n <>"
(let ((org-return-follows-link nil)) (org-return))
(looking-at-p "<>")))
(should-not
(org-test-with-temp-text "# Comment [[target]]\n <>"
(let ((org-return-follows-link t)
(org-link-search-must-match-exact-headline nil))
(org-return))
(looking-at-p "<>")))
;; Non-nil `org-return-follows-link' ignores read-only state of
;; a buffer.
(should
(org-test-with-temp-text "Link [[target]] <>"
(let ((org-return-follows-link t)
(org-link-search-must-match-exact-headline nil))
(setq buffer-read-only t)
(call-interactively #'org-return))
(looking-at-p "<>")))
;; `org-return-follows-link' handle multi-line lines.
(should
(org-test-with-temp-text
"[[target][This is a very\n long description]]\n <>"
(let ((org-return-follows-link t)
(org-link-search-must-match-exact-headline nil))
(org-return))
(looking-at-p "<>")))
(should-not
(org-test-with-temp-text
"[[target][This is a very\n long description]]\n <>"
(let ((org-return-follows-link t)
(org-link-search-must-match-exact-headline nil))
(org-return))
(looking-at-p "<>")))
;; However, do not open link when point is in a table.
(should
(org-test-with-temp-text "| [[target]] |\n| between |\n| <> |"
(let ((org-return-follows-link t)) (org-return))
(looking-at-p "between")))
;; Special case: in a list, when indenting, do not break structure.
(should
(equal "- A\n B"
(org-test-with-temp-text "- A B"
(org-return t)
(buffer-string))))
(should
(equal "- A\n\n- B"
(org-test-with-temp-text "- A\n- B"
(org-return t)
(buffer-string))))
;; On tags part of a headline, add a newline below it instead of
;; breaking it.
(should
(equal "* H :tag:\n"
(org-test-with-temp-text "* H :tag:"
(org-return)
(buffer-string))))
;; Before headline text, add a newline below it instead of breaking
;; it.
(should
(equal "* TODO H :tag:\n"
(org-test-with-temp-text "* TODO H :tag:"
(org-return)
(buffer-string))))
(should
(equal "* TODO [#B] H :tag:\n"
(org-test-with-temp-text "* TODO [#B] H :tag:"
(org-return)
(buffer-string))))
(should ;TODO are case-sensitive
(equal "* \nTodo"
(org-test-with-temp-text "* Todo"
(org-return)
(buffer-string))))
;; At headline text, break headline text but preserve tags.
(should
(equal "* TODO [#B] foo :tag:\nbar"
(let (org-auto-align-tags)
(org-test-with-temp-text "* TODO [#B] foobar :tag:"
(org-return)
(buffer-string)))))
;; At bol of headline insert newline.
(should
(equal "\n* h"
(org-test-with-temp-text "* h"
(org-return)
(buffer-string))))
;; Refuse to leave invalid headline in buffer.
(should
(equal "* h\n"
(org-test-with-temp-text "* h"
(org-return)
(buffer-string))))
;; Before first column or after last one in a table, split the
;; table.
(should
(equal "| a |\n\n| b |"
(org-test-with-temp-text "| a |\n| b |"
(org-return)
(buffer-string))))
(should
(equal "| a |\n\n| b |"
(org-test-with-temp-text "| a |\n| b |"
(org-return)
(buffer-string))))
;; Do not auto-fill on hitting inside a property drawer.
(should
(equal "* Heading\n:PROPERTIES:\n:SOME_PROP: This is a very long property value that goes beyond the fill-column. But this is inside a property drawer, so the auto-filling should be disabled.\n\n:END:"
(org-test-with-temp-text "* Heading\n:PROPERTIES:\n:SOME_PROP: This is a very long property value that goes beyond the fill-column. But this is inside a property drawer, so the auto-filling should be disabled.\n:END:"
(setq-local fill-column 10)
(auto-fill-mode 1)
(org-return)
(buffer-string))))
;; When `org-return-follows-link' is non-nil, run `org-open-at-point'
;; on citation.
(should
(org-test-with-temp-text "[cite: @key1; @key2]"
(catch :called
(cl-letf (((symbol-function 'org-open-at-point)
(lambda (&rest _) (interactive) (throw :called t))))
(let ((org-return-follows-link t)) (org-return))
nil))))
(should-not
(org-test-with-temp-text "[cite: @key1; @key2]"
(catch :called
(cl-letf (((symbol-function 'org-open-at-point)
(lambda (&rest _) (interactive) (throw :called t))))
(let ((org-return-follows-link nil)) (org-return))
nil))))
(should
(org-test-with-temp-text "[cite: @key1; @key2]"
(catch :called
(cl-letf (((symbol-function 'org-open-at-point)
(lambda (&rest _) (interactive) (throw :called t))))
(let ((org-return-follows-link t)) (org-return))
nil))))
(should-not
(org-test-with-temp-text "[cite: @key1; @key2]"
(catch :called
(cl-letf (((symbol-function 'org-open-at-point)
(lambda (&rest _) (interactive) (throw :called t))))
(let ((org-return-follows-link nil)) (org-return))
nil)))))
(ert-deftest test-org/with-electric-indent ()
"Test RET and C-j specifications with `electric-indent-mode' on."
;; Call commands interactively, since this is how `newline' knows it
;; must run `post-self-insert-hook'.
;;
;; RET, like `newline', should indent.
(should
(equal " Para\n graph"
(org-test-with-temp-text " Paragraph"
(electric-indent-local-mode 1)
(call-interactively 'org-return)
(buffer-string))))
(should
(equal "- item1\n item2"
(org-test-with-temp-text "- item1item2"
(electric-indent-local-mode 1)
(call-interactively 'org-return)
(buffer-string))))
;; TODO: test more values of `org-adapt-indentation'.
(let ((org-adapt-indentation t))
(should
(equal "* heading\n body"
(org-test-with-temp-text "* headingbody"
(electric-indent-local-mode 1)
(call-interactively 'org-return)
(buffer-string)))))
;; C-j, like `electric-newline-and-maybe-indent', should not indent.
(should
(equal " Para\ngraph"
(org-test-with-temp-text " Paragraph"
(electric-indent-local-mode 1)
(call-interactively 'org-return-and-maybe-indent)
(buffer-string))))
(should
(equal "- item1\nitem2"
(org-test-with-temp-text "- item1item2"
(electric-indent-local-mode 1)
(call-interactively 'org-return-and-maybe-indent)
(buffer-string))))
;; TODO: test more values of `org-adapt-indentation'.
(let ((org-adapt-indentation t))
(should
(equal "* heading\nbody"
(org-test-with-temp-text "* headingbody"
(electric-indent-local-mode 1)
(call-interactively 'org-return-and-maybe-indent)
(buffer-string))))))
(ert-deftest test-org/without-electric-indent ()
"Test RET and C-j specifications with `electric-indent-mode' off."
;; Call commands interactively, since this is how `newline' knows it
;; must run `post-self-insert-hook'.
;;
;; RET, like `newline', should not indent.
(should
(equal " Para\ngraph"
(org-test-with-temp-text " Paragraph"
(electric-indent-local-mode 0)
(call-interactively 'org-return)
(buffer-string))))
(should
(equal "- item1\nitem2"
(org-test-with-temp-text "- item1item2"
(electric-indent-local-mode 0)
(call-interactively 'org-return)
(buffer-string))))
(should
(equal "* heading\nbody"
(org-test-with-temp-text "* headingbody"
(electric-indent-local-mode 0)
(call-interactively 'org-return)
(buffer-string))))
;; C-j, like `electric-newline-and-maybe-indent', should indent.
(should
(equal " Para\n graph"
(org-test-with-temp-text " Paragraph"
(electric-indent-local-mode 0)
(call-interactively 'org-return-and-maybe-indent)
(buffer-string))))
(should
(equal "- item1\n item2"
(org-test-with-temp-text "- item1item2"
(electric-indent-local-mode 0)
(call-interactively 'org-return-and-maybe-indent)
(buffer-string))))
;; TODO: test more values of `org-adapt-indentation'.
(let ((org-adapt-indentation t))
(should
(equal "* heading\n body"
(org-test-with-temp-text "* headingbody"
(electric-indent-local-mode 0)
(call-interactively 'org-return-and-maybe-indent)
(buffer-string))))))
(ert-deftest test-org/meta-return ()
"Test M-RET (`org-meta-return') specifications."
;; In a table field insert a row above.
(should
(org-test-with-temp-text "| a |"
(forward-char)
(org-meta-return)
(forward-line -1)
(looking-at "| |$")))
;; In a paragraph change current line into a header.
(should
(org-test-with-temp-text "a"
(org-meta-return)
(beginning-of-line)
(looking-at "\* a$")))
;; In an item insert an item, in this case above.
(should
(org-test-with-temp-text "- a"
(org-meta-return)
(beginning-of-line)
(looking-at "- $")))
;; In a drawer and item insert an item, in this case above.
(should
(org-test-with-temp-text ":MYDRAWER:\n- a\n:END:"
(forward-line)
(org-fold-reveal)
(org-meta-return)
(beginning-of-line)
(looking-at "- $"))))
(ert-deftest test-org/insert-heading ()
"Test `org-insert-heading' specifications."
;; In an empty buffer, insert a new headline.
(should
(equal "* "
(org-test-with-temp-text ""
(org-insert-heading)
(buffer-string))))
;; At the beginning of a line, turn it into a headline.
(should
(equal "* P"
(org-test-with-temp-text "P"
(org-insert-heading)
(buffer-string))))
;; In the middle of a line, split the line if allowed, otherwise,
;; insert the headline at its end.
(should
(equal "Para\n* graph"
(org-test-with-temp-text "Paragraph"
(let ((org-M-RET-may-split-line '((default . t))))
(org-insert-heading))
(buffer-string))))
(should
(equal "Paragraph\n* "
(org-test-with-temp-text "Paragraph"
(let ((org-M-RET-may-split-line '((default . nil))))
(org-insert-heading))
(buffer-string))))
;; At the beginning of a headline, create one above.
(should
(equal "* \n* H"
(org-test-with-temp-text "* H"
(org-insert-heading)
(buffer-string))))
;; In the middle of a headline, split it if allowed.
(should
(equal "* H\n* 1"
(org-test-with-temp-text "* H1"
(let ((org-M-RET-may-split-line '((headline . t))))
(org-insert-heading))
(buffer-string))))
(should
(equal "* H1\n* "
(org-test-with-temp-text "* H1"
(let ((org-M-RET-may-split-line '((headline . nil))))
(org-insert-heading))
(buffer-string))))
;; However, splitting cannot happen on TODO keywords, priorities or
;; tags.
(should
(equal "* TODO H1\n* "
(org-test-with-temp-text "* TODO H1"
(let ((org-M-RET-may-split-line '((headline . t))))
(org-insert-heading))
(buffer-string))))
(should
(equal "* [#A] H1\n* "
(org-test-with-temp-text "* [#A] H1"
(let ((org-M-RET-may-split-line '((headline . t))))
(org-insert-heading))
(buffer-string))))
(should
(equal "* H1 :tag:\n* "
(org-test-with-temp-text "* H1 :tag:"
(let ((org-M-RET-may-split-line '((headline . t))))
(org-insert-heading))
(buffer-string))))
;; New headline level depends on the level of the headline above.
(should
(equal "** H\n** P"
(org-test-with-temp-text "** H\nP"
(org-insert-heading)
(buffer-string))))
(should
(equal "** H\nPara\n** graph"
(org-test-with-temp-text "** H\nParagraph"
(let ((org-M-RET-may-split-line '((default . t))))
(org-insert-heading))
(buffer-string))))
(should
(equal "** \n** H"
(org-test-with-temp-text "** H"
(org-insert-heading)
(buffer-string))))
;; When called with one universal argument, insert a new headline at
;; the end of the current subtree, independently on the position of
;; point.
(should
(equal
"* \n"
(org-test-with-temp-text ""
(let ((org-insert-heading-respect-content nil))
(org-insert-heading '(4)))
(buffer-string))))
(should
(equal
"* 1
** 1.1
** 1.2
*
* 2"
(org-test-with-temp-text "* 1
** 1.1
** 1.2
* 2"
(let ((org-insert-heading-respect-content nil))
(org-insert-heading '(4)))
(buffer-string))))
(should
(equal
"entry
* \n"
(org-test-with-temp-text "entry"
(let ((org-insert-heading-respect-content nil))
(org-insert-heading '(4)))
(buffer-string))))
(should
(equal
"* H1\n** H2\n* \n"
(org-test-with-temp-text "* H1\n** H2"
(let ((org-insert-heading-respect-content nil))
(org-insert-heading '(4)))
(buffer-string))))
(should
(equal
"* H1\n** H2\n* \n"
(org-test-with-temp-text "* H1\n** H2"
(let ((org-insert-heading-respect-content nil))
(org-insert-heading '(4)))
(buffer-string))))
;; When called with two universal arguments, insert a new headline
;; at the end of the grandparent subtree.
(should
(equal "* H1\n** H3\n- item\n** H2\n** \n"
(org-test-with-temp-text "* H1\n** H3\n- item\n** H2"
(let ((org-insert-heading-respect-content nil))
(org-insert-heading '(16)))
(buffer-string))))
;; When optional TOP-LEVEL argument is non-nil, always insert
;; a level 1 heading.
(should
(equal "* H1\n** H2\n* "
(org-test-with-temp-text "* H1\n** H2"
(org-insert-heading nil nil t)
(buffer-string))))
(should
(equal "* H1\n- item\n* "
(org-test-with-temp-text "* H1\n- item"
(org-insert-heading nil nil t)
(buffer-string))))
;; Obey `org-blank-before-new-entry'.
(should
(equal "* H1\n\n* "
(org-test-with-temp-text "* H1"
(let ((org-blank-before-new-entry '((heading . t))))
(org-insert-heading))
(buffer-string))))
(should
(equal "* H1\n* "
(org-test-with-temp-text "* H1"
(let ((org-blank-before-new-entry '((heading . nil))))
(org-insert-heading))
(buffer-string))))
(should
(equal "* H1\n* H2\n* "
(org-test-with-temp-text "* H1\n* H2"
(let ((org-blank-before-new-entry '((heading . auto))))
(org-insert-heading))
(buffer-string))))
(should
(equal "* H1\n\n* H2\n\n* "
(org-test-with-temp-text "* H1\n\n* H2"
(let ((org-blank-before-new-entry '((heading . auto))))
(org-insert-heading))
(buffer-string))))
;; Corner case: correctly insert a headline after an empty one.
(should
(equal "* \n* "
(org-test-with-temp-text "* "
(org-insert-heading)
(buffer-string))))
(should
(org-test-with-temp-text "* \n"
(org-insert-heading)
(looking-at-p "\n\\'")))
;; Do not insert spurious headlines when inserting a new headline.
(should
(equal "* H1\n* H2\n* \n"
(org-test-with-temp-text "* H1\n* H2\n"
(org-insert-heading)
(buffer-string))))
;; Preserve visibility at beginning of line. In particular, when
;; removing spurious blank lines, do not visually merge heading with
;; the line visible above.
(should-not
(org-test-with-temp-text "* H1\nContents\n\n* H2\n"
(org-overview)
(let ((org-blank-before-new-entry '((heading . nil))))
(org-insert-heading '(4)))
(invisible-p (line-end-position 0))))
;; Properly handle empty lines when forcing a headline below current
;; one.
(should
(equal "* H1\n\n* H\n\n* \n"
(org-test-with-temp-text "* H1\n\n* H"
(let ((org-blank-before-new-entry '((heading . t))))
(org-insert-heading '(4))
(buffer-string)))))
;; Do not include potentially folded empty lines.
(org-test-with-temp-text "
* Sec1
** SubSec1
text
** SubSec2
text
"
(org-content)
(org-insert-heading '(4))
(should-not (org-fold-folded-p))))
(ert-deftest test-org/insert-todo-heading-respect-content ()
"Test `org-insert-todo-heading-respect-content' specifications."
;; Create a TODO heading.
(should
(org-test-with-temp-text "* H1\n Body"
(org-insert-todo-heading-respect-content)
(nth 2 (org-heading-components))))
;; Add headline at the end of the first subtree
(should
(equal
"* TODO \n"
(org-test-with-temp-text "* H1\nH1Body\n** H2\nH2Body"
(org-insert-todo-heading-respect-content)
(buffer-substring-no-properties (line-beginning-position) (point-max)))))
;; In a list, do not create a new item.
(should
(equal
"* TODO \n"
(org-test-with-temp-text "* H\n- an item\n- another one"
(search-forward "an ")
(org-insert-todo-heading-respect-content)
(buffer-substring-no-properties (line-beginning-position) (point-max)))))
;; Use the same TODO keyword as current heading.
(should
(equal
"* TODO \n"
(org-test-with-temp-text "* TODO\n** WAITING\n"
(org-insert-todo-heading-respect-content)
(buffer-substring-no-properties (line-beginning-position) (point-max)))))
(should
(equal
"* TODO \n"
(let ((org-todo-keywords '((sequence "FIRST" "TODO" "|" "DONE"))))
(org-test-with-temp-text "* TODO\n** WAITING\n"
(org-insert-todo-heading-respect-content)
(buffer-substring-no-properties (line-beginning-position) (point-max))))))
;; Pass prefix argument.
(should
(equal
"* FIRST \n"
(let ((org-todo-keywords '((sequence "FIRST" "TODO" "|" "DONE"))))
(org-test-with-temp-text "* TODO\n** WAITING\n"
(org-insert-todo-heading-respect-content '(4))
(buffer-substring-no-properties (line-beginning-position) (point-max))))))
)
(ert-deftest test-org/clone-with-time-shift ()
"Test `org-clone-subtree-with-time-shift'."
;; Raise an error before first heading.
(should-error
(org-test-with-temp-text ""
(org-clone-subtree-with-time-shift 1)))
;; Raise an error on invalid number of clones.
(should-error
(org-test-with-temp-text "* Clone me"
(org-clone-subtree-with-time-shift -1)))
;; Clone non-repeating once.
(should
(equal "\
* H1\n<2015-06-21>
* H1\n<2015-06-23>
"
(org-test-with-temp-text "* H1\n<2015-06-21>"
(org-test-without-dow
(org-test-with-result 'buffer
(org-clone-subtree-with-time-shift 1 "+2d"))))))
;; Clone repeating once.
(should
(equal "\
* H1\n<2015-06-21>
* H1\n<2015-06-23>
* H1\n<2015-06-25 +1w>
"
(org-test-with-temp-text "* H1\n<2015-06-21 +1w>"
(org-test-without-dow
(org-test-with-result 'buffer
(org-clone-subtree-with-time-shift 1 "+2d"))))))
;; Clone repeating once in backward.
(should
(equal "\
* H1\n<2015-06-21>
* H1\n<2015-06-19>
* H1\n<2015-06-17 +1w>
"
(org-test-with-temp-text "* H1\n<2015-06-21 +1w>"
(org-test-without-dow
(org-test-with-result 'buffer
(org-clone-subtree-with-time-shift 1 "-2d"))))))
;; Clone non-repeating zero times.
(should
(equal "\
* H1\n<2015-06-21>
"
(org-test-with-temp-text "* H1\n<2015-06-21>"
(org-test-without-dow
(org-test-with-result 'buffer
(org-clone-subtree-with-time-shift 0 "+2d"))))))
;; Clone repeating "zero" times.
(should
(equal "\
* H1\n<2015-06-21>
* H1\n<2015-06-23 +1w>
"
(org-test-with-temp-text "* H1\n<2015-06-21 +1w>"
(org-test-without-dow
(org-test-with-result 'buffer
(org-clone-subtree-with-time-shift 0 "+2d"))))))
;; Clone with blank SHIFT argument.
(should
(string-prefix-p
"* H <2012-03-29"
(org-test-with-temp-text "* H <2012-03-29 Thu>"
(org-test-without-dow
(org-test-with-result 2
(org-clone-subtree-with-time-shift 1 ""))))))
;; Find time stamps before point. If SHIFT is not specified, ask
;; for a time shift.
(should
(string-prefix-p
"* H <2012-03-30"
(org-test-with-temp-text "* H <2012-03-29 Thu>"
(org-test-without-dow
(org-test-with-result 2
(org-clone-subtree-with-time-shift 1 "+1d"))))))
(should
(string-prefix-p
"* H <2014-03-05"
(org-test-with-temp-text "* H <2014-03-04 Tue>"
(cl-letf (((symbol-function 'read-from-minibuffer)
(lambda (&rest _args) "+1d")))
(org-test-without-dow
(org-test-with-result 2
(org-clone-subtree-with-time-shift 1)))))))
;; Hour shift.
(should
(equal "\
* H1\n<2015-06-21 20:00>
* H1\n<2015-06-21 23:00>
* H1\n<2015-06-22 02:00>
"
(org-test-with-temp-text "* H1\n<2015-06-21 20:00>"
(org-test-without-dow
(org-test-with-result 'buffer
(org-clone-subtree-with-time-shift 2 "+3h"))))))
(should
(equal "\
* H1\n<2015-06-21 20:00>
* H1\n<2015-06-21 18:00>
"
(org-test-with-temp-text "* H1\n<2015-06-21 20:00>"
(org-test-without-dow
(org-test-with-result 'buffer
(org-clone-subtree-with-time-shift 1 "-2h")))))))
;;; Fixed-Width Areas
(ert-deftest test-org/toggle-fixed-width ()
"Test `org-toggle-fixed-width' specifications."
;; No region: Toggle on fixed-width marker in paragraphs.
(should
(equal ": A"
(org-test-with-temp-text "A"
(org-toggle-fixed-width)
(buffer-string))))
;; No region: Toggle off fixed-width markers in fixed-width areas.
(should
(equal "A"
(org-test-with-temp-text ": A"
(org-toggle-fixed-width)
(buffer-string))))
;; No region: Toggle on marker in blank lines after elements or just
;; after a headline.
(should
(equal "* H\n: "
(org-test-with-temp-text "* H\n"
(forward-line)
(org-toggle-fixed-width)
(buffer-string))))
(should
(equal "#+BEGIN_EXAMPLE\nContents\n#+END_EXAMPLE\n: "
(org-test-with-temp-text "#+BEGIN_EXAMPLE\nContents\n#+END_EXAMPLE\n"
(goto-char (point-max))
(org-toggle-fixed-width)
(buffer-string))))
;; No region: Toggle on marker in front of one line elements (e.g.,
;; headlines, clocks)
(should
(equal ": * Headline"
(org-test-with-temp-text "* Headline"
(org-toggle-fixed-width)
(buffer-string))))
(should
(equal ": #+KEYWORD: value"
(org-test-with-temp-text "#+KEYWORD: value"
(org-toggle-fixed-width)
(buffer-string))))
;; No region: error in other situations.
(should-error
(org-test-with-temp-text "#+BEGIN_EXAMPLE\n: A\n#+END_EXAMPLE"
(forward-line)
(org-toggle-fixed-width)
(buffer-string)))
;; No region: Indentation is preserved.
(should
(equal "- A\n : B"
(org-test-with-temp-text "- A\n B"
(forward-line)
(org-toggle-fixed-width)
(buffer-string))))
;; Region: If it contains only fixed-width elements and blank lines,
;; toggle off fixed-width markup.
(should
(equal "A\n\nB"
(org-test-with-temp-text ": A\n\n: B"
(transient-mark-mode 1)
(push-mark (point) t t)
(goto-char (point-max))
(org-toggle-fixed-width)
(buffer-string))))
;; Region: If it contains anything else, toggle on fixed-width but
;; not on fixed-width areas.
(should
(equal ": A\n: \n: B\n: \n: C"
(org-test-with-temp-text "A\n\n: B\n\nC"
(transient-mark-mode 1)
(push-mark (point) t t)
(goto-char (point-max))
(org-toggle-fixed-width)
(buffer-string))))
;; Region: Ignore blank lines at its end, unless it contains only
;; such lines.
(should
(equal ": A\n\n"
(org-test-with-temp-text "A\n\n"
(transient-mark-mode 1)
(push-mark (point) t t)
(goto-char (point-max))
(org-toggle-fixed-width)
(buffer-string))))
(should
(equal ": \n: \n"
(org-test-with-temp-text "\n\n"
(transient-mark-mode 1)
(push-mark (point) t t)
(goto-char (point-max))
(org-toggle-fixed-width)
(buffer-string)))))
(ert-deftest test-org/kill-line ()
"Test `org-kill-line' specifications."
;; At the beginning of a line, kill whole line.
(should
(equal ""
(org-test-with-temp-text "abc"
(org-kill-line)
(buffer-string))))
;; In the middle of a line, kill line until its end.
(should
(equal "a"
(org-test-with-temp-text "abc"
(org-kill-line)
(buffer-string))))
;; Do not kill newline character.
(should
(equal "\n123"
(org-test-with-temp-text "abc\n123"
(org-kill-line)
(buffer-string))))
(should
(equal "a\n123"
(org-test-with-temp-text "abc\n123"
(org-kill-line)
(buffer-string))))
;; When `org-special-ctrl-k' is non-nil and point is at a headline,
;; kill until tags.
(should
(equal "* A :tag:"
(org-test-with-temp-text "* AB :tag:"
(let ((org-special-ctrl-k t)
(org-tags-column 0))
(org-kill-line))
(buffer-string))))
;; If point is on tags, only kill part left until the end of line.
(should
(equal "* A :tag:"
(org-test-with-temp-text "* A :tag:tag2:"
(let ((org-special-ctrl-k t)
(org-tags-column 0))
(org-kill-line))
(buffer-string))))
;; However, if point is at the beginning of the line, kill whole
;; headline.
(should
(equal ""
(org-test-with-temp-text "* AB :tag:"
(let ((org-special-ctrl-k t)
(org-tags-column 0))
(org-kill-line))
(buffer-string))))
;; When `org-ctrl-k-protect-subtree' is non-nil, and point is in
;; invisible text, ask before removing it. When set to `error',
;; throw an error.
(should-error
(org-test-with-temp-text "* H\n** H2\nContents\n* H3"
(org-overview)
(let ((org-special-ctrl-k nil)
(org-ctrl-k-protect-subtree t))
(cl-letf (((symbol-function 'y-or-n-p) 'ignore))
(org-kill-line)))))
(should-error
(org-test-with-temp-text "* H\n** H2\nContents\n* H3"
(org-overview)
(let ((org-special-ctrl-k nil)
(org-ctrl-k-protect-subtree 'error))
(org-kill-line)))))
;;; Headline
(ert-deftest test-org/org-back-to-heading ()
"Test `org-back-to-heading' specifications."
;; On heading already
(org-test-with-temp-text "* Heading"
(org-back-to-heading)
(should (bobp)))
;; Below heading
(org-test-with-temp-text "* Heading
Text"
(org-back-to-heading)
(should (bobp)))
;; At inlinetask
(let ((org-inlinetask-min-level 3))
(org-test-with-temp-text "* Heading
*** Inlinetask "
(org-back-to-heading)
(should (= 11 (point)))))
;; Below inlinetask
(let ((org-inlinetask-min-level 3))
(org-test-with-temp-text "* Heading
*** Inlinetask
Test "
(org-back-to-heading)
;; Not at or inside inlinetask. Move to parent heading.
(should (bobp))))
;; Inside inlinetask
(let ((org-inlinetask-min-level 3))
(org-test-with-temp-text "* Heading
*** Inlinetask
Test
*** END"
(org-back-to-heading)
(should (= 11 (point)))))
;; At END
(let ((org-inlinetask-min-level 3))
(org-test-with-temp-text "* Heading
*** Inlinetask
Test
*** END"
(org-back-to-heading)
(should (= 11 (point))))))
(ert-deftest test-org/up-heading-safe ()
"Test `org-up-heading-safe' specifications."
;; Jump to parent. Simple case.
(org-test-with-temp-text "
* H1
** H2"
(should (= 1 (org-up-heading-safe)))
(should (looking-at-p "^\\* H1")))
;; Do not jump beyond the level 1 heading.
(org-test-with-temp-text "
Text.
* Heading "
(let ((pos (point)))
(should-not (org-up-heading-safe))
(should (looking-at-p "^\\* Heading"))))
;; Jump from inside a heading.
(org-test-with-temp-text "
* H1
** H2
Text "
(should (= 1 (org-up-heading-safe)))
(should (looking-at-p "^\\* H1")))
;; Test inlinetask.
(let ((org-inlinetask-min-level 3))
(org-test-with-temp-text "
** Heading
Text.
*** Inlinetask
Text
*** END"
(should (= 2 (org-up-heading-safe)))
(should (looking-at-p "^\\*\\{2\\} Heading"))))
(let ((org-inlinetask-min-level 3))
(org-test-with-temp-text "
** Heading
Text.
*** Inlinetask"
(should (= 2 (org-up-heading-safe)))
(should (looking-at-p "^\\*\\{2\\} Heading"))))
;; Respect narrowing.
(org-test-with-temp-text "
* H1
** text
** H2"
(save-excursion
(search-backward "** text")
(narrow-to-region (point) (point-max)))
(should-not (org-up-heading-safe))
(should (looking-at-p "^\\*\\* H2"))))
(ert-deftest test-org/goto-sibling ()
"Test `org-goto-sibling' specifications."
(org-test-with-temp-text
"* Parent
** Heading 1
** Heading 2
** Heading 3"
(should (org-goto-sibling))
(should (looking-at-p "^\\*\\* Heading 3"))
(should-not (org-goto-sibling))
(should (org-goto-sibling 'previous))
(should (looking-at-p "^\\*\\* Heading 2"))
(should (org-goto-sibling 'previous))
(should (looking-at-p "^\\*\\* Heading 1"))
(should-not (org-goto-sibling 'previous)))
;; Inside heading.
(org-test-with-temp-text
"* Parent
** Heading 1
** Heading 2
Some text.
** Heading 3"
(should (org-goto-sibling))
(should (looking-at-p "^\\*\\* Heading 3")))
(org-test-with-temp-text
"* Parent
** Heading 1
** Heading 2
Some text.
** Heading 3"
(should (org-goto-sibling 'previous))
(should (looking-at-p "^\\*\\* Heading 1")))
(org-test-with-temp-text
"* Parent
** Heading 2
Some text.
"
(should-not (org-goto-sibling))
(should-not (org-goto-sibling 'previous)))
;; Ignore inlinetasks.
(let ((org-inlinetask-min-level 3))
(org-test-with-temp-text
"* Parent
** Heading 1
** Heading 2
*** Inlinetask 1
test
*** END
*** Inlinetask 2
** Heading 3"
(should (org-goto-sibling))
(should (looking-at-p "^\\*\\* Heading 3"))
(should-not (org-goto-sibling))
(should (org-goto-sibling 'previous))
(should (looking-at-p "^\\*\\* Heading 2"))
(should (org-goto-sibling 'previous))
(should (looking-at-p "^\\*\\* Heading 1"))
(should-not (org-goto-sibling 'previous)))))
(ert-deftest test-org/get-heading ()
"Test `org-get-heading' specifications."
;; Return current heading, even if point is not on it.
(should
(equal "H"
(org-test-with-temp-text "* H"
(org-get-heading))))
(should
(equal "H"
(org-test-with-temp-text "* H\nText"
(org-get-heading))))
;; Without any optional argument, return TODO keyword, priority
;; cookie, COMMENT keyword and tags.
(should
(equal "TODO H"
(org-test-with-temp-text "#+TODO: TODO | DONE\n* TODO H"
(org-get-heading))))
(should
(equal "[#A] H"
(org-test-with-temp-text "* [#A] H"
(org-get-heading))))
(should
(equal "COMMENT H"
(org-test-with-temp-text "* COMMENT H"
(org-get-heading))))
(should
(equal "H :tag:"
(org-test-with-temp-text "* H :tag:"
(org-get-heading))))
;; With NO-TAGS argument, ignore tags.
(should
(equal "TODO H"
(org-test-with-temp-text "#+TODO: TODO | DONE\n* TODO H"
(org-get-heading t))))
(should
(equal "H"
(org-test-with-temp-text "* H :tag:"
(org-get-heading t))))
;; With NO-TODO, ignore TODO keyword.
(should
(equal "H"
(org-test-with-temp-text "#+TODO: TODO | DONE\n* TODO H"
(org-get-heading nil t))))
(should
(equal "H :tag:"
(org-test-with-temp-text "* H :tag:"
(org-get-heading nil t))))
;; TODO keywords are case-sensitive.
(should
(equal "Todo H"
(org-test-with-temp-text "#+TODO: TODO | DONE\n* Todo H"
(org-get-heading nil t))))
;; With NO-PRIORITY, ignore priority.
(should
(equal "H"
(org-test-with-temp-text "* [#A] H"
(org-get-heading nil nil t))))
(should
(equal "H"
(org-test-with-temp-text "* H"
(org-get-heading nil nil t))))
(should
(equal "TODO H"
(org-test-with-temp-text "* TODO [#A] H"
(org-get-heading nil nil t))))
;; With NO-COMMENT, ignore COMMENT keyword.
(should
(equal "H"
(org-test-with-temp-text "* COMMENT H"
(org-get-heading nil nil nil t))))
(should
(equal "H"
(org-test-with-temp-text "* H"
(org-get-heading nil nil nil t))))
(should
(equal "TODO [#A] H"
(org-test-with-temp-text "* TODO [#A] COMMENT H"
(org-get-heading nil nil nil t))))
;; On an empty headline, return value is consistent.
(should (equal "" (org-test-with-temp-text "* " (org-get-heading))))
(should (equal "" (org-test-with-temp-text "* " (org-get-heading t))))
(should (equal "" (org-test-with-temp-text "* " (org-get-heading nil t))))
(should (equal "" (org-test-with-temp-text "* " (org-get-heading nil nil t))))
(should
(equal "" (org-test-with-temp-text "* " (org-get-heading nil nil nil t)))))
(ert-deftest test-org/in-commented-heading-p ()
"Test `org-in-commented-heading-p' specifications."
;; Commented headline.
(should
(org-test-with-temp-text "* COMMENT Headline\nBody"
(goto-char (point-max))
(org-in-commented-heading-p)))
;; Commented ancestor.
(should
(org-test-with-temp-text "* COMMENT Headline\n** Level 2\nBody"
(goto-char (point-max))
(org-in-commented-heading-p)))
;; Comment keyword is case-sensitive.
(should-not
(org-test-with-temp-text "* Comment Headline\nBody"
(goto-char (point-max))
(org-in-commented-heading-p)))
;; Keyword is standalone.
(should-not
(org-test-with-temp-text "* COMMENTHeadline\nBody"
(goto-char (point-max))
(org-in-commented-heading-p)))
;; Optional argument.
(should-not
(org-test-with-temp-text "* COMMENT Headline\n** Level 2\nBody"
(goto-char (point-max))
(org-in-commented-heading-p t))))
(ert-deftest test-org/in-archived-heading-p ()
"Test `org-in-archived-heading-p' specifications."
;; Archived headline.
(should
(org-test-with-temp-text "* Headline :ARCHIVE:\nBody"
(goto-char (point-max))
(org-in-archived-heading-p)))
;; Archived ancestor.
(should
(org-test-with-temp-text "* Headline :ARCHIVE:\n** Level 2\nBody"
(goto-char (point-max))
(org-in-archived-heading-p)))
;; Optional argument.
(should-not
(org-test-with-temp-text "* Headline :ARCHIVE:\n** Level 2\nBody"
(goto-char (point-max))
(org-in-archived-heading-p t)))
;; Archive tag containing ARCHIVE as substring
(should-not
(org-test-with-temp-text "* Headline :NOARCHIVE:\n** Level 2\nBody"
(goto-char (point-max))
(org-in-archived-heading-p))))
(ert-deftest test-org/entry-blocked-p ()
;; Check other dependencies.
(should
(org-test-with-temp-text "* TODO Blocked\n** DONE one\n** TODO two"
(let ((org-enforce-todo-dependencies t)
(org-blocker-hook
'(org-block-todo-from-children-or-siblings-or-parent)))
(org-entry-blocked-p))))
(should-not
(org-test-with-temp-text "* TODO Blocked\n** DONE one\n** DONE two"
(let ((org-enforce-todo-dependencies t)
(org-blocker-hook
'(org-block-todo-from-children-or-siblings-or-parent)))
(org-entry-blocked-p))))
;; Entry without a TODO keyword or with a DONE keyword cannot be
;; blocked.
(should-not
(org-test-with-temp-text "* Blocked\n** TODO one"
(let ((org-enforce-todo-dependencies t)
(org-blocker-hook
'(org-block-todo-from-children-or-siblings-or-parent)))
(org-entry-blocked-p))))
(should-not
(org-test-with-temp-text "* DONE Blocked\n** TODO one"
(let ((org-enforce-todo-dependencies t)
(org-blocker-hook
'(org-block-todo-from-children-or-siblings-or-parent)))
(org-entry-blocked-p))))
;; Follow :ORDERED: specifications.
(should
(org-test-with-temp-text
"* H\n:PROPERTIES:\n:ORDERED: t\n:END:\n** TODO one\n** TODO two"
(let ((org-enforce-todo-dependencies t)
(org-blocker-hook
'(org-block-todo-from-children-or-siblings-or-parent)))
(org-entry-blocked-p))))
(should-not
(org-test-with-temp-text
"* H\n:PROPERTIES:\n:ORDERED: t\n:END:\n** TODO one\n** DONE two"
(let ((org-enforce-todo-dependencies t)
(org-blocker-hook
'(org-block-todo-from-children-or-siblings-or-parent)))
(org-entry-blocked-p)))))
(ert-deftest test-org/get-outline-path ()
"Test `org-get-outline-path' specifications."
;; Top-level headlines have no outline path.
(should-not
(org-test-with-temp-text "* H"
(org-get-outline-path)))
;; Otherwise, outline path is the path leading to the headline.
(should
(equal '("H")
(org-test-with-temp-text "* H\n** S"
(org-get-outline-path))))
;; Find path even when point is not on a headline.
(should
(equal '("H")
(org-test-with-temp-text "* H\n** S\nText"
(org-get-outline-path))))
;; TODO keywords, tags and statistics cookies are ignored.
(should
(equal '("H")
(org-test-with-temp-text "* TODO H [0/1] :tag:\n** S"
(org-get-outline-path))))
;; Links are replaced with their description or their path.
(should
(equal '("Org")
(org-test-with-temp-text
"* [[https://orgmode.org][Org]]\n** S"
(org-get-outline-path))))
(should
(equal '("https://orgmode.org")
(org-test-with-temp-text
"* [[https://orgmode.org]]\n** S"
(org-get-outline-path))))
;; When WITH-SELF is non-nil, include current heading.
(should
(equal '("H")
(org-test-with-temp-text "* H"
(org-get-outline-path t))))
(should
(equal '("H" "S")
(org-test-with-temp-text "* H\n** S\nText"
(org-get-outline-path t))))
;; Using cache is transparent to the user.
(should
(equal '("H")
(org-test-with-temp-text "* H\n** S"
(setq org-outline-path-cache nil)
(org-get-outline-path nil t))))
;; Do not corrupt cache when finding outline path in distant part of
;; the buffer.
(should
(equal '("H2")
(org-test-with-temp-text "* H\n** S\n* H2\n** S2"
(setq org-outline-path-cache nil)
(org-get-outline-path nil t)
(search-forward "S2")
(org-get-outline-path nil t))))
;; Do not choke on empty headlines.
(should
(org-test-with-temp-text "* H\n** "
(org-get-outline-path)))
(should
(org-test-with-temp-text "* \n** H"
(org-get-outline-path)))
;; Remove COMMENTED keywords.
(should
(equal '("This" "is")
(org-test-with-temp-text
"* COMMENT This
** COMMENT is
*** test
"
(org-get-outline-path)))))
(ert-deftest test-org/format-outline-path ()
"Test `org-format-outline-path' specifications."
(should
(string= (org-format-outline-path (list "one" "two" "three"))
"one/two/three"))
;; Empty path.
(should
(string= (org-format-outline-path '())
""))
(should
(string= (org-format-outline-path '(nil))
""))
;; Empty path and prefix.
(should
(string= (org-format-outline-path '() nil ">>")
">>"))
;; Trailing whitespace in headings.
(should
(string= (org-format-outline-path (list "one\t" "tw o " "three "))
"one/tw o/three"))
;; Non-default prefix and separators.
(should
(string= (org-format-outline-path (list "one" "two" "three") nil ">>" "|")
">>|one|two|three"))
;; Truncate.
(should
(string= (org-format-outline-path (list "one" "two" "three" "four") 10)
"one/two/.."))
;; Give a very narrow width.
(should
(string= (org-format-outline-path (list "one" "two" "three" "four") 2)
"on"))
;; Give a prefix that extends beyond the width.
(should
(string= (org-format-outline-path (list "one" "two" "three" "four") 10
">>>>>>>>>>")
">>>>>>>>..")))
(ert-deftest test-org/org-find-olp ()
"Test `org-find-olp' specifications."
(org-test-with-temp-text
"
* Headline
** COMMENT headline2
** TODO headline3
*** [#A] headline4 :tags:
** [#A]headline5
** [0%] headline6
** headline7 [100%]
** headline8 [1/5] :some:more:tags:
* Test
"
(should (org-find-olp '("Headline") t))
(should-error (org-find-olp '("Headline" "Test") t))
(should-error (org-find-olp '("Headlinealksjd") t))
(should (org-find-olp '("Headline" "headline2") t))
(should (org-find-olp '("Headline" "headline3") t))
(should (org-find-olp '("Headline" "headline3" "headline4") t))
(should-error (org-find-olp '("Headline" "headline5") t))
(should (org-find-olp '("Headline" "headline6") t))
(should (org-find-olp '("Headline" "headline7") t))
(should (org-find-olp '("Headline" "headline8") t))))
(ert-deftest test-org/map-entries ()
"Test `org-map-entries' specifications."
(dolist (org-element-use-cache '(t nil))
;; Full match.
(should
(equal '(1 11)
(org-test-with-temp-text "* Level 1\n** Level 2"
(org-map-entries #'point))))
;; Level match.
(should
(equal '(1)
(org-test-with-temp-text "* Level 1\n** Level 2"
(let (org-odd-levels-only) (org-map-entries #'point "LEVEL=1")))))
(should
(equal '(11)
(org-test-with-temp-text "* Level 1\n** Level 2"
(let (org-odd-levels-only) (org-map-entries #'point "LEVEL>1")))))
;; Category match.
(should
(equal '(59)
(org-test-with-temp-text "
#+CATEGORY: foo
* H1
:PROPERTIES:
:CATEGORY: bar
:END:
* H2"
(org-map-entries #'point "CATEGORY=\"foo\""))))
;; Todo match.
(should
(equal '(6)
(org-test-with-temp-text "* H1\n* TODO H2\n* DONE H3"
(org-map-entries #'point "TODO=\"TODO\""))))
;; Tag match.
(should
(equal '(11)
(org-test-with-temp-text "* H1 :no:\n* H2 :yes:"
(org-map-entries #'point "yes"))))
(should
(equal '(14)
(org-test-with-temp-text "* H1 :yes:a:\n* H2 :yes:b:"
(org-map-entries #'point "+yes-a"))))
(should
(equal '(11 23)
(org-test-with-temp-text "* H1 :no:\n* H2 :yes1:\n* H3 :yes2:"
(org-map-entries #'point "{yes.?}"))))
;; Priority match.
(should
(equal '(1)
(org-test-with-temp-text "* [#A] H1\n* [#B] H2"
(org-map-entries #'point "PRIORITY=\"A\""))))
;; Negative priority match.
(should
(equal '(11)
(org-test-with-temp-text "* [#A] H1\n* [#B] H2"
(org-map-entries #'point "PRIORITY/=\"A\""))))
;; Date match.
(should
(equal '(36)
(org-test-with-temp-text "
* H1
SCHEDULED: <2012-03-29 thu.>
* H2
SCHEDULED: <2014-03-04 tue.>"
(org-map-entries #'point "SCHEDULED=\"<2014-03-04 tue.>\""))))
(should
(equal '(2)
(org-test-with-temp-text "
* H1
SCHEDULED: <2012-03-29 thu.>
* H2
SCHEDULED: <2014-03-04 tue.>"
(org-map-entries #'point "SCHEDULED<\"<2013-01-01>\""))))
;; Regular property match.
(should
(equal '(2)
(org-test-with-temp-text "
* H1
:PROPERTIES:
:TEST: 1
:END:
* H2
:PROPERTIES:
:TEST: 2
:END:"
(org-map-entries #'point "TEST=1"))))
;; Regular negative property match.
(should
(equal '(35 68)
(org-test-with-temp-text "
* H1
:PROPERTIES:
:TEST: 1
:END:
* H2
:PROPERTIES:
:TEST: 2
:END:
* H3"
(org-map-entries #'point "TEST!=1"))))
;; Starred negative property match.
(should
(equal '(35)
(org-test-with-temp-text "
* H1
:PROPERTIES:
:TEST: 1
:END:
* H2
:PROPERTIES:
:TEST: 2
:END:
* H3"
(org-map-entries #'point "TEST!=*1"))))
;; Property matches on names containing quoted characters.
(org-test-with-temp-text
"
* H1 :BAR:
:PROPERTIES:
:TEST-FOO: 1
:END:
* H2 :FOO:
:PROPERTIES:
:TEST-FOO: 2
:END:
* H3 :BAR:
:PROPERTIES:
:-FOO: 1
:END:
* H4 :FOO:
:PROPERTIES:
:-FOO: 2
:END:
* H5 :TEST:"
(should (equal '(2) (org-map-entries #'point "TEST\\-FOO!=*0-FOO")))
(should (equal '(2) (org-map-entries #'point "-FOO+TEST\\-FOO!=*0")))
(should (equal '(88) (org-map-entries #'point "\\-FOO!=*0-FOO")))
(should (equal '(88) (org-map-entries #'point "-FOO+\\-FOO!=*0")))
(should (equal '(88) (org-map-entries #'point "-TEST-FOO-TEST\\-FOO=1"))))
;; Multiple criteria.
(should
(equal '(23)
(org-test-with-temp-text "* H1 :no:\n** H2 :yes:\n* H3 :yes:"
(let (org-odd-levels-only
(org-use-tag-inheritance nil))
(org-map-entries #'point "yes+LEVEL=1")))))
;; "or" criteria.
(should
(equal '(12 24)
(org-test-with-temp-text "* H1 :yes:\n** H2 :yes:\n** H3 :no:"
(let (org-odd-levels-only)
(org-map-entries #'point "LEVEL=2|no")))))
(should
(equal '(1 12)
(org-test-with-temp-text "* H1 :yes:\n* H2 :no:\n* H3 :maybe:"
(let (org-odd-levels-only)
(org-map-entries #'point "yes|no")))))
;; "and" criteria.
(should
(equal '(22)
(org-test-with-temp-text "* H1 :yes:\n* H2 :no:\n* H3 :yes:no:"
(let (org-odd-levels-only)
(org-map-entries #'point "yes&no")))))
;; Setting `org-map-continue-from'
(should
(string= ""
(org-test-with-temp-text "* H1\n* H2\n* H3n* H4"
(org-map-entries
(lambda ()
(org-cut-subtree)
(setq org-map-continue-from (point))))
(buffer-string))))
(should
(string= "* H1\n* H2\n* H3\n"
(org-test-with-temp-text "* H1\n* H2\n* H3\n* H4"
(org-map-entries
(lambda ()
(when (string= "H4"
(org-element-property
:raw-value (org-element-at-point)))
(org-cut-subtree)
(setq org-map-continue-from
(org-element-property
:begin (org-element-at-point))))))
(buffer-string))))
;; Move point.
(should
(= 1
(org-test-with-temp-text "* H1\n** H1.1\n** H1.2\n"
(let (acc)
(org-map-entries
(lambda ()
(push (org-element-property :title (org-element-at-point)) acc)
(setq org-map-continue-from
(org-element-property :end (org-element-at-point)))))
(length acc)))))
(should
(= 2
(org-test-with-temp-text "* H1\n** H1.1\n** H1.2\n"
(let (acc)
(org-map-entries
(lambda ()
(push (org-element-property :title (org-element-at-point)) acc)
(setq org-map-continue-from
(line-end-position 2))))
(length acc)))))
;; Modifications inside indirect buffer.
(should
(= 3
(org-test-with-temp-text "* H1\n** H1.1\n** H1.2\n"
(with-current-buffer (org-get-indirect-buffer)
(let ((acc 0))
(org-map-entries
(lambda ()
(cl-incf acc)
(beginning-of-line 2)
(insert "test\n")
(beginning-of-line -1)))
acc)))))
;; Removing heading being processed.
(should
(equal
"Some text
Some text
Some more text
Let’s stop here
"
(org-test-with-temp-text
"* Heading 1
Some text
** Heading 1.1
Some text
* Heading 2
Some more text
** Heading 2.1
Let’s stop here
"
(org-map-entries
(lambda ()
(delete-region (point) (line-beginning-position 2))
(setq org-map-continue-from (point))))
(buffer-string))))))
(ert-deftest test-org/edit-headline ()
"Test `org-edit-headline' specifications."
(should
(equal "* B"
(org-test-with-temp-text "* A"
(org-edit-headline "B")
(buffer-string))))
;; Handle empty headings.
(should
(equal "* "
(org-test-with-temp-text "* A"
(org-edit-headline "")
(buffer-string))))
(should
(equal "* A"
(org-test-with-temp-text "* "
(org-edit-headline "A")
(buffer-string))))
;; Handle TODO keywords and priority cookies.
(should
(equal "* TODO B"
(org-test-with-temp-text "* TODO A"
(org-edit-headline "B")
(buffer-string))))
(should
(equal "* [#A] B"
(org-test-with-temp-text "* [#A] A"
(org-edit-headline "B")
(buffer-string))))
(should
(equal "* TODO [#A] B"
(org-test-with-temp-text "* TODO [#A] A"
(org-edit-headline "B")
(buffer-string))))
;; Handle tags.
(equal "* B :tag:"
(org-test-with-temp-text "* A :tag:"
(let ((org-tags-column 4)) (org-edit-headline "B"))
(buffer-string))))
;;; Keywords
(ert-deftest test-org/set-regexps-and-options ()
"Test `org-set-regexps-and-options' specifications."
;; TAGS keyword.
(should
(equal '(("A"))
(let ((org-tag-alist '(("A")))
(org-tag-persistent-alist nil))
(org-test-with-temp-text ""
(org-mode-restart)
org-current-tag-alist))))
(should
(equal '(("B"))
(let ((org-tag-alist '(("A")))
(org-tag-persistent-alist nil))
(org-test-with-temp-text "#+TAGS: B"
(org-mode-restart)
org-current-tag-alist))))
(should
(equal '(("C") ("B"))
(let ((org-tag-alist '(("A")))
(org-tag-persistent-alist '(("C"))))
(org-test-with-temp-text "#+TAGS: B"
(org-mode-restart)
org-current-tag-alist))))
(should
(equal '(("B"))
(let ((org-tag-alist '(("A")))
(org-tag-persistent-alist '(("C"))))
(org-test-with-temp-text "#+STARTUP: noptag\n#+TAGS: B"
(org-mode-restart)
org-current-tag-alist))))
(should
(equal '(("A" . ?a) ("B") ("C"))
(let ((org-tag-persistent-alist nil))
(org-test-with-temp-text "#+TAGS: A(a) B C"
(org-mode-restart)
org-current-tag-alist))))
(should
(equal '(("A") (:newline) ("B"))
(let ((org-tag-persistent-alist nil))
(org-test-with-temp-text "#+TAGS: A\n#+TAGS: B"
(org-mode-restart)
org-current-tag-alist))))
(should
(equal '((:startgroup) ("A") ("B") (:endgroup) ("C"))
(let ((org-tag-persistent-alist nil))
(org-test-with-temp-text "#+TAGS: { A B } C"
(org-mode-restart)
org-current-tag-alist))))
(should
(equal '((:startgroup) ("A") (:grouptags) ("B") ("C") (:endgroup))
(let ((org-tag-persistent-alist nil))
(org-test-with-temp-text "#+TAGS: { A : B C }"
(org-mode-restart)
org-current-tag-alist))))
(should
(equal '(("A" "B" "C"))
(let ((org-tag-persistent-alist nil))
(org-test-with-temp-text "#+TAGS: { A : B C }"
(org-mode-restart)
org-tag-groups-alist))))
(should
(equal '((:startgrouptag) ("A") (:grouptags) ("B") ("C") (:endgrouptag))
(let ((org-tag-persistent-alist nil))
(org-test-with-temp-text "#+TAGS: [ A : B C ]"
(org-mode-restart)
org-current-tag-alist))))
(should
(equal '(("A" "B" "C"))
(let ((org-tag-persistent-alist nil))
(org-test-with-temp-text "#+TAGS: [ A : B C ]"
(org-mode-restart)
org-tag-groups-alist))))
(should-not
(let ((org-tag-alist '(("A"))))
(org-test-with-temp-text "#+TAGS:"
(org-mode-restart)
org-current-tag-alist)))
;; FILETAGS keyword.
(should
(equal '("A" "B" "C")
(org-test-with-temp-text "#+FILETAGS: :A:B:C:"
(org-mode-restart)
org-file-tags)))
;; PROPERTY keyword. Property names are case-insensitive.
(should
(equal "foo=1"
(org-test-with-temp-text "#+PROPERTY: var foo=1"
(org-mode-restart)
(cdr (assoc "var" org-keyword-properties)))))
(should
(equal
"foo=1 bar=2"
(org-test-with-temp-text "#+PROPERTY: var foo=1\n#+PROPERTY: var+ bar=2"
(org-mode-restart)
(cdr (assoc "var" org-keyword-properties)))))
(should
(equal
"foo=1 bar=2"
(org-test-with-temp-text "#+PROPERTY: var foo=1\n#+PROPERTY: VAR+ bar=2"
(org-mode-restart)
(cdr (assoc "var" org-keyword-properties)))))
;; ARCHIVE keyword.
(should
(equal "%s_done::"
(org-test-with-temp-text "#+ARCHIVE: %s_done::"
(org-mode-restart)
org-archive-location)))
;; CATEGORY keyword.
(should
(eq 'test
(org-test-with-temp-text "#+CATEGORY: test"
(org-mode-restart)
org-category)))
(should
(equal "test"
(org-test-with-temp-text "#+CATEGORY: test"
(org-mode-restart)
(cdr (assoc "CATEGORY" org-keyword-properties)))))
;; COLUMNS keyword.
(should
(equal "%25ITEM %TAGS %PRIORITY %TODO"
(org-test-with-temp-text "#+COLUMNS: %25ITEM %TAGS %PRIORITY %TODO"
(org-mode-restart)
org-columns-default-format)))
;; CONSTANTS keyword. Constants names are case sensitive.
(should
(equal '("299792458." "3.14")
(org-test-with-temp-text "#+CONSTANTS: c=299792458. pi=3.14"
(org-mode-restart)
(mapcar
(lambda (n) (cdr (assoc n org-table-formula-constants-local)))
'("c" "pi")))))
(should
(equal "3.14"
(org-test-with-temp-text "#+CONSTANTS: pi=22/7 pi=3.14"
(org-mode-restart)
(cdr (assoc "pi" org-table-formula-constants-local)))))
(should
(equal "22/7"
(org-test-with-temp-text "#+CONSTANTS: PI=22/7 pi=3.14"
(org-mode-restart)
(cdr (assoc "PI" org-table-formula-constants-local)))))
;; LINK keyword.
(should
(equal
'("url1" "url2")
(org-test-with-temp-text "#+LINK: a url1\n#+LINK: b url2"
(org-mode-restart)
(mapcar (lambda (abbrev) (cdr (assoc abbrev org-link-abbrev-alist-local)))
'("a" "b")))))
;; PRIORITIES keyword. Incomplete priorities sets are ignored.
(should
(equal
'(?X ?Z ?Y)
(org-test-with-temp-text "#+PRIORITIES: X Z Y"
(org-mode-restart)
(list org-priority-highest org-priority-lowest org-priority-default))))
(should
(equal
'(?A ?C ?B)
(org-test-with-temp-text "#+PRIORITIES: X Z"
(org-mode-restart)
(list org-priority-highest org-priority-lowest org-priority-default))))
;; STARTUP keyword.
(should
(equal '(t t)
(org-test-with-temp-text "#+STARTUP: fold odd"
(org-mode-restart)
(list org-startup-folded org-odd-levels-only))))
;; TODO keywords.
(should
(equal '(("A" "B") ("C"))
(org-test-with-temp-text "#+TODO: A B | C"
(org-mode-restart)
(list org-not-done-keywords org-done-keywords))))
(should
(equal '(("A" "C") ("B" "D"))
(org-test-with-temp-text "#+TODO: A | B\n#+TODO: C | D"
(org-mode-restart)
(list org-not-done-keywords org-done-keywords))))
(should
(equal '(("A" "B") ("C"))
(org-test-with-temp-text "#+TYP_TODO: A B | C"
(org-mode-restart)
(list org-not-done-keywords org-done-keywords))))
(should
(equal '((:startgroup) ("A" . ?a) (:endgroup))
(org-test-with-temp-text "#+TODO: A(a)"
(org-mode-restart)
org-todo-key-alist)))
(should
(equal '(("D" note nil) ("C" time nil) ("B" note time))
(org-test-with-temp-text "#+TODO: A(a) B(b@/!) | C(c!) D(d@)"
(org-mode-restart)
org-todo-log-states)))
;; Enter SETUPFILE keyword.
(should
(equal "1"
(org-test-with-temp-text
(format "#+SETUPFILE: \"%s/examples/setupfile.org\"" org-test-dir)
(org-mode-restart)
(cdr (assoc "a" org-keyword-properties))))))
(ert-deftest test-org/collect-keywords ()
"Test `org-collect-keywords'."
(should-not
(org-test-with-temp-text "#+begin_example\n#+foo: bar\n#+end_example"
(org-collect-keywords '("FOO")))))
;;; Links
;;;; Coderefs
(ert-deftest test-org/coderef ()
"Test coderef links specifications."
(should
(org-test-with-temp-text "
#+BEGIN_SRC emacs-lisp
\(+ 1 1) (ref:sc)
#+END_SRC
\[[(sc)]]"
(org-open-at-point)
(looking-at "(ref:sc)")))
;; Find coderef even with alternate label format.
(should
(org-test-with-temp-text "
#+BEGIN_SRC emacs-lisp -l \"{ref:%s}\"
\(+ 1 1) {ref:sc}
#+END_SRC
\[[(sc)]]"
(org-open-at-point)
(looking-at "{ref:sc}"))))
;;;; Custom ID
(ert-deftest test-org/custom-id ()
"Test custom ID links specifications."
(should
(org-test-with-temp-text
"* H1\n:PROPERTIES:\n:CUSTOM_ID: custom\n:END:\n* H2\n[[#custom]]"
(org-open-at-point)
(looking-at-p "\\* H1")))
;; Handle escape characters.
(should
(org-test-with-temp-text
"* H1\n:PROPERTIES:\n:CUSTOM_ID: [%]\n:END:\n* H2\n[[#\\[%\\]]]"
(org-open-at-point)
(looking-at-p "\\* H1")))
;; Throw an error on false positives.
(should-error
(org-test-with-temp-text
"* H1\n:DRAWER:\n:CUSTOM_ID: custom\n:END:\n* H2\n[[#custom]]"
(org-open-at-point)
(looking-at-p "\\* H1"))))
;;;; Fuzzy Links
;; Fuzzy links [[text]] encompass links to a target (<>), to
;; a named element (#+name: text) and to headlines (* Text).
(ert-deftest test-org/fuzzy-links ()
"Test fuzzy links specifications."
;; Fuzzy link goes in priority to a matching target.
(should
(org-test-with-temp-text
"#+NAME: Test\n|a|b|\n<>\n* Test\n[[Test]]"
(let ((org-link-search-must-match-exact-headline nil)) (org-open-at-point))
(looking-at "<>")))
;; Then fuzzy link points to an element with a given name.
(should
(org-test-with-temp-text "Test\n#+NAME: Test\n|a|b|\n* Test\n[[Test]]"
(let ((org-link-search-must-match-exact-headline nil)) (org-open-at-point))
(looking-at "#\\+NAME: Test")))
;; A target still lead to a matching headline otherwise.
(should
(org-test-with-temp-text "* Head1\n* Head2\n*Head3\n[[Head2]]"
(let ((org-link-search-must-match-exact-headline nil)) (org-open-at-point))
(looking-at "\\* Head2")))
;; With a leading star in link, enforce heading match.
(should
(org-test-with-temp-text "* Test\n<>\n[[*Test]]"
(let ((org-link-search-must-match-exact-headline nil)) (org-open-at-point))
(looking-at "\\* Test")))
;; With a leading star in link, enforce exact heading match, even
;; with `org-link-search-must-match-exact-headline' set to nil.
(should-error
(org-test-with-temp-text "* Test 1\nFoo Bar\n[[*Test]]"
(let ((org-link-search-must-match-exact-headline nil))
(org-open-at-point))))
;; Handle non-nil `org-link-search-must-match-exact-headline'.
(should
(org-test-with-temp-text "* Test\nFoo Bar\n[[Test]]"
(let ((org-link-search-must-match-exact-headline t)) (org-open-at-point))
(looking-at "\\* Test")))
(should
(org-test-with-temp-text "* Test\nFoo Bar\n[[*Test]]"
(let ((org-link-search-must-match-exact-headline t)) (org-open-at-point))
(looking-at "\\* Test")))
;; Heading match should not care about spaces, cookies, TODO
;; keywords, priorities, and tags. However, TODO keywords are
;; case-sensitive.
(should
(let ((first-line
"** TODO [#A] [/] Test [1/2] [33%] 1 \t 2 [%] :work:urgent: "))
(org-test-with-temp-text
(concat first-line "\nFoo Bar\n[[*Test 1 2]]")
(let ((org-link-search-must-match-exact-headline nil)
(org-todo-regexp "TODO"))
(org-open-at-point))
(looking-at (regexp-quote first-line)))))
(should-error
(org-test-with-temp-text "** todo Test 1 2\nFoo Bar\n[[*Test 1 2]]"
(let ((org-link-search-must-match-exact-headline nil)
(org-todo-regexp "TODO"))
(org-open-at-point))))
;; Heading match should still be exact.
(should-error
(org-test-with-temp-text "
** TODO [#A] [/] Test [1/2] [33%] 1 \t 2 [%] :work:urgent:
Foo Bar
[[*Test 1]]"
(let ((org-link-search-must-match-exact-headline nil)
(org-todo-regexp "TODO"))
(org-open-at-point))))
(should
(org-test-with-temp-text "* Test 1 2 3\n** Test 1 2\n[[*Test 1 2]]"
(let ((org-link-search-must-match-exact-headline nil)
(org-todo-regexp "TODO"))
(org-open-at-point))
(looking-at-p (regexp-quote "** Test 1 2"))))
;; Heading match ignores COMMENT keyword.
(should
(org-test-with-temp-text "[[*Test]]\n* COMMENT Test"
(org-open-at-point)
(looking-at "\\* COMMENT Test")))
(should
(org-test-with-temp-text "[[*Test]]\n* TODO COMMENT Test"
(org-open-at-point)
(looking-at "\\* TODO COMMENT Test")))
;; Correctly un-escape fuzzy links.
(should
(org-test-with-temp-text "* [foo]\n[[*\\[foo\\]][With escaped characters]]"
(org-open-at-point)
(bobp)))
;; Match search strings containing newline characters, including
;; blank lines.
(should
(org-test-with-temp-text-in-file "Paragraph\n\nline1\nline2\n\n"
(let ((file (buffer-file-name)))
(goto-char (point-max))
(insert (format "[[file:%s::line1 line2]]" file))
(beginning-of-line)
(let ((org-link-search-must-match-exact-headline nil))
(org-open-at-point 0))
(looking-at-p "line1"))))
(should
(org-test-with-temp-text-in-file "Paragraph\n\nline1\n\nline2\n\n"
(let ((file (buffer-file-name)))
(goto-char (point-max))
(insert (format "[[file:%s::line1 line2]]" file))
(beginning-of-line)
(let ((org-link-search-must-match-exact-headline nil))
(org-open-at-point 0))
(looking-at-p "line1")))))
;;;; Open at point
(ert-deftest test-org/open-at-point/keyword ()
"Does `org-open-at-point' open link in a keyword line?"
(should
(org-test-with-temp-text
"<>\n#+KEYWORD: [[top]]"
(org-open-at-point) t))
(should
(org-test-with-temp-text
"* H\n<>\n#+KEYWORD: [[top]]"
(org-open-at-point) t)))
(ert-deftest test-org/open-at-point/property ()
"Does `org-open-at-point' open link in property drawer?"
(should
(org-test-with-temp-text
"* Headline
:PROPERTIES:
:URL: [[*Headline]]
:END:"
(org-open-at-point) t)))
(ert-deftest test-org/open-at-point/comment ()
"Does `org-open-at-point' open link in a commented line?"
(should
(org-test-with-temp-text
"<>\n# [[top]]"
(org-open-at-point) t))
(should
(org-test-with-temp-text
"* H\n<>\n# [[top]]"
(org-open-at-point) t)))
(ert-deftest test-org/open-at-point/inline-image ()
"Test `org-open-at-point' on nested links."
(should
(org-test-with-temp-text "<>\n[[top][file:unicorn.jpg]]"
(org-open-at-point)
(bobp))))
(ert-deftest test-org/open-at-point/radio-target ()
"Test `org-open-at-point' on radio targets."
(should
(org-test-with-temp-text "<<>> target"
(org-update-radio-target-regexp)
(org-open-at-point)
(org-element-type-p (org-element-context) 'radio-target))))
(ert-deftest test-org/open-at-point/radio-target-shadowed ()
"Test `org-open-at-point' on shadowed radio targets."
(should
(org-test-with-temp-text
"<<>> <<>> target shadowed"
(org-update-radio-target-regexp)
(org-open-at-point)
(string=
(org-element-property :value
(org-element-radio-target-parser))
"target shadowed"))))
(ert-deftest test-org/open-at-point/tag ()
"Test `org-open-at-point' on tags."
(should
(org-test-with-temp-text "* H :tag:"
(catch :result
(cl-letf (((symbol-function 'org-tags-view)
(lambda (&rest _args) (throw :result t))))
(org-open-at-point)
nil))))
(should-not
(org-test-with-temp-text-in-file "* H :tag:"
(catch :result
(cl-letf (((symbol-function 'org-tags-view)
(lambda (&rest _args) (throw :result t))))
;; When point isn't on a tag it's going to try other things,
;; possibly trying to open attachments which will return an
;; error if there isn't an attachment. Suppress that error.
(ignore-errors
(org-open-at-point))
nil)))))
;;; Node Properties
(ert-deftest test-org/accumulated-properties-in-drawers ()
"Ensure properties accumulate in subtree drawers."
(org-test-at-id "75282ba2-f77a-4309-a970-e87c149fe125"
(org-babel-next-src-block)
(should (equal '(2 1) (org-babel-execute-src-block)))))
(ert-deftest test-org/custom-properties ()
"Test custom properties specifications."
;; Standard test.
(should
(let ((org-custom-properties '("FOO")))
(org-test-with-temp-text "* H\n:PROPERTIES:\n:FOO: val\n:END:\n"
(org-toggle-custom-properties-visibility)
(org-invisible-p2))))
;; Properties are case-insensitive.
(should
(let ((org-custom-properties '("FOO")))
(org-test-with-temp-text "* H\n:PROPERTIES:\n:foo: val\n:END:\n"
(org-toggle-custom-properties-visibility)
(org-invisible-p2))))
(should
(let ((org-custom-properties '("foo")))
(org-test-with-temp-text "* H\n:PROPERTIES:\n:FOO: val\n:END:\n"
(org-toggle-custom-properties-visibility)
(org-invisible-p2))))
;; Multiple custom properties in the same drawer.
(should
(let ((org-custom-properties '("FOO" "BAR")))
(org-test-with-temp-text
"* H\n:PROPERTIES:\n:FOO: val\n:P: 1\n:BAR: baz\n:END:\n"
(org-fold-reveal)
(org-toggle-custom-properties-visibility)
(and (org-invisible-p2)
(not (progn (forward-line) (org-invisible-p2)))
(progn (forward-line) (org-invisible-p2))))))
;; Hide custom properties with an empty value.
(should
(let ((org-custom-properties '("FOO")))
(org-test-with-temp-text "* H\n:PROPERTIES:\n:FOO:\n:END:\n"
(org-toggle-custom-properties-visibility)
(org-invisible-p2))))
;; Do not hide fake properties.
(should-not
(let ((org-custom-properties '("FOO")))
(org-test-with-temp-text ":FOO: val\n"
(org-toggle-custom-properties-visibility)
(org-invisible-p2))))
(should-not
(let ((org-custom-properties '("A")))
(org-test-with-temp-text
"* H\n:PROPERTIES:\n:A: 1\n:END:\n\n:PROPERTIES:\n:A: 2\n:END:"
(org-fold-reveal)
(org-toggle-custom-properties-visibility)
(org-invisible-p2)))))
;;; Mark Region
(ert-deftest test-org/mark-element ()
"Test `org-mark-element' specifications."
;; Mark beginning and end of element.
(should
(equal '(t t)
(org-test-with-temp-text "Paragraph"
(org-mark-element)
(list (bobp) (= (mark) (point-max))))))
(should
(equal '(t t)
(org-test-with-temp-text "P1\n\nParagraph\n\nP2"
(org-mark-element)
(list (looking-at "Paragraph")
(org-with-point-at (mark) (looking-at "P2"))))))
;; Do not set mark past (point-max).
(should
(org-test-with-temp-text "Paragraph"
(narrow-to-region 2 6)
(org-mark-element)
(= 6 (mark)))))
(ert-deftest test-org/mark-subtree ()
"Test `org-mark-subtree' specifications."
;; Error when point is before first headline.
(should-error
(org-test-with-temp-text "Paragraph\n* Headline\nBody"
(progn (transient-mark-mode 1)
(org-mark-subtree))))
;; Without argument, mark current subtree.
(should
(equal
'(12 32)
(org-test-with-temp-text "* Headline\n** Sub-headline\nBody"
(progn (transient-mark-mode 1)
(forward-line 2)
(org-mark-subtree)
(list (region-beginning) (region-end))))))
;; With an argument, move ARG up.
(should
(equal
'(1 32)
(org-test-with-temp-text "* Headline\n** Sub-headline\nBody"
(progn (transient-mark-mode 1)
(forward-line 2)
(org-mark-subtree 1)
(list (region-beginning) (region-end))))))
;; Do not get fooled by inlinetasks.
(when (featurep 'org-inlinetask)
(should
(= 1
(org-test-with-temp-text "* Headline\n*************** Task\nContents"
(progn (transient-mark-mode 1)
(forward-line 1)
(let ((org-inlinetask-min-level 15)) (org-mark-subtree))
(region-beginning)))))))
;;; Miscellaneous
(ert-deftest test-org/sort-entries ()
"Test `org-sort-entries'."
;; Sort alphabetically.
(should
(equal "\n* abc\n* def\n* xyz\n"
(org-test-with-temp-text "\n* def\n* xyz\n* abc\n"
(org-sort-entries nil ?a)
(buffer-string))))
(should
(equal "\n* xyz\n* def\n* abc\n"
(org-test-with-temp-text "\n* def\n* xyz\n* abc\n"
(org-sort-entries nil ?A)
(buffer-string))))
(should
(equal "\n* \n* klm\n* xyz\n"
(org-test-with-temp-text "\n* xyz\n* \n* klm\n"
(org-sort-entries nil ?a)
(buffer-string))))
;; Sort numerically.
(should
(equal "\n* 1\n* 2\n* 10\n"
(org-test-with-temp-text "\n* 10\n* 1\n* 2\n"
(org-sort-entries nil ?n)
(buffer-string))))
(should
(equal "\n* 10\n* 2\n* 1\n"
(org-test-with-temp-text "\n* 10\n* 1\n* 2\n"
(org-sort-entries nil ?N)
(buffer-string))))
(should
(equal "\n* \n* 1\n* 2\n"
(org-test-with-temp-text "\n* 1\n* \n* 2\n"
(org-sort-entries nil ?n)
(buffer-string))))
;; Sort by custom function.
(should
(equal "\n* b\n* aa\n* ccc\n"
(org-test-with-temp-text "\n* ccc\n* b\n* aa\n"
(org-sort-entries nil ?f
(lambda ()
(length (buffer-substring (point-at-bol)
(point-at-eol))))
#'<)
(buffer-string))))
(should
(equal "\n* ccc\n* aa\n* b\n"
(org-test-with-temp-text "\n* ccc\n* b\n* aa\n"
(org-sort-entries nil ?F
(lambda ()
(length (buffer-substring (point-at-bol)
(point-at-eol))))
#'<)
(buffer-string))))
;; Sort by TODO keyword.
(should
(equal "\n* TODO h1\n* TODO h3\n* DONE h2\n"
(org-test-with-temp-text
"\n* TODO h1\n* DONE h2\n* TODO h3\n"
(org-sort-entries nil ?o)
(buffer-string))))
(should
(equal "\n* DONE h2\n* TODO h1\n* TODO h3\n"
(org-test-with-temp-text
"\n* TODO h1\n* DONE h2\n* TODO h3\n"
(org-sort-entries nil ?O)
(buffer-string))))
;; Sort by priority.
(should
(equal "\n* [#A] h2\n* [#B] h3\n* [#C] h1\n"
(org-test-with-temp-text
"\n* [#C] h1\n* [#A] h2\n* [#B] h3\n"
(org-sort-entries nil ?p)
(buffer-string))))
(should
(equal "\n* [#C] h1\n* [#B] h3\n* [#A] h2\n"
(org-test-with-temp-text
"\n* [#C] h1\n* [#A] h2\n* [#B] h3\n"
(org-sort-entries nil ?P)
(buffer-string))))
;; Sort by creation time.
(should
(equal "
* h3
[2017-05-08 Mon]
* h2
[2017-05-09 Tue]
* h1
[2018-05-09 Wed]
"
(org-test-with-temp-text
"
* h1
[2018-05-09 Wed]
* h2
[2017-05-09 Tue]
* h3
[2017-05-08 Mon]
"
(org-sort-entries nil ?c)
(buffer-string))))
;; Sort by scheduled date.
(should
(equal "
* TODO h4
SCHEDULED: <2017-05-06 Sat>
* TODO h3
SCHEDULED: <2017-05-08 Mon>
* TODO h2
DEADLINE: <2017-05-09 Tue>
* TODO h1
DEADLINE: <2017-05-07 Sun>
"
(org-test-with-temp-text
"
* TODO h2
DEADLINE: <2017-05-09 Tue>
* TODO h1
DEADLINE: <2017-05-07 Sun>
* TODO h3
SCHEDULED: <2017-05-08 Mon>
* TODO h4
SCHEDULED: <2017-05-06 Sat>
"
(org-sort-entries nil ?s)
(buffer-string))))
;; Sort by deadline date.
(should
(equal "
* TODO h1
DEADLINE: <2017-05-07 Sun>
* TODO h2
DEADLINE: <2017-05-09 Tue>
* TODO h3
SCHEDULED: <2017-05-08 Mon>
* TODO h4
SCHEDULED: <2017-05-06 Sat>
"
(org-test-with-temp-text
"
* TODO h2
DEADLINE: <2017-05-09 Tue>
* TODO h1
DEADLINE: <2017-05-07 Sun>
* TODO h3
SCHEDULED: <2017-05-08 Mon>
* TODO h4
SCHEDULED: <2017-05-06 Sat>
"
(org-sort-entries nil ?d)
(buffer-string))))
;; Sort by any date/time
(should
(equal "
* TODO h4
SCHEDULED: <2017-05-06 Sat>
* TODO h1
DEADLINE: <2017-05-07 Sun>
* TODO h3
SCHEDULED: <2017-05-08 Mon>
* TODO h2
DEADLINE: <2017-05-09 Tue>
"
(org-test-with-temp-text
"
* TODO h2
DEADLINE: <2017-05-09 Tue>
* TODO h1
DEADLINE: <2017-05-07 Sun>
* TODO h3
SCHEDULED: <2017-05-08 Mon>
* TODO h4
SCHEDULED: <2017-05-06 Sat>
"
(org-sort-entries nil ?t)
(buffer-string))))
;; Sort by clocking time.
(should
(equal "
* clocked h2
:LOGBOOK:
CLOCK: [2017-05-09 Tue 00:15]--[2017-05-09 Tue 00:22] => 0:07
CLOCK: [2017-05-09 Tue 00:00]--[2017-05-09 Tue 00:10] => 0:10
:END:
* clocked h1
:LOGBOOK:
CLOCK: [2017-05-09 Tue 00:15]--[2017-05-09 Tue 00:22] => 0:07
CLOCK: [2017-05-09 Tue 00:00]--[2017-05-09 Tue 00:12] => 0:12
:END:
"
(org-test-with-temp-text
"
* clocked h1
:LOGBOOK:
CLOCK: [2017-05-09 Tue 00:15]--[2017-05-09 Tue 00:22] => 0:07
CLOCK: [2017-05-09 Tue 00:00]--[2017-05-09 Tue 00:12] => 0:12
:END:
* clocked h2
:LOGBOOK:
CLOCK: [2017-05-09 Tue 00:15]--[2017-05-09 Tue 00:22] => 0:07
CLOCK: [2017-05-09 Tue 00:00]--[2017-05-09 Tue 00:10] => 0:10
:END:
"
(org-sort-entries nil ?k)
(buffer-string))))
;; Preserve file local variables when sorting.
(should
(equal "\n* A\n* B\n# Local Variables:\n# foo: t\n# End:\n"
(org-test-with-temp-text
"\n* B\n* A\n# Local Variables:\n# foo: t\n# End:"
(org-sort-entries nil ?a)
(buffer-string))))
;; Sort region
(should
(equal "
* [#A] h2
* [#B] h3
* [#C] h1
"
(org-test-with-temp-text
"
* [#C] h1
* [#A] h2
* [#B] h3"
(transient-mark-mode 1)
(push-mark (point) t t)
(search-forward "h3")
(org-sort-entries nil ?p)
(buffer-string))))
)
(ert-deftest test-org/string-collate-greaterp ()
"Test `org-string-collate-greaterp' specifications."
(should (org-string-collate-greaterp "def" "abc"))
(should-not (org-string-collate-greaterp "abc" "def")))
(ert-deftest test-org/file-contents ()
"Test `org-file-contents' specifications."
;; Open files.
(should
(string= "#+BIND: variable value
#+DESCRIPTION: l2
#+LANGUAGE: en
#+SELECT_TAGS: b
#+TITLE: b
#+PROPERTY: a 1
" (org-file-contents (expand-file-name "setupfile3.org"
(concat org-test-dir "examples/")))))
;; Throw error when trying to access an invalid file.
(should-error (org-file-contents "this-file-must-not-exist"))
;; Try to access an invalid file, but do not throw an error.
(should
(progn (org-file-contents "this-file-must-not-exist" :noerror) t))
;; Open URL.
(should
(let ((org-resource-download-policy t))
(string= "foo"
(let ((buffer (generate-new-buffer "url-retrieve-output")))
(unwind-protect
;; Simulate successful retrieval of a URL.
(cl-letf (((symbol-function 'url-retrieve-synchronously)
(lambda (&rest_)
(with-current-buffer buffer
(insert "HTTP/1.1 200 OK\n\nfoo"))
buffer)))
(org-file-contents "http://some-valid-url"))
(kill-buffer buffer))))))
;; Throw error when trying to access an invalid URL.
(should-error
(let ((buffer (generate-new-buffer "url-retrieve-output")))
(unwind-protect
;; Simulate unsuccessful retrieval of a URL.
(cl-letf (((symbol-function 'url-retrieve-synchronously)
(lambda (&rest_)
(with-current-buffer buffer
(insert "HTTP/1.1 404 Not found\n\ndoes not matter"))
buffer)))
(org-file-contents "http://this-url-must-not-exist"))
(kill-buffer buffer))))
;; Try to access an invalid URL, but do not throw an error.
(should
(let ((buffer (generate-new-buffer "url-retrieve-output")))
(unwind-protect
;; Simulate unsuccessful retrieval of a URL.
(cl-letf (((symbol-function 'url-retrieve-synchronously)
(lambda (&rest_)
(with-current-buffer buffer
(insert "HTTP/1.1 404 Not found\n\ndoes not matter"))
buffer)))
(org-file-contents "http://this-url-must-not-exist" :noerror))
(kill-buffer buffer))
t)))
(ert-deftest test-org/org-ctrl-c-ctrl-c ()
"Test `org-ctrl-c-ctrl-c' specifications."
;; FIXME: Improve coverage.
;; Preserve visibility after refreshing Org setup.
(org-test-with-temp-text
"#+TITLE: Test
* Heading
text"
(org-overview)
(should (org-fold-folded-p (point) 'outline))
(save-excursion
(goto-char (point-min))
(org-ctrl-c-ctrl-c))
(should (org-fold-folded-p (point) 'outline))))
;;; Navigation
(ert-deftest test-org/next-visible-heading ()
"Test `org-next-visible-heading' specifications."
;; Move to the beginning of the next headline, taking into
;; consideration ARG.
(should
(org-test-with-temp-text "* H1\n* H2"
(org-next-visible-heading 1)
(looking-at "\\* H2")))
(should
(org-test-with-temp-text "* H1\n* H2\n* H3"
(org-next-visible-heading 2)
(looking-at "\\* H3")))
;; Ignore invisible headlines.
(should
(org-test-with-temp-text "* H1\n** H2\n* H3"
(org-cycle)
(org-next-visible-heading 1)
(looking-at "\\* H3")))
;; Move point between headlines, not on blank lines between.
(should
(org-test-with-temp-text "* H1\n** H2\n\n\n\n* H3"
(let ((org-cycle-separator-lines 1))
(org-cycle)
(org-next-visible-heading 1))
(looking-at "\\* H3")))
;; Move at end of buffer when there is no more headline.
(should
(org-test-with-temp-text "* H1"
(org-next-visible-heading 1)
(eobp)))
(should
(org-test-with-temp-text "* H1\n* H2"
(org-next-visible-heading 2)
(eobp)))
;; With a negative argument, move backwards.
(should
(org-test-with-temp-text "* H1\n* H2\n* H3"
(org-next-visible-heading -1)
(looking-at "\\* H2")))
(should
(org-test-with-temp-text "* H1\n* H2\n* H3"
(org-next-visible-heading -2)
(looking-at "\\* H1")))
;; Edge case: visible links.
(should
(let ((org-link-descriptive nil))
(org-test-with-temp-text "* H1\n* [[https://orgmode.org][Org mode]]\n* H3"
(org-next-visible-heading 1)
(looking-at "\\* \\[\\[https:")))))
(ert-deftest test-org/previous-visible-heading ()
"Test `org-previous-visible-heading' specifications."
;; Move to the beginning of the next headline, taking into
;; consideration ARG.
(should
(org-test-with-temp-text "* H1\n* H2"
(org-previous-visible-heading 1)
(looking-at "\\* H1")))
(should
(org-test-with-temp-text "* H1\n* H2\n* H3"
(org-previous-visible-heading 2)
(looking-at "\\* H1")))
;; Ignore invisible headlines.
(should
(org-test-with-temp-text "* H1\n** H2\n* H3"
(org-overview)
(org-previous-visible-heading 1)
(looking-at "\\* H1")))
;; Move point between headlines, not on blank lines between.
(should
(org-test-with-temp-text "* H1\n\n\n\n** H2\n* H3"
(let ((org-cycle-separator-lines 1))
(org-overview)
(org-previous-visible-heading 1))
(looking-at "\\* H1")))
;; Move at end of buffer when there is no more headline.
(should
(org-test-with-temp-text "* H1"
(org-previous-visible-heading 1)
(bobp)))
(should
(org-test-with-temp-text "* H1\n* H2"
(org-previous-visible-heading 2)
(bobp)))
;; Invisible parts may not start at a headline, i.e., when revealing
;; parts of the buffer. Handle this.
(should
(org-test-with-temp-text "* Main\n** H1\nFoo\n** H2\nBar\n** H3\nBaz"
(org-overview)
(search-forward "H1")
(org-show-context 'minimal)
(org-cycle)
(search-forward "H3")
(org-show-context 'minimal)
;; At this point, buffer displays, with point at "|",
;;
;; * Main
;; ** H1
;; Foo
;; ** H3|
(org-previous-visible-heading 1)
(looking-at "\\*+ H1"))))
(ert-deftest test-org/forward-heading-same-level ()
"Test `org-forward-heading-same-level' specifications."
;; Test navigation at top level, forward and backward.
(should
(equal "* H2"
(org-test-with-temp-text "* H1\n* H2"
(org-forward-heading-same-level 1)
(buffer-substring-no-properties (point) (line-end-position)))))
(should
(equal "* H1"
(org-test-with-temp-text "* H1\n* H2"
(org-forward-heading-same-level -1)
(buffer-substring-no-properties (point) (line-end-position)))))
;; Test navigation in a sub-tree, forward and backward.
(should
(equal "* H2"
(org-test-with-temp-text "* H1\n** H11\n** H12\n* H2"
(org-forward-heading-same-level 1)
(buffer-substring-no-properties (point) (line-end-position)))))
(should
(equal "* H1"
(org-test-with-temp-text "* H1\n** H11\n** H12\n* H2"
(org-forward-heading-same-level -1)
(buffer-substring-no-properties (point) (line-end-position)))))
;; Stop at first or last sub-heading.
(should-not
(equal "* H2"
(org-test-with-temp-text "* H1\n** H11\n** H12\n* H2"
(org-forward-heading-same-level 1)
(buffer-substring-no-properties (point) (line-end-position)))))
(should-not
(equal "* H2"
(org-test-with-temp-text "* H1\n** H11\n** H12\n* H2"
(org-forward-heading-same-level -1)
(buffer-substring-no-properties (point) (line-end-position)))))
;; Allow multiple moves.
(should
(equal "* H3"
(org-test-with-temp-text "* H1\n* H2\n* H3"
(org-forward-heading-same-level 2)
(buffer-substring-no-properties (point) (line-end-position)))))
(should
(equal "* H1"
(org-test-with-temp-text "* H1\n* H2\n* H3"
(org-forward-heading-same-level -2)
(buffer-substring-no-properties (point) (line-end-position)))))
;; Ignore spurious moves when first (or last) sibling is reached.
(should
(equal "** H3"
(org-test-with-temp-text "* First\n** H1\n** H2\n** H3\n* Last"
(org-forward-heading-same-level 100)
(buffer-substring-no-properties (point) (line-end-position)))))
(should
(equal "** H1"
(org-test-with-temp-text "* First\n** H1\n** H2\n