aboutsummaryrefslogtreecommitdiff
path: root/tests/test-chime-update-modeline-helpers.el
blob: 429265cba6e24d30cec48c25062b42c2f690ac51 (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
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
;;; test-chime-update-modeline-helpers.el --- Tests for modeline helper functions -*- lexical-binding: t; -*-

;;; Commentary:
;; Unit tests for the refactored modeline helper functions:
;; - chime--find-soonest-time-in-window
;; - chime--build-upcoming-events-list
;; - chime--find-soonest-modeline-event
;; - chime--render-modeline-string

;;; Code:

(require 'test-bootstrap (expand-file-name "test-bootstrap.el"))
(require 'testutil-time (expand-file-name "testutil-time.el"))
(require 'testutil-general (expand-file-name "testutil-general.el"))
(require 'testutil-events (expand-file-name "testutil-events.el"))

;;;; Tests for chime--find-soonest-time-in-window

(ert-deftest test-chime-find-soonest-time-empty-list ()
  "Test that empty times list returns nil."
  (let ((now (test-time-now))
        (times '()))
    (should (null (chime--find-soonest-time-in-window times now 60)))))

(ert-deftest test-chime-find-soonest-time-single-within-window ()
  "Test single time within window returns that time."
  (let* ((now (test-time-now))
         (event-time (time-add now (seconds-to-time 1800))) ; 30 minutes
         (times (list (cons "<2025-01-01 Wed 12:30>" event-time))))
    (let ((result (chime--find-soonest-time-in-window times now 60)))
      (should result)
      (should (equal (nth 0 result) "<2025-01-01 Wed 12:30>"))
      (should (time-equal-p (nth 1 result) event-time))
      (should (< (abs (- (nth 2 result) 30)) 1))))) ; ~30 minutes

(ert-deftest test-chime-find-soonest-time-multiple-returns-soonest ()
  "Test multiple times returns the soonest one."
  (let* ((now (test-time-now))
         (time1 (time-add now (seconds-to-time 3600))) ; 60 min
         (time2 (time-add now (seconds-to-time 1800))) ; 30 min (soonest)
         (time3 (time-add now (seconds-to-time 5400))) ; 90 min
         (times (list (cons "<2025-01-01 Wed 13:00>" time1)
                      (cons "<2025-01-01 Wed 12:30>" time2)
                      (cons "<2025-01-01 Wed 13:30>" time3))))
    (let ((result (chime--find-soonest-time-in-window times now 120)))
      (should result)
      (should (equal (nth 0 result) "<2025-01-01 Wed 12:30>"))
      (should (< (abs (- (nth 2 result) 30)) 1))))) ; ~30 minutes

(ert-deftest test-chime-find-soonest-time-outside-window ()
  "Test times outside window returns nil."
  (let* ((now (test-time-now))
         (event-time (time-add now (seconds-to-time 7200))) ; 120 minutes
         (times (list (cons "<2025-01-01 Wed 14:00>" event-time))))
    (should (null (chime--find-soonest-time-in-window times now 60)))))

(ert-deftest test-chime-find-soonest-time-mix-inside-outside ()
  "Test mix of times inside/outside window returns soonest inside."
  (let* ((now (test-time-now))
         (time-outside (time-add now (seconds-to-time 7200))) ; 120 min (outside)
         (time-inside (time-add now (seconds-to-time 1800)))  ; 30 min (inside, soonest)
         (times (list (cons "<2025-01-01 Wed 14:00>" time-outside)
                      (cons "<2025-01-01 Wed 12:30>" time-inside))))
    (let ((result (chime--find-soonest-time-in-window times now 60)))
      (should result)
      (should (equal (nth 0 result) "<2025-01-01 Wed 12:30>")))))

(ert-deftest test-chime-find-soonest-time-past-event ()
  "Test past events are excluded."
  (let* ((now (test-time-now))
         (past-time (time-subtract now (seconds-to-time 1800))) ; -30 minutes
         (times (list (cons "<2025-01-01 Wed 11:30>" past-time))))
    (should (null (chime--find-soonest-time-in-window times now 60)))))

;;;; Tests for chime--build-upcoming-events-list

(ert-deftest test-chime-build-upcoming-empty-events ()
  "Test empty events list returns empty."
  (let ((now (test-time-now))
        (events '()))
    (should (null (chime--build-upcoming-events-list events now 1440 t)))))

(ert-deftest test-chime-build-upcoming-single-event ()
  "Test single event within window is included."
  (with-test-setup
    (let* ((now (test-time-now))
           (event-time (time-add now (seconds-to-time 1800)))
           (content (test-create-org-event "Meeting" event-time))
           (events (test-gather-events-from-content content))
           (result (chime--build-upcoming-events-list events now 1440 t)))
      (should (= (length result) 1))
      (should (string= (cdr (assoc 'title (car (car result)))) "Meeting")))))

(ert-deftest test-chime-build-upcoming-sorted-by-time ()
  "Test multiple events are sorted by time (soonest first)."
  (with-test-setup
    (let* ((now (test-time-now))
           (time1 (time-add now (seconds-to-time 5400))) ; 90 min
           (time2 (time-add now (seconds-to-time 1800))) ; 30 min (soonest)
           (time3 (time-add now (seconds-to-time 3600))) ; 60 min
           (content (test-create-org-events
                     `(("Meeting 1" ,time1)
                       ("Meeting 2" ,time2)
                       ("Meeting 3" ,time3))))
           (events (test-gather-events-from-content content))
           (result (chime--build-upcoming-events-list events now 1440 t)))
      (should (= (length result) 3))
      ;; First should be Meeting 2 (soonest at 30 min)
      (should (string= (cdr (assoc 'title (car (nth 0 result)))) "Meeting 2"))
      ;; Second should be Meeting 3 (60 min)
      (should (string= (cdr (assoc 'title (car (nth 1 result)))) "Meeting 3"))
      ;; Third should be Meeting 1 (90 min)
      (should (string= (cdr (assoc 'title (car (nth 2 result)))) "Meeting 1")))))

(ert-deftest test-chime-build-upcoming-excludes-outside-window ()
  "Test events outside lookahead window are excluded."
  (with-test-setup
    (let* ((now (test-time-now))
           (near-time (time-add now (seconds-to-time 1800)))   ; 30 min (included)
           (far-time (time-add now (seconds-to-time 10800)))   ; 180 min (excluded)
           (content (test-create-org-events
                     `(("Near Meeting" ,near-time)
                       ("Far Meeting" ,far-time))))
           (events (test-gather-events-from-content content))
           (result (chime--build-upcoming-events-list events now 60 t))) ; 60 min window
      (should (= (length result) 1))
      (should (string= (cdr (assoc 'title (car (car result)))) "Near Meeting")))))

;;;; Tests for chime--find-soonest-modeline-event

(ert-deftest test-chime-find-soonest-modeline-empty-events ()
  "Test empty events list returns nil."
  (let ((now (test-time-now))
        (events '()))
    (should (null (chime--find-soonest-modeline-event events now 60)))))

(ert-deftest test-chime-find-soonest-modeline-single-timed-event ()
  "Test single timed event within window is returned."
  (with-test-setup
    (let* ((now (test-time-now))
           (event-time (time-add now (seconds-to-time 1800)))
           (content (test-create-org-event "Meeting" event-time))
           (events (test-gather-events-from-content content))
           (result (chime--find-soonest-modeline-event events now 60)))
      (should result)
      (should (string= (cdr (assoc 'title (nth 0 result))) "Meeting")))))

(ert-deftest test-chime-find-soonest-modeline-excludes-all-day ()
  "Test all-day events are excluded from modeline."
  (with-test-setup
    (let* ((now (test-time-today-at 10 0))
           (all-day-time (test-time-today-at 0 0))
           (timed-time (time-add now (seconds-to-time 1800)))
           (content (test-create-org-events
                     `(("All Day Event" ,all-day-time nil t)
                       ("Timed Event" ,timed-time))))
           (events (test-gather-events-from-content content))
           (result (chime--find-soonest-modeline-event events now 60)))
      (should result)
      (should (string= (cdr (assoc 'title (nth 0 result))) "Timed Event")))))

;;;; Tests for chime--render-modeline-string

(ert-deftest test-chime-render-modeline-string-normal-soonest-returns-formatted-string ()
  "Normal: SOONEST branch formats event-text via `chime-modeline-format'."
  (let ((chime-modeline-format "[%s]")
        (chime--upcoming-events nil)
        (soonest '(event-placeholder "ts" 10 "Standup in 10 min")))
    (let ((result (chime--render-modeline-string soonest nil 60)))
      (should (stringp result))
      (should (string= result "[Standup in 10 min]")))))

(ert-deftest test-chime-render-modeline-string-normal-no-soonest-with-upcoming-binds-both-clicks ()
  "Normal: no SOONEST + UPCOMING propertizes no-events-text with mouse-1 and mouse-3."
  (let* ((chime-modeline-no-events-text "*")
         (event-time (current-time))
         (upcoming (list (list '((title . "Test"))
                               (cons "<2026-01-01 Thu 12:00>" event-time)
                               5)))
         (result (chime--render-modeline-string nil upcoming 60))
         (map (get-text-property 0 'local-map result)))
    (should (stringp result))
    (should (string= (substring-no-properties result) "*"))
    (should (eq (lookup-key map [mode-line mouse-1]) #'chime--open-calendar-url))
    (should (eq (lookup-key map [mode-line mouse-3]) #'chime--jump-to-first-event))
    (should (stringp (get-text-property 0 'help-echo result)))))

(ert-deftest test-chime-render-modeline-string-normal-no-soonest-no-upcoming-only-mouse-1 ()
  "Normal: no SOONEST + no UPCOMING binds only mouse-1, uses no-events-tooltip."
  (let* ((chime-modeline-no-events-text "*")
         (result (chime--render-modeline-string nil nil 60))
         (map (get-text-property 0 'local-map result))
         (tooltip (get-text-property 0 'help-echo result)))
    (should (stringp result))
    (should (eq (lookup-key map [mode-line mouse-1]) #'chime--open-calendar-url))
    (should-not (lookup-key map [mode-line mouse-3]))
    (should (string-match-p "No calendar events" tooltip))))

(ert-deftest test-chime-render-modeline-string-boundary-no-soonest-no-events-text-nil-returns-nil ()
  "Boundary: returns nil when no SOONEST and `chime-modeline-no-events-text' is nil."
  (let ((chime-modeline-no-events-text nil))
    (should (null (chime--render-modeline-string nil nil 60)))
    (should (null (chime--render-modeline-string nil '(some-upcoming) 60)))))

(ert-deftest test-chime-render-modeline-string-boundary-soonest-renders-when-no-events-text-nil ()
  "Boundary: SOONEST branch renders regardless of `chime-modeline-no-events-text'."
  (let ((chime-modeline-format "%s")
        (chime-modeline-no-events-text nil)
        (chime--upcoming-events nil)
        (soonest '(event-placeholder "ts" 10 "Event!")))
    (should (string= "Event!" (chime--render-modeline-string soonest nil 60)))))

(provide 'test-chime-update-modeline-helpers)
;;; test-chime-update-modeline-helpers.el ends here