diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test-pearl-compose.el | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/tests/test-pearl-compose.el b/tests/test-pearl-compose.el new file mode 100644 index 0000000..bda75cb --- /dev/null +++ b/tests/test-pearl-compose.el @@ -0,0 +1,153 @@ +;;; test-pearl-compose.el --- Tests for the compose buffer -*- lexical-binding: t; -*- + +;; Copyright (C) 2026 Craig Jennings + +;; Author: Craig Jennings <c@cjennings.net> + +;; 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 <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Tests for the shared compose buffer (`pearl--compose-in-buffer' and its +;; submit/abort/body helpers) and the two commands wired onto it: +;; `pearl-add-comment' (interactive composer path) and +;; `pearl-compose-current-description'. The body is extracted below a read-only +;; header and converted Org->Markdown by the callers. + +;;; Code: + +(require 'test-bootstrap (expand-file-name "test-bootstrap.el")) +(require 'cl-lib) + +(defmacro test-pearl--with-compose (initial &rest body) + "Open a compose buffer holding INITIAL, run BODY in it, capturing the submit. +Binds `captured' to the value the on-finish receives (or `:none'). Stubs +`pop-to-buffer' so the helper does not touch windows in batch." + (declare (indent 1)) + `(let ((captured :none)) + (cl-letf (((symbol-function 'pop-to-buffer) (lambda (b &rest _) b))) + (let ((buf (pearl--compose-in-buffer + "test" "# header line\n" ,initial + (lambda (text) (setq captured text))))) + (with-current-buffer buf ,@body) + (when (buffer-live-p buf) (kill-buffer buf)))))) + +;;; --compose-in-buffer setup + +(ert-deftest test-pearl-compose-sets-up-org-buffer () + "The compose buffer is an Org buffer with the header and the initial body." + (test-pearl--with-compose "draft text" + (should (eq major-mode 'org-mode)) + (should (markerp pearl--compose-body-start)) + (should (string-match-p "# header line" (buffer-string))) + (should (string= "draft text" (pearl--compose-body))))) + +(ert-deftest test-pearl-compose-body-excludes-header () + "The body is only the text below the read-only header." + (test-pearl--with-compose "" + (goto-char (point-max)) + (insert "the new body") + (should (string= "the new body" (pearl--compose-body))))) + +(ert-deftest test-pearl-compose-header-is-read-only () + "Editing inside the header is refused." + (test-pearl--with-compose "body" + (goto-char (+ (point-min) 3)) + (should-error (insert "x") :type 'text-read-only))) + +;;; submit / abort + +(ert-deftest test-pearl-compose-submit-hands-body-to-callback () + "C-c C-c hands the trimmed body to the callback and kills the buffer." + (let ((captured :none) buf) + (cl-letf (((symbol-function 'pop-to-buffer) (lambda (b &rest _) b))) + (setq buf (pearl--compose-in-buffer + "t" "# h\n" " hello " + (lambda (text) (setq captured text)))) + (with-current-buffer buf (pearl--compose-submit)) + (should (string= "hello" captured)) + (should-not (buffer-live-p buf))))) + +(ert-deftest test-pearl-compose-abort-skips-callback () + "C-c C-k kills the buffer without invoking the callback." + (let ((called nil) buf) + (cl-letf (((symbol-function 'pop-to-buffer) (lambda (b &rest _) b))) + (setq buf (pearl--compose-in-buffer + "t" "# h\n" "body" (lambda (_) (setq called t)))) + (with-current-buffer buf (pearl--compose-abort)) + (should-not called) + (should-not (buffer-live-p buf))))) + +;;; add-comment composer wiring + +(defmacro test-pearl--in-issue (&rest body) + "Run BODY in an org buffer with point inside a Linear issue subtree." + (declare (indent 0)) + `(let ((pearl-state-to-todo-mapping '(("Todo" . "TODO")))) + (with-temp-buffer + (insert "** TODO [#B] ENG-1: Issue\n:PROPERTIES:\n:LINEAR-ID: a\n:END:\nBody.\n") + (org-mode) + (goto-char (point-min)) + (re-search-forward "Body\\.") + ,@body))) + +(ert-deftest test-pearl-add-comment-interactive-composes-and-converts () + "An interactive add-comment composes Org and sends the Markdown conversion." + (test-pearl--in-issue + (let (sent) + (cl-letf (((symbol-function 'pearl--compose-in-buffer) + (lambda (_label _instr initial on-finish) + ;; the composer would start empty for a new comment + (should (string= "" initial)) + (funcall on-finish "see *bold* and `code`"))) + ((symbol-function 'pearl--create-comment-async) + (lambda (_id body _cb) (setq sent body)))) + (pearl-add-comment) ; interactive form: body nil + ;; Org markup converted to Markdown before sending + (should (string= "see **bold** and `code`" sent)))))) + +(ert-deftest test-pearl-add-comment-with-body-skips-composer () + "A non-interactive add-comment with BODY sends it directly, no composer." + (test-pearl--in-issue + (let ((composed nil) sent) + (cl-letf (((symbol-function 'pearl--compose-in-buffer) + (lambda (&rest _) (setq composed t))) + ((symbol-function 'pearl--create-comment-async) + (lambda (_id body _cb) (setq sent body)))) + (pearl-add-comment "literal body") + (should-not composed) + (should (string= "literal body" sent)))))) + +;;; compose-current-description wiring + +(ert-deftest test-pearl-compose-description-seeds-body-and-syncs () + "The description composer seeds the current body and, on submit, writes it back and syncs." + (test-pearl--in-issue + (let (seeded synced) + (cl-letf (((symbol-function 'pearl--compose-in-buffer) + (lambda (_label _instr initial on-finish) + (setq seeded initial) + (funcall on-finish "Edited description"))) + ((symbol-function 'pearl-sync-current-issue) + (lambda () (setq synced t)))) + (pearl-compose-current-description) + ;; seeded with the existing body + (should (string= "Body." seeded)) + ;; the edited text was written into the issue body, then synced + (should synced) + (goto-char (point-min)) + (should (re-search-forward "Edited description" nil t)))))) + +(provide 'test-pearl-compose) +;;; test-pearl-compose.el ends here |
