aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-13 16:37:33 -0500
committerCraig Jennings <c@cjennings.net>2026-05-13 16:37:33 -0500
commit2faf3c598e827bb0ba15a692a9486085ec66359f (patch)
tree6d7d37a07a67992b8d2f23a1efd8a3a793433e5e
parent0d4d0da6d34b3c81b82aebbaecbaed1da9940998 (diff)
downloaddotemacs-2faf3c598e827bb0ba15a692a9486085ec66359f.tar.gz
dotemacs-2faf3c598e827bb0ba15a692a9486085ec66359f.zip
test(jumper): cover the three interactive wrappers + setup-keys
`test-jumper.el` covered the internal helpers (`jumper--do-store-location`, `jumper--do-jump-to-location`, `jumper--do-remove-location`) thoroughly. The interactive entry points (`jumper-store-location`, `jumper-jump-to-location`, `jumper-remove-location`) and `jumper-setup-keys` were the gap. 10 new tests capture `message` and stub `completing-read` to cover: fresh-store messaging, duplicate detection, no-space full-registers path, no-locations / already-there / multi-prompt jump branches, no-locations / prompt-and-delete / cancel remove branches, and the prefix-key wiring.
-rw-r--r--tests/test-jumper-public-wrappers.el175
1 files changed, 175 insertions, 0 deletions
diff --git a/tests/test-jumper-public-wrappers.el b/tests/test-jumper-public-wrappers.el
new file mode 100644
index 00000000..99279344
--- /dev/null
+++ b/tests/test-jumper-public-wrappers.el
@@ -0,0 +1,175 @@
+;;; test-jumper-public-wrappers.el --- Tests for the interactive jumper wrappers -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Internal helpers (`jumper--do-store-location',
+;; `jumper--do-jump-to-location', `jumper--do-remove-location') are
+;; already tested in test-jumper.el. This file covers the three
+;; interactive entry points that dispatch user input (and messages)
+;; into those helpers:
+;;
+;; jumper-store-location
+;; jumper-jump-to-location
+;; jumper-remove-location
+;;
+;; `message' is captured to verify the user-visible feedback path;
+;; `completing-read' is stubbed when the wrapper prompts the user.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(require 'jumper)
+
+(defmacro test-jumper-wrappers--with-clean-state (&rest body)
+ "Reset jumper state and capture messages, then evaluate BODY."
+ `(let ((jumper--registers (make-vector jumper-max-locations nil))
+ (jumper--next-index 0)
+ (captured-message nil))
+ ;; Clear the 'z' last-location register so completing-read prompts
+ ;; don't show it from a previous test run in the same process.
+ (set-register jumper--last-location-register nil)
+ (dotimes (i jumper-max-locations)
+ (set-register (+ ?0 i) nil))
+ (cl-letf (((symbol-function 'message)
+ (lambda (fmt &rest args)
+ (setq captured-message (apply #'format fmt args)))))
+ ,@body)))
+
+;;; jumper-store-location
+
+(ert-deftest test-jumper-wrapper-store-fresh-location-messages-register ()
+ "Normal: storing a fresh location messages the register that was assigned."
+ (test-jumper-wrappers--with-clean-state
+ (with-temp-buffer
+ (insert "alpha\n")
+ (goto-char (point-min))
+ (jumper-store-location)
+ (should (string-match-p "register" captured-message))
+ (should (= jumper--next-index 1)))))
+
+(ert-deftest test-jumper-wrapper-store-duplicate-messages-already-stored ()
+ "Boundary: storing the same location twice surfaces the duplicate path."
+ (test-jumper-wrappers--with-clean-state
+ (with-temp-buffer
+ (insert "alpha\n")
+ (goto-char (point-min))
+ (jumper-store-location)
+ (setq captured-message nil)
+ (jumper-store-location)
+ (should (string-match-p "already stored" captured-message))
+ (should (= jumper--next-index 1)))))
+
+(ert-deftest test-jumper-wrapper-store-when-full-messages-no-space ()
+ "Boundary: storing when every register is taken surfaces the no-space path."
+ (test-jumper-wrappers--with-clean-state
+ (with-temp-buffer
+ (dotimes (i jumper-max-locations)
+ (insert (format "line %d\n" i))
+ (goto-char (point-max))
+ (jumper-store-location))
+ (insert "one-more\n")
+ (goto-char (point-max))
+ (setq captured-message nil)
+ (jumper-store-location)
+ (should (string-match-p "all jump locations" captured-message)))))
+
+;;; jumper-jump-to-location
+
+(ert-deftest test-jumper-wrapper-jump-no-locations-messages ()
+ "Normal: with no stored locations the wrapper surfaces the empty state."
+ (test-jumper-wrappers--with-clean-state
+ (jumper-jump-to-location)
+ (should (string-match-p "No locations" captured-message))))
+
+(ert-deftest test-jumper-wrapper-jump-single-location-already-there ()
+ "Normal: single-location toggle from the stored position surfaces the
+already-there message."
+ (test-jumper-wrappers--with-clean-state
+ (with-temp-buffer
+ (insert "alpha\n")
+ (goto-char (point-min))
+ (jumper-store-location)
+ (setq captured-message nil)
+ ;; Don't move -- jump should detect we're at the stored location.
+ (jumper-jump-to-location)
+ (should (string-match-p "already at" captured-message)))))
+
+(ert-deftest test-jumper-wrapper-jump-multi-location-prompts ()
+ "Normal: with multiple locations the wrapper prompts via completing-read
+and jumps to the selected entry."
+ (test-jumper-wrappers--with-clean-state
+ (with-temp-buffer
+ (insert "alpha\nbeta\n")
+ ;; Two stored locations at different points.
+ (goto-char (point-min))
+ (jumper-store-location)
+ (forward-line)
+ (jumper-store-location)
+ (setq captured-message nil)
+ ;; Stub the prompt to pick the first candidate (index 0).
+ (cl-letf (((symbol-function 'completing-read)
+ (lambda (_prompt collection &rest _)
+ ;; Pick whichever candidate maps to integer 0.
+ (car (cl-find-if (lambda (cell) (equal (cdr cell) 0))
+ collection)))))
+ (jumper-jump-to-location))
+ (should (string-match-p "Jumped" captured-message)))))
+
+;;; jumper-remove-location
+
+(ert-deftest test-jumper-wrapper-remove-no-locations-messages ()
+ "Normal: removing with no stored locations surfaces the empty state."
+ (test-jumper-wrappers--with-clean-state
+ (jumper-remove-location)
+ (should (string-match-p "No locations" captured-message))))
+
+(ert-deftest test-jumper-wrapper-remove-prompts-and-deletes ()
+ "Normal: with locations stored, removing prompts and deletes the chosen one."
+ (test-jumper-wrappers--with-clean-state
+ (with-temp-buffer
+ (insert "alpha\nbeta\n")
+ (goto-char (point-min))
+ (jumper-store-location)
+ (forward-line)
+ (jumper-store-location)
+ (should (= jumper--next-index 2))
+ (cl-letf (((symbol-function 'completing-read)
+ (lambda (_prompt collection &rest _)
+ (car (cl-find-if (lambda (cell) (equal (cdr cell) 0))
+ collection)))))
+ (setq captured-message nil)
+ (jumper-remove-location))
+ (should (string-match-p "Location removed" captured-message))
+ (should (= jumper--next-index 1)))))
+
+(ert-deftest test-jumper-wrapper-remove-cancel-keeps-state ()
+ "Boundary: choosing Cancel surfaces the cancelled message and changes nothing."
+ (test-jumper-wrappers--with-clean-state
+ (with-temp-buffer
+ (insert "alpha\n")
+ (goto-char (point-min))
+ (jumper-store-location)
+ (cl-letf (((symbol-function 'completing-read)
+ (lambda (_prompt collection &rest _)
+ (car (cl-find-if (lambda (cell) (equal (cdr cell) -1))
+ collection)))))
+ (setq captured-message nil)
+ (jumper-remove-location))
+ (should (string-match-p "cancelled" captured-message))
+ (should (= jumper--next-index 1)))))
+
+;;; jumper-setup-keys
+
+(ert-deftest test-jumper-setup-keys-binds-prefix-to-jumper-map ()
+ "Normal: `jumper-setup-keys' wires the prefix key to `jumper-map'."
+ (let ((jumper-prefix-key "C-c M-j"))
+ (jumper-setup-keys)
+ (should (eq (keymap-lookup (current-global-map) jumper-prefix-key)
+ jumper-map))
+ ;; Cleanup so the test binding doesn't leak.
+ (keymap-global-unset jumper-prefix-key)))
+
+(provide 'test-jumper-public-wrappers)
+;;; test-jumper-public-wrappers.el ends here