aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tests/test-org-noter-config--generate-notes-template.el62
-rw-r--r--tests/test-org-noter-config--predicates.el125
-rw-r--r--tests/test-org-noter-config--preferred-split.el52
-rw-r--r--tests/test-org-noter-config--title-to-slug.el50
4 files changed, 289 insertions, 0 deletions
diff --git a/tests/test-org-noter-config--generate-notes-template.el b/tests/test-org-noter-config--generate-notes-template.el
new file mode 100644
index 00000000..c4638fa7
--- /dev/null
+++ b/tests/test-org-noter-config--generate-notes-template.el
@@ -0,0 +1,62 @@
+;;; test-org-noter-config--generate-notes-template.el --- Tests for cj/org-noter--generate-notes-template -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for `cj/org-noter--generate-notes-template'. The function
+;; produces an org-roam-shaped buffer template with a generated UUID,
+;; ROAM_REFS / NOTER_DOCUMENT pointing at the document path, and the
+;; title in #+title and #+CATEGORY. `org-id-uuid' is mocked at the
+;; boundary so the test is deterministic.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(require 'user-constants)
+(require 'keybindings)
+(require 'org-noter-config)
+
+(defmacro test-org-noter-config--with-uuid (uuid &rest body)
+ "Run BODY with `org-id-uuid' returning UUID."
+ (declare (indent 1) (debug t))
+ `(cl-letf (((symbol-function 'org-id-uuid) (lambda () ,uuid)))
+ ,@body))
+
+(ert-deftest test-org-noter-config-generate-template-includes-id-property ()
+ "Normal: template includes the supplied UUID in the :ID: property."
+ (test-org-noter-config--with-uuid "deadbeef-1234"
+ (let ((text (cj/org-noter--generate-notes-template
+ "The Pragmatic Programmer" "/books/pragmatic.pdf")))
+ (should (string-match-p ":ID: deadbeef-1234" text)))))
+
+(ert-deftest test-org-noter-config-generate-template-roam-refs-and-noter-document ()
+ "Normal: ROAM_REFS and NOTER_DOCUMENT both point at the document path."
+ (test-org-noter-config--with-uuid "x"
+ (let ((text (cj/org-noter--generate-notes-template
+ "Title" "/books/file.pdf")))
+ (should (string-match-p ":ROAM_REFS: /books/file\\.pdf" text))
+ (should (string-match-p ":NOTER_DOCUMENT: /books/file\\.pdf" text)))))
+
+(ert-deftest test-org-noter-config-generate-template-title-and-category ()
+ "Normal: #+title shows the title with prefix; #+CATEGORY uses the title."
+ (test-org-noter-config--with-uuid "x"
+ (let ((text (cj/org-noter--generate-notes-template
+ "Anaphora Studies" "/x")))
+ (should (string-match-p "^#\\+title: Notes on Anaphora Studies$" text))
+ (should (string-match-p "^#\\+CATEGORY: Anaphora Studies$" text)))))
+
+(ert-deftest test-org-noter-config-generate-template-includes-readingnotes-tag ()
+ "Boundary: the FILETAGS line carries the :ReadingNotes: tag."
+ (test-org-noter-config--with-uuid "x"
+ (let ((text (cj/org-noter--generate-notes-template "T" "/x")))
+ (should (string-match-p "^#\\+FILETAGS: :ReadingNotes:$" text)))))
+
+(ert-deftest test-org-noter-config-generate-template-ends-with-notes-heading ()
+ "Boundary: the template ends with a top-level Notes heading."
+ (test-org-noter-config--with-uuid "x"
+ (let ((text (cj/org-noter--generate-notes-template "T" "/x")))
+ (should (string-match-p "\\* Notes\n\\'" text)))))
+
+(provide 'test-org-noter-config--generate-notes-template)
+;;; test-org-noter-config--generate-notes-template.el ends here
diff --git a/tests/test-org-noter-config--predicates.el b/tests/test-org-noter-config--predicates.el
new file mode 100644
index 00000000..09fbe3e8
--- /dev/null
+++ b/tests/test-org-noter-config--predicates.el
@@ -0,0 +1,125 @@
+;;; test-org-noter-config--predicates.el --- Tests for the org-noter-config predicates -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for the predicate helpers in org-noter-config.el:
+;; cj/org-noter--in-document-p, cj/org-noter--in-notes-file-p,
+;; cj/org-noter--session-active-p, and cj/org-noter--get-document-path
+;; (mode-dispatch helper).
+;;
+;; Mocks are at the boundary: derived-mode-p for mode dispatch,
+;; org-entry-get for the NOTER_DOCUMENT lookup. The tests don't load
+;; pdf-view or nov; they only need the symbols recognised by
+;; derived-mode-p.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(require 'user-constants)
+(require 'keybindings)
+(require 'org-noter-config)
+
+;; Forward-declare and initialise so cl-letf on symbol-value works.
+(defvar org-noter--session nil)
+(defvar nov-file-name nil)
+
+(defmacro test-org-noter-config--with-mode (mode &rest body)
+ "Run BODY with `derived-mode-p' returning MODE for any matching arg.
+MODE may be a symbol or nil; nil means \"not derived from anything we ask\"."
+ (declare (indent 1) (debug t))
+ `(cl-letf (((symbol-function 'derived-mode-p)
+ (lambda (&rest modes) (and ,mode (memq ,mode modes)))))
+ ,@body))
+
+;;; cj/org-noter--in-document-p
+
+(ert-deftest test-org-noter-config-in-document-p-true-in-pdf-view ()
+ "Normal: pdf-view-mode buffer counts as a document."
+ (test-org-noter-config--with-mode 'pdf-view-mode
+ (should (cj/org-noter--in-document-p))))
+
+(ert-deftest test-org-noter-config-in-document-p-true-in-nov ()
+ "Normal: nov-mode buffer counts as a document."
+ (test-org-noter-config--with-mode 'nov-mode
+ (should (cj/org-noter--in-document-p))))
+
+(ert-deftest test-org-noter-config-in-document-p-false-in-org ()
+ "Boundary: org-mode buffer is not a document."
+ (test-org-noter-config--with-mode 'org-mode
+ (should-not (cj/org-noter--in-document-p))))
+
+(ert-deftest test-org-noter-config-in-document-p-false-in-fundamental ()
+ "Boundary: a fundamental-mode buffer is not a document."
+ (test-org-noter-config--with-mode nil
+ (should-not (cj/org-noter--in-document-p))))
+
+;;; cj/org-noter--in-notes-file-p
+
+(ert-deftest test-org-noter-config-in-notes-file-p-true-when-noter-document-set ()
+ "Normal: org-mode + NOTER_DOCUMENT property → notes file."
+ (test-org-noter-config--with-mode 'org-mode
+ (cl-letf (((symbol-function 'org-entry-get)
+ (lambda (_pos prop)
+ (when (equal prop "NOTER_DOCUMENT") "/some/doc.pdf"))))
+ (should (cj/org-noter--in-notes-file-p)))))
+
+(ert-deftest test-org-noter-config-in-notes-file-p-false-when-no-property ()
+ "Boundary: org-mode without NOTER_DOCUMENT property → not a notes file."
+ (test-org-noter-config--with-mode 'org-mode
+ (cl-letf (((symbol-function 'org-entry-get) (lambda (&rest _) nil)))
+ (should-not (cj/org-noter--in-notes-file-p)))))
+
+(ert-deftest test-org-noter-config-in-notes-file-p-false-outside-org ()
+ "Boundary: non-org buffers are never notes files even with the property."
+ (test-org-noter-config--with-mode 'pdf-view-mode
+ (cl-letf (((symbol-function 'org-entry-get)
+ (lambda (_pos prop)
+ (when (equal prop "NOTER_DOCUMENT") "/some/doc.pdf"))))
+ (should-not (cj/org-noter--in-notes-file-p)))))
+
+;;; cj/org-noter--session-active-p
+
+(ert-deftest test-org-noter-config-session-active-p-true-when-session-set ()
+ "Normal: session-active when org-noter--session is non-nil."
+ (cl-letf (((symbol-value 'org-noter--session) 'fake-session))
+ (should (cj/org-noter--session-active-p))))
+
+(ert-deftest test-org-noter-config-session-active-p-false-when-nil ()
+ "Boundary: session-active is nil when org-noter--session is nil."
+ (cl-letf (((symbol-value 'org-noter--session) nil))
+ (should-not (cj/org-noter--session-active-p))))
+
+;;; cj/org-noter--get-document-path
+
+(ert-deftest test-org-noter-config-get-document-path-pdf-uses-buffer-file-name ()
+ "Normal: PDF mode returns `buffer-file-name'."
+ (test-org-noter-config--with-mode 'pdf-view-mode
+ (cl-letf (((symbol-function 'buffer-file-name)
+ (lambda (&optional _) "/books/file.pdf")))
+ (should (equal "/books/file.pdf" (cj/org-noter--get-document-path))))))
+
+(ert-deftest test-org-noter-config-get-document-path-nov-uses-nov-file-name ()
+ "Normal: nov-mode returns `nov-file-name'."
+ (test-org-noter-config--with-mode 'nov-mode
+ (cl-letf (((symbol-value 'nov-file-name) "/books/book.epub"))
+ (should (equal "/books/book.epub" (cj/org-noter--get-document-path))))))
+
+(ert-deftest test-org-noter-config-get-document-path-other-mode-returns-nil ()
+ "Boundary: a non-document mode returns nil."
+ (test-org-noter-config--with-mode 'org-mode
+ (should-not (cj/org-noter--get-document-path))))
+
+;;; cj/org-noter--extract-document-title
+
+(ert-deftest test-org-noter-config-extract-document-title-strips-extension ()
+ "Normal: extract-document-title returns the basename without extension."
+ (test-org-noter-config--with-mode 'pdf-view-mode
+ (cl-letf (((symbol-function 'buffer-file-name)
+ (lambda (&optional _) "/books/the-pragmatic-programmer.pdf")))
+ (should (equal "the-pragmatic-programmer"
+ (cj/org-noter--extract-document-title))))))
+
+(provide 'test-org-noter-config--predicates)
+;;; test-org-noter-config--predicates.el ends here
diff --git a/tests/test-org-noter-config--preferred-split.el b/tests/test-org-noter-config--preferred-split.el
new file mode 100644
index 00000000..c99d63f4
--- /dev/null
+++ b/tests/test-org-noter-config--preferred-split.el
@@ -0,0 +1,52 @@
+;;; test-org-noter-config--preferred-split.el --- Tests for cj/org-noter--preferred-split -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for `cj/org-noter--preferred-split'. The function returns
+;; `horizontal-split' when the frame's width-to-height ratio exceeds
+;; 1.4, otherwise `vertical-split'. Tests mock the frame-pixel-*
+;; primitives to walk the threshold from both sides.
+
+;;; Code:
+
+(require 'ert)
+(require 'cl-lib)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(require 'user-constants)
+(require 'keybindings)
+(require 'org-noter-config)
+
+(defmacro test-org-noter-config--with-frame-size (width height &rest body)
+ "Run BODY with `frame-pixel-width' / `frame-pixel-height' mocked to WIDTH and HEIGHT."
+ (declare (indent 2) (debug t))
+ `(cl-letf (((symbol-function 'frame-pixel-width) (lambda (&optional _) ,width))
+ ((symbol-function 'frame-pixel-height) (lambda (&optional _) ,height)))
+ ,@body))
+
+(ert-deftest test-org-noter-config-preferred-split-wide-returns-horizontal ()
+ "Normal: a 16:9 wide frame returns horizontal-split (side-by-side)."
+ (test-org-noter-config--with-frame-size 1920 1080
+ (should (eq 'horizontal-split (cj/org-noter--preferred-split)))))
+
+(ert-deftest test-org-noter-config-preferred-split-tall-returns-vertical ()
+ "Normal: a tall portrait frame returns vertical-split (stacked)."
+ (test-org-noter-config--with-frame-size 800 1200
+ (should (eq 'vertical-split (cj/org-noter--preferred-split)))))
+
+(ert-deftest test-org-noter-config-preferred-split-square-returns-vertical ()
+ "Boundary: a square frame is below the 1.4 threshold; returns vertical."
+ (test-org-noter-config--with-frame-size 1000 1000
+ (should (eq 'vertical-split (cj/org-noter--preferred-split)))))
+
+(ert-deftest test-org-noter-config-preferred-split-just-above-threshold-horizontal ()
+ "Boundary: a width-to-height ratio of 1.5 returns horizontal."
+ (test-org-noter-config--with-frame-size 1500 1000
+ (should (eq 'horizontal-split (cj/org-noter--preferred-split)))))
+
+(ert-deftest test-org-noter-config-preferred-split-just-below-threshold-vertical ()
+ "Boundary: a width-to-height ratio of 1.3 returns vertical."
+ (test-org-noter-config--with-frame-size 1300 1000
+ (should (eq 'vertical-split (cj/org-noter--preferred-split)))))
+
+(provide 'test-org-noter-config--preferred-split)
+;;; test-org-noter-config--preferred-split.el ends here
diff --git a/tests/test-org-noter-config--title-to-slug.el b/tests/test-org-noter-config--title-to-slug.el
new file mode 100644
index 00000000..3122086d
--- /dev/null
+++ b/tests/test-org-noter-config--title-to-slug.el
@@ -0,0 +1,50 @@
+;;; test-org-noter-config--title-to-slug.el --- Tests for cj/org-noter--title-to-slug -*- lexical-binding: t; -*-
+
+;;; Commentary:
+;; Tests for `cj/org-noter--title-to-slug'. The function lowercases its
+;; input, replaces every run of non-alphanumeric characters with a
+;; single hyphen, and trims leading/trailing hyphens. It's used to
+;; turn a book title into a filename-safe slug.
+
+;;; Code:
+
+(require 'ert)
+
+(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
+(require 'user-constants)
+(require 'keybindings)
+(require 'org-noter-config)
+
+(ert-deftest test-org-noter-config-title-to-slug-multi-word ()
+ "Normal: a multi-word title becomes hyphen-separated lowercase."
+ (should (equal "the-pragmatic-programmer"
+ (cj/org-noter--title-to-slug "The Pragmatic Programmer"))))
+
+(ert-deftest test-org-noter-config-title-to-slug-single-word ()
+ "Normal: a single-word lowercase title is unchanged."
+ (should (equal "anaphora" (cj/org-noter--title-to-slug "anaphora"))))
+
+(ert-deftest test-org-noter-config-title-to-slug-mixed-punctuation ()
+ "Boundary: punctuation is collapsed into single hyphens."
+ (should (equal "godel-escher-bach"
+ (cj/org-noter--title-to-slug "Godel, Escher, Bach!"))))
+
+(ert-deftest test-org-noter-config-title-to-slug-leading-trailing-non-alnum ()
+ "Boundary: leading and trailing non-alnum runs are trimmed."
+ (should (equal "title"
+ (cj/org-noter--title-to-slug " ...title!?? "))))
+
+(ert-deftest test-org-noter-config-title-to-slug-collapses-multiple-non-alnum ()
+ "Boundary: a run of non-alnum chars in the middle becomes one hyphen."
+ (should (equal "a-b" (cj/org-noter--title-to-slug "a --- b"))))
+
+(ert-deftest test-org-noter-config-title-to-slug-numbers-preserved ()
+ "Boundary: digits are preserved as-is."
+ (should (equal "1984" (cj/org-noter--title-to-slug "1984"))))
+
+(ert-deftest test-org-noter-config-title-to-slug-empty-string ()
+ "Error: empty input yields empty string."
+ (should (equal "" (cj/org-noter--title-to-slug ""))))
+
+(provide 'test-org-noter-config--title-to-slug)
+;;; test-org-noter-config--title-to-slug.el ends here