diff options
| author | Craig Jennings <c@cjennings.net> | 2026-07-21 07:27:58 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-07-21 07:27:58 -0500 |
| commit | a30074db7a7ca06e1baf312928076130be37e676 (patch) | |
| tree | f4fa4dd6ca6e1429fd88769edf66d47074379093 /tests | |
| parent | 24f8c5aac8873837824ca6d61e566e3e24c96ad5 (diff) | |
| download | org-drill-a30074db7a7ca06e1baf312928076130be37e676.tar.gz org-drill-a30074db7a7ca06e1baf312928076130be37e676.zip | |
With the default file scope, the dashboard rendered and exported from its own buffer, so every count read zero and cards.csv came out empty. The render, refresh, cycle, and CSV-export paths now run their card scans in the deck buffer the command was invoked from, stored buffer-locally, and a dead deck signals a clear user-error instead of scanning the wrong buffer.
Card links are now a registered org-drill-card link type with a follow handler, and the link path carries the file identity so RET jumps work across files. Eleven new ERT tests cover the render, refresh, export, link, and dead-buffer paths.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test-org-drill-statistics-render-attention.el | 12 | ||||
| -rw-r--r-- | tests/test-org-drill-statistics-source-buffer.el | 174 |
2 files changed, 181 insertions, 5 deletions
diff --git a/tests/test-org-drill-statistics-render-attention.el b/tests/test-org-drill-statistics-render-attention.el index b3f2375..4adcfcc 100644 --- a/tests/test-org-drill-statistics-render-attention.el +++ b/tests/test-org-drill-statistics-render-attention.el @@ -65,8 +65,9 @@ current time so the fixture never hardcodes today." (test-org-drill-stats--attn-fixture) (let ((out (org-drill-statistics--render-attention 'file))) (should (string-match-p "| Card |" out)) - (should (string-match-p "\\[\\[org-drill-card:[0-9]+\\]\\[Leech 00\\]\\]" - out))))) + (should (string-match-p + "\\[\\[org-drill-card:[0-9]+@[^]]*\\]\\[Leech 00\\]\\]" + out))))) (ert-deftest test-org-drill-statistics-attention-cap-and-footer () "Twelve leeches over a 10 cap show 10 rows and a +2 more footer." @@ -126,15 +127,16 @@ current time so the fixture never hardcodes today." (ert-deftest test-org-drill-statistics-card-link-sanitizes-brackets () "Closing brackets in a heading cannot terminate the link early." (let ((link (org-drill-statistics--card-link "a]] b" 42))) - (should (string-prefix-p "[[org-drill-card:42][" link)) + (should (string-prefix-p "[[org-drill-card:42@][" link)) (should (string-suffix-p "]]" link)) (should-not (string-match-p "a]] b" link)))) (ert-deftest test-org-drill-statistics-card-link-empty-heading-fallback () "An empty heading falls back to a position-based description." (let ((link (org-drill-statistics--card-link "" 99))) - (should (string-match-p "\\[\\[org-drill-card:99\\]\\[card at 99\\]\\]" - link)))) + (should (string-match-p + "\\[\\[org-drill-card:99@[^]]*\\]\\[card at 99\\]\\]" + link)))) (provide 'test-org-drill-statistics-render-attention) diff --git a/tests/test-org-drill-statistics-source-buffer.el b/tests/test-org-drill-statistics-source-buffer.el new file mode 100644 index 0000000..5732b63 --- /dev/null +++ b/tests/test-org-drill-statistics-source-buffer.el @@ -0,0 +1,174 @@ +;;; test-org-drill-statistics-source-buffer.el --- Dashboard source-buffer tests -*- lexical-binding: t; -*- + +;;; Commentary: +;; ERT tests for the statistics dashboard's source-buffer plumbing and +;; the org-drill-card link type. The dashboard must scan the deck the +;; command was invoked from, not its own buffer, and its card links must +;; be followable. + +;;; Code: + +(require 'ert) +(require 'org-drill) +(require 'org) + +(defmacro org-drill-statistics-source-test--with-deck (var &rest body) + "Run BODY with VAR bound to a live org deck buffer holding two drill cards." + (declare (indent 1)) + `(let ((,var (generate-new-buffer "*source-deck*"))) + (unwind-protect + (with-current-buffer ,var + (org-mode) + (insert "* Card one :drill:\nBody one.\n" + "* Card two :drill:\nBody two.\n") + (goto-char (point-min)) + ,@body) + (when (buffer-live-p ,var) (kill-buffer ,var))))) + +;;; Normal cases + +(ert-deftest test-org-drill-statistics-render-counts-source-deck-cards () + "Rendering into the dashboard counts the cards of the source deck. +With the default file scope, the card scans must run in the source +buffer, not the dashboard buffer being rendered into." + (org-drill-statistics-source-test--with-deck deck + (let ((dash (generate-new-buffer "*dash-test*")) + (org-drill-scope 'file) + (org-drill-session-log nil)) + (unwind-protect + (progn + (with-current-buffer dash (org-mode)) + (org-drill-statistics--render dash 'file "last 90d" nil deck) + (with-current-buffer dash + (goto-char (point-min)) + ;; Overview table data row: | total | new | mature | lapsed | + (should (search-forward "| 2 | " nil t)))) + (kill-buffer dash))))) + +(ert-deftest test-org-drill-statistics-refresh-uses-stored-source () + "Refresh from inside the dashboard re-scans the stored source deck." + (org-drill-statistics-source-test--with-deck deck + (let ((dash (generate-new-buffer "*dash-test*")) + (org-drill-scope 'file) + (org-drill-session-log nil)) + (unwind-protect + (progn + (with-current-buffer dash (org-mode)) + (org-drill-statistics--render dash 'file "last 90d" nil deck) + (with-current-buffer dash + (org-drill-statistics-refresh) + (goto-char (point-min)) + (should (search-forward "| 2 | " nil t)))) + (kill-buffer dash))))) + +(ert-deftest test-org-drill-statistics-card-link-type-is-registered () + "The org-drill-card link type has a follow handler registered." + (should (functionp (org-link-get-parameter "org-drill-card" :follow)))) + +(ert-deftest test-org-drill-statistics-card-link-encodes-file () + "Card links carry the file identity after the position." + (let ((link (org-drill-statistics--card-link "Heading" 42 "/tmp/deck.org"))) + (should (string-prefix-p "[[org-drill-card:42@/tmp/deck.org][" link)))) + +(ert-deftest test-org-drill-statistics-follow-jumps-to-file-position () + "Following a card link visits the file and moves point to the position." + (let* ((file (make-temp-file "org-drill-follow-test" nil ".org" + "* Card one :drill:\nBody.\n")) + (buf nil)) + (unwind-protect + (progn + (org-drill-statistics--follow-card-link (format "1@%s" file)) + (setq buf (current-buffer)) + (should (equal (buffer-file-name) file)) + (should (= (point) 1))) + (when (and buf (buffer-live-p buf)) (kill-buffer buf)) + (delete-file file)))) + +;;; Boundary cases + +(ert-deftest test-org-drill-statistics-render-empty-deck-counts-zero () + "An empty source deck renders a zero-card overview, not an error." + (let ((deck (generate-new-buffer "*empty-deck*")) + (dash (generate-new-buffer "*dash-test*")) + (org-drill-scope 'file) + (org-drill-session-log nil)) + (unwind-protect + (progn + (with-current-buffer deck (org-mode)) + (with-current-buffer dash (org-mode)) + (org-drill-statistics--render dash 'file "last 90d" nil deck) + (with-current-buffer dash + (goto-char (point-min)) + (should (search-forward "| 0 | " nil t)))) + (kill-buffer deck) + (kill-buffer dash)))) + +(ert-deftest test-org-drill-statistics-card-link-empty-file-omits-identity () + "A card link built with no file still carries the position." + (let ((link (org-drill-statistics--card-link "Heading" 7 nil))) + (should (string-prefix-p "[[org-drill-card:7@][" link)))) + +(ert-deftest test-org-drill-statistics-export-cards-from-source-deck () + "CSV export scans the stored source deck, not the dashboard buffer." + (org-drill-statistics-source-test--with-deck deck + (let ((dash (generate-new-buffer "*dash-test*")) + (dir (make-temp-file "org-drill-export-test" t)) + (org-drill-scope 'file) + (org-drill-session-log nil)) + (unwind-protect + (progn + (with-current-buffer dash (org-mode)) + (org-drill-statistics--render dash 'file "last 90d" nil deck) + (with-current-buffer dash + (org-drill-statistics-export-csv dir)) + (with-temp-buffer + (insert-file-contents (expand-file-name "cards.csv" dir)) + ;; Header plus one row per deck card. + (should (= 3 (count-lines (point-min) (point-max)))))) + (kill-buffer dash) + (delete-directory dir t))))) + +(ert-deftest test-org-drill-statistics-follow-clamps-position-floor () + "A zero or garbage position clamps to buffer start instead of erroring." + (let* ((file (make-temp-file "org-drill-follow-clamp" nil ".org" + "* Card :drill:\n")) + (buf nil)) + (unwind-protect + (progn + (org-drill-statistics--follow-card-link (format "0@%s" file)) + (setq buf (current-buffer)) + (should (= (point) 1))) + (when (and buf (buffer-live-p buf)) (kill-buffer buf)) + (delete-file file)))) + +;;; Error cases + +(ert-deftest test-org-drill-statistics-refresh-dead-source-signals-user-error () + "Refresh after the source deck is killed signals a clear user-error." + (let ((dash (generate-new-buffer "*dash-test*")) + (deck (generate-new-buffer "*doomed-deck*")) + (org-drill-scope 'file) + (org-drill-session-log nil)) + (unwind-protect + (progn + (with-current-buffer deck + (org-mode) + (insert "* Card :drill:\n")) + (with-current-buffer dash (org-mode)) + (org-drill-statistics--render dash 'file "last 90d" nil deck) + (kill-buffer deck) + (with-current-buffer dash + (should-error (org-drill-statistics-refresh) + :type 'user-error))) + (when (buffer-live-p deck) (kill-buffer deck)) + (kill-buffer dash)))) + +(ert-deftest test-org-drill-statistics-follow-dead-source-signals-user-error () + "Following a bufferless link with no live source signals a user-error." + (with-temp-buffer + ;; No dashboard source var set in this buffer; empty file identity. + (should-error (org-drill-statistics--follow-card-link "5@") + :type 'user-error))) + +(provide 'test-org-drill-statistics-source-buffer) +;;; test-org-drill-statistics-source-buffer.el ends here |
