aboutsummaryrefslogtreecommitdiff
path: root/tests/test-chime-notification-text.el
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2025-11-18 11:13:39 -0600
committerCraig Jennings <c@cjennings.net>2025-11-18 11:13:39 -0600
commit4835fadabf243b33fb78557e45428055675e7300 (patch)
tree2e8ccd7995ffa6f6dd99943d829fb8b7e3112874 /tests/test-chime-notification-text.el
downloadchime-4835fadabf243b33fb78557e45428055675e7300.tar.gz
chime-4835fadabf243b33fb78557e45428055675e7300.zip
changed repositories
Diffstat (limited to 'tests/test-chime-notification-text.el')
-rw-r--r--tests/test-chime-notification-text.el542
1 files changed, 542 insertions, 0 deletions
diff --git a/tests/test-chime-notification-text.el b/tests/test-chime-notification-text.el
new file mode 100644
index 0000000..71a2969
--- /dev/null
+++ b/tests/test-chime-notification-text.el
@@ -0,0 +1,542 @@
+;;; test-chime-notification-text.el --- Tests for chime--notification-text -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024 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--notification-text function.
+;; Tests cover normal cases, boundary cases, and error cases.
+
+;;; Code:
+
+;; Initialize package system for batch mode
+(when noninteractive
+ (package-initialize))
+
+(require 'ert)
+
+;; Load dependencies required by chime
+(require 'dash)
+(require 'alert)
+(require 'async)
+(require 'org-agenda)
+
+;; Load chime from parent directory
+(load (expand-file-name "../chime.el") nil t)
+
+;; Load test utilities
+(require 'testutil-general (expand-file-name "testutil-general.el"))
+(require 'testutil-time (expand-file-name "testutil-time.el"))
+
+;;; Setup and Teardown
+
+(defun test-chime-notification-text-setup ()
+ "Setup function run before each test."
+ (chime-create-test-base-dir)
+ ;; Reset display format to default
+ (setq chime-display-time-format-string "%I:%M %p")
+ ;; Reset notification text format to default
+ (setq chime-notification-text-format "%t at %T (%u)")
+ ;; Reset time-left formats to defaults
+ (setq chime-time-left-format-at-event "right now")
+ (setq chime-time-left-format-short "in %M")
+ (setq chime-time-left-format-long "in %H %M")
+ ;; Reset title truncation to default (no truncation)
+ (setq chime-max-title-length nil))
+
+(defun test-chime-notification-text-teardown ()
+ "Teardown function run after each test."
+ (chime-delete-test-base-dir))
+
+;;; Normal Cases
+
+(ert-deftest test-chime-notification-text-standard-event-formats-correctly ()
+ "Test that standard event formats correctly.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Team Meeting")))
+ (result (chime--notification-text str-interval event)))
+ ;; Should format: "Team Meeting at 02:30 PM (in X minutes)"
+ (should (stringp result))
+ (should (string-match-p "Team Meeting" result))
+ (should (string-match-p "02:30 PM" result))
+ (should (string-match-p "in 10 minutes" result)))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-morning-time-formats-with-am ()
+ "Test that morning time uses AM.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 9 15))
+ (str-interval (cons (test-timestamp-string time) '(5 . medium)))
+ (event '((title . "Standup")))
+ (result (chime--notification-text str-interval event)))
+ (should (string-match-p "Standup" result))
+ (should (string-match-p "09:15 AM" result))
+ (should (string-match-p "in 5 minutes" result)))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-midnight-formats-correctly ()
+ "Test that midnight time formats correctly.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 0 0))
+ (str-interval (cons (test-timestamp-string time) '(30 . medium)))
+ (event '((title . "Midnight Event")))
+ (result (chime--notification-text str-interval event)))
+ (should (string-match-p "Midnight Event" result))
+ (should (string-match-p "12:00 AM" result))
+ (should (string-match-p "in 30 minutes" result)))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-noon-formats-correctly ()
+ "Test that noon time formats correctly.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 12 0))
+ (str-interval (cons (test-timestamp-string time) '(15 . medium)))
+ (event '((title . "Lunch")))
+ (result (chime--notification-text str-interval event)))
+ (should (string-match-p "Lunch" result))
+ (should (string-match-p "12:00 PM" result))
+ (should (string-match-p "in 15 minutes" result)))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-zero-minutes-shows-right-now ()
+ "Test that zero minutes shows 'right now'.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 0))
+ (str-interval (cons (test-timestamp-string time) '(0 . high)))
+ (event '((title . "Current Event")))
+ (result (chime--notification-text str-interval event)))
+ (should (string-match-p "Current Event" result))
+ (should (string-match-p "02:00 PM" result))
+ (should (string-match-p "right now" result)))
+ (test-chime-notification-text-teardown)))
+
+;;; Boundary Cases
+
+(ert-deftest test-chime-notification-text-very-long-title-included ()
+ "Test that very long titles are included in full.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 15 45))
+ (str-interval (cons (test-timestamp-string time) '(20 . medium)))
+ (long-title "This is a very long event title that contains many words and might wrap in the notification display")
+ (event `((title . ,long-title)))
+ (result (chime--notification-text str-interval event)))
+ ;; Should include the full title
+ (should (string-match-p long-title result))
+ (should (string-match-p "03:45 PM" result))
+ (should (string-match-p "in 20 minutes" result)))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-title-with-special-characters ()
+ "Test that titles with special characters work correctly.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 16 30))
+ (str-interval (cons (test-timestamp-string time) '(5 . medium)))
+ (event '((title . "Review: Alice's PR #123 (urgent!)")))
+ (result (chime--notification-text str-interval event)))
+ (should (string-match-p "Review: Alice's PR #123 (urgent!)" result))
+ (should (string-match-p "04:30 PM" result)))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-custom-time-format ()
+ "Test that custom time format string is respected.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Meeting")))
+ (chime-display-time-format-string "%H:%M") ; 24-hour format
+ (result (chime--notification-text str-interval event)))
+ ;; Should use 24-hour format
+ (should (string-match-p "Meeting" result))
+ (should (string-match-p "14:30" result))
+ (should-not (string-match-p "PM" result)))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-large-interval-shows-hours ()
+ "Test that large intervals show hours.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 18 0))
+ (str-interval (cons (test-timestamp-string time) '(120 . low))) ; 2 hours
+ (event '((title . "Evening Event")))
+ (result (chime--notification-text str-interval event)))
+ (should (string-match-p "Evening Event" result))
+ (should (string-match-p "06:00 PM" result))
+ ;; Should show hours format
+ (should (string-match-p "in 2 hours" result)))
+ (test-chime-notification-text-teardown)))
+
+;;; Error Cases
+
+(ert-deftest test-chime-notification-text-empty-title-shows-empty ()
+ "Test that empty title still generates output.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "")))
+ (result (chime--notification-text str-interval event)))
+ ;; Should still format, even with empty title
+ (should (stringp result))
+ (should (string-match-p "02:30 PM" result))
+ (should (string-match-p "in 10 minutes" result)))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-missing-title-shows-nil ()
+ "Test that missing title shows nil in output.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '()) ; No title
+ (result (chime--notification-text str-interval event)))
+ ;; Should still generate output with nil title
+ (should (stringp result))
+ (should (string-match-p "02:30 PM" result))
+ (should (string-match-p "in 10 minutes" result)))
+ (test-chime-notification-text-teardown)))
+
+;;; Custom Format Cases
+
+(ert-deftest test-chime-notification-text-custom-title-only ()
+ "Test custom format showing title only.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Team Meeting")))
+ (chime-notification-text-format "%t"))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-equal "Team Meeting" result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-custom-title-and-time-no-countdown ()
+ "Test custom format with title and time, no countdown.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Team Meeting")))
+ (chime-notification-text-format "%t at %T"))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-equal "Team Meeting at 02:30 PM" result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-custom-title-and-countdown-no-time ()
+ "Test custom format with title and countdown, no time.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Team Meeting")))
+ (chime-notification-text-format "%t (%u)"))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-equal "Team Meeting (in 10 minutes)" result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-custom-separator ()
+ "Test custom format with custom separator.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Team Meeting")))
+ (chime-notification-text-format "%t - %T"))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-equal "Team Meeting - 02:30 PM" result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-custom-order-time-first ()
+ "Test custom format with time before title.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Team Meeting")))
+ (chime-notification-text-format "%T: %t"))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-equal "02:30 PM: Team Meeting" result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-custom-compact-format ()
+ "Test custom compact format.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Meeting")))
+ (chime-notification-text-format "%t@%T"))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-equal "Meeting@02:30 PM" result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-custom-with-compact-time-left ()
+ "Test custom format with compact time-left format.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Meeting")))
+ (chime-notification-text-format "%t (%u)")
+ (chime-time-left-format-short "in %mm"))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-equal "Meeting (in 10m)" result))))
+ (test-chime-notification-text-teardown)))
+
+;;; Time Format Cases
+
+(ert-deftest test-chime-notification-text-24-hour-time-format ()
+ "Test 24-hour time format (14:30).
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Meeting")))
+ (chime-display-time-format-string "%H:%M"))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-match-p "14:30" result))
+ (should-not (string-match-p "PM" result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-12-hour-no-space-before-ampm ()
+ "Test 12-hour format without space before AM/PM (02:30PM).
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Meeting")))
+ (chime-display-time-format-string "%I:%M%p"))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-match-p "02:30PM" result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-lowercase-ampm ()
+ "Test 12-hour format with lowercase am/pm (02:30 pm).
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Meeting")))
+ (chime-display-time-format-string "%I:%M %P"))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-match-p "02:30 pm" result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-24-hour-morning ()
+ "Test 24-hour format for morning time (09:15).
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 9 15))
+ (str-interval (cons (test-timestamp-string time) '(5 . medium)))
+ (event '((title . "Standup")))
+ (chime-display-time-format-string "%H:%M"))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-match-p "09:15" result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-24-hour-midnight ()
+ "Test 24-hour format for midnight (00:00).
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 0 0))
+ (str-interval (cons (test-timestamp-string time) '(30 . medium)))
+ (event '((title . "Midnight")))
+ (chime-display-time-format-string "%H:%M"))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-match-p "00:00" result))))
+ (test-chime-notification-text-teardown)))
+
+;;; Title Truncation Cases
+
+(ert-deftest test-chime-notification-text-truncate-nil-no-truncation ()
+ "Test that nil chime-max-title-length shows full title.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Very Long Meeting Title That Goes On And On")))
+ (chime-max-title-length nil))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-match-p "Very Long Meeting Title That Goes On And On" result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-truncate-25-chars ()
+ "Test truncation to 25 characters.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Very Long Meeting Title That Goes On")))
+ (chime-max-title-length 25))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-match-p "Very Long Meeting Titl\\.\\.\\." result))
+ (should-not (string-match-p "That Goes On" result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-truncate-15-chars ()
+ "Test truncation to 15 characters.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Very Long Meeting Title")))
+ (chime-max-title-length 15))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-match-p "Very Long Me\\.\\.\\." result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-truncate-10-chars ()
+ "Test truncation to 10 characters.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Very Long Title")))
+ (chime-max-title-length 10))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-match-p "Very Lo\\.\\.\\." result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-truncate-short-title-unchanged ()
+ "Test that short titles are not truncated.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Short")))
+ (chime-max-title-length 25))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-match-p "Short" result))
+ (should-not (string-match-p "\\.\\.\\." result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-truncate-exact-length-unchanged ()
+ "Test that title exactly at max length is not truncated.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '((title . "Exactly Twenty-Five C"))) ; 21 chars
+ (chime-max-title-length 21))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-match-p "Exactly Twenty-Five C" result))
+ (should-not (string-match-p "\\.\\.\\." result))))
+ (test-chime-notification-text-teardown)))
+
+(ert-deftest test-chime-notification-text-truncate-nil-title-handled ()
+ "Test that nil title is handled gracefully with truncation enabled.
+
+REFACTORED: Uses dynamic timestamps"
+ (test-chime-notification-text-setup)
+ (unwind-protect
+ (let* ((time (test-time-tomorrow-at 14 30))
+ (str-interval (cons (test-timestamp-string time) '(10 . medium)))
+ (event '()) ; No title
+ (chime-max-title-length 25))
+ (let ((result (chime--notification-text str-interval event)))
+ (should (stringp result))
+ (should (string-match-p "02:30 PM" result))))
+ (test-chime-notification-text-teardown)))
+
+(provide 'test-chime-notification-text)
+;;; test-chime-notification-text.el ends here