diff options
| -rw-r--r-- | modules/hugo-config.el | 4 | ||||
| -rw-r--r-- | modules/org-export-config.el | 2 | ||||
| -rw-r--r-- | modules/org-reveal-config.el | 11 | ||||
| -rw-r--r-- | modules/org-webclipper.el | 3 | ||||
| -rw-r--r-- | tests/test-hugo-config-commands.el | 11 | ||||
| -rw-r--r-- | tests/test-hugo-config-open-blog-dir-external.el | 15 | ||||
| -rw-r--r-- | tests/test-org-export-config-pdf-open-guard.el | 45 | ||||
| -rw-r--r-- | tests/test-org-reveal-config-commands.el | 18 | ||||
| -rw-r--r-- | tests/test-org-webclipper-commands.el | 23 |
9 files changed, 130 insertions, 2 deletions
diff --git a/modules/hugo-config.el b/modules/hugo-config.el index 94be9dd5..7afa45a7 100644 --- a/modules/hugo-config.el +++ b/modules/hugo-config.el @@ -109,6 +109,8 @@ new file with Hugo front matter keywords pre-filled." ((env-macos-p) "open") ((env-windows-p) "explorer.exe") (t "xdg-open")))) + (unless (executable-find cmd) + (user-error "Cannot open blog dir: file-manager opener %S not found on PATH" cmd)) (start-process "hugo-file-manager" nil cmd cj/hugo-content-org-dir))) ;; ----------------------------- Draft Management ------------------------------ @@ -215,6 +217,8 @@ a template error), the sentinel reports the failure." (kill-process cj/hugo--preview-process) (setq cj/hugo--preview-process nil) (message "hugo server stopped")) + (unless (executable-find "hugo") + (user-error "Cannot start preview: hugo binary not found on PATH")) (let ((default-directory website-dir)) (setq cj/hugo--preview-process (start-process "hugo-server" "*hugo-server*" diff --git a/modules/org-export-config.el b/modules/org-export-config.el index 493316f9..5a6f09fc 100644 --- a/modules/org-export-config.el +++ b/modules/org-export-config.el @@ -98,6 +98,8 @@ (defun my/org-pandoc-export-to-pdf-and-open () "Export to PDF via pandoc and open with Zathura." (interactive) + (unless (executable-find "zathura") + (user-error "Cannot open the exported PDF: zathura is not installed or not on PATH")) (let ((pdf-file (org-pandoc-export-to-latex-pdf))) (when pdf-file (start-process "zathura-pdf" nil "zathura" pdf-file) diff --git a/modules/org-reveal-config.el b/modules/org-reveal-config.el index bc002276..be702bf7 100644 --- a/modules/org-reveal-config.el +++ b/modules/org-reveal-config.el @@ -129,6 +129,15 @@ Returns the number of lines removed." (delete-char 1))) removed)) +(defun cj/--reveal-ensure-root () + "Signal a `user-error' when the local reveal.js checkout is missing. +Export needs the clone at `cj/reveal-root', installed by +scripts/setup-reveal.sh. Without it the exporter produces a broken +presentation, so fail early with an actionable message." + (unless (file-directory-p cj/reveal-root) + (user-error "Local reveal.js not found at %s — run scripts/setup-reveal.sh" + cj/reveal-root))) + (defun cj/--reveal-preview-export-on-save () "Export current org buffer to reveal.js HTML silently. Intended for use as a buffer-local `after-save-hook'." @@ -152,6 +161,7 @@ Intended for use as a buffer-local `after-save-hook'." (interactive) (unless (derived-mode-p 'org-mode) (user-error "Not in an Org buffer")) + (cj/--reveal-ensure-root) (let ((html-file (org-reveal-export-to-html))) (when html-file (browse-url-of-file html-file) @@ -164,6 +174,7 @@ re-export silently; refresh the browser to see changes." (interactive) (unless (derived-mode-p 'org-mode) (user-error "Not in an Org buffer")) + (cj/--reveal-ensure-root) (add-hook 'after-save-hook #'cj/--reveal-preview-export-on-save nil t) (let ((html-file (org-reveal-export-to-html))) (when html-file diff --git a/modules/org-webclipper.el b/modules/org-webclipper.el index 9c2f1061..99e837e6 100644 --- a/modules/org-webclipper.el +++ b/modules/org-webclipper.el @@ -168,6 +168,9 @@ It fetches the page content and converts it to Org format." ;; and fail with a `void-variable widget-field-keymap' error from ;; the customize machinery loading lazily. (require 'org-web-tools) + (unless (executable-find "pandoc") + (user-error + "pandoc not found on PATH; it is required to convert the clipped page to Org")) (setq org-web-tools-pandoc-sleep-time 0.5) (let ((url cj/--webclip-url) diff --git a/tests/test-hugo-config-commands.el b/tests/test-hugo-config-commands.el index a387d57d..01df5fc1 100644 --- a/tests/test-hugo-config-commands.el +++ b/tests/test-hugo-config-commands.el @@ -210,6 +210,7 @@ stubbed before the org-mode-derived guard runs." (let ((cj/hugo--preview-process nil) (start-args nil)) (cl-letf (((symbol-function 'process-live-p) (lambda (_) nil)) + ((symbol-function 'executable-find) (lambda (_) "/usr/bin/hugo")) ((symbol-function 'start-process) (lambda (&rest args) (setq start-args args) @@ -221,6 +222,16 @@ stubbed before the org-mode-derived guard runs." (should (eq cj/hugo--preview-process 'fake-proc)) (should (member "server" start-args)))) +(ert-deftest test-hugo-preview-errors-when-hugo-missing () + "Error: a missing hugo binary signals user-error before start-process." + (let ((cj/hugo--preview-process nil)) + (cl-letf (((symbol-function 'process-live-p) (lambda (_) nil)) + ((symbol-function 'executable-find) (lambda (_) nil)) + ((symbol-function 'start-process) + (lambda (&rest _) (error "start-process should not run"))) + ((symbol-function 'message) #'ignore)) + (should-error (cj/hugo-preview) :type 'user-error)))) + ;;; cj/hugo-publish (ert-deftest test-hugo-publish-opens-magit-on-website-dir () diff --git a/tests/test-hugo-config-open-blog-dir-external.el b/tests/test-hugo-config-open-blog-dir-external.el index ae4a25ba..0bf68982 100644 --- a/tests/test-hugo-config-open-blog-dir-external.el +++ b/tests/test-hugo-config-open-blog-dir-external.el @@ -44,6 +44,7 @@ filesystem checks." (cl-letf (((symbol-function 'env-macos-p) (lambda () ,macos-p)) ((symbol-function 'env-windows-p) (lambda () ,windows-p)) ((symbol-function 'file-directory-p) (lambda (_d) t)) + ((symbol-function 'executable-find) (lambda (cmd) cmd)) ((symbol-function 'start-process) (lambda (_name _buf cmd &rest _args) (setq test-hugo--captured-process-cmd cmd)))) @@ -85,6 +86,7 @@ filesystem checks." ((symbol-function 'file-directory-p) (lambda (_d) nil)) ((symbol-function 'make-directory) (lambda (_dir &rest _args) (setq mkdir-called t))) + ((symbol-function 'executable-find) (lambda (cmd) cmd)) ((symbol-function 'start-process) #'ignore)) (cj/hugo-open-blog-dir-external) (should mkdir-called)))) @@ -97,9 +99,22 @@ filesystem checks." ((symbol-function 'file-directory-p) (lambda (_d) t)) ((symbol-function 'make-directory) (lambda (_dir &rest _args) (setq mkdir-called t))) + ((symbol-function 'executable-find) (lambda (cmd) cmd)) ((symbol-function 'start-process) #'ignore)) (cj/hugo-open-blog-dir-external) (should-not mkdir-called)))) +;;; Error Cases + +(ert-deftest test-hugo-config-open-blog-dir-external-error-opener-missing () + "Error: missing opener executable signals user-error before start-process." + (cl-letf (((symbol-function 'env-macos-p) (lambda () nil)) + ((symbol-function 'env-windows-p) (lambda () nil)) + ((symbol-function 'file-directory-p) (lambda (_d) t)) + ((symbol-function 'executable-find) (lambda (_) nil)) + ((symbol-function 'start-process) + (lambda (&rest _) (error "start-process should not run")))) + (should-error (cj/hugo-open-blog-dir-external) :type 'user-error))) + (provide 'test-hugo-config-open-blog-dir-external) ;;; test-hugo-config-open-blog-dir-external.el ends here diff --git a/tests/test-org-export-config-pdf-open-guard.el b/tests/test-org-export-config-pdf-open-guard.el new file mode 100644 index 00000000..8d798871 --- /dev/null +++ b/tests/test-org-export-config-pdf-open-guard.el @@ -0,0 +1,45 @@ +;;; test-org-export-config-pdf-open-guard.el --- zathura open guard test -*- lexical-binding: t; -*- + +;;; Commentary: +;; `my/org-pandoc-export-to-pdf-and-open' opens the exported PDF with zathura +;; via `start-process'. Without zathura on PATH that call fails with an opaque +;; error. These tests pin the command-time guard: a missing zathura signals a +;; `user-error' naming the tool, and a present zathura lets the open proceed. +;; Requiring the module then ox fires the deferred ox-pandoc :config in batch, +;; which is where the function is defined. + +;;; Code: + +;; Initialize package system for batch mode so elpa packages (ox-pandoc) load. +(when noninteractive + (package-initialize)) + +(require 'ert) +(require 'cl-lib) +(require 'org) +(require 'org-export-config) +(require 'ox) +;; The function under test lives in ox-pandoc's :config block. In batch the +;; deferred :after-ox config does not fire on a bare module load, so require +;; ox-pandoc directly to ensure the function is defined. +(require 'ox-pandoc) + +(ert-deftest test-org-export-config-pdf-open-missing-zathura-errors () + "Error: missing zathura signals a user-error before opening the PDF." + (cl-letf (((symbol-function 'executable-find) (lambda (&rest _) nil))) + (should-error (my/org-pandoc-export-to-pdf-and-open) :type 'user-error))) + +(ert-deftest test-org-export-config-pdf-open-present-zathura-proceeds () + "Normal: with zathura present, the open step runs without error." + (let ((started nil)) + (cl-letf (((symbol-function 'executable-find) + (lambda (cmd &rest _) (if (equal cmd "zathura") "/usr/bin/zathura" nil))) + ((symbol-function 'org-pandoc-export-to-latex-pdf) + (lambda (&rest _) "/tmp/out.pdf")) + ((symbol-function 'start-process) + (lambda (&rest _) (setq started t) 'fake-process))) + (my/org-pandoc-export-to-pdf-and-open) + (should started)))) + +(provide 'test-org-export-config-pdf-open-guard) +;;; test-org-export-config-pdf-open-guard.el ends here diff --git a/tests/test-org-reveal-config-commands.el b/tests/test-org-reveal-config-commands.el index c2829f0c..fdec9bea 100644 --- a/tests/test-org-reveal-config-commands.el +++ b/tests/test-org-reveal-config-commands.el @@ -80,7 +80,8 @@ (msg nil)) (with-temp-buffer (delay-mode-hooks (org-mode)) - (cl-letf (((symbol-function 'org-reveal-export-to-html) + (cl-letf (((symbol-function 'file-directory-p) (lambda (_) t)) + ((symbol-function 'org-reveal-export-to-html) (lambda (&rest _) "/tmp/talk.html")) ((symbol-function 'browse-url-of-file) (lambda (f) (setq opened f))) @@ -91,6 +92,18 @@ (should (equal opened "/tmp/talk.html")) (should (string-match-p "Opened presentation" msg)))) +(ert-deftest test-reveal-export-errors-when-reveal-root-missing () + "Error: when the local reveal.js dir is absent, export signals user-error." + (with-temp-buffer + (delay-mode-hooks (org-mode)) + (cl-letf (((symbol-function 'file-directory-p) (lambda (_) nil)) + ((symbol-function 'org-reveal-export-to-html) + (lambda (&rest _) (error "Exporter should not be reached")))) + (let ((err (should-error (cj/reveal-export) :type 'user-error))) + (should (string-match-p "reveal\\.js" (error-message-string err))) + (should (string-match-p "setup-reveal\\.sh" + (error-message-string err))))))) + ;;; cj/reveal-preview-start (ert-deftest test-reveal-preview-start-installs-hook-and-exports () @@ -99,7 +112,8 @@ (opened nil)) (with-temp-buffer (delay-mode-hooks (org-mode)) - (cl-letf (((symbol-function 'org-reveal-export-to-html) + (cl-letf (((symbol-function 'file-directory-p) (lambda (_) t)) + ((symbol-function 'org-reveal-export-to-html) (lambda (&rest _) (setq exported t) "/tmp/preview.html")) ((symbol-function 'browse-url-of-file) (lambda (f) (setq opened f))) diff --git a/tests/test-org-webclipper-commands.el b/tests/test-org-webclipper-commands.el index 5ec5fbfe..be7fc38c 100644 --- a/tests/test-org-webclipper-commands.el +++ b/tests/test-org-webclipper-commands.el @@ -115,11 +115,34 @@ that registers the webclip entry. Providing `'org-protocol' fires the block." (cl-letf (((symbol-function 'require) (lambda (&rest _) t))) (should-error (cj/org-protocol-webclip-handler) :type 'error)))) +(ert-deftest test-webclipper-protocol-handler-errors-when-pandoc-missing () + "Error: handler signals a user-error naming pandoc when it's not on PATH." + (let ((cj/--webclip-url "https://example.com") + (cj/--webclip-title "Title")) + (cl-letf (((symbol-function 'require) (lambda (&rest _) t)) + ((symbol-function 'executable-find) (lambda (_) nil))) + (let ((err (should-error (cj/org-protocol-webclip-handler) + :type 'user-error))) + (should (string-match-p "pandoc" (cadr err))))))) + +(ert-deftest test-webclipper-protocol-handler-proceeds-when-pandoc-present () + "Normal: with pandoc on PATH the guard passes through to conversion." + (let ((cj/--webclip-url "https://example.com") + (cj/--webclip-title "Title")) + (cl-letf (((symbol-function 'require) (lambda (&rest _) t)) + ((symbol-function 'executable-find) (lambda (_) "/usr/bin/pandoc")) + ((symbol-function 'org-web-tools--url-as-readable-org) + (lambda (_) "* Page Title\n** Sub heading\nBody.\n")) + ((symbol-function 'message) #'ignore)) + (should (string-match-p "Body" + (cj/org-protocol-webclip-handler)))))) + (ert-deftest test-webclipper-protocol-handler-returns-processed-content () "Normal: handler converts the bound URL into processed org content." (let ((cj/--webclip-url "https://example.com") (cj/--webclip-title "Title")) (cl-letf (((symbol-function 'require) (lambda (&rest _) t)) + ((symbol-function 'executable-find) (lambda (_) "/usr/bin/pandoc")) ((symbol-function 'org-web-tools--url-as-readable-org) (lambda (_) "* Page Title\n** Sub heading\nBody.\n")) ((symbol-function 'message) #'ignore)) |
