aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-06-16 16:49:08 -0500
committerCraig Jennings <c@cjennings.net>2026-06-16 16:49:08 -0500
commit0ab4afcd1e4e336cb69bd983e498bc441828ed5b (patch)
tree28c4d27cca4abb01198a12bf17692d3702ed1f7e /tests
parente723aa663ea62e5ddfda4a7ca360adf9e88d36e2 (diff)
downloadpearl-0ab4afcd1e4e336cb69bd983e498bc441828ed5b.tar.gz
pearl-0ab4afcd1e4e336cb69bd983e498bc441828ed5b.zip
fix(views): save a local view from any Custom View, not just favorites
pearl-save-linear-view-locally only listed favorited Linear views, while pearl-run-linear-view lists every Custom View in the workspace. A view you could run often wasn't offered to save, so the copy-down silently produced nothing. The command now saves the current buffer's view when one is showing, and otherwise prompts over all Custom Views. Favorited custom views also came through nameless: the favorites query fetched customView id but not name, and Linear leaves the favorite's title blank for entity favorites. The picker showed a blank entry and the save dead-ended at a blank name. The query now selects customView name and pearl--normalize-favorite falls back to it, which also fixes blank names in pearl-pick-source. The success message now names the saved view and how to run it or set it as the default.
Diffstat (limited to 'tests')
-rw-r--r--tests/test-pearl-favorites.el17
-rw-r--r--tests/test-pearl-reverse-compile.el78
2 files changed, 88 insertions, 7 deletions
diff --git a/tests/test-pearl-favorites.el b/tests/test-pearl-favorites.el
index 55808b3..3f1452f 100644
--- a/tests/test-pearl-favorites.el
+++ b/tests/test-pearl-favorites.el
@@ -56,6 +56,23 @@ For an issue node, IDENTIFIER fills the issue's user-facing key."
(should (string= "Active bugs" (plist-get p :title)))
(should (string= "view-1" (plist-get p :id)))))
+(ert-deftest test-pearl-normalize-favorite-custom-view-blank-title-uses-name ()
+ "A favorited custom view with a blank own title takes the customView's name.
+Linear populates Favorite.title only for favorite folders, so an entity favorite
+arrives with a null/empty title and the entity's own name is the only display
+string -- the favorites query selects customView.name for exactly this."
+ (let* ((node (list (cons 'id "fav-customView")
+ (cons 'type "customView")
+ (cons 'title nil)
+ (cons 'url "https://linear.app/x/view/v1")
+ (cons 'sortOrder 0.1)
+ (cons 'customView (list (cons 'id "view-1")
+ (cons 'name "Pearl Open Issues")))))
+ (p (pearl--normalize-favorite node)))
+ (should (eq 'view (plist-get p :kind)))
+ (should (string= "Pearl Open Issues" (plist-get p :title)))
+ (should (string= "view-1" (plist-get p :id)))))
+
(ert-deftest test-pearl-normalize-favorite-project ()
(let ((p (pearl--normalize-favorite
(test-pearl-fav--node "project" "Platform" "https://linear.app/x/project/p1" 0.2
diff --git a/tests/test-pearl-reverse-compile.el b/tests/test-pearl-reverse-compile.el
index 382c72a..209d9a6 100644
--- a/tests/test-pearl-reverse-compile.el
+++ b/tests/test-pearl-reverse-compile.el
@@ -195,13 +195,77 @@
;;; copy-down command-side: candidate builder + finish
-(ert-deftest test-pearl-linear-view-favorites-extracts-views ()
- "Only view-kind favorites become copy-down candidates, as (title . id)."
- (let ((favs (list (list :kind 'view :title "V1" :id "vid-1")
- (list :kind 'project :title "P" :id "pid")
- (list :kind 'view :title "V2" :id "vid-2"))))
- (should (equal (pearl--linear-view-favorites favs)
- '(("V1" . "vid-1") ("V2" . "vid-2"))))))
+(ert-deftest test-pearl-save-linear-view-locally-saves-current-view ()
+ "When the buffer shows a Linear view, the command offers to save that view
+directly -- without touching favorites or fetching the custom-view list -- and
+hands its name + id to the finisher."
+ (let (finished favorites-called custom-views-called)
+ (cl-letf (((symbol-function 'pearl--require-account-context) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--read-active-source)
+ (lambda () '(:type view :name "Pearl Open Issues" :id "view-1")))
+ ((symbol-function 'pearl--read-yes-no) (lambda (&rest _) t))
+ ((symbol-function 'pearl--progress) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--favorites-async)
+ (lambda (&rest _) (setq favorites-called t)))
+ ((symbol-function 'pearl--custom-views)
+ (lambda (&rest _) (setq custom-views-called t) nil))
+ ((symbol-function 'pearl--customview-filterdata-async)
+ (lambda (view-id cb) (funcall cb (list view-id))))
+ ((symbol-function 'pearl--save-linear-view-locally-finish)
+ (lambda (title view-id _fd) (setq finished (cons title view-id)))))
+ (pearl-save-linear-view-locally)
+ (should (equal finished '("Pearl Open Issues" . "view-1")))
+ (should-not favorites-called)
+ (should-not custom-views-called))))
+
+(ert-deftest test-pearl-save-linear-view-locally-picks-from-all-views ()
+ "With no view in the buffer, the picker lists ALL custom views (not just
+favorites) and hands the chosen view's name + id to the finisher."
+ (let (finished favorites-called)
+ (cl-letf (((symbol-function 'pearl--require-account-context) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--read-active-source) (lambda () nil))
+ ((symbol-function 'pearl--progress) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--favorites-async)
+ (lambda (&rest _) (setq favorites-called t)))
+ ((symbol-function 'pearl--custom-views)
+ (lambda (&rest _)
+ '(((id . "v1") (name . "Pearl Open Issues"))
+ ((id . "v2") (name . "My bugs")))))
+ ((symbol-function 'completing-read) (lambda (&rest _) "My bugs"))
+ ((symbol-function 'pearl--customview-filterdata-async)
+ (lambda (view-id cb) (funcall cb (list view-id))))
+ ((symbol-function 'pearl--save-linear-view-locally-finish)
+ (lambda (title view-id _fd) (setq finished (cons title view-id)))))
+ (pearl-save-linear-view-locally)
+ (should (equal finished '("My bugs" . "v2")))
+ (should-not favorites-called))))
+
+(ert-deftest test-pearl-save-linear-view-locally-declined-current-falls-to-picker ()
+ "Declining the current-view offer falls through to the all-views picker."
+ (let (finished)
+ (cl-letf (((symbol-function 'pearl--require-account-context) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--read-active-source)
+ (lambda () '(:type view :name "Current" :id "cur")))
+ ((symbol-function 'pearl--read-yes-no) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--progress) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--custom-views)
+ (lambda (&rest _) '(((id . "v9") (name . "Picked")))))
+ ((symbol-function 'completing-read) (lambda (&rest _) "Picked"))
+ ((symbol-function 'pearl--customview-filterdata-async)
+ (lambda (view-id cb) (funcall cb (list view-id))))
+ ((symbol-function 'pearl--save-linear-view-locally-finish)
+ (lambda (title view-id _fd) (setq finished (cons title view-id)))))
+ (pearl-save-linear-view-locally)
+ (should (equal finished '("Picked" . "v9"))))))
+
+(ert-deftest test-pearl-save-linear-view-locally-no-views-errors ()
+ "With no current view and no custom views, the command errors rather than
+silently saving nothing."
+ (cl-letf (((symbol-function 'pearl--require-account-context) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--read-active-source) (lambda () nil))
+ ((symbol-function 'pearl--progress) (lambda (&rest _) nil))
+ ((symbol-function 'pearl--custom-views) (lambda (&rest _) nil)))
+ (should-error (pearl-save-linear-view-locally) :type 'user-error)))
(ert-deftest test-pearl-save-linear-view-locally-finish-nil-filterdata-errors ()
"A nil filterData (fetch failure / no filter) errors rather than saving."