aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/hugo-config.el4
-rw-r--r--modules/org-export-config.el2
-rw-r--r--modules/org-reveal-config.el11
-rw-r--r--modules/org-webclipper.el3
-rw-r--r--tests/test-hugo-config-commands.el11
-rw-r--r--tests/test-hugo-config-open-blog-dir-external.el15
-rw-r--r--tests/test-org-export-config-pdf-open-guard.el45
-rw-r--r--tests/test-org-reveal-config-commands.el18
-rw-r--r--tests/test-org-webclipper-commands.el23
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))