aboutsummaryrefslogtreecommitdiff
path: root/tests/test-pearl-views.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-24 13:44:34 -0500
committerCraig Jennings <c@cjennings.net>2026-05-24 13:44:34 -0500
commitb081d62276378b3168c92c06153fd59db0589535 (patch)
tree9be7f7d22e0c9b4a73432fe744c09bb456c671a9 /tests/test-pearl-views.el
downloadpearl-b081d62276378b3168c92c06153fd59db0589535.tar.gz
pearl-b081d62276378b3168c92c06153fd59db0589535.zip
feat: pearl — manage Linear issues from org-mode
Pearl fetches Linear issues into an org file and syncs edits back. It covers list / custom views / saved queries, per-issue and bulk rendering with comments inline, conflict-aware sync of descriptions, titles, and comments, field commands for priority / state / assignee / labels, and a transient dispatch menu. The render folds to a scannable outline and nests issues under a sortable parent. Based on and inspired by Gael Blanchemain's linear-emacs.
Diffstat (limited to 'tests/test-pearl-views.el')
-rw-r--r--tests/test-pearl-views.el130
1 files changed, 130 insertions, 0 deletions
diff --git a/tests/test-pearl-views.el b/tests/test-pearl-views.el
new file mode 100644
index 0000000..2a5d6bd
--- /dev/null
+++ b/tests/test-pearl-views.el
@@ -0,0 +1,130 @@
+;;; test-pearl-views.el --- Tests for Linear Custom Views -*- 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 reading and running Linear Custom Views: the cached views list,
+;; the server-side `--query-view-async' run, `pearl-run-view', the view
+;; branch of refresh, and opening the active view in the browser. HTTP is
+;; stubbed.
+
+;;; Code:
+
+(require 'test-bootstrap (expand-file-name "test-bootstrap.el"))
+(require 'testutil-request (expand-file-name "testutil-request.el"))
+(require 'cl-lib)
+
+;;; --query-view-async
+
+(ert-deftest test-pearl-query-view-async-extracts-issues ()
+ "Running a view extracts the server-side issue nodes into an ok result."
+ (testutil-linear-with-response
+ '((data (customView
+ (issues (nodes . [((id . "i1") (identifier . "ENG-1") (title . "A"))])
+ (pageInfo (hasNextPage . :json-false) (endCursor . nil))))))
+ (let (result)
+ (pearl--query-view-async "view-1" (lambda (r) (setq result r)))
+ (should (eq 'ok (pearl--query-result-status result)))
+ (should (= 1 (length (pearl--query-result-issues result)))))))
+
+;;; --custom-views (cached)
+
+(ert-deftest test-pearl-custom-views-caches ()
+ "The views list is fetched once and served from cache."
+ (let ((pearl-api-key "test-key")
+ (pearl--cache-views nil)
+ (calls 0))
+ (cl-letf (((symbol-function 'request)
+ (lambda (_url &rest args)
+ (cl-incf calls)
+ (funcall (plist-get args :success) :data
+ '((data (customViews
+ (nodes . [((id . "v1") (name . "My View") (url . "https://x"))])
+ (pageInfo (hasNextPage . :json-false)))))))))
+ (let ((views (pearl--custom-views)))
+ (should (= 1 (length views)))
+ (pearl--custom-views)
+ (should (= 1 calls))))))
+
+;;; run-view
+
+(ert-deftest test-pearl-run-view-renders-with-view-source ()
+ "Running a view resolves its id and renders with a view-typed source."
+ (let ((ran-id nil) (rendered-source nil))
+ (cl-letf (((symbol-function 'pearl--custom-views)
+ (lambda (&optional _force)
+ '(((id . "v1") (name . "My View") (url . "https://linear.app/view/v1")))))
+ ((symbol-function 'pearl--query-view-async)
+ (lambda (id cb) (setq ran-id id)
+ (funcall cb (pearl--make-query-result 'ok :issues nil))))
+ ((symbol-function 'pearl--render-query-result)
+ (lambda (_result source) (setq rendered-source source))))
+ (pearl-run-view "My View")
+ (should (string= "v1" ran-id))
+ (should (eq 'view (plist-get rendered-source :type)))
+ (should (string= "v1" (plist-get rendered-source :id)))
+ (should (string= "https://linear.app/view/v1" (plist-get rendered-source :url))))))
+
+;;; refresh-current-view, view branch
+
+(ert-deftest test-pearl-refresh-current-view-runs-view-source ()
+ "Refresh on a view source calls the view query, not the filter query."
+ (let ((view-ran nil)
+ (source '(:type view :name "My View" :id "v1" :url "https://x")))
+ (with-temp-buffer
+ (insert (format "#+title: Linear — My View\n#+LINEAR-SOURCE: %s\n\n"
+ (prin1-to-string source)))
+ (org-mode)
+ (cl-letf (((symbol-function 'pearl--query-view-async)
+ (lambda (id cb) (setq view-ran id)
+ (funcall cb (pearl--make-query-result 'ok :issues nil))))
+ ((symbol-function 'pearl--merge-query-result)
+ (lambda (&rest _) nil)))
+ (pearl-refresh-current-view)
+ (should (string= "v1" view-ran))))))
+
+;;; open-current-view-in-linear
+
+(ert-deftest test-pearl-open-current-view-visits-url ()
+ "Opening the active view visits the source's url."
+ (let ((visited nil)
+ (source '(:type view :name "My View" :id "v1" :url "https://linear.app/view/v1")))
+ (with-temp-buffer
+ (insert (format "#+LINEAR-SOURCE: %s\n" (prin1-to-string source)))
+ (org-mode)
+ (cl-letf (((symbol-function 'browse-url) (lambda (u &rest _) (setq visited u))))
+ (pearl-open-current-view-in-linear)
+ (should (string= "https://linear.app/view/v1" visited))))))
+
+(ert-deftest test-pearl-open-current-view-no-url-errors ()
+ "Opening a non-view or url-less source signals a user error."
+ (let ((source '(:type filter :name "My open issues" :filter (:assignee :me))))
+ (with-temp-buffer
+ (insert (format "#+LINEAR-SOURCE: %s\n" (prin1-to-string source)))
+ (org-mode)
+ (should-error (pearl-open-current-view-in-linear) :type 'user-error))))
+
+;;; the view query fetches comments too
+
+(ert-deftest test-pearl-view-issues-query-requests-comments ()
+ "The Custom View query selects comments, so a view-populated list shows them."
+ (should (string-match-p "comments[[:space:]]*{[[:space:]]*nodes" pearl--view-issues-query)))
+
+(provide 'test-pearl-views)
+;;; test-pearl-views.el ends here