From b7cb1c51e5663419344d8b55766635801f3ee4c8 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 5 Feb 2026 15:13:57 -0600 Subject: =?UTF-8?q?feat(calendar-sync):=20add=20event=20details=20?= =?UTF-8?q?=E2=80=94=20attendees,=20organizer,=20status,=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add ICS text unescaping (RFC 5545), HTML stripping, and new fields (attendees/status, organizer, meeting URL) to calendar-sync.el. event-to-org now outputs org property drawers. 88 new tests across 10 test files, 146/146 pass. Also fix pre-existing test require order and keymap guard issues. --- ...test-integration-calendar-sync-event-details.el | 177 +++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 tests/test-integration-calendar-sync-event-details.el (limited to 'tests/test-integration-calendar-sync-event-details.el') diff --git a/tests/test-integration-calendar-sync-event-details.el b/tests/test-integration-calendar-sync-event-details.el new file mode 100644 index 00000000..2ecc73e0 --- /dev/null +++ b/tests/test-integration-calendar-sync-event-details.el @@ -0,0 +1,177 @@ +;;; test-integration-calendar-sync-event-details.el --- Integration tests for event details -*- lexical-binding: t; -*- + +;;; Commentary: +;; Integration tests for the enhanced event details workflow. +;; Tests the complete flow from raw ICS with HTML description, attendees, +;; organizer through parse-ics to verify org output. +;; +;; Components integrated: +;; - calendar-sync--unescape-ics-text (ICS text unescaping) +;; - calendar-sync--strip-html (HTML tag removal) +;; - calendar-sync--clean-text (combined cleaning) +;; - calendar-sync--get-all-property-lines (multi-line property extraction) +;; - calendar-sync--parse-attendee-line (attendee parsing) +;; - calendar-sync--find-user-status (user status lookup) +;; - calendar-sync--parse-organizer (organizer extraction) +;; - calendar-sync--extract-meeting-url (meeting URL extraction) +;; - calendar-sync--parse-event (full event parsing) +;; - calendar-sync--event-to-org (org format output with property drawer) +;; - calendar-sync--parse-ics (full ICS pipeline) + +;;; Code: + +(require 'ert) +(require 'testutil-calendar-sync) +(require 'calendar-sync) + +;;; Test Helpers + +(defun test-integration-details--make-full-ics (start-time) + "Create ICS with HTML description, attendees, organizer, and meeting URL. +START-TIME is (year month day hour minute)." + (let ((dtstart (test-calendar-sync-ics-datetime start-time)) + (dtend (test-calendar-sync-ics-datetime + (list (nth 0 start-time) (nth 1 start-time) (nth 2 start-time) + (1+ (nth 3 start-time)) (nth 4 start-time))))) + (concat "BEGIN:VCALENDAR\n" + "VERSION:2.0\n" + "PRODID:-//Google Inc//Google Calendar//EN\n" + "BEGIN:VEVENT\n" + "UID:full-test@example.com\n" + "SUMMARY:Team Planning\n" + "DTSTART:" dtstart "\n" + "DTEND:" dtend "\n" + "DESCRIPTION:Discuss Q1 goals
Review metrics\\, update roadmap\n" + "LOCATION:Conference Room B\n" + "ORGANIZER;CN=John Smith:mailto:john@example.com\n" + "ATTENDEE;CN=John Smith;PARTSTAT=ACCEPTED:mailto:john@example.com\n" + "ATTENDEE;CN=Craig Jennings;PARTSTAT=ACCEPTED:mailto:craigmartinjennings@gmail.com\n" + "ATTENDEE;CN=Alice;PARTSTAT=DECLINED:mailto:alice@example.com\n" + "X-GOOGLE-CONFERENCE:https://meet.google.com/abc-defg-hij\n" + "END:VEVENT\n" + "END:VCALENDAR"))) + +(defun test-integration-details--make-plain-ics (start-time) + "Create simple ICS without attendees or special properties. +START-TIME is (year month day hour minute)." + (let ((dtstart (test-calendar-sync-ics-datetime start-time)) + (dtend (test-calendar-sync-ics-datetime + (list (nth 0 start-time) (nth 1 start-time) (nth 2 start-time) + (1+ (nth 3 start-time)) (nth 4 start-time))))) + (concat "BEGIN:VCALENDAR\n" + "VERSION:2.0\n" + "PRODID:-//Test//Test//EN\n" + "BEGIN:VEVENT\n" + "UID:plain-test@example.com\n" + "SUMMARY:Simple Reminder\n" + "DTSTART:" dtstart "\n" + "DTEND:" dtend "\n" + "END:VEVENT\n" + "END:VCALENDAR"))) + +(defun test-integration-details--make-recurring-with-attendees (start-time) + "Create ICS with recurring event that has attendees. +START-TIME is (year month day hour minute)." + (let ((dtstart (test-calendar-sync-ics-datetime start-time)) + (dtend (test-calendar-sync-ics-datetime + (list (nth 0 start-time) (nth 1 start-time) (nth 2 start-time) + (1+ (nth 3 start-time)) (nth 4 start-time))))) + (concat "BEGIN:VCALENDAR\n" + "VERSION:2.0\n" + "PRODID:-//Test//Test//EN\n" + "BEGIN:VEVENT\n" + "UID:recurring-attendees@example.com\n" + "SUMMARY:Weekly Standup\n" + "DTSTART:" dtstart "\n" + "DTEND:" dtend "\n" + "RRULE:FREQ=WEEKLY;COUNT=3\n" + "ORGANIZER;CN=Manager:mailto:manager@example.com\n" + "ATTENDEE;CN=Craig;PARTSTAT=ACCEPTED:mailto:craigmartinjennings@gmail.com\n" + "LOCATION:Virtual\n" + "END:VEVENT\n" + "END:VCALENDAR"))) + +(defun test-integration-details--make-ics-escaped-location (start-time) + "Create ICS with ICS-escaped location field. +START-TIME is (year month day hour minute)." + (let ((dtstart (test-calendar-sync-ics-datetime start-time)) + (dtend (test-calendar-sync-ics-datetime + (list (nth 0 start-time) (nth 1 start-time) (nth 2 start-time) + (1+ (nth 3 start-time)) (nth 4 start-time))))) + (concat "BEGIN:VCALENDAR\n" + "VERSION:2.0\n" + "PRODID:-//Test//Test//EN\n" + "BEGIN:VEVENT\n" + "UID:escaped-loc@example.com\n" + "SUMMARY:Offsite Meeting\n" + "DTSTART:" dtstart "\n" + "DTEND:" dtend "\n" + "LOCATION:123 Main St\\, Suite 400\\, New Orleans\\, LA\n" + "END:VEVENT\n" + "END:VCALENDAR"))) + +;;; Integration Tests + +(ert-deftest test-integration-details-full-pipeline () + "Test full pipeline: ICS with HTML + attendees + organizer → clean org output. +Verifies: +- HTML in description is cleaned +- ICS escapes in description are unescaped +- Property drawer contains LOCATION, ORGANIZER, STATUS, URL +- Description appears as body text after drawer" + (let* ((start-time (test-calendar-sync-time-days-from-now 7 14 0)) + (ics (test-integration-details--make-full-ics start-time)) + (calendar-sync-user-emails '("craigmartinjennings@gmail.com")) + (org-output (calendar-sync--parse-ics ics))) + (should org-output) + ;; Summary present + (should (string-match-p "Team Planning" org-output)) + ;; Clean description (HTML
→ newline, ICS \, → comma) + (should (string-match-p "Discuss Q1 goals" org-output)) + (should (string-match-p "Review metrics, update roadmap" org-output)) + (should-not (string-match-p "
" org-output)) + ;; Property drawer + (should (string-match-p ":PROPERTIES:" org-output)) + (should (string-match-p ":LOCATION: Conference Room B" org-output)) + (should (string-match-p ":ORGANIZER: John Smith" org-output)) + (should (string-match-p ":STATUS: accepted" org-output)) + (should (string-match-p ":URL: https://meet.google.com/abc-defg-hij" org-output)) + (should (string-match-p ":END:" org-output)))) + +(ert-deftest test-integration-details-plain-event-no-drawer () + "Test plain event without attendees/location produces no property drawer." + (let* ((start-time (test-calendar-sync-time-days-from-now 7 14 0)) + (ics (test-integration-details--make-plain-ics start-time)) + (org-output (calendar-sync--parse-ics ics))) + (should org-output) + (should (string-match-p "Simple Reminder" org-output)) + (should-not (string-match-p ":PROPERTIES:" org-output)))) + +(ert-deftest test-integration-details-recurring-with-attendees () + "Test recurring event with attendees flows details to expanded instances." + (let* ((start-time (test-calendar-sync-time-days-from-now 7 14 0)) + (ics (test-integration-details--make-recurring-with-attendees start-time)) + (calendar-sync-user-emails '("craigmartinjennings@gmail.com")) + (org-output (calendar-sync--parse-ics ics))) + (should org-output) + ;; Should have multiple occurrences of Weekly Standup + (let ((count 0) + (pos 0)) + (while (string-match "Weekly Standup" org-output pos) + (setq count (1+ count)) + (setq pos (match-end 0))) + (should (>= count 2))))) + +(ert-deftest test-integration-details-escaped-location () + "Test ICS escapes in location are cleaned in org output." + (let* ((start-time (test-calendar-sync-time-days-from-now 7 14 0)) + (ics (test-integration-details--make-ics-escaped-location start-time)) + (org-output (calendar-sync--parse-ics ics))) + (should org-output) + (should (string-match-p "Offsite Meeting" org-output)) + ;; Location should have real commas, not escaped + (should (string-match-p "123 Main St, Suite 400" org-output)) + (should-not (string-match-p "\\\\," org-output)))) + +(provide 'test-integration-calendar-sync-event-details) +;;; test-integration-calendar-sync-event-details.el ends here -- cgit v1.2.3