diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-25 17:11:13 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-25 17:11:13 -0500 |
| commit | 37917d5b34155c072752697e0309011d3f17a56e (patch) | |
| tree | 50cf7c2b5653e9f88238971c16de49f8c7da7aec /tests | |
| parent | eb015e6e912724c79946f2095a3a604a7e0a2334 (diff) | |
| download | dotemacs-37917d5b34155c072752697e0309011d3f17a56e.tar.gz dotemacs-37917d5b34155c072752697e0309011d3f17a56e.zip | |
test(mu4e-org-contacts): cover the completion-at-point and TAB dispatch logic
mu4e-org-contacts-integration.el had no tests. I added ten characterization tests for the completion glue. The capf only fires inside a header field of a compose buffer, so I check it both ways (wrong mode, wrong field) plus the bounds and table it returns when contacts exist and the empty-contacts case. TAB dispatches three ways depending on context, so each branch gets a test: completion-at-point in a header, org-cycle in the org-msg body, indent elsewhere. Comma completion and the direct-insert path round it out.
The header predicate, the mode actions, and cj/get-all-contact-emails are stubbed, so the run stays headless with no mu4e or org-contacts dependency.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test-mu4e-org-contacts-integration.el | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/tests/test-mu4e-org-contacts-integration.el b/tests/test-mu4e-org-contacts-integration.el new file mode 100644 index 00000000..c86d61b3 --- /dev/null +++ b/tests/test-mu4e-org-contacts-integration.el @@ -0,0 +1,135 @@ +;;; test-mu4e-org-contacts-integration.el --- Tests for mu4e org-contacts completion -*- lexical-binding: t; -*- + +;;; Commentary: + +;; Characterization tests for the org-contacts completion glue. The module +;; loads without mu4e or org-contacts (both are required with noerror), and the +;; tests stub the header-field predicate, the mode actions, and the contact +;; source, so they run headless with no package dependency. Coverage centers +;; on the two highest-value behaviors: the capf only fires inside a header +;; field of a compose buffer, and TAB dispatches to completion / org-cycle / +;; indent by context. + +;;; Code: + +(require 'ert) +(require 'cl-lib) +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(require 'mu4e-org-contacts-integration) + +;;; cj/org-contacts-completion-at-point + +(ert-deftest test-mu4e-org-contacts-capf-nil-outside-header () + "Boundary: the capf declines when point is not in a header field." + (with-temp-buffer + (cl-letf (((symbol-function 'derived-mode-p) (lambda (&rest _) t)) + ((symbol-function 'mail-abbrev-in-expansion-header-p) (lambda () nil))) + (should-not (cj/org-contacts-completion-at-point))))) + +(ert-deftest test-mu4e-org-contacts-capf-nil-outside-compose-mode () + "Boundary: the capf declines outside the mu4e/org-msg compose modes." + (with-temp-buffer + (cl-letf (((symbol-function 'derived-mode-p) (lambda (&rest _) nil)) + ((symbol-function 'mail-abbrev-in-expansion-header-p) (lambda () t))) + (should-not (cj/org-contacts-completion-at-point))))) + +(ert-deftest test-mu4e-org-contacts-capf-returns-spec-in-header () + "Normal: in a header field with contacts the capf returns bounds and a table." + (with-temp-buffer + (insert "To: ali") + (cl-letf (((symbol-function 'derived-mode-p) (lambda (&rest _) t)) + ((symbol-function 'mail-abbrev-in-expansion-header-p) (lambda () t)) + ((symbol-function 'cj/get-all-contact-emails) + (lambda () '("Alice <alice@example.com>" "Bob <bob@example.com>")))) + (let ((result (cj/org-contacts-completion-at-point))) + (should result) + (should (integerp (nth 0 result))) + (should (= (point) (nth 1 result))) + ;; the completion start sits just after the "To:" prefix + (should (string= "ali" (buffer-substring-no-properties + (nth 0 result) (nth 1 result)))) + (should (member "Alice <alice@example.com>" + (all-completions "" (nth 2 result)))))))) + +(ert-deftest test-mu4e-org-contacts-capf-nil-when-no-contacts () + "Boundary: a header field with no contacts yields no completion." + (with-temp-buffer + (insert "To: ") + (cl-letf (((symbol-function 'derived-mode-p) (lambda (&rest _) t)) + ((symbol-function 'mail-abbrev-in-expansion-header-p) (lambda () t)) + ((symbol-function 'cj/get-all-contact-emails) (lambda () nil))) + (should-not (cj/org-contacts-completion-at-point))))) + +;;; cj/mu4e-org-contacts-tab-complete + +(ert-deftest test-mu4e-org-contacts-tab-completes-in-header () + "Normal: TAB in a header field triggers completion-at-point." + (let ((capf-calls 0) (org-cycled nil) (indented nil)) + (cl-letf (((symbol-function 'mail-abbrev-in-expansion-header-p) (lambda () t)) + ((symbol-function 'completion-at-point) (lambda () (cl-incf capf-calls))) + ((symbol-function 'org-cycle) (lambda (&rest _) (setq org-cycled t))) + ((symbol-function 'indent-for-tab-command) + (lambda (&rest _) (setq indented t)))) + (cj/mu4e-org-contacts-tab-complete) + (should (= capf-calls 1)) + (should-not org-cycled) + (should-not indented)))) + +(ert-deftest test-mu4e-org-contacts-tab-cycles-in-org-msg-body () + "Normal: TAB in the org-msg body (not a header) calls org-cycle." + (with-temp-buffer + (setq major-mode 'org-msg-edit-mode) + (let ((org-cycled nil) (capf-called nil)) + (cl-letf (((symbol-function 'mail-abbrev-in-expansion-header-p) (lambda () nil)) + ((symbol-function 'org-cycle) (lambda (&rest _) (setq org-cycled t))) + ((symbol-function 'completion-at-point) + (lambda () (setq capf-called t)))) + (cj/mu4e-org-contacts-tab-complete) + (should org-cycled) + (should-not capf-called))))) + +(ert-deftest test-mu4e-org-contacts-tab-indents-elsewhere () + "Boundary: TAB outside a header and outside the org-msg body indents." + (with-temp-buffer + (setq major-mode 'fundamental-mode) + (let ((indented nil)) + (cl-letf (((symbol-function 'mail-abbrev-in-expansion-header-p) (lambda () nil)) + ((symbol-function 'indent-for-tab-command) + (lambda (&rest _) (setq indented t)))) + (cj/mu4e-org-contacts-tab-complete) + (should indented))))) + +;;; cj/mu4e-org-contacts-comma-complete + +(ert-deftest test-mu4e-org-contacts-comma-inserts-spaced-in-header () + "Normal: a comma in a header field inserts a comma and a space." + (with-temp-buffer + (insert "To: alice@example.com") + (cl-letf (((symbol-function 'mail-abbrev-in-expansion-header-p) (lambda () t)) + ((symbol-function 'completion-at-point) #'ignore)) + (cj/mu4e-org-contacts-comma-complete) + (should (string-suffix-p ", " (buffer-string)))))) + +(ert-deftest test-mu4e-org-contacts-comma-plain-outside-header () + "Boundary: a comma outside a header field inserts a bare comma." + (with-temp-buffer + (insert "some body text") + (cl-letf (((symbol-function 'mail-abbrev-in-expansion-header-p) (lambda () nil))) + (cj/mu4e-org-contacts-comma-complete) + (should (string-suffix-p "text," (buffer-string))) + (should-not (string-suffix-p ", " (buffer-string)))))) + +;;; cj/mu4e-org-contacts-insert-email + +(ert-deftest test-mu4e-org-contacts-insert-email-noop-outside-header () + "Boundary: direct insert does nothing and does not prompt outside a header." + (with-temp-buffer + (insert "body") + (cl-letf (((symbol-function 'mail-abbrev-in-expansion-header-p) (lambda () nil)) + ((symbol-function 'completing-read) + (lambda (&rest _) (error "should not prompt outside a header")))) + (cj/mu4e-org-contacts-insert-email) + (should (string= "body" (buffer-string)))))) + +(provide 'test-mu4e-org-contacts-integration) +;;; test-mu4e-org-contacts-integration.el ends here |
