From dcfecf3e4f46c920eb77ca9b3169d0719fa0b9fd Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Wed, 27 May 2026 22:54:43 -0500 Subject: feat(sources): favorites, views, and filters via pick-source Pearl now treats Linear sources (favorites, custom views, ad-hoc filters, saved queries) as one surface. `pearl-pick-source` is the unified picker: it lists every Linear favorite alongside every locally saved query, and dispatches by kind. View / project / cycle / label / user favorites resolve to runnable filter or view sources; issue / document / dashboard favorites open in the browser since they aren't lists. The filter compiler grows id-based forms (`:label-id` and `:assignee-id`), so favorites that name a Linear entity (a user, a label) compile to rename-proof queries rather than fragile name matching. The ad-hoc builder gets a matching member-assignee option ("me / member / any"), so a one-off "what's a teammate working on?" doesn't require building a saved query first. Two pre-existing builder bugs surfaced during verify and got fixed in the same pass. The builder previously passed display names straight to the compiler, but `:team` compiles to `team.key.eq` and `:project` to `project.id.eq`, so Linear rejected every builder run that pinned a team or project. Now the builder resolves the picked name to its key / id before assembling. And `pearl-list-issues-by-project` previously injected `:assignee :me` into the filter, hiding every teammate's work in the project. It now returns the whole project. The save prompt on the builder was rewritten so the local-only nature is unambiguous, and the README grew a Sources section plus a keymap-table entry for `f s`. --- tests/test-pearl-commands.el | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) (limited to 'tests/test-pearl-commands.el') diff --git a/tests/test-pearl-commands.el b/tests/test-pearl-commands.el index ccd39c8..64e38b4 100644 --- a/tests/test-pearl-commands.el +++ b/tests/test-pearl-commands.el @@ -78,16 +78,21 @@ ;;; pearl-list-issues-by-project -(ert-deftest test-pearl-list-issues-by-project-dispatches-with-project-id () - "Selecting a team and project forwards the project id to list-issues." +(ert-deftest test-pearl-list-issues-by-project-dispatches-with-project-id-and-name () + "Selecting a team and project forwards both id and name to list-issues, so +the source can render 'Project issues: '." (let ((pid 'unset) + (pname 'unset) (pearl-default-team-id nil)) (cl-letf (((symbol-function 'pearl-select-team) (lambda () '((id . "t1")))) ((symbol-function 'pearl-select-project) (lambda (_t) '((id . "p1") (name . "Platform")))) - ((symbol-function 'pearl-list-issues) (lambda (project-id) (setq pid project-id)))) + ((symbol-function 'pearl-list-issues) + (lambda (project-id &optional project-name) + (setq pid project-id pname project-name)))) (pearl-list-issues-by-project) - (should (string-equal "p1" pid))))) + (should (string-equal "p1" pid)) + (should (string-equal "Platform" pname))))) (ert-deftest test-pearl-list-issues-by-project-no-team-stops () "With no team selected, list-issues is not called." @@ -149,5 +154,28 @@ (pearl-check-setup) (should-not called)))) +;;; pearl-list-issues source construction (by-project me-lock fix) + +(ert-deftest test-pearl-list-issues-source-default-is-my-open-issues () + "Without a project, pearl-list-issues is me-scoped and named 'My open issues'." + (let ((s (pearl--list-issues-source nil nil))) + (should (equal '(:assignee :me :open t) (car s))) + (should (string= "My open issues" (plist-get (cdr s) :name))))) + +(ert-deftest test-pearl-list-issues-source-project-drops-the-me-lock () + "With a project id (and no name), the filter pins the project only -- no +:assignee key -- and the source name is 'Project issues' (regression against +the old me-locked by-project behavior)." + (let ((s (pearl--list-issues-source "proj-1" nil))) + (should (equal '(:project "proj-1" :open t) (car s))) + (should-not (plist-member (car s) :assignee)) + (should (string= "Project issues" (plist-get (cdr s) :name))))) + +(ert-deftest test-pearl-list-issues-source-project-name-in-display () + "With a project id and name, the source name reads 'Project issues: NAME'." + (let ((s (pearl--list-issues-source "proj-1" "Platform"))) + (should (equal '(:project "proj-1" :open t) (car s))) + (should (string= "Project issues: Platform" (plist-get (cdr s) :name))))) + (provide 'test-pearl-commands) ;;; test-pearl-commands.el ends here -- cgit v1.2.3