aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/test-chime-modeline-faces.el180
1 files changed, 180 insertions, 0 deletions
diff --git a/tests/test-chime-modeline-faces.el b/tests/test-chime-modeline-faces.el
new file mode 100644
index 0000000..dc8eae8
--- /dev/null
+++ b/tests/test-chime-modeline-faces.el
@@ -0,0 +1,180 @@
+;;; test-chime-modeline-faces.el --- Tests for themeable modeline faces -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2026 Craig Jennings
+
+;; Author: Craig Jennings <c@cjennings.net>
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Unit tests for chime's themeable modeline faces:
+;; - the four deffaces are defined
+;; - chime--modeline-urgency-face maps minutes-until to the right face
+;; - chime--propertize-modeline-string applies a passed face
+;; - chime--render-modeline-string applies the urgency / no-events faces
+
+;;; Code:
+
+(require 'test-bootstrap (expand-file-name "test-bootstrap.el"))
+
+(require 'testutil-general (expand-file-name "testutil-general.el"))
+(require 'testutil-time (expand-file-name "testutil-time.el"))
+(require 'testutil-events (expand-file-name "testutil-events.el"))
+
+;;; Setup/Teardown
+
+(defun test-chime-faces-setup ()
+ "Set tooltip formats so render paths produce a tooltip string."
+ (chime-create-test-base-dir)
+ (setq chime-tooltip-header-format "Upcoming Events as of %a %b %d %Y @ %I:%M %p")
+ (setq chime-display-time-format-string "%I:%M %p")
+ (setq chime-time-left-formats
+ (list (cons 'at-event "right now")
+ (cons 'short "in %M")
+ (cons 'long "in %H %M"))))
+
+(defun test-chime-faces-teardown ()
+ "Teardown."
+ (chime-delete-test-base-dir)
+ (setq chime--upcoming-events nil))
+
+;;; Faces are defined
+
+(ert-deftest test-chime-modeline-faces-are-defined ()
+ "Normal: chime defines the four themeable modeline faces."
+ (should (facep 'chime-modeline-face))
+ (should (facep 'chime-modeline-soon-face))
+ (should (facep 'chime-modeline-urgent-face))
+ (should (facep 'chime-modeline-no-events-face)))
+
+;;; Urgency-face mapping — Normal
+
+(ert-deftest test-chime-modeline-urgency-face-default-when-distant ()
+ "Normal: an event beyond the soon threshold maps to the default face."
+ (let ((chime-modeline-urgent-threshold-minutes 5)
+ (chime-modeline-soon-threshold-minutes 30))
+ (should (eq 'chime-modeline-face (chime--modeline-urgency-face 60)))))
+
+(ert-deftest test-chime-modeline-urgency-face-soon ()
+ "Normal: an event inside the soon window maps to the soon face."
+ (let ((chime-modeline-urgent-threshold-minutes 5)
+ (chime-modeline-soon-threshold-minutes 30))
+ (should (eq 'chime-modeline-soon-face (chime--modeline-urgency-face 20)))))
+
+(ert-deftest test-chime-modeline-urgency-face-urgent ()
+ "Normal: an imminent event maps to the urgent face."
+ (let ((chime-modeline-urgent-threshold-minutes 5)
+ (chime-modeline-soon-threshold-minutes 30))
+ (should (eq 'chime-modeline-urgent-face (chime--modeline-urgency-face 2)))))
+
+;;; Urgency-face mapping — Boundary
+
+(ert-deftest test-chime-modeline-urgency-face-at-urgent-threshold ()
+ "Boundary: minutes exactly at the urgent threshold is urgent."
+ (let ((chime-modeline-urgent-threshold-minutes 5)
+ (chime-modeline-soon-threshold-minutes 30))
+ (should (eq 'chime-modeline-urgent-face (chime--modeline-urgency-face 5)))))
+
+(ert-deftest test-chime-modeline-urgency-face-at-soon-threshold ()
+ "Boundary: minutes exactly at the soon threshold is soon."
+ (let ((chime-modeline-urgent-threshold-minutes 5)
+ (chime-modeline-soon-threshold-minutes 30))
+ (should (eq 'chime-modeline-soon-face (chime--modeline-urgency-face 30)))))
+
+(ert-deftest test-chime-modeline-urgency-face-zero-is-urgent ()
+ "Boundary: an event happening now (0 minutes) is urgent."
+ (let ((chime-modeline-urgent-threshold-minutes 5)
+ (chime-modeline-soon-threshold-minutes 30))
+ (should (eq 'chime-modeline-urgent-face (chime--modeline-urgency-face 0)))))
+
+(ert-deftest test-chime-modeline-urgency-face-fractional-minutes ()
+ "Boundary: fractional minutes (minutes-until is a float) bucket correctly."
+ (let ((chime-modeline-urgent-threshold-minutes 5)
+ (chime-modeline-soon-threshold-minutes 30))
+ (should (eq 'chime-modeline-urgent-face (chime--modeline-urgency-face 4.5)))
+ (should (eq 'chime-modeline-soon-face (chime--modeline-urgency-face 5.5)))))
+
+;;; propertize applies face
+
+(ert-deftest test-chime-propertize-applies-face-when-passed ()
+ "Normal: a non-nil FACE argument becomes the `face' text property."
+ (test-chime-faces-setup)
+ (unwind-protect
+ (let* ((now (test-time-now))
+ (event-time (time-add now (seconds-to-time 120)))
+ (ts (test-timestamp-string event-time)))
+ (setq chime--upcoming-events
+ (list (list `((title . "Meeting") (times . ((,ts . ,event-time))))
+ (cons ts event-time) 2)))
+ (with-test-time now
+ (let ((result (chime--propertize-modeline-string
+ " ⏰ Meeting" 'chime-modeline-urgent-face)))
+ (should (eq 'chime-modeline-urgent-face
+ (get-text-property 0 'face result))))))
+ (test-chime-faces-teardown)))
+
+(ert-deftest test-chime-propertize-no-face-when-omitted ()
+ "Boundary: with no FACE argument, no `face' property is added."
+ (test-chime-faces-setup)
+ (unwind-protect
+ (let* ((now (test-time-now))
+ (event-time (time-add now (seconds-to-time 1800)))
+ (ts (test-timestamp-string event-time)))
+ (setq chime--upcoming-events
+ (list (list `((title . "Meeting") (times . ((,ts . ,event-time))))
+ (cons ts event-time) 30)))
+ (with-test-time now
+ (let ((result (chime--propertize-modeline-string " ⏰ Meeting")))
+ (should-not (get-text-property 0 'face result)))))
+ (test-chime-faces-teardown)))
+
+;;; render applies the urgency / no-events faces
+
+(ert-deftest test-chime-render-applies-urgency-face-to-soonest ()
+ "Normal: render applies the urgency face matching the soonest event."
+ (test-chime-faces-setup)
+ (unwind-protect
+ (let* ((chime-modeline-urgent-threshold-minutes 5)
+ (chime-modeline-soon-threshold-minutes 30)
+ (chime-modeline-format " ⏰ %s")
+ (now (test-time-now))
+ (event-time (time-add now (seconds-to-time 120)))
+ (ts (test-timestamp-string event-time))
+ (soonest (list `((title . "Meeting")) ts 2 "Meeting in 2 min")))
+ (setq chime--upcoming-events
+ (list (list `((title . "Meeting") (times . ((,ts . ,event-time))))
+ (cons ts event-time) 2)))
+ (with-test-time now
+ (let ((result (chime--render-modeline-string
+ soonest chime--upcoming-events 168)))
+ (should (eq 'chime-modeline-urgent-face
+ (get-text-property 0 'face result))))))
+ (test-chime-faces-teardown)))
+
+(ert-deftest test-chime-render-no-events-applies-no-events-face ()
+ "Normal: the no-events modeline applies the no-events face."
+ (test-chime-faces-setup)
+ (unwind-protect
+ (let ((chime-modeline-no-events-text " ⏰")
+ (now (test-time-now)))
+ (setq chime--upcoming-events nil)
+ (with-test-time now
+ (let ((result (chime--render-modeline-string nil nil 168)))
+ (should (eq 'chime-modeline-no-events-face
+ (get-text-property 0 'face result))))))
+ (test-chime-faces-teardown)))
+
+(provide 'test-chime-modeline-faces)
+;;; test-chime-modeline-faces.el ends here