diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-03 09:12:04 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-03 09:12:04 -0500 |
| commit | 1f729465d2247fb6882185e426860de869fe9b71 (patch) | |
| tree | 09902839c64633a583cdf696c7422ecc6cfd331d /tests/test-pearl-output.el | |
| parent | 580309bb65a5e529c7aab5e58d3983a37c471023 (diff) | |
| download | pearl-1f729465d2247fb6882185e426860de869fe9b71.tar.gz pearl-1f729465d2247fb6882185e426860de869fe9b71.zip | |
feat(views): render a Linear view's grouping as Org sections
A Linear view can group its issues (by status, project, assignee, priority, cycle), and until now pearl ignored that: a "by status" or "by project" view came back as one flat list and didn't look like itself. The sort half of matching the view UI shipped earlier. This is the grouping half.
I read issueGrouping off the view preferences (next to the ordering pearl already fetches) and stamp it on the source as :group. When it's a single-valued dimension, the builder partitions issues into groups in first-appearance order and renders each as a level-2 section heading with its issues at level 3. An issue with no value for the dimension lands in a "No project" / "No assignee" bucket, mirroring Linear. The fold depth tracks where issues render (show3levels grouped, show2levels flat), so the buffer always opens unfolded to the issue level. issueGrouping "none", label-grouping (an issue carries several labels, deferred), and sub-grouping render the flat list.
The group headings carry no LINEAR-ID, so save and merge skip them like the help header. Two merge points needed grouping-awareness: an in-place update now re-renders at the issue's existing heading level rather than flattening to level 2, and a new issue on refresh is placed under its group section (creating the section if it doesn't exist yet) instead of appended at the end. Both entry points share the resolver, so run-linear-view and a favorited view picked in pearl-pick-source group the same way.
Verified live against the workspace: a by-project view renders one section per project plus a "No project" bucket, issues at level 3, opened to show3levels.
Diffstat (limited to 'tests/test-pearl-output.el')
| -rw-r--r-- | tests/test-pearl-output.el | 47 |
1 files changed, 46 insertions, 1 deletions
diff --git a/tests/test-pearl-output.el b/tests/test-pearl-output.el index 8b02fcb..f02df5f 100644 --- a/tests/test-pearl-output.el +++ b/tests/test-pearl-output.el @@ -70,6 +70,51 @@ (should (string-match-p "^#\\+title:" out)) (should-not (string-match-p "^\\*\\*\\* " out)))) +;;; --build-org-content grouping (issueGrouping -> Org sections) + +(ert-deftest test-pearl-build-org-content-flat-startup-show2levels () + "An ungrouped view folds to show2levels and renders issues at level 2." + (let* ((issues '((:id "1" :identifier "SE-1" :title "One" + :state (:name "Started" :type "started") :priority 0))) + (source '(:type view :name "Flat" :id "v1")) + (out (pearl--build-org-content issues source))) + (should (string-match-p "^#\\+STARTUP: show2levels$" out)) + (should (string-match-p "^\\*\\* STARTED " out)) + (should-not (string-match-p "^\\*\\*\\* " out)))) + +(ert-deftest test-pearl-build-org-content-grouped-sections-and-startup () + "A grouped view folds to show3levels, emits a level-2 group heading per group +in first-appearance order, and renders issues at level 3 under them." + (let* ((issues '((:id "1" :identifier "SE-1" :title "One" + :state (:name "Started" :type "started") :priority 0) + (:id "2" :identifier "SE-2" :title "Two" + :state (:name "Backlog" :type "backlog") :priority 0) + (:id "3" :identifier "SE-3" :title "Three" + :state (:name "Started" :type "started") :priority 0))) + (source '(:type view :name "By status" :id "v1" :group "workflowState")) + (out (pearl--build-org-content issues source))) + (should (string-match-p "^#\\+STARTUP: show3levels$" out)) + (should (string-match-p "^\\*\\* Started$" out)) + (should (string-match-p "^\\*\\* Backlog$" out)) + ;; Issues live at level 3 under the group headings. + (should (string-match-p "^\\*\\*\\* STARTED " out)) + ;; First-appearance order: SE-1 (Started) before SE-2 (Backlog). + (should (< (string-match "^\\*\\* Started$" out) + (string-match "^\\*\\* Backlog$" out))) + ;; The group heading carries no LINEAR-ID, so save/merge skip it. + (let ((started (string-match "^\\*\\* Started$" out)) + (next-id (string-match "LINEAR-ID:" out))) + (should started) + (should (< started next-id))))) + +(ert-deftest test-pearl-build-org-content-grouped-source-roundtrips () + "A grouped view's source (with :group) reads back from the rendered header." + (let* ((source '(:type view :name "By project" :id "v1" :group "project")) + (out (pearl--build-org-content '() source))) + (with-temp-buffer + (insert out) + (should (equal source (pearl--read-active-source)))))) + (ert-deftest test-pearl-build-org-content-title-uses-view-name-verbatim () "The view name in the file title matches Linear's capitalization verbatim, independent of `pearl-title-case-headings' (which governs issue headings only)." @@ -336,7 +381,7 @@ refresh prompt stubbed to return CHOICE. BUF and TMP are bound in BODY." (let ((merged nil)) (test-pearl-output--with-dirty-buffer ?m (cl-letf (((symbol-function 'pearl--merge-issues-into-buffer) - (lambda (_issues) (setq merged t) '(:updated 1 :added 0 :dropped 0 :skipped 0))) + (lambda (_issues &optional _grouping) (setq merged t) '(:updated 1 :added 0 :dropped 0 :skipped 0))) ((symbol-function 'pearl--update-source-header) #'ignore) ((symbol-function 'pearl--update-derived-todo-header) #'ignore) ((symbol-function 'pearl-highlight-comments) #'ignore) |
