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-merge.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-merge.el')
| -rw-r--r-- | tests/test-pearl-merge.el | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/tests/test-pearl-merge.el b/tests/test-pearl-merge.el index 931849d..9f9307c 100644 --- a/tests/test-pearl-merge.el +++ b/tests/test-pearl-merge.el @@ -72,6 +72,22 @@ "#+LINEAR-TRUNCATED: no\n\n" (mapconcat #'pearl--format-issue-as-org-entry issues ""))) +(defun test-pearl-merge--issue-st (id title state-name state-type) + "A normalized issue plist in state STATE-NAME/STATE-TYPE, for grouping tests." + (list :id id :identifier (concat "ENG-" id) :title title + :description (concat "Desc " id ".") + :priority 2 :url (concat "https://linear.app/x/" id) + :updated-at "2026-05-23T03:00:00.000Z" + :state (list :id (concat "s-" state-name) :name state-name :type state-type) + :team (list :id "t1" :key "ENG" :name "Engineering"))) + +(defun test-pearl-merge--grouped-buffer (grouping &rest issues) + "Render ISSUES into a grouped view buffer (no help header, network stubbed)." + (let ((pearl-help-header nil)) + (cl-letf (((symbol-function 'pearl--team-states) (lambda (_team-id) nil))) + (pearl--build-org-content + issues (list :type 'view :name "By status" :id "v1" :group grouping))))) + ;;; --merge-issues-into-buffer (ert-deftest test-pearl-merge-updates-existing-in-place () @@ -123,6 +139,69 @@ (goto-char (point-min)) (should-not (re-search-forward "Beta" nil t))))) +;;; grouped-view merge (issueGrouping sections) + +(ert-deftest test-pearl-merge-grouped-update-preserves-level () + "Updating an issue in a grouped buffer re-renders it at level 3 (under its +group), not promoted to level 2 the way a flat append would render it." + (test-pearl-merge--in-org + (test-pearl-merge--grouped-buffer + "workflowState" + (test-pearl-merge--issue-st "a" "Alpha" "Todo" "unstarted") + (test-pearl-merge--issue-st "b" "Beta" "Done" "completed")) + (let ((counts (pearl--merge-issues-into-buffer + (list (test-pearl-merge--issue-st "a" "Alpha Renamed" "Todo" "unstarted") + (test-pearl-merge--issue-st "b" "Beta" "Done" "completed")) + "workflowState"))) + (should (= 2 (plist-get counts :updated))) + (goto-char (point-min)) + (should (re-search-forward "^\\*\\*\\* .*Alpha Renamed" nil t)) + ;; Not promoted to a level-2 heading. + (should-not (save-excursion (goto-char (point-min)) + (re-search-forward "^\\*\\* [A-Z].*Alpha Renamed" nil t)))))) + +(ert-deftest test-pearl-merge-grouped-new-issue-joins-existing-group () + "A new issue whose group already exists is inserted under that group's section +at level 3, not appended at the buffer end." + (test-pearl-merge--in-org + (test-pearl-merge--grouped-buffer + "workflowState" + (test-pearl-merge--issue-st "a" "Alpha" "Todo" "unstarted") + (test-pearl-merge--issue-st "b" "Beta" "Done" "completed")) + (let ((counts (pearl--merge-issues-into-buffer + (list (test-pearl-merge--issue-st "a" "Alpha" "Todo" "unstarted") + (test-pearl-merge--issue-st "b" "Beta" "Done" "completed") + (test-pearl-merge--issue-st "c" "Gamma" "Todo" "unstarted")) + "workflowState"))) + (should (= 1 (plist-get counts :added))) + ;; Gamma renders at level 3, inside the Todo section (before Done). + (goto-char (point-min)) + (let ((todo (re-search-forward "^\\*\\* Todo$" nil t)) + (gamma (re-search-forward "^\\*\\*\\* .*Gamma" nil t)) + (done (save-excursion (goto-char (point-min)) + (re-search-forward "^\\*\\* Done$" nil t)))) + (should (and todo gamma done)) + (should (< todo gamma)) + (should (< gamma done)))))) + +(ert-deftest test-pearl-merge-grouped-new-issue-creates-missing-group () + "A new issue whose group doesn't exist yet gets a fresh `** LABEL' section +created for it, with the issue at level 3 under it." + (test-pearl-merge--in-org + (test-pearl-merge--grouped-buffer + "workflowState" + (test-pearl-merge--issue-st "a" "Alpha" "Todo" "unstarted")) + (should-not (save-excursion (re-search-forward "^\\*\\* In Progress$" nil t))) + (let ((counts (pearl--merge-issues-into-buffer + (list (test-pearl-merge--issue-st "a" "Alpha" "Todo" "unstarted") + (test-pearl-merge--issue-st "d" "Delta" "In Progress" "started")) + "workflowState"))) + (should (= 1 (plist-get counts :added))) + (goto-char (point-min)) + (let ((grp (re-search-forward "^\\*\\* In Progress$" nil t))) + (should grp) + (should (re-search-forward "^\\*\\*\\* .*Delta" nil t)))))) + (ert-deftest test-pearl-merge-keeps-unpushed-edit-on-update () "An existing subtree with unpushed body edits is kept, not overwritten." (test-pearl-merge--in-org |
