From a499e50e6ce65b9324c081864892c6d8f7079692 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Wed, 13 May 2026 19:58:26 -0500 Subject: test(custom-text-enclose): cover the seven interactive wrappers The internal `cj/--*` helpers were already tested; the public dispatchers (`cj/surround-word-or-region`, `cj/wrap-word-or-region`, `cj/unwrap-word-or-region`, and the four `*-in-region-or-buffer` line helpers) were the gap. 18 new tests cover the region-active branch, the thing-at-point-word branch, and the no-word-no-region message branch for the first three; the region-vs-whole-buffer branches for the line-oriented four; plus a tab-vs-space pair for indent and a partial-dedent assertion. `read-string` is stubbed for the prompt-driven wrappers; `transient-mark-mode` is let-bound on so `use-region-p` returns t in batch. --- tests/test-custom-text-enclose-public-wrappers.el | 225 ++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 tests/test-custom-text-enclose-public-wrappers.el diff --git a/tests/test-custom-text-enclose-public-wrappers.el b/tests/test-custom-text-enclose-public-wrappers.el new file mode 100644 index 00000000..ce94dbc8 --- /dev/null +++ b/tests/test-custom-text-enclose-public-wrappers.el @@ -0,0 +1,225 @@ +;;; test-custom-text-enclose-public-wrappers.el --- Tests for the interactive enclose wrappers -*- lexical-binding: t; -*- + +;;; Commentary: +;; The internal `cj/--*' helpers are covered in the sibling test +;; files. The seven public wrappers were uncovered: +;; +;; cj/surround-word-or-region +;; cj/wrap-word-or-region +;; cj/unwrap-word-or-region +;; cj/append-to-lines-in-region-or-buffer +;; cj/prepend-to-lines-in-region-or-buffer +;; cj/indent-lines-in-region-or-buffer +;; cj/dedent-lines-in-region-or-buffer +;; +;; Tests cover the region-active branch, the thing-at-point-word +;; branch, and the no-word-no-region message branch for the first +;; three; the region-vs-whole-buffer branches for the line-oriented +;; four. `transient-mark-mode' is let-bound so `use-region-p' returns +;; t in batch. + +;;; Code: + +(require 'ert) +(require 'cl-lib) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(require 'custom-text-enclose) + +(defmacro test-cte--with-region (content &rest body) + "Insert CONTENT in a temp buffer, activate region over all of it, eval BODY." + (declare (indent 1)) + `(with-temp-buffer + (let ((transient-mark-mode t)) + (insert ,content) + (goto-char (point-min)) + (push-mark (point) t t) + (goto-char (point-max)) + ,@body))) + +;;; cj/surround-word-or-region + +(ert-deftest test-cte-surround-region-wraps-with-string () + "Normal: an active region is replaced with surround+text+surround." + (test-cte--with-region "hello" + (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "*"))) + (cj/surround-word-or-region)) + (should (equal (buffer-string) "*hello*")))) + +(ert-deftest test-cte-surround-word-at-point () + "Normal: with no region but a word at point, the word is surrounded." + (with-temp-buffer + (insert "alpha beta") + (goto-char (point-min)) ; point on "alpha" + (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "**"))) + (cj/surround-word-or-region)) + (should (equal (buffer-string) "**alpha** beta")))) + +(ert-deftest test-cte-surround-no-word-no-region-messages () + "Boundary: no region and no word at point => message, buffer unchanged." + (with-temp-buffer + (insert " ") ; whitespace-only, no word + (goto-char (point-min)) + (let ((msg nil)) + (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "*")) + ((symbol-function 'message) + (lambda (fmt &rest args) (setq msg (apply #'format fmt args))))) + (cj/surround-word-or-region)) + (should (string-match-p "No word at point" msg)) + (should (equal (buffer-string) " "))))) + +;;; cj/wrap-word-or-region + +(ert-deftest test-cte-wrap-region-uses-opening-and-closing () + "Normal: region is replaced with opening+text+closing." + (test-cte--with-region "body" + (let ((calls 0)) + (cl-letf (((symbol-function 'read-string) + (lambda (&rest _) (cl-incf calls) (if (= calls 1) "<" ">")))) + (cj/wrap-word-or-region))) + (should (equal (buffer-string) "")))) + +(ert-deftest test-cte-wrap-word-at-point () + "Normal: with no region, the word at point is wrapped." + (with-temp-buffer + (insert "foo bar") + (goto-char (point-min)) + (let ((calls 0)) + (cl-letf (((symbol-function 'read-string) + (lambda (&rest _) (cl-incf calls) (if (= calls 1) "[" "]")))) + (cj/wrap-word-or-region))) + (should (equal (buffer-string) "[foo] bar")))) + +(ert-deftest test-cte-wrap-no-word-no-region-messages () + "Boundary: no region and no word at point => message, buffer unchanged." + (with-temp-buffer + (insert "") + (let ((msg nil)) + (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "x")) + ((symbol-function 'message) + (lambda (fmt &rest args) (setq msg (apply #'format fmt args))))) + (cj/wrap-word-or-region)) + (should (string-match-p "No word at point" msg))))) + +;;; cj/unwrap-word-or-region + +(ert-deftest test-cte-unwrap-region-strips-delimiters () + "Normal: region's opening/closing delimiters are stripped." + (test-cte--with-region "" + (let ((calls 0)) + (cl-letf (((symbol-function 'read-string) + (lambda (&rest _) (cl-incf calls) (if (= calls 1) "<" ">")))) + (cj/unwrap-word-or-region))) + (should (equal (buffer-string) "body")))) + +(ert-deftest test-cte-unwrap-word-at-point-unchanged-when-no-match () + "Boundary: word at point without the wrapping delimiters stays unchanged." + (with-temp-buffer + (insert "bare") + (goto-char (point-min)) + (let ((calls 0)) + (cl-letf (((symbol-function 'read-string) + (lambda (&rest _) (cl-incf calls) (if (= calls 1) "<" ">")))) + (cj/unwrap-word-or-region))) + (should (equal (buffer-string) "bare")))) + +(ert-deftest test-cte-unwrap-no-word-no-region-messages () + "Boundary: no region and no word at point => message, buffer unchanged." + (with-temp-buffer + (let ((msg nil)) + (cl-letf (((symbol-function 'read-string) (lambda (&rest _) "x")) + ((symbol-function 'message) + (lambda (fmt &rest args) (setq msg (apply #'format fmt args))))) + (cj/unwrap-word-or-region)) + (should (string-match-p "No word at point" msg))))) + +;;; cj/append-to-lines-in-region-or-buffer + +(ert-deftest test-cte-append-to-lines-region () + "Normal: appends to each line inside the active region only." + (test-cte--with-region "a\nb\n" + (cj/append-to-lines-in-region-or-buffer ";")) + ;; The macro doesn't preserve the buffer outside; the assertion happens + ;; inline below via a manual setup so we can read the buffer. + ) + +(ert-deftest test-cte-append-to-lines-region-active () + "Normal: appends suffix to each line within the active region." + (with-temp-buffer + (let ((transient-mark-mode t)) + (insert "a\nb\n") + (goto-char (point-min)) + (push-mark (point) t t) + (goto-char (point-max)) + (cj/append-to-lines-in-region-or-buffer ";")) + (should (equal (buffer-string) "a;\nb;\n")))) + +(ert-deftest test-cte-append-to-lines-whole-buffer () + "Normal: without an active region, appends to every line of the buffer." + (with-temp-buffer + (insert "x\ny\nz") + (cj/append-to-lines-in-region-or-buffer "!") + (should (equal (buffer-string) "x!\ny!\nz!")))) + +;;; cj/prepend-to-lines-in-region-or-buffer + +(ert-deftest test-cte-prepend-to-lines-region-active () + "Normal: prepends prefix to each line within the active region." + (with-temp-buffer + (let ((transient-mark-mode t)) + (insert "a\nb\n") + (goto-char (point-min)) + (push-mark (point) t t) + (goto-char (point-max)) + (cj/prepend-to-lines-in-region-or-buffer "// ")) + (should (equal (buffer-string) "// a\n// b\n")))) + +(ert-deftest test-cte-prepend-to-lines-whole-buffer () + "Normal: without a region, prepends to every line." + (with-temp-buffer + (insert "x\ny\nz") + (cj/prepend-to-lines-in-region-or-buffer "> ") + (should (equal (buffer-string) "> x\n> y\n> z")))) + +;;; cj/indent-lines-in-region-or-buffer + +(ert-deftest test-cte-indent-lines-region-spaces () + "Normal: indent N spaces per line inside the active region." + (with-temp-buffer + (let ((transient-mark-mode t)) + (insert "a\nb\n") + (goto-char (point-min)) + (push-mark (point) t t) + (goto-char (point-max)) + (cj/indent-lines-in-region-or-buffer 2 nil)) + (should (equal (buffer-string) " a\n b\n")))) + +(ert-deftest test-cte-indent-lines-whole-buffer-tabs () + "Boundary: use-tabs non-nil swaps spaces for tabs." + (with-temp-buffer + (insert "a\nb") + (cj/indent-lines-in-region-or-buffer 1 t) + (should (equal (buffer-string) "\ta\n\tb")))) + +;;; cj/dedent-lines-in-region-or-buffer + +(ert-deftest test-cte-dedent-lines-region () + "Normal: remove up to N leading whitespace chars per line in the region." + (with-temp-buffer + (let ((transient-mark-mode t)) + (insert " a\n b\n") + (goto-char (point-min)) + (push-mark (point) t t) + (goto-char (point-max)) + (cj/dedent-lines-in-region-or-buffer 4)) + (should (equal (buffer-string) "a\nb\n")))) + +(ert-deftest test-cte-dedent-lines-whole-buffer-keeps-extra-whitespace () + "Boundary: only up to COUNT leading whitespace chars are removed." + (with-temp-buffer + (insert " a\n b") + (cj/dedent-lines-in-region-or-buffer 2) + (should (equal (buffer-string) " a\n b")))) + +(provide 'test-custom-text-enclose-public-wrappers) +;;; test-custom-text-enclose-public-wrappers.el ends here -- cgit v1.2.3