aboutsummaryrefslogtreecommitdiff
path: root/tests/test-pearl-compose.el
diff options
context:
space:
mode:
Diffstat (limited to 'tests/test-pearl-compose.el')
-rw-r--r--tests/test-pearl-compose.el153
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