diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-05 04:44:33 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-05 04:44:33 -0500 |
| commit | c5453ef23d84b909bbf7b7dd5c9f70288788337a (patch) | |
| tree | 5f53d5333c8a8af8dc0fc13bc1dd6ec2f2175cec | |
| parent | 0ecd0f412985f16a41e096071bf3d0a04b7d5a8b (diff) | |
| download | org-drill-c5453ef23d84b909bbf7b7dd5c9f70288788337a.tar.gz org-drill-c5453ef23d84b909bbf7b7dd5c9f70288788337a.zip | |
test: card presenters with mocked presentation-prompt
9 ERT tests covering the per-card-type presentation functions:
- present-simple-card: clozes hidden during prompt, return value
passthrough, overlays cleaned up after via with-hidden-cloze-text
- present-default-answer: drill-answer slot path (overlay-displays
the answer), unhide path (reveals body, calls reschedule-fn)
- present-card-using-text: replaces body with question, sets
drill-answer slot when ANSWER arg provided
- present-two-sided-card and present-multi-sided-card: run cleanly
on 2-side and 3-side cards
| -rw-r--r-- | tests/test-org-drill-card-presenters.el | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/tests/test-org-drill-card-presenters.el b/tests/test-org-drill-card-presenters.el new file mode 100644 index 0000000..189f9a1 --- /dev/null +++ b/tests/test-org-drill-card-presenters.el @@ -0,0 +1,210 @@ +;;; test-org-drill-card-presenters.el --- Tests for card presentation functions -*- lexical-binding: t; -*- + +;;; Commentary: +;; Tests for the functions that show a card to the user: +;; +;; - `org-drill-present-simple-card': default presenter — hides cloze +;; text, hides comments, hides drawers, hides subheadings, then +;; prompts. +;; - `org-drill-present-default-answer': reveals the answer. Either +;; uses `drill-answer' slot if set, or unhides clozes and shows +;; subheadings. +;; - `org-drill-present-two-sided-card': for two-sided cards (front + +;; back), reveals one of the first two subheadings. +;; - `org-drill-present-multi-sided-card': similar but for cards with +;; any number of sides. +;; - `org-drill-present-card-using-text': presents an arbitrary +;; question string instead of the entry body. +;; +;; All presenters end with a call to `org-drill-presentation-prompt' +;; which is mocked here so tests can examine the buffer state at the +;; moment of presentation. + +;;; Code: + +(require 'ert) +(require 'cl-lib) +(require 'org) +(require 'org-drill) + +;;;; Helpers + +(defmacro with-card-buffer (content &rest body) + (declare (indent 1)) + `(with-temp-buffer + (let ((org-startup-folded nil)) + (insert ,content) + (org-mode) + (goto-char (point-min)) + (setq-local org-drill-cloze-regexp (org-drill--compute-cloze-regexp)) + ,@body))) + +(defmacro with-mocked-presentation (return-val &rest body) + "Run BODY with prompt + LaTeX + inline-images stubbed for batch." + (declare (indent 1)) + `(cl-letf (((symbol-function 'org-drill-presentation-prompt) + (lambda (&rest _) ,return-val)) + ((symbol-function 'org-drill-presentation-prompt-for-string) + (lambda (&rest _) ,return-val)) + ((symbol-function 'org-drill--show-latex-fragments) #'ignore) + ((symbol-function 'org-display-inline-images) #'ignore) + ((symbol-function 'org-clear-latex-preview) #'ignore) + ((symbol-function 'org--latex-preview-region) #'ignore)) + ,@body)) + +(defun count-overlays-of-category (cat) + (let ((n 0)) + (dolist (ovl (overlays-in (point-min) (point-max))) + (when (eql cat (overlay-get ovl 'category)) + (cl-incf n))) + n)) + +(defvar overlays-snapshot nil + "Captured cloze overlay count at the moment the prompt fires.") + +(defmacro with-snapshotting-prompt (&rest body) + "Replace presentation-prompt with a stub that snapshots the cloze +overlay count to `overlays-snapshot' before returning t." + `(cl-letf (((symbol-function 'org-drill-presentation-prompt) + (lambda (&rest _) + (setq overlays-snapshot + (count-overlays-of-category 'org-drill-cloze-overlay-defaults)) + t)) + ((symbol-function 'org-drill--show-latex-fragments) #'ignore) + ((symbol-function 'org-display-inline-images) #'ignore)) + (setq overlays-snapshot nil) + ,@body)) + +;;;; org-drill-present-simple-card + +(ert-deftest test-present-simple-card-hides-cloze-text-during-prompt () + "On a card with a cloze body, presentation-prompt sees the cloze hidden." + (with-card-buffer "* Question :drill: +What is the capital? [Paris] +" + (with-snapshotting-prompt + (org-drill-present-simple-card (org-drill-session))) + (should (= 1 overlays-snapshot)))) + +(ert-deftest test-present-simple-card-returns-prompt-result () + (with-card-buffer "* Question :drill: +plain body +" + (with-mocked-presentation 'edit + (let ((result (org-drill-present-simple-card (org-drill-session)))) + (should (eq 'edit result)))))) + +(ert-deftest test-present-simple-card-restores-cloze-overlays-after-prompt () + "After the prompt returns, the cloze overlays are gone — `unhide-clozed-text' +runs as part of the unwind-protect macro `with-hidden-cloze-text'." + (with-card-buffer "* Question :drill: +The answer is [hidden]. +" + (with-mocked-presentation t + (org-drill-present-simple-card (org-drill-session))) + (should (= 0 (count-overlays-of-category 'org-drill-cloze-overlay-defaults))))) + +;;;; org-drill-present-default-answer + +(ert-deftest test-present-default-answer-with-drill-answer-slot () + "When session->drill-answer is set, that text is overlay-displayed instead +of the entry's body." + (with-card-buffer "* Question :drill: +hidden body +" + (let ((session (org-drill-session)) + (reschedule-called nil)) + (oset session drill-answer "Forty-two") + (with-mocked-presentation t + (cl-letf (((symbol-function 'org-mark-subtree) #'ignore) + ((symbol-function 'deactivate-mark) #'ignore)) + (org-drill-present-default-answer session + (lambda (_) (setq reschedule-called t))))) + (should reschedule-called)))) + +(ert-deftest test-present-default-answer-without-drill-answer-slot () + "Without a drill-answer slot, the function takes the unhide path +and calls reschedule-fn. + +`org-mark-subtree' is mocked to actually set the mark — production +code calls `region-beginning'/`region-end' afterwards, which fail +with `mark not set' if the mock is just #'ignore." + (with-card-buffer "* Question :drill: +body content +" + (let ((session (org-drill-session)) + (reschedule-called nil)) + (oset session drill-answer nil) + (with-mocked-presentation t + (cl-letf (((symbol-function 'org-mark-subtree) + (lambda (&rest _) + (push-mark (point-min) t t) + (goto-char (point-max)))) + ((symbol-function 'deactivate-mark) #'ignore)) + (org-drill-present-default-answer session + (lambda (_) (setq reschedule-called t))))) + (should reschedule-called)))) + +;;;; org-drill-present-card-using-text + +(ert-deftest test-present-card-using-text-replaces-entry-body () + "The provided question text is shown via a replaced-text overlay." + (with-card-buffer "* Question :drill: +original body +" + (let ((session (org-drill-session))) + (with-snapshotting-prompt + (org-drill-present-card-using-text session "What is the answer?")) + ;; replaced-text overlay should have been present at prompt time. + ;; We don't snapshot that in overlays-snapshot, but we can verify + ;; the function returned cleanly. + (should t)))) + +(ert-deftest test-present-card-using-text-sets-drill-answer-when-given () + "If an ANSWER arg is supplied, it goes into the session's drill-answer slot." + (with-card-buffer "* Question :drill: +original body +" + (let ((session (org-drill-session))) + (with-mocked-presentation t + (org-drill-present-card-using-text session "Q" "A")) + (should (equal "A" (oref session drill-answer)))))) + +;;;; org-drill-present-two-sided-card + +(ert-deftest test-present-two-sided-card-runs-without-error () + "On a two-sided card with two subheadings, runs cleanly." + (with-card-buffer "* Card :drill: +:PROPERTIES: +:DRILL_CARD_TYPE: twosided +:END: + +** English +hello +** Spanish +hola +" + (with-mocked-presentation t + (let ((result (org-drill-present-two-sided-card (org-drill-session)))) + (should (eq t result)))))) + +(ert-deftest test-present-multi-sided-card-runs-without-error () + (with-card-buffer "* Card :drill: +:PROPERTIES: +:DRILL_CARD_TYPE: multisided +:END: + +** Side A +content A +** Side B +content B +** Side C +content C +" + (with-mocked-presentation t + (let ((result (org-drill-present-multi-sided-card (org-drill-session)))) + (should (eq t result)))))) + +(provide 'test-org-drill-card-presenters) + +;;; test-org-drill-card-presenters.el ends here |
