From 54f54840686b29d510b360caeb43d254aec09ffe Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 14 May 2026 01:30:33 -0500 Subject: test(hugo-config): cover new-post, export, navigation, preview, publish Sibling tests covered the pure helpers (`cj/hugo--post-file-path`, `cj/hugo--post-template`, `cj/hugo--post-metadata`, `cj/hugo--collect-drafts`, `cj/hugo-open-blog-dir-external`, `cj/hugo-toggle-draft`). This batch covers the remaining commands and preview helpers: - `cj/hugo-new-post`: writes the template to a slugged file, errors when one already exists. - `cj/hugo-export-post`: errors outside org-mode, delegates to `org-hugo-export-to-md` otherwise. - `cj/hugo-open-blog-dir`: ensures the dir exists and opens dired on it. - `cj/hugo-open-draft`: messages when no drafts, otherwise picks via `completing-read` and opens. - `cj/hugo--preview-filter`: opens the browser when Hugo prints "Web Server is available", ignores arbitrary output. - `cj/hugo--preview-sentinel`: clears the process var on clean exit, messages on a non-zero exit. - `cj/hugo-preview`: stops a running server, starts one when stopped (verifying `start-process` got the "server" arg). - `cj/hugo-publish`: hands off to `magit-status-setup-buffer` on `website-dir`. ox-hugo's `org-hugo-slug` is stubbed via `defun` (the package isn't installed in batch); `require`, `org-hugo-export-to-md`, `dired`, `find-file`, `browse-url`, `start-process`, `kill-process`, and `magit-status-setup-buffer` are stubbed per-test. --- tests/test-hugo-config-commands.el | 235 +++++++++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 tests/test-hugo-config-commands.el (limited to 'tests') diff --git a/tests/test-hugo-config-commands.el b/tests/test-hugo-config-commands.el new file mode 100644 index 00000000..a387d57d --- /dev/null +++ b/tests/test-hugo-config-commands.el @@ -0,0 +1,235 @@ +;;; test-hugo-config-commands.el --- Tests for hugo-config command wrappers + preview helpers -*- lexical-binding: t; -*- + +;;; Commentary: +;; Sibling tests cover the pure helpers (`cj/hugo--post-file-path`, +;; `cj/hugo--post-template`, `cj/hugo--post-metadata`, +;; `cj/hugo--collect-drafts`, `cj/hugo-open-blog-dir-external`, +;; `cj/hugo-toggle-draft`). This file covers the remaining +;; commands + preview helpers: +;; +;; cj/hugo-new-post +;; cj/hugo-export-post +;; cj/hugo-open-blog-dir +;; cj/hugo-open-draft +;; cj/hugo--preview-filter +;; cj/hugo--preview-sentinel +;; cj/hugo-preview +;; cj/hugo-publish +;; +;; ox-hugo / magit / hugo / browse-url / dired are all stubbed. + +;;; Code: + +(require 'ert) +(require 'cl-lib) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(require 'hugo-config) + +;; ox-hugo isn't installed in the batch environment. Provide a tiny +;; slug stub so the post-creation tests can build a filename. +(unless (fboundp 'org-hugo-slug) + (defun org-hugo-slug (title) + "Stub slugifier for batch tests." + (downcase (replace-regexp-in-string "[^a-zA-Z0-9]+" "-" title)))) + +;;; cj/hugo-new-post + +(ert-deftest test-hugo-new-post-creates-file-with-template () + "Normal: a new post writes the template to a slugged file." + (let* ((tmp-dir (make-temp-file "test-hugo-" t)) + (cj/hugo-content-org-dir tmp-dir)) + (unwind-protect + (cl-letf (((symbol-function 'read-from-minibuffer) + (lambda (&rest _) "My New Post")) + ((symbol-function 'message) #'ignore)) + (cj/hugo-new-post) + (let ((expected (cj/hugo--post-file-path "My New Post"))) + (should (file-exists-p expected)) + (with-temp-buffer + (insert-file-contents expected) + (should (string-match-p "#\\+title: My New Post" (buffer-string))) + (should (string-match-p "#\\+hugo_draft: true" (buffer-string)))) + ;; The find-file opened a buffer; kill it so the temp dir can go. + (when-let ((buf (get-file-buffer expected))) + (kill-buffer buf)))) + (delete-directory tmp-dir t)))) + +(ert-deftest test-hugo-new-post-errors-when-file-exists () + "Error: re-creating an existing post signals user-error." + (let* ((tmp-dir (make-temp-file "test-hugo-" t)) + (cj/hugo-content-org-dir tmp-dir)) + (unwind-protect + (let* ((path (cj/hugo--post-file-path "Duplicate Post"))) + (with-temp-file path (insert "already here")) + (cl-letf (((symbol-function 'read-from-minibuffer) + (lambda (&rest _) "Duplicate Post")) + ((symbol-function 'message) #'ignore)) + (should-error (cj/hugo-new-post) :type 'user-error))) + (delete-directory tmp-dir t)))) + +;;; cj/hugo-export-post + +(ert-deftest test-hugo-export-post-error-not-in-org-mode () + "Error: outside org-mode the export errors with user-error. + +ox-hugo isn't installed in the batch environment, so `require' is +stubbed before the org-mode-derived guard runs." + (with-temp-buffer + (cl-letf (((symbol-function 'require) + (lambda (feat &rest _) (if (eq feat 'ox-hugo) t (error "unstubbed require: %s" feat)))) + ((symbol-function 'org-hugo-export-to-md) + (lambda () (error "shouldn't run")))) + (should-error (cj/hugo-export-post) :type 'user-error)))) + +(ert-deftest test-hugo-export-post-normal-delegates-to-ox-hugo () + "Normal: in an org-mode buffer the export delegates to `org-hugo-export-to-md'." + (let ((called nil) + (msg nil)) + (with-temp-buffer + (delay-mode-hooks (org-mode)) + (cl-letf (((symbol-function 'require) + (lambda (feat &rest _) (if (eq feat 'ox-hugo) t (error "unstubbed require: %s" feat)))) + ((symbol-function 'org-hugo-export-to-md) + (lambda () (setq called t))) + ((symbol-function 'message) + (lambda (fmt &rest args) (setq msg (apply #'format fmt args))))) + (cj/hugo-export-post))) + (should called) + (should (string-match-p "Exported" msg)))) + +;;; cj/hugo-open-blog-dir + +(ert-deftest test-hugo-open-blog-dir-creates-and-opens-dired () + "Normal: missing dir gets created, then dired opens it." + (let* ((tmp (concat (make-temp-file "test-hugo-blog-" t) "/sub")) + (cj/hugo-content-org-dir tmp) + (dired-arg nil)) + (unwind-protect + (cl-letf (((symbol-function 'dired) + (lambda (d) (setq dired-arg d)))) + (cj/hugo-open-blog-dir)) + (when (file-directory-p tmp) (delete-directory tmp t))) + (should (equal dired-arg tmp)))) + +;;; cj/hugo-open-draft + +(ert-deftest test-hugo-open-draft-empty-list-messages () + "Boundary: no drafts -> messages \"No drafts\"." + (let ((msg nil)) + (cl-letf (((symbol-function 'cj/hugo--collect-drafts) + (lambda (_) nil)) + ((symbol-function 'message) + (lambda (fmt &rest args) + (setq msg (apply #'format fmt args))))) + (cj/hugo-open-draft)) + (should (string-match-p "No drafts" msg)))) + +(ert-deftest test-hugo-open-draft-picks-via-completing-read () + "Normal: with drafts, completing-read picks one and find-file opens it." + (let ((opened nil)) + (cl-letf (((symbol-function 'cj/hugo--collect-drafts) + (lambda (_) '(("Foo Post" . "/tmp/foo.org") + ("Bar Post" . "/tmp/bar.org")))) + ((symbol-function 'completing-read) + (lambda (&rest _) "Foo Post")) + ((symbol-function 'find-file) + (lambda (f) (setq opened f)))) + (cj/hugo-open-draft)) + (should (equal opened "/tmp/foo.org")))) + +;;; cj/hugo--preview-filter + +(ert-deftest test-hugo-preview-filter-opens-browser-on-ready-line () + "Normal: a 'Web Server is available' line in OUTPUT triggers `browse-url'." + (let ((url nil)) + (cl-letf (((symbol-function 'process-buffer) + (lambda (_) nil)) + ((symbol-function 'process-live-p) + (lambda (_) t)) + ((symbol-function 'browse-url) + (lambda (u) (setq url u))) + ((symbol-function 'set-process-filter) #'ignore)) + (cj/hugo--preview-filter 'fake-proc + "Web Server is available at http://localhost:1313/\n")) + (should (equal url "http://localhost:1313/")))) + +(ert-deftest test-hugo-preview-filter-does-not-open-on-other-output () + "Boundary: arbitrary output doesn't trigger the browser." + (let ((url nil)) + (cl-letf (((symbol-function 'process-buffer) (lambda (_) nil)) + ((symbol-function 'process-live-p) (lambda (_) t)) + ((symbol-function 'browse-url) + (lambda (u) (setq url u))) + ((symbol-function 'set-process-filter) #'ignore)) + (cj/hugo--preview-filter 'fake-proc "Hugo built site in 5ms\n")) + (should-not url))) + +;;; cj/hugo--preview-sentinel + +(ert-deftest test-hugo-preview-sentinel-clears-process-var-on-clean-exit () + "Normal: a clean exit clears `cj/hugo--preview-process'." + (let ((cj/hugo--preview-process 'fake-proc)) + (cl-letf (((symbol-function 'process-status) (lambda (_) 'exit)) + ((symbol-function 'process-exit-status) (lambda (_) 0))) + (cj/hugo--preview-sentinel 'fake-proc "finished\n")) + (should-not cj/hugo--preview-process))) + +(ert-deftest test-hugo-preview-sentinel-messages-on-crash () + "Boundary: a non-zero exit logs the crash message." + (let ((cj/hugo--preview-process 'fake-proc) + (msg nil)) + (cl-letf (((symbol-function 'process-status) (lambda (_) 'exit)) + ((symbol-function 'process-exit-status) (lambda (_) 1)) + ((symbol-function 'message) + (lambda (fmt &rest args) + (setq msg (apply #'format fmt args))))) + (cj/hugo--preview-sentinel 'fake-proc "exited abnormally\n")) + (should (string-match-p "hugo server crashed" msg)))) + +;;; cj/hugo-preview + +(ert-deftest test-hugo-preview-stops-running-server () + "Normal: with a live process, preview kills it and clears the var." + (let ((cj/hugo--preview-process 'fake-proc) + (killed nil) + (msg nil)) + (cl-letf (((symbol-function 'process-live-p) (lambda (_) t)) + ((symbol-function 'kill-process) + (lambda (p) (setq killed p))) + ((symbol-function 'message) + (lambda (fmt &rest args) + (setq msg (apply #'format fmt args))))) + (cj/hugo-preview)) + (should (eq killed 'fake-proc)) + (should-not cj/hugo--preview-process) + (should (string-match-p "stopped" msg)))) + +(ert-deftest test-hugo-preview-starts-server-when-stopped () + "Normal: with no live process, preview starts the hugo server." + (let ((cj/hugo--preview-process nil) + (start-args nil)) + (cl-letf (((symbol-function 'process-live-p) (lambda (_) nil)) + ((symbol-function 'start-process) + (lambda (&rest args) + (setq start-args args) + 'fake-proc)) + ((symbol-function 'set-process-filter) #'ignore) + ((symbol-function 'set-process-sentinel) #'ignore) + ((symbol-function 'message) #'ignore)) + (cj/hugo-preview)) + (should (eq cj/hugo--preview-process 'fake-proc)) + (should (member "server" start-args)))) + +;;; cj/hugo-publish + +(ert-deftest test-hugo-publish-opens-magit-on-website-dir () + "Normal: publish hands off to `magit-status-setup-buffer' on the website dir." + (let ((called-with nil)) + (cl-letf (((symbol-function 'magit-status-setup-buffer) + (lambda (d) (setq called-with d)))) + (cj/hugo-publish)) + (should (equal called-with website-dir)))) + +(provide 'test-hugo-config-commands) +;;; test-hugo-config-commands.el ends here -- cgit v1.2.3