diff options
| author | Craig Jennings <c@cjennings.net> | 2025-11-18 11:13:39 -0600 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2025-11-18 11:13:39 -0600 |
| commit | 4835fadabf243b33fb78557e45428055675e7300 (patch) | |
| tree | 2e8ccd7995ffa6f6dd99943d829fb8b7e3112874 /tests/test-integration-recurring-events-tooltip.el | |
| download | chime-4835fadabf243b33fb78557e45428055675e7300.tar.gz chime-4835fadabf243b33fb78557e45428055675e7300.zip | |
changed repositories
Diffstat (limited to 'tests/test-integration-recurring-events-tooltip.el')
| -rw-r--r-- | tests/test-integration-recurring-events-tooltip.el | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/tests/test-integration-recurring-events-tooltip.el b/tests/test-integration-recurring-events-tooltip.el new file mode 100644 index 0000000..6acfac3 --- /dev/null +++ b/tests/test-integration-recurring-events-tooltip.el @@ -0,0 +1,370 @@ +;;; test-integration-recurring-events-tooltip.el --- Integration tests for recurring events in tooltip -*- 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: + +;; Integration tests for bug001: Recurring Events Show Duplicate Entries in Tooltip +;; +;; Tests the complete workflow from org entries with recurring timestamps +;; through org-agenda-list expansion to final tooltip display. +;; +;; Components integrated: +;; - org-agenda-list (org-mode function that expands recurring events) +;; - chime--gather-info (extracts event information from org markers) +;; - chime--deduplicate-events-by-title (deduplicates expanded recurring events) +;; - chime--update-modeline (updates modeline and upcoming events list) +;; - chime--make-tooltip (generates tooltip from upcoming events) +;; +;; Validates: +;; - Recurring events expanded by org-agenda-list are deduplicated correctly +;; - Tooltip shows each recurring event only once (the soonest occurrence) +;; - Multiple different events are all preserved +;; - Mixed recurring and non-recurring events work correctly + +;;; 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 +(setq chime-debug t) +(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 + +(defvar test-integration-recurring--orig-agenda-files nil + "Original org-agenda-files value before test.") + +(defvar test-integration-recurring--orig-modeline-lookahead nil + "Original chime-modeline-lookahead-minutes value.") + +(defvar test-integration-recurring--orig-tooltip-lookahead nil + "Original chime-tooltip-lookahead-hours value.") + +(defun test-integration-recurring-setup () + "Setup function run before each test." + (chime-create-test-base-dir) + ;; Save original values + (setq test-integration-recurring--orig-agenda-files org-agenda-files) + (setq test-integration-recurring--orig-modeline-lookahead chime-modeline-lookahead-minutes) + (setq test-integration-recurring--orig-tooltip-lookahead chime-tooltip-lookahead-hours) + ;; Set lookahead to 1 year for testing recurring events + (setq chime-modeline-lookahead-minutes 525600) ; 365 days + (setq chime-tooltip-lookahead-hours 8760)) ; 365 days + +(defun test-integration-recurring-teardown () + "Teardown function run after each test." + ;; Restore original values + (setq org-agenda-files test-integration-recurring--orig-agenda-files) + (setq chime-modeline-lookahead-minutes test-integration-recurring--orig-modeline-lookahead) + (setq chime-tooltip-lookahead-hours test-integration-recurring--orig-tooltip-lookahead) + (chime-delete-test-base-dir)) + +;;; Helper Functions + +(defun test-integration-recurring--create-org-file (content) + "Create org file with CONTENT and set it as org-agenda-files. +Returns the file path." + (let* ((base-file (chime-create-temp-test-file "recurring-test-")) + (org-file (concat base-file ".org"))) + ;; Rename to have .org extension + (rename-file base-file org-file) + ;; Write content to the .org file + (with-temp-buffer + (insert content) + (write-file org-file)) + ;; Set as agenda file + (setq org-agenda-files (list org-file)) + org-file)) + +(defun test-integration-recurring--run-agenda-and-gather-events (agenda-span) + "Run org-agenda-list with AGENDA-SPAN and gather event markers. +Returns list of event info gathered from markers." + (let ((markers nil) + (events nil) + (org-buffers nil)) + ;; Remember which buffers are open before agenda + (setq org-buffers (buffer-list)) + ;; org-agenda-list doesn't return the buffer, it creates "*Org Agenda*" + (org-agenda-list agenda-span (org-read-date nil nil "today")) + (with-current-buffer "*Org Agenda*" + ;; Extract all org-markers from agenda buffer + (setq markers + (->> (org-split-string (buffer-string) "\n") + (--map (plist-get + (org-fix-agenda-info (text-properties-at 0 it)) + 'org-marker)) + (-non-nil)))) + ;; Gather info for each marker BEFORE killing buffers + ;; (markers point to the org file buffers, which must stay alive) + (setq events (-map 'chime--gather-info markers)) + ;; Now kill agenda buffer to clean up + (when (get-buffer "*Org Agenda*") + (kill-buffer "*Org Agenda*")) + events)) + +(defun test-integration-recurring--count-in-string (regexp string) + "Count occurrences of REGEXP in STRING." + (let ((count 0) + (start 0)) + (while (string-match regexp string start) + (setq count (1+ count)) + (setq start (match-end 0))) + count)) + +;;; Normal Cases - Recurring Event Deduplication + +(ert-deftest test-integration-recurring-events-tooltip-daily-repeater-shows-once () + "Test that daily recurring event appears only once in tooltip. + +When org-agenda-list expands a daily recurring event (e.g., +1d) over a +year-long lookahead window, it creates ~365 separate agenda entries. +The tooltip should show only the soonest occurrence, not all 365. + +Components integrated: +- org-agenda-list (expands recurring events into separate instances) +- chime--gather-info (extracts info from each expanded marker) +- chime--deduplicate-events-by-title (removes duplicate titles) +- chime--update-modeline (updates upcoming events list) +- chime--make-tooltip (generates tooltip display) + +Validates: +- Recurring event expanded 365 times is deduplicated to one entry +- Tooltip shows event title exactly once +- The shown occurrence is the soonest one" + (test-integration-recurring-setup) + (unwind-protect + (let* ((now (test-time-now)) + (event-time (test-time-today-at 22 0)) ; 10 PM today + (timestamp (test-timestamp-repeating event-time "+1d")) + (content (format "* Daily Wrap Up\n%s\n" timestamp))) + + ;; Create org file with recurring event + (test-integration-recurring--create-org-file content) + + (with-test-time now + ;; Run org-agenda-list to expand recurring events (365 days) + (let* ((events (test-integration-recurring--run-agenda-and-gather-events 365))) + + ;; Verify org-agenda-list expanded the recurring event multiple times + ;; (should be ~365 instances, one per day) + (should (> (length events) 300)) + + ;; All events should have the same title + (let ((titles (mapcar (lambda (e) (cdr (assoc 'title e))) events))) + (should (cl-every (lambda (title) (string= "Daily Wrap Up" title)) titles))) + + ;; Update modeline with these events (this triggers deduplication) + (chime--update-modeline events) + + ;; The upcoming events list should have only ONE entry + (should (= 1 (length chime--upcoming-events))) + + ;; Verify it's the right event + (let* ((item (car chime--upcoming-events)) + (event (car item)) + (title (cdr (assoc 'title event)))) + (should (string= "Daily Wrap Up" title))) + + ;; Generate tooltip and verify event appears only once + (let ((tooltip (chime--make-tooltip chime--upcoming-events))) + (should (stringp tooltip)) + ;; Count occurrences of "Daily Wrap Up" in tooltip + (let ((count (test-integration-recurring--count-in-string + "Daily Wrap Up" tooltip))) + (should (= 1 count))))))) + (test-integration-recurring-teardown))) + +(ert-deftest test-integration-recurring-events-tooltip-weekly-repeater-shows-once () + "Test that weekly recurring event appears only once in tooltip. + +Weekly repeater (+1w) over 1 year creates ~52 instances. +Should be deduplicated to show only the soonest. + +Components integrated: +- org-agenda-list (expands +1w into 52 instances) +- chime--gather-info (processes each instance) +- chime--deduplicate-events-by-title (deduplicates by title) +- chime--update-modeline (updates events list) +- chime--make-tooltip (generates display) + +Validates: +- Weekly recurring events are deduplicated correctly +- Tooltip shows only soonest occurrence" + (test-integration-recurring-setup) + (unwind-protect + (let* ((now (test-time-now)) + (event-time (test-time-tomorrow-at 14 0)) ; 2 PM tomorrow + (timestamp (test-timestamp-repeating event-time "+1w")) + (content (format "* Weekly Team Sync\n%s\n" timestamp))) + + (test-integration-recurring--create-org-file content) + + (with-test-time now + (let* ((events (test-integration-recurring--run-agenda-and-gather-events 365))) + + ;; Should have ~52 weekly instances + (should (> (length events) 45)) + (should (< (length events) 60)) + + ;; Update modeline + (chime--update-modeline events) + + ;; Should be deduplicated to one + (should (= 1 (length chime--upcoming-events))) + + ;; Verify tooltip shows it once + (let ((tooltip (chime--make-tooltip chime--upcoming-events))) + (let ((count (test-integration-recurring--count-in-string + "Weekly Team Sync" tooltip))) + (should (= 1 count))))))) + (test-integration-recurring-teardown))) + +(ert-deftest test-integration-recurring-events-tooltip-mixed-recurring-and-unique () + "Test mix of recurring and non-recurring events in tooltip. + +When multiple events exist, some recurring and some not, all should +appear but recurring ones should be deduplicated. + +Components integrated: +- org-agenda-list (expands only the recurring event) +- chime--gather-info (processes all events) +- chime--deduplicate-events-by-title (deduplicates recurring only) +- chime--update-modeline (updates events list) +- chime--make-tooltip (generates display) + +Validates: +- Recurring event appears once +- Non-recurring events all appear +- Total count is correct (recurring deduplicated, others preserved)" + (test-integration-recurring-setup) + (unwind-protect + (let* ((now (test-time-now)) + (daily-time (test-time-today-at 22 0)) + (meeting-time (test-time-tomorrow-at 14 0)) + (review-time (test-time-days-from-now 2 15 0)) + (daily-ts (test-timestamp-repeating daily-time "+1d")) + (meeting-ts (test-timestamp-string meeting-time)) + (review-ts (test-timestamp-string review-time)) + (content (concat + (format "* Daily Standup\n%s\n\n" daily-ts) + (format "* Project Review\n%s\n\n" meeting-ts) + (format "* Client Meeting\n%s\n" review-ts)))) + + (test-integration-recurring--create-org-file content) + + (with-test-time now + (let* ((events (test-integration-recurring--run-agenda-and-gather-events 365))) + + ;; Should have many events due to daily repeater + (should (> (length events) 300)) + + ;; Update modeline (triggers deduplication) + (chime--update-modeline events) + + ;; Should have exactly 3 events (daily deduplicated + 2 unique) + (should (= 3 (length chime--upcoming-events))) + + ;; Verify all three titles appear + (let ((titles (mapcar (lambda (item) + (cdr (assoc 'title (car item)))) + chime--upcoming-events))) + (should (member "Daily Standup" titles)) + (should (member "Project Review" titles)) + (should (member "Client Meeting" titles)) + ;; No duplicates + (should (= 3 (length (delete-dups (copy-sequence titles)))))) + + ;; Verify tooltip shows each event once + (let ((tooltip (chime--make-tooltip chime--upcoming-events))) + (should (= 1 (test-integration-recurring--count-in-string + "Daily Standup" tooltip))) + (should (= 1 (test-integration-recurring--count-in-string + "Project Review" tooltip))) + (should (= 1 (test-integration-recurring--count-in-string + "Client Meeting" tooltip))))))) + (test-integration-recurring-teardown))) + +;;; Boundary Cases + +(ert-deftest test-integration-recurring-events-tooltip-multiple-different-recurring () + "Test multiple different recurring events are all shown once. + +When there are multiple recurring events with different titles, +each should appear once in the tooltip. + +Components integrated: +- org-agenda-list (expands all recurring events) +- chime--gather-info (processes all expanded instances) +- chime--deduplicate-events-by-title (deduplicates each title separately) +- chime--update-modeline (updates events list) +- chime--make-tooltip (generates display) + +Validates: +- Each recurring event title appears exactly once +- Different recurring frequencies handled correctly +- Deduplication works independently for each title" + (test-integration-recurring-setup) + (unwind-protect + (let* ((now (test-time-now)) + (daily-time (test-time-today-at 11 0)) ; 11 AM (after 10 AM now) + (weekly-time (test-time-tomorrow-at 14 0)) ; 2 PM tomorrow + (daily-ts (test-timestamp-repeating daily-time "+1d")) + (weekly-ts (test-timestamp-repeating weekly-time "+1w")) + (content (concat + (format "* Daily Standup\n%s\n\n" daily-ts) + (format "* Weekly Review\n%s\n" weekly-ts)))) + + (test-integration-recurring--create-org-file content) + + (with-test-time now + (let* ((events (test-integration-recurring--run-agenda-and-gather-events 365))) + + ;; Should have many events (~365 daily + ~52 weekly) + (should (> (length events) 400)) + + ;; Update modeline + (chime--update-modeline events) + + ;; Should be deduplicated to 2 events + (should (= 2 (length chime--upcoming-events))) + + ;; Verify tooltip shows each once + (let ((tooltip (chime--make-tooltip chime--upcoming-events))) + (should (= 1 (test-integration-recurring--count-in-string + "Daily Standup" tooltip))) + (should (= 1 (test-integration-recurring--count-in-string + "Weekly Review" tooltip))))))) + (test-integration-recurring-teardown))) + +(provide 'test-integration-recurring-events-tooltip) +;;; test-integration-recurring-events-tooltip.el ends here |
