diff options
| author | Craig Jennings <c@cjennings.net> | 2026-04-04 12:42:46 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-04-04 12:42:46 -0500 |
| commit | 130bbc076b17f8a5a275bea00e5aa2a354baad49 (patch) | |
| tree | e096edef1655355689e2e1be893013a6623fe601 | |
| parent | 2441aa4b76b992d8ebe7a7d8c8beb85a08c2fc77 (diff) | |
| download | emacs-wttrin-130bbc076b17f8a5a275bea00e5aa2a354baad49.tar.gz emacs-wttrin-130bbc076b17f8a5a275bea00e5aa2a354baad49.zip | |
refactor: extract mode-line icon and string helpers
Add wttrin--make-emoji-icon and wttrin--set-mode-line-string to
eliminate three near-identical propertize blocks across
update-placeholder-error, set-placeholder, and update-display.
Reduces wttrin--mode-line-update-display from 40 lines to 25.
| -rw-r--r-- | tests/test-wttrin--mode-line-helpers.el | 128 | ||||
| -rw-r--r-- | wttrin.el | 72 |
2 files changed, 160 insertions, 40 deletions
diff --git a/tests/test-wttrin--mode-line-helpers.el b/tests/test-wttrin--mode-line-helpers.el new file mode 100644 index 0000000..b4c6b4e --- /dev/null +++ b/tests/test-wttrin--mode-line-helpers.el @@ -0,0 +1,128 @@ +;;; test-wttrin--mode-line-helpers.el --- Tests for mode-line helper functions -*- lexical-binding: t; -*- + +;; Copyright (C) 2025 Craig Jennings + +;;; Commentary: + +;; Unit tests for wttrin--make-emoji-icon and wttrin--set-mode-line-string. +;; These helpers extract the common mode-line icon creation and string +;; assignment pattern used by placeholder, error, and display functions. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +;;; Setup and Teardown + +(defun test-wttrin--mode-line-helpers-setup () + "Setup for mode-line helper tests." + (testutil-wttrin-setup) + (setq wttrin-mode-line-string nil)) + +(defun test-wttrin--mode-line-helpers-teardown () + "Teardown for mode-line helper tests." + (testutil-wttrin-teardown) + (setq wttrin-mode-line-string nil)) + +;;; -------------------------------------------------------------------------- +;;; wttrin--make-emoji-icon +;;; -------------------------------------------------------------------------- + +;;; Normal Cases + +(ert-deftest test-wttrin--make-emoji-icon-normal-without-font () + "Without emoji font configured, should return the plain emoji string." + (let ((wttrin-mode-line-emoji-font nil)) + (should (equal (wttrin--make-emoji-icon "☀") "☀")))) + +(ert-deftest test-wttrin--make-emoji-icon-normal-with-font () + "With emoji font configured, should apply font family face property." + (let ((wttrin-mode-line-emoji-font "Noto Color Emoji")) + (let ((result (wttrin--make-emoji-icon "☀"))) + (should (stringp result)) + (let ((face (get-text-property 0 'face result))) + (should (equal (plist-get face :family) "Noto Color Emoji")) + (should (equal (plist-get face :height) 1.0)))))) + +(ert-deftest test-wttrin--make-emoji-icon-normal-with-foreground () + "Foreground color should be applied when specified." + (let ((wttrin-mode-line-emoji-font nil)) + (let ((result (wttrin--make-emoji-icon "☀" "gray60"))) + (let ((face (get-text-property 0 'face result))) + (should (equal (plist-get face :foreground) "gray60")))))) + +(ert-deftest test-wttrin--make-emoji-icon-normal-with-font-and-foreground () + "Both font and foreground should be applied together." + (let ((wttrin-mode-line-emoji-font "Noto Color Emoji")) + (let ((result (wttrin--make-emoji-icon "⏳" "gray60"))) + (let ((face (get-text-property 0 'face result))) + (should (equal (plist-get face :family) "Noto Color Emoji")) + (should (equal (plist-get face :foreground) "gray60")))))) + +;;; Boundary Cases + +(ert-deftest test-wttrin--make-emoji-icon-boundary-nil-foreground-no-color () + "Nil foreground should not add any :foreground property when no font." + (let ((wttrin-mode-line-emoji-font nil)) + (let ((result (wttrin--make-emoji-icon "☀" nil))) + ;; Without font or foreground, should be plain string + (should (equal result "☀"))))) + +;;; -------------------------------------------------------------------------- +;;; wttrin--set-mode-line-string +;;; -------------------------------------------------------------------------- + +;;; Normal Cases + +(ert-deftest test-wttrin--set-mode-line-string-normal-sets-string () + "Should set wttrin-mode-line-string to a non-nil propertized value." + (test-wttrin--mode-line-helpers-setup) + (unwind-protect + (progn + (wttrin--set-mode-line-string "X" "tooltip text") + (should wttrin-mode-line-string)) + (test-wttrin--mode-line-helpers-teardown))) + +(ert-deftest test-wttrin--set-mode-line-string-normal-includes-icon () + "The icon text should appear in the mode-line string." + (test-wttrin--mode-line-helpers-setup) + (unwind-protect + (progn + (wttrin--set-mode-line-string "⏳" "tip") + (should (string-match-p "⏳" (substring-no-properties wttrin-mode-line-string)))) + (test-wttrin--mode-line-helpers-teardown))) + +(ert-deftest test-wttrin--set-mode-line-string-normal-has-tooltip () + "The help-echo property should contain the tooltip text." + (test-wttrin--mode-line-helpers-setup) + (unwind-protect + (progn + (wttrin--set-mode-line-string "X" "Weather is sunny") + (should (equal (get-text-property 0 'help-echo wttrin-mode-line-string) + "Weather is sunny"))) + (test-wttrin--mode-line-helpers-teardown))) + +(ert-deftest test-wttrin--set-mode-line-string-normal-has-keymap () + "The local-map property should be the mode-line keymap." + (test-wttrin--mode-line-helpers-setup) + (unwind-protect + (progn + (wttrin--set-mode-line-string "X" "tip") + (should (eq (get-text-property 0 'local-map wttrin-mode-line-string) + wttrin--mode-line-map))) + (test-wttrin--mode-line-helpers-teardown))) + +(ert-deftest test-wttrin--set-mode-line-string-normal-has-mouse-face () + "The mouse-face property should highlight on hover." + (test-wttrin--mode-line-helpers-setup) + (unwind-protect + (progn + (wttrin--set-mode-line-string "X" "tip") + (should (equal (get-text-property 0 'mouse-face wttrin-mode-line-string) + 'mode-line-highlight))) + (test-wttrin--mode-line-helpers-teardown))) + +(provide 'test-wttrin--mode-line-helpers) +;;; test-wttrin--mode-line-helpers.el ends here @@ -508,6 +508,27 @@ This creates headroom to avoid frequent cleanups." ;;; Mode-line weather display +(defun wttrin--make-emoji-icon (emoji &optional foreground) + "Create EMOJI string with optional font face and FOREGROUND color. +Uses `wttrin-mode-line-emoji-font' when configured." + (if wttrin-mode-line-emoji-font + (propertize emoji + 'face (list :family wttrin-mode-line-emoji-font + :height 1.0 + :foreground foreground)) + (if foreground + (propertize emoji 'face (list :foreground foreground)) + emoji))) + +(defun wttrin--set-mode-line-string (icon tooltip) + "Set mode-line weather string to ICON with TOOLTIP and standard properties." + (setq wttrin-mode-line-string + (propertize (concat " " icon) + 'help-echo tooltip + 'mouse-face 'mode-line-highlight + 'local-map wttrin--mode-line-map)) + (force-mode-line-update t)) + (defun wttrin--mode-line-valid-response-p (weather-string) "Return non-nil if WEATHER-STRING looks like a valid mode-line response. Expected format: \"Location: emoji temp conditions\", @@ -520,19 +541,11 @@ e.g., \"Paris: ☀️ +61°F Clear\"." "Update placeholder to show fetch error state. Keeps the hourglass icon but updates tooltip to explain the failure and indicate when retry will occur." - (let* ((icon (if wttrin-mode-line-emoji-font - (propertize "⏳" - 'face (list :family wttrin-mode-line-emoji-font - :height 1.0)) - "⏳")) - (retry-minutes (ceiling (/ wttrin-mode-line-refresh-interval 60.0)))) - (setq wttrin-mode-line-string - (propertize (concat " " icon) - 'help-echo (format "Weather fetch failed for %s — will retry in %d minutes" - wttrin-favorite-location retry-minutes) - 'mouse-face 'mode-line-highlight - 'local-map wttrin--mode-line-map))) - (force-mode-line-update t)) + (let ((retry-minutes (ceiling (/ wttrin-mode-line-refresh-interval 60.0)))) + (wttrin--set-mode-line-string + (wttrin--make-emoji-icon "⏳") + (format "Weather fetch failed for %s — will retry in %d minutes" + wttrin-favorite-location retry-minutes)))) (defun wttrin--mode-line-fetch-weather () "Fetch weather for favorite location and update mode-line display. @@ -587,27 +600,15 @@ shows staleness info in tooltip." (let* ((emoji (if (string-match ":\\s-*\\(.\\)" weather-string) (match-string 1 weather-string) "?")) - (emoji-with-font - (if wttrin-mode-line-emoji-font - (propertize emoji - 'face (list :family wttrin-mode-line-emoji-font - :height 1.0 - :foreground (when stale-p "gray60"))) - (if stale-p - (propertize emoji 'face '(:foreground "gray60")) - emoji))) (tooltip (if stale-p (format "%s\nStale: updated %s — fetch failed, will retry" weather-string age-str) (format "%s\nUpdated %s" weather-string age-str)))) (wttrin--debug-log "mode-line-display: Extracted emoji = %S, stale = %s" emoji stale-p) - (setq wttrin-mode-line-string - (propertize (concat " " emoji-with-font) - 'help-echo tooltip - 'mouse-face 'mode-line-highlight - 'local-map wttrin--mode-line-map))))) - (force-mode-line-update t)) + (wttrin--set-mode-line-string + (wttrin--make-emoji-icon emoji (when stale-p "gray60")) + tooltip))))) (defun wttrin-mode-line-click () "Handle left-click on mode-line weather widget. @@ -626,18 +627,9 @@ Force-refresh cache and update tooltip without opening buffer." (defun wttrin--mode-line-set-placeholder () "Set a placeholder icon in the mode-line while waiting for weather data." - (let ((icon (if wttrin-mode-line-emoji-font - (propertize "⏳" - 'face (list :family wttrin-mode-line-emoji-font - :height 1.0)) - "⏳"))) - (setq wttrin-mode-line-string - (propertize (concat " " icon) - 'help-echo (format "Fetching weather for %s..." - wttrin-favorite-location) - 'mouse-face 'mode-line-highlight - 'local-map wttrin--mode-line-map))) - (force-mode-line-update t)) + (wttrin--set-mode-line-string + (wttrin--make-emoji-icon "⏳") + (format "Fetching weather for %s..." wttrin-favorite-location))) (defvar wttrin--buffer-refresh-timer nil "Timer object for proactive buffer cache refresh.") |
