aboutsummaryrefslogtreecommitdiff
path: root/tests/test-org-capture-config-popup-window.el
blob: 671d55ab95cd2ad5f696573204f72ee26f942d85 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
;;; test-org-capture-config-popup-window.el --- Quick-capture popup tests -*- lexical-binding: t; -*-

;;; Commentary:
;; Tests for the Hyprland Super+Shift+N quick-capture popup.  The popup opens an
;; emacsclient frame named "org-capture" and runs `cj/quick-capture', which
;; captures a single Task into the global inbox with no template menu.  Covered
;; here: the sole-window predicate and display action (the CAPTURE-* buffer
;; fills the frame), the single-Task template builder, frame discovery and focus
;; (the emacsclient focus race), and frame cleanup on every exit path.

;;; Code:

(require 'ert)
(require 'cl-lib)
(require 'org)
(require 'org-capture)            ; makes `org-capture-templates' a real special var
(require 'user-constants)
(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory))
(require 'org-capture-config)

;;; cj/org-capture--popup-sole-window-p

(ert-deftest test-org-capture-config-popup-sole-window-p-select-menu ()
  "Normal: the *Org Select* menu in the popup frame wants the sole window."
  (should (cj/org-capture--popup-sole-window-p "org-capture" "*Org Select*")))

(ert-deftest test-org-capture-config-popup-sole-window-p-capture-buffer ()
  "Normal: a CAPTURE-* buffer in the popup frame wants the sole window."
  (should (cj/org-capture--popup-sole-window-p "org-capture" "CAPTURE-todo.org")))

(ert-deftest test-org-capture-config-popup-sole-window-p-capture-prefix-only ()
  "Boundary: the bare \"CAPTURE-\" prefix still matches."
  (should (cj/org-capture--popup-sole-window-p "org-capture" "CAPTURE-")))

(ert-deftest test-org-capture-config-popup-sole-window-p-other-frame ()
  "Boundary: the same menu in a normal frame is left alone."
  (should-not (cj/org-capture--popup-sole-window-p "emacs" "*Org Select*"))
  (should-not (cj/org-capture--popup-sole-window-p nil "CAPTURE-todo.org")))

(ert-deftest test-org-capture-config-popup-sole-window-p-other-buffer ()
  "Boundary: an unrelated buffer in the popup frame is left alone."
  (should-not (cj/org-capture--popup-sole-window-p "org-capture" "todo.org"))
  (should-not (cj/org-capture--popup-sole-window-p "org-capture" "*scratch*")))

(ert-deftest test-org-capture-config-popup-sole-window-p-nil-buffer ()
  "Error: a nil or non-string buffer name returns nil without raising."
  (should-not (cj/org-capture--popup-sole-window-p "org-capture" nil))
  (should-not (cj/org-capture--popup-sole-window-p "org-capture" 42)))

;;; Integration: the display-buffer-alist entry routes to a sole window

(ert-deftest test-integration-org-capture-popup-display-sole-window ()
  "Integration: in an \"org-capture\"-named frame, displaying a CAPTURE-*
buffer fills the frame's sole window via the registered display-buffer-alist
entry, instead of splitting.

Components integrated:
- cj/org-capture--popup-display-condition (real)
- cj/org-capture--display-sole-window (real)
- display-buffer / display-buffer-alist (real)

Validates the popup frame ends with one window showing the CAPTURE buffer."
  (let ((buf (get-buffer-create "CAPTURE-itest")))
    (unwind-protect
        (progn
          (set-frame-parameter nil 'name "org-capture")
          (delete-other-windows)
          (display-buffer buf)
          (should (= (length (window-list)) 1))
          (should (eq (window-buffer (selected-window)) buf)))
      (set-frame-parameter nil 'name nil)
      (when (buffer-live-p buf) (kill-buffer buf)))))

;;; cj/--quick-capture-template  (single Task into the inbox)

(ert-deftest test-org-capture-config-quick-capture-template ()
  "Normal: the quick-capture template is a single Task into INBOX's Inbox."
  (let* ((tmpl (cj/--quick-capture-template "/inbox.org"))
         (task (assoc "t" tmpl)))
    (should (equal (mapcar #'car tmpl) '("t")))
    (should (equal (nth 1 task) "Task"))
    (should (eq (nth 2 task) 'entry))
    (should (equal (nth 3 task) '(file+headline "/inbox.org" "Inbox")))
    (should (equal (nth 4 task) "* TODO %?"))
    (should (memq :prepend task))))

;;; cj/quick-capture  (single Task; stubbed org-capture)

(ert-deftest test-integration-org-capture-quick-capture-binds-task-only ()
  "Integration: cj/quick-capture runs org-capture with a single Task template
targeting the inbox, dispatched by key.

Components integrated:
- cj/quick-capture (real)
- cj/--quick-capture-template (real)
- org-capture (MOCKED — records the bound templates and dispatch key)"
  (let (captured key)
    (cl-letf (((symbol-function 'org-capture)
               (lambda (&optional _goto k) (setq captured org-capture-templates key k))))
      (cj/quick-capture))
    (should (equal (mapcar #'car captured) '("t")))
    (should (equal (nth 3 (assoc "t" captured)) (list 'file+headline inbox-file "Inbox")))
    (should (equal (nth 4 (assoc "t" captured)) "* TODO %?"))
    (should (equal key "t"))))

(ert-deftest test-integration-org-capture-quick-capture-closes-frame-on-abort ()
  "Integration: when capture aborts (org-capture signals), cj/quick-capture
deletes the popup frame instead of leaving it orphaned.

Components integrated:
- cj/quick-capture (real)
- org-capture (MOCKED — signals user-error \"Abort\")
- cj/org-capture--delete-popup-frame (MOCKED — records the call)"
  (let ((deleted 0))
    (cl-letf (((symbol-function 'org-capture)
               (lambda (&rest _) (user-error "Abort")))
              ((symbol-function 'cj/org-capture--delete-popup-frame)
               (lambda () (cl-incf deleted))))
      (cj/quick-capture))
    (should (= deleted 1))))

(ert-deftest test-integration-org-capture-quick-capture-closes-frame-on-quit ()
  "Integration: a C-g (quit) during capture also closes the popup frame."
  (let ((deleted 0))
    (cl-letf (((symbol-function 'org-capture)
               (lambda (&rest _) (signal 'quit nil)))
              ((symbol-function 'cj/org-capture--delete-popup-frame)
               (lambda () (cl-incf deleted))))
      (cj/quick-capture))
    (should (= deleted 1))))

(ert-deftest test-integration-org-capture-quick-capture-keeps-frame-on-success ()
  "Integration: a successful capture (no signal) does NOT delete the frame —
the finalize hook owns that."
  (let ((deleted 0))
    (cl-letf (((symbol-function 'org-capture) (lambda (&rest _) nil))
              ((symbol-function 'cj/org-capture--delete-popup-frame)
               (lambda () (cl-incf deleted))))
      (cj/quick-capture))
    (should (= deleted 0))))

;;; cj/org-capture--popup-frame-p

(ert-deftest test-org-capture-config-popup-frame-p ()
  "Normal/Boundary: true only when the selected frame is named \"org-capture\"."
  (cl-letf (((symbol-function 'frame-parameter) (lambda (&rest _) "org-capture")))
    (should (cj/org-capture--popup-frame-p)))
  (cl-letf (((symbol-function 'frame-parameter) (lambda (&rest _) "emacs")))
    (should-not (cj/org-capture--popup-frame-p))))

;;; cj/org-capture--popup-frame  (find the popup frame by name)

(ert-deftest test-org-capture-config-popup-frame-found ()
  "Normal: returns the live frame whose name is \"org-capture\"."
  (cl-letf (((symbol-function 'frame-list) (lambda () '(fa fb fc)))
            ((symbol-function 'frame-live-p) (lambda (_f) t))
            ((symbol-function 'frame-parameter)
             (lambda (f _p) (if (eq f 'fb) "org-capture" "other"))))
    (should (eq (cj/org-capture--popup-frame) 'fb))))

(ert-deftest test-org-capture-config-popup-frame-none ()
  "Boundary: no popup frame present yields nil."
  (cl-letf (((symbol-function 'frame-list) (lambda () '(fa fc)))
            ((symbol-function 'frame-live-p) (lambda (_f) t))
            ((symbol-function 'frame-parameter) (lambda (_f _p) "other")))
    (should-not (cj/org-capture--popup-frame))))

;;; cj/quick-capture targets the popup frame

(ert-deftest test-integration-org-capture-quick-capture-selects-named-frame ()
  "Integration: cj/quick-capture selects the \"org-capture\" frame found by name,
not whatever frame happens to be selected (the emacsclient -c focus race)."
  (let ((focused nil))
    (cl-letf (((symbol-function 'cj/org-capture--popup-frame) (lambda () 'popup-frame))
              ((symbol-function 'select-frame-set-input-focus)
               (lambda (f &rest _) (setq focused f)))
              ((symbol-function 'org-capture) (lambda (&rest _) nil)))
      (cj/quick-capture))
    (should (eq focused 'popup-frame))))

(ert-deftest test-integration-org-capture-quick-capture-no-frame-still-captures ()
  "Integration: when no popup frame is found, cj/quick-capture skips the focus
call and still runs the capture (no error)."
  (let ((focused 'unset)
        (captured nil))
    (cl-letf (((symbol-function 'cj/org-capture--popup-frame) (lambda () nil))
              ((symbol-function 'select-frame-set-input-focus)
               (lambda (f &rest _) (setq focused f)))
              ((symbol-function 'org-capture) (lambda (&rest _) (setq captured t))))
      (cj/quick-capture))
    (should (eq focused 'unset))
    (should captured)))

(provide 'test-org-capture-config-popup-window)
;;; test-org-capture-config-popup-window.el ends here