aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-05 04:34:03 -0500
committerCraig Jennings <c@cjennings.net>2026-05-05 04:34:03 -0500
commit949a297da4ddd47840c4f0e427c17c753fd8219c (patch)
tree56f0f3f868f96b5071391093e878499555e7072c
parentbc75218917e8e01bc67a2582d3fb2bac32bd28fe (diff)
downloadorg-drill-949a297da4ddd47840c4f0e427c17c753fd8219c.tar.gz
org-drill-949a297da4ddd47840c4f0e427c17c753fd8219c.zip
test: add navigation, key-binding, push-end, and leitner-rebox coverage
15 ERT tests covering: - org-drill-goto-entry: marker → buffer + position - org-drill-goto-drill-entry-heading: stays put on the drill heading, walks up from a child sub-heading, errors outside any drill entry - org-drill-command-keybinding-to-string: nil for unbound, string for bound commands - org-drill-push-end: appends to non-empty and empty lists - org-drill-leitner-rebox (interactive — read-key-sequence mocked): rating 0 resets to box 1, rating 1 decrements (with floor at 1), rating 2 stays, ratings 3-5 promote, quit-key returns 'quit
-rw-r--r--tests/test-org-drill-navigation-and-leitner-rebox.el191
1 files changed, 191 insertions, 0 deletions
diff --git a/tests/test-org-drill-navigation-and-leitner-rebox.el b/tests/test-org-drill-navigation-and-leitner-rebox.el
new file mode 100644
index 0000000..a0b17aa
--- /dev/null
+++ b/tests/test-org-drill-navigation-and-leitner-rebox.el
@@ -0,0 +1,191 @@
+;;; test-org-drill-navigation-and-leitner-rebox.el --- Tests for navigation helpers and leitner-rebox -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for:
+;;
+;; - `org-drill-goto-entry': switches to a marker's buffer and position
+;; - `org-drill-goto-drill-entry-heading': moves up the outline until
+;; it finds the heading that owns the :drill: tag (handles being
+;; called from a sub-heading)
+;; - `org-drill-command-keybinding-to-string': human-readable key
+;; description, used in messages like "Press C-c d to resume"
+;; - `org-drill-push-end': in-place list-end push macro
+;; - `org-drill-leitner-rebox': the rating loop for Leitner cards.
+;; Tests drive ratings 0..5 by mocking `read-key-sequence' and
+;; verify the DRILL_LEITNER_BOX property updates correctly.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+(require 'org)
+(require 'org-drill)
+
+;;;; Helpers
+
+(defmacro with-fresh-drill-entry (&rest body)
+ (declare (indent 0))
+ `(with-temp-buffer
+ (let ((org-startup-folded nil))
+ (insert "* Question :drill:\nbody\n")
+ (org-mode)
+ (goto-char (point-min))
+ ,@body)))
+
+(defmacro with-leitner-entry-in-box (box-num &rest body)
+ "Run BODY at point on a leitner-tagged entry currently in BOX-NUM."
+ (declare (indent 1))
+ `(with-temp-buffer
+ (let ((org-startup-folded nil))
+ (insert (format "* Question :leitner:\n:PROPERTIES:\n:DRILL_LEITNER_BOX: %d\n:END:\nbody\n" ,box-num))
+ (org-mode)
+ (goto-char (point-min))
+ ,@body)))
+
+(defun stub-read-key-sequence-with-string (return-string)
+ "Make `read-key-sequence' return the given string."
+ (cl-letf (((symbol-function 'read-key-sequence)
+ (lambda (_prompt) return-string)))))
+
+;;;; org-drill-goto-entry
+
+(ert-deftest test-org-drill-goto-entry-jumps-to-marker-position ()
+ "After `goto-entry', point matches the marker's position."
+ (with-temp-buffer
+ (insert "abcdefghij\n")
+ (let ((m (make-marker)))
+ (set-marker m 5)
+ (org-drill-goto-entry m)
+ (should (= 5 (point))))))
+
+;;;; org-drill-goto-drill-entry-heading
+
+(ert-deftest test-org-drill-goto-drill-entry-heading-on-drill-stays-put ()
+ "Called on the drill heading itself, point doesn't move."
+ (with-fresh-drill-entry
+ (let ((before (point)))
+ (org-drill-goto-drill-entry-heading)
+ (should (= before (point))))))
+
+(ert-deftest test-org-drill-goto-drill-entry-heading-from-child-walks-up ()
+ "Called inside a sub-heading of a drill entry, walks up to the parent."
+ (with-temp-buffer
+ (let ((org-startup-folded nil))
+ (insert "* Parent :drill:\n** Child\nchild body\n")
+ (org-mode)
+ (re-search-backward "^\\*\\* Child")
+ (org-drill-goto-drill-entry-heading)
+ (should (looking-at "^\\* Parent")))))
+
+(ert-deftest test-org-drill-goto-drill-entry-heading-non-drill-errors ()
+ "Called outside a drill entry → user-visible error."
+ (with-temp-buffer
+ (let ((org-startup-folded nil))
+ (insert "* Plain heading\nbody\n")
+ (org-mode)
+ (goto-char (point-min))
+ (should-error (org-drill-goto-drill-entry-heading)))))
+
+;;;; org-drill-command-keybinding-to-string
+
+(ert-deftest test-org-drill-command-keybinding-to-string-unbound-returns-nil ()
+ "When CMD has no binding, returns nil."
+ (let ((cmd (make-symbol "fictional-org-drill-command-no-binding")))
+ (should (null (org-drill-command-keybinding-to-string cmd)))))
+
+(ert-deftest test-org-drill-command-keybinding-to-string-bound-returns-string ()
+ (let ((cmd (make-symbol "fictional-test-cmd")))
+ (defalias cmd #'ignore)
+ (let ((map (make-sparse-keymap)))
+ (define-key map (kbd "C-c x") cmd)
+ (let ((overriding-local-map map))
+ (let ((result (org-drill-command-keybinding-to-string cmd)))
+ (should (stringp result))
+ (should (string-match-p "C-c x" result)))))))
+
+;;;; org-drill-push-end
+
+(ert-deftest test-org-drill-push-end-appends-to-list-place ()
+ (let ((lst (list 1 2 3)))
+ (org-drill-push-end 4 lst)
+ (should (equal '(1 2 3 4) lst))))
+
+(ert-deftest test-org-drill-push-end-on-empty-list ()
+ (let ((lst nil))
+ (org-drill-push-end 'a lst)
+ (should (equal '(a) lst))))
+
+;;;; org-drill-leitner-rebox
+
+(ert-deftest test-org-drill-leitner-rebox-rating-0-resets-to-box-1 ()
+ "Quality 0 demotes the card all the way down to box 1."
+ (with-leitner-entry-in-box 4
+ (let ((session (org-drill-session)))
+ (cl-letf (((symbol-function 'read-key-sequence)
+ (lambda (_prompt) "0"))
+ ((symbol-function 'sit-for) #'ignore))
+ (let ((result (org-drill-leitner-rebox session)))
+ (should (eq t result))
+ (should (equal "1" (org-entry-get (point) "DRILL_LEITNER_BOX"))))))))
+
+(ert-deftest test-org-drill-leitner-rebox-rating-1-decrements-by-one ()
+ "Quality 1 demotes by one box."
+ (with-leitner-entry-in-box 3
+ (let ((session (org-drill-session)))
+ (cl-letf (((symbol-function 'read-key-sequence)
+ (lambda (_prompt) "1"))
+ ((symbol-function 'sit-for) #'ignore))
+ (org-drill-leitner-rebox session)
+ (should (equal "2" (org-entry-get (point) "DRILL_LEITNER_BOX")))))))
+
+(ert-deftest test-org-drill-leitner-rebox-rating-1-from-box-1-stays-at-box-1 ()
+ "Quality 1 from box 1 stays at box 1 — can't go lower."
+ (with-leitner-entry-in-box 1
+ (let ((session (org-drill-session)))
+ (cl-letf (((symbol-function 'read-key-sequence)
+ (lambda (_prompt) "1"))
+ ((symbol-function 'sit-for) #'ignore))
+ (org-drill-leitner-rebox session)
+ (should (equal "1" (org-entry-get (point) "DRILL_LEITNER_BOX")))))))
+
+(ert-deftest test-org-drill-leitner-rebox-rating-2-stays-in-current-box ()
+ "Quality 2 leaves the box unchanged."
+ (with-leitner-entry-in-box 3
+ (let ((session (org-drill-session)))
+ (cl-letf (((symbol-function 'read-key-sequence)
+ (lambda (_prompt) "2"))
+ ((symbol-function 'sit-for) #'ignore))
+ (org-drill-leitner-rebox session)
+ (should (equal "3" (org-entry-get (point) "DRILL_LEITNER_BOX")))))))
+
+(ert-deftest test-org-drill-leitner-rebox-rating-3-promotes-by-one ()
+ (with-leitner-entry-in-box 2
+ (let ((session (org-drill-session)))
+ (cl-letf (((symbol-function 'read-key-sequence)
+ (lambda (_prompt) "3"))
+ ((symbol-function 'sit-for) #'ignore))
+ (org-drill-leitner-rebox session)
+ (should (equal "3" (org-entry-get (point) "DRILL_LEITNER_BOX")))))))
+
+(ert-deftest test-org-drill-leitner-rebox-rating-5-promotes-by-one ()
+ (with-leitner-entry-in-box 3
+ (let ((session (org-drill-session)))
+ (cl-letf (((symbol-function 'read-key-sequence)
+ (lambda (_prompt) "5"))
+ ((symbol-function 'sit-for) #'ignore))
+ (org-drill-leitner-rebox session)
+ (should (equal "4" (org-entry-get (point) "DRILL_LEITNER_BOX")))))))
+
+(ert-deftest test-org-drill-leitner-rebox-quit-key-returns-quit ()
+ "Pressing the configured quit key returns the symbol `quit'."
+ (with-leitner-entry-in-box 2
+ (let ((session (org-drill-session)))
+ (cl-letf (((symbol-function 'read-key-sequence)
+ (lambda (_prompt) (string org-drill--quit-key)))
+ ((symbol-function 'sit-for) #'ignore))
+ (let ((result (org-drill-leitner-rebox session)))
+ (should (eq 'quit result)))))))
+
+(provide 'test-org-drill-navigation-and-leitner-rebox)
+
+;;; test-org-drill-navigation-and-leitner-rebox.el ends here