From 846065d536e363d8afc48f597243ebcd1932bd38 Mon Sep 17 00:00:00 2001 From: Bozhidar Batsov Date: Wed, 8 Jul 2026 14:38:54 +0300 Subject: [PATCH] Raise test coverage to ~55% Cover the command/dispatch layer that regressions would hit hardest: - `helm-projectile' entry point (in-project sources vs. switch-project fallback); - `helm-projectile-grep' argument hand-off; - `helm-projectile-file-persistent-action' file preview; - the single-candidate paths of `helm-projectile--find-file-dwim-1' and `helm-projectile--find-other-file-1'; - `helm-projectile-dired-files-delete-action', the remaining virtual-Dired action, against a sandbox project. Also resolve the sandbox root to its truename so it matches the paths Dired reports back (on macOS the temp dir is under a symlinked /var). Coverage goes from ~46% to ~55% (75 specs, up from 68). --- CHANGELOG.md | 1 + test/helm-projectile-test-helper.el | 5 +- test/helm-projectile-test.el | 84 +++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 759f6ba..f1af9ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Cover the search-command construction: the grep/git-grep/ack command templates built by `helm-projectile-grep-or-ack`, the per-searcher ignore globs (`--ignore` vs `--glob !`) built by `helm-projectile--ag-1`, and the ignored-files/directories unions. * Measure test coverage with `undercover` and report it to Codecov from a dedicated (non-blocking) CI job. * Raise test coverage from ~34% to ~46%: cover `helm-projectile-ack`'s ignore-argument building and executable detection, the ag/rg default-input helpers, `helm-projectile-remove-known-project`, `helm-projectile-all-dired-buffers`, the `helm-projectile-switch-project-by-name` variants, and the `helm-projectile-dired-files-new-action` virtual-Dired builder. +* Raise test coverage further to ~55%: cover the `helm-projectile` entry point, `helm-projectile-grep`, `helm-projectile-file-persistent-action`, the `find-file-dwim` / `find-other-file` single-candidate paths, and the `helm-projectile-dired-files-delete-action` virtual-Dired action. ## 1.6.0 (2026-07-01) diff --git a/test/helm-projectile-test-helper.el b/test/helm-projectile-test-helper.el index 3d7b0a2..7184945 100644 --- a/test/helm-projectile-test-helper.el +++ b/test/helm-projectile-test-helper.el @@ -36,8 +36,11 @@ The directory is created before BODY and deleted afterwards, so a suite can build a throwaway project tree on disk without touching the user's files." (declare (indent 0) (debug t)) + ;; `file-truename' so the root matches the truenames Dired reports back + ;; (on macOS the temp dir lives under a symlinked /var -> /private/var). `(let ((default-directory (file-name-as-directory - (make-temp-file "helm-projectile-test" t)))) + (file-truename + (make-temp-file "helm-projectile-test" t))))) (unwind-protect (progn ,@body) (delete-directory default-directory t)))) diff --git a/test/helm-projectile-test.el b/test/helm-projectile-test.el index 8f67e73..12db0ac 100644 --- a/test/helm-projectile-test.el +++ b/test/helm-projectile-test.el @@ -571,6 +571,90 @@ (expect (helm-projectile-test--sources) :to-be 'helm-source-projectile-files-streaming-other-tab))) +(describe "helm-projectile (entry point)" + (before-each + (spy-on 'projectile-maybe-invalidate-cache) + (spy-on 'projectile-project-name :and-return-value "demo") + (spy-on 'projectile-prepend-project-name :and-call-fake #'identity)) + + (it "shows the configured project sources when in a project" + (spy-on 'helm) + (spy-on 'projectile-project-p :and-return-value t) + (helm-projectile) + (expect (helm-projectile-test--sources) :to-equal helm-projectile-sources-list)) + + (it "falls back to switching project when not in a project" + (spy-on 'projectile-project-p :and-return-value nil) + (spy-on 'helm-projectile-switch-project) + (helm-projectile) + (expect 'helm-projectile-switch-project :to-have-been-called))) + +(describe "helm-projectile-grep (entry point)" + (it "hands the project root and includes to the deferred runner" + (spy-on 'projectile-project-root :and-return-value "/proj/") + (spy-on 'helm-projectile--run-grep-or-ack) + (helm-projectile-grep) + (expect 'helm-projectile--run-grep-or-ack + :to-have-been-called-with "/proj/" nil nil nil nil))) + +(describe "helm-projectile-file-persistent-action" + (it "previews the file's contents in the preview buffer" + (spy-on 'helm-get-attr :and-return-value nil) + (spy-on 'helm-set-attr) + (helm-projectile-test-with-sandbox + (helm-projectile-test-with-files '("a.el") + (write-region "hello preview" nil (expand-file-name "a.el") nil 'silent) + (unwind-protect + (progn + (helm-projectile-file-persistent-action (expand-file-name "a.el")) + (expect (with-current-buffer " *helm-projectile persistent*" + (buffer-string)) + :to-match "hello preview")) + (when (get-buffer " *helm-projectile persistent*") + (kill-buffer " *helm-projectile persistent*"))))))) + +(describe "helm-projectile--find-file-dwim-1" + (it "runs the one-candidate action when a single file matches" + (spy-on 'projectile-project-root :and-return-value "/proj/") + (spy-on 'projectile-current-project-files :and-return-value '("a.el" "b.el")) + (spy-on 'projectile-select-files :and-return-value '("a.el")) + (let (opened) + (helm-projectile--find-file-dwim-1 (lambda (f) (setq opened f)) nil "Find: ") + (expect opened :to-equal "/proj/a.el")))) + +(describe "helm-projectile--find-other-file-1" + (it "runs the one-candidate action when a single other file exists" + (spy-on 'projectile-project-root :and-return-value "/proj/") + (spy-on 'buffer-file-name :and-return-value "/proj/a.c") + (spy-on 'projectile-get-other-files :and-return-value '("a.h")) + (let (opened) + (helm-projectile--find-other-file-1 (lambda (f) (setq opened f)) nil "Find: " nil) + (expect opened :to-equal "/proj/a.h")))) + +(describe "helm-projectile-dired-files-delete-action" + ;; Remove a marked file from a virtual Dired buffer: the buffer is rebuilt + ;; from the set-exclusive-or of its files and the marked ones. + (it "rebuilds the Dired buffer without the marked file" + (helm-projectile-test-with-sandbox + (helm-projectile-test-with-files '("a.el" "b.el" "c.el") + (spy-on 'projectile-project-root :and-return-value default-directory) + (let ((buf (dired (cons "hp-del-dired" '("a.el" "b.el" "c.el")))) + (helm-current-buffer nil)) + (setq helm-current-buffer buf) + (spy-on 'helm-marked-candidates + :and-return-value (list (file-truename (expand-file-name "b.el")))) + (unwind-protect + (progn + (helm-projectile-dired-files-delete-action + (file-truename (expand-file-name "b.el"))) + (with-current-buffer "hp-del-dired" + (let ((entries (helm-projectile-files-in-current-dired-buffer))) + (expect entries :to-contain (file-truename (expand-file-name "a.el"))) + (expect entries :to-contain (file-truename (expand-file-name "c.el"))) + (expect entries :not :to-contain + (file-truename (expand-file-name "b.el")))))) + (when (get-buffer "hp-del-dired") (kill-buffer "hp-del-dired")))))))) + (describe "user-facing error conditions" ;; Normal situations (no project, no other file, ...) should signal ;; `user-error', not `error', so they don't trip the debugger when a user