diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-03 06:52:14 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-03 06:52:14 -0500 |
| commit | 483660f9477cc9504a35a3a29ee1fbca52cccdb1 (patch) | |
| tree | 35177a9c002d37d5773289a6a04b4e05dbe3aa09 /tests/test-pearl-views.el | |
| parent | 8acc99d88527a9dfecf7ebceb858f344819d10c3 (diff) | |
| download | pearl-483660f9477cc9504a35a3a29ee1fbca52cccdb1.tar.gz pearl-483660f9477cc9504a35a3a29ee1fbca52cccdb1.zip | |
feat(views): run a Linear view in its configured sort order
A Linear view's issue query passed no orderBy, so issues came back in Linear's API default, createdAt descending. That's neither the order set on the view in Linear nor the updatedAt-desc the rest of pearl uses, and the two unrelated defaults colliding is what made a view's order look random.
Linear exposes a view's configured sort in viewPreferencesValues (viewOrdering plus viewOrderingDirection), and the issues connection accepts an IssueSortInput sort arg that applies it server-side. I read the view's ordering when running it, translate it to that sort arg, and pass it through, so a view arrives in the order you gave it in Linear. The sort is stamped on the recorded source so a refresh keeps the same order. An ordering pearl can't map, or a view with no preference, falls back to updatedAt-desc rather than the createdAt default, so it lines up with every other pearl fetch.
This is the sort half of matching the Linear UI. Grouping (issueGrouping into org sections) is the larger remaining half and is tracked separately.
Diffstat (limited to 'tests/test-pearl-views.el')
| -rw-r--r-- | tests/test-pearl-views.el | 86 |
1 files changed, 84 insertions, 2 deletions
diff --git a/tests/test-pearl-views.el b/tests/test-pearl-views.el index e22a6d3..7a211df 100644 --- a/tests/test-pearl-views.el +++ b/tests/test-pearl-views.el @@ -101,6 +101,51 @@ and leaves the run-view picker empty (the 2026-06-02 regression)." (should-not (pearl--view-url nil)) (should-not (pearl--view-url "")))) +;;; view sort — match the Linear view's configured ordering + +(ert-deftest test-pearl-view-sort-arg-maps-priority-desc () + "A priority ordering with desc direction maps to an IssueSortInput list." + (should (equal (vector '(("priority" ("order" . "Descending")))) + (pearl--view-sort-arg "priority" "desc")))) + +(ert-deftest test-pearl-view-sort-arg-honors-asc-direction () + "An asc direction maps to Ascending." + (should (equal (vector '(("accumulatedStateUpdatedAt" ("order" . "Ascending")))) + (pearl--view-sort-arg "accumulatedStateUpdatedAt" "asc")))) + +(ert-deftest test-pearl-view-sort-arg-defaults-nil-direction-to-descending () + "A nil direction defaults to Descending — the field's natural order, and what +Linear's UI shows for a priority view with no explicit direction." + (should (equal (vector '(("priority" ("order" . "Descending")))) + (pearl--view-sort-arg "priority" nil)))) + +(ert-deftest test-pearl-view-sort-arg-unsupported-ordering-is-nil () + "An ordering with no IssueSortInput key yields nil, so the caller falls back +to the default sort rather than sending an invalid sort key." + (should-not (pearl--view-sort-arg "someExoticOrdering" "asc"))) + +(ert-deftest test-pearl-view-issues-query-accepts-sort () + "The view issues query declares the sort variable and forwards it to the +issues connection, so a view runs under a chosen order." + (let ((q (pearl--view-issues-query))) + (should (string-match-p "\\$sort: \\[IssueSortInput!\\]" q)) + (should (string-match-p "sort: \\$sort" q)))) + +(ert-deftest test-pearl-query-view-async-defaults-sort-to-updated-desc () + "With no explicit sort, a view runs under updatedAt-desc rather than Linear's +createdAt-desc API default, so its order matches the rest of pearl." + (let ((sent 'unset)) + (cl-letf (((symbol-function 'pearl--graphql-request-async) + (lambda (_q vars success &optional _err) + (setq sent (cdr (assoc "sort" vars))) + (funcall success + '((data (customView + (issues (nodes . []) + (pageInfo (hasNextPage . :json-false) + (endCursor)))))))))) + (pearl--query-view-async "v1" #'ignore) + (should (equal pearl--default-issue-sort sent))))) + ;;; run-view (ert-deftest test-pearl-run-view-renders-with-view-source () @@ -113,7 +158,7 @@ and leaves the run-view picker empty (the 2026-06-02 regression)." '(((id . "v1") (name . "My View"))))) ((symbol-function 'pearl--org-url-key) (lambda (&optional _) "deepsat")) ((symbol-function 'pearl--query-view-async) - (lambda (id cb) (setq ran-id id) + (lambda (id cb &optional _sort) (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)))) @@ -124,6 +169,26 @@ and leaves the run-view picker empty (the 2026-06-02 regression)." (should (string= "https://linear.app/deepsat/view/v1" (plist-get rendered-source :url)))))) +(ert-deftest test-pearl-run-view-applies-and-stamps-view-sort () + "Running a view resolves its configured ordering, passes it to the query, and +records it on the source so a later refresh reuses the same order." + (let ((passed-sort 'unset) (rendered-source nil)) + (cl-letf (((symbol-function 'pearl--custom-views) + (lambda (&optional _force) + '(((id . "v1") (name . "My View") + (viewPreferencesValues (viewOrdering . "priority") + (viewOrderingDirection . "desc")))))) + ((symbol-function 'pearl--org-url-key) (lambda (&optional _) "deepsat")) + ((symbol-function 'pearl--query-view-async) + (lambda (_id cb &optional sort) (setq passed-sort sort) + (funcall cb (pearl--make-query-result 'ok :issues nil)))) + ((symbol-function 'pearl--render-query-result) + (lambda (_result source) (setq rendered-source source)))) + (pearl-run-linear-view "My View") + (should (equal (vector '(("priority" ("order" . "Descending")))) passed-sort)) + (should (equal (vector '(("priority" ("order" . "Descending")))) + (plist-get rendered-source :sort)))))) + ;;; refresh-current-view, view branch (ert-deftest test-pearl-refresh-current-view-runs-view-source () @@ -135,13 +200,30 @@ and leaves the run-view picker empty (the 2026-06-02 regression)." (prin1-to-string source))) (org-mode) (cl-letf (((symbol-function 'pearl--query-view-async) - (lambda (id cb) (setq view-ran id) + (lambda (id cb &optional _sort) (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)))))) +(ert-deftest test-pearl-refresh-current-view-passes-stored-sort () + "Refreshing a view source forwards the :sort recorded on it, so the refreshed +list keeps the order the view was run under." + (let ((passed-sort 'unset) + (source '(:type view :name "My View" :id "v1" + :sort [(("priority" ("order" . "Descending")))]))) + (with-temp-buffer + (insert (format "#+LINEAR-SOURCE: %s\n\n" (prin1-to-string source))) + (org-mode) + (cl-letf (((symbol-function 'pearl--query-view-async) + (lambda (_id cb &optional sort) (setq passed-sort sort) + (funcall cb (pearl--make-query-result 'ok :issues nil)))) + ((symbol-function 'pearl--merge-query-result) + (lambda (&rest _) nil))) + (pearl-refresh-current-view) + (should (equal [(("priority" ("order" . "Descending")))] passed-sort)))))) + ;;; open-current-view-in-linear (ert-deftest test-pearl-open-current-view-visits-url () |
