From b34909973bcf9f0fed940cab5e3192e54708ad7f Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 28 May 2026 01:11:22 -0500 Subject: feat(saved): add pearl-delete-saved-query and a sentinel cancel for run `pearl-saved-queries' was write-only from a user's perspective. Save was bound to the builder, run was bound to a key, but removing a stale entry meant `M-x customize-variable RET pearl-saved-queries RET` and a manual delete from the Customize buffer. `pearl-delete-saved-query` under `C-; L k q` (and `q` in the transient's Delete group) closes that gap: completes over the saved query names with `[ None. ]` at the top as a cancel, confirms, removes the entry, and persists via `customize-save-variable`. Linear is untouched. Saved queries are local. Pushing them up to Linear as custom views is a separate spec. While in the same path, `pearl-run-saved-query` got the same sentinel cancel. Both prompts use the keep-order completion table so `[ None. ]` stays at the top regardless of the user's completion sorter. Both error cleanly when `pearl-saved-queries` is empty rather than offering an empty picker. Six tests cover the new command (delete-on-yes, no-at-confirm, sentinel-cancels, unknown-name-errors, empty-list-errors) plus run's sentinel cancel. --- tests/test-pearl-adhoc.el | 61 ++++++++++++++++++++++++++++++++++++++++++++++ tests/test-pearl-keymap.el | 3 ++- 2 files changed, 63 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/test-pearl-adhoc.el b/tests/test-pearl-adhoc.el index 2bcc575..2dec1a3 100644 --- a/tests/test-pearl-adhoc.el +++ b/tests/test-pearl-adhoc.el @@ -124,6 +124,67 @@ candidate list, so completion still works the way the framework expects." (should (equal '(:priority 1) (plist-get (cdr (assoc "Dup" pearl-saved-queries)) :filter)))))) +;;; pearl-delete-saved-query + +(ert-deftest test-pearl-delete-saved-query-removes-entry-on-yes () + "Confirming the prompt removes the named entry and persists." + (let ((pearl-saved-queries '(("Keep" :filter (:open t)) + ("Drop" :filter (:priority 1)))) + (persisted nil)) + (cl-letf (((symbol-function 'yes-or-no-p) (lambda (&rest _) t)) + ((symbol-function 'customize-save-variable) + (lambda (&rest _) (setq persisted t)))) + (pearl-delete-saved-query "Drop") + (should-not (assoc "Drop" pearl-saved-queries)) + (should (assoc "Keep" pearl-saved-queries)) + (should persisted)))) + +(ert-deftest test-pearl-delete-saved-query-no-at-confirm-keeps-entry () + "Declining the confirm prompt leaves the entry in place and does not persist." + (let ((pearl-saved-queries '(("Keep" :filter (:open t)))) + (persisted nil)) + (cl-letf (((symbol-function 'yes-or-no-p) (lambda (&rest _) nil)) + ((symbol-function 'customize-save-variable) + (lambda (&rest _) (setq persisted t)))) + (pearl-delete-saved-query "Keep") + (should (assoc "Keep" pearl-saved-queries)) + (should-not persisted)))) + +(ert-deftest test-pearl-delete-saved-query-sentinel-cancels () + "Picking the `pearl--filter-none' sentinel cancels without touching anything." + (let ((pearl-saved-queries '(("Keep" :filter (:open t)))) + (confirmed nil)) + (cl-letf (((symbol-function 'yes-or-no-p) + (lambda (&rest _) (setq confirmed t) t)) + ((symbol-function 'customize-save-variable) + (lambda (&rest _) nil))) + (pearl-delete-saved-query pearl--filter-none) + (should (assoc "Keep" pearl-saved-queries)) + (should-not confirmed)))) + +(ert-deftest test-pearl-delete-saved-query-unknown-name-errors () + "An unknown name (e.g. typed past completion) signals a user-error." + (let ((pearl-saved-queries '(("Keep" :filter (:open t))))) + (should-error (pearl-delete-saved-query "Nope") :type 'user-error))) + +(ert-deftest test-pearl-delete-saved-query-empty-list-errors-on-interactive () + "Called interactively with no saved queries, the command errors cleanly +rather than offering an empty picker." + (let ((pearl-saved-queries nil)) + (should-error (call-interactively #'pearl-delete-saved-query) + :type 'user-error))) + +;;; pearl-run-saved-query sentinel cancel + +(ert-deftest test-pearl-run-saved-query-sentinel-cancels () + "Picking the `pearl--filter-none' sentinel cancels the run without fetching." + (let ((fetched nil) + (pearl-saved-queries '(("Open" :filter (:open t))))) + (cl-letf (((symbol-function 'pearl--query-issues-async) + (lambda (&rest _) (setq fetched t)))) + (should-error (pearl-run-saved-query pearl--filter-none) :type 'user-error) + (should-not fetched)))) + ;;; pearl-list-issues-filtered (ert-deftest test-pearl-list-issues-filtered-runs-with-source () diff --git a/tests/test-pearl-keymap.el b/tests/test-pearl-keymap.el index 5cc658d..d04e309 100644 --- a/tests/test-pearl-keymap.el +++ b/tests/test-pearl-keymap.el @@ -89,7 +89,8 @@ The lowercase `c' is no longer a direct command -- it is the create prefix." (should (eq 'pearl-create-issue (lookup-key pearl-prefix-map (kbd "c t")))) (should (eq 'pearl-create-comment (lookup-key pearl-prefix-map (kbd "c c")))) (should (eq 'pearl-delete-current-issue (lookup-key pearl-prefix-map (kbd "k t")))) - (should (eq 'pearl-delete-current-comment (lookup-key pearl-prefix-map (kbd "k c"))))) + (should (eq 'pearl-delete-current-comment (lookup-key pearl-prefix-map (kbd "k c")))) + (should (eq 'pearl-delete-saved-query (lookup-key pearl-prefix-map (kbd "k q"))))) (ert-deftest test-pearl-prefix-map-open-and-copy-groups () "The open group resolves the two open actions; copy resolves copy-url." -- cgit v1.2.3