diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-28 04:11:45 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-28 04:11:45 -0400 |
| commit | da7ee0841924dfbd91c9a944e0bfeff6903bd8a1 (patch) | |
| tree | b3461a7d9aa3971b55bfeeab9aeddb06d1b088fe | |
| parent | ce74e30562dfac3f599a94a45ac16c3c2319ab41 (diff) | |
| download | emacs-wttrin-da7ee0841924dfbd91c9a944e0bfeff6903bd8a1.tar.gz emacs-wttrin-da7ee0841924dfbd91c9a944e0bfeff6903bd8a1.zip | |
fix: don't steal focus when an async weather response renders
wttrin--display-weather ran switch-to-buffer from the async callback, so a
response arriving after the user moved to another buffer yanked them back to
*wttr.in*. It now renders with set-buffer; selecting the buffer is the
interactive command's job (wttrin-query already does it at invocation), so a
late response updates the buffer in place without changing the selected window.
| -rw-r--r-- | tests/test-wttrin--display-weather-focus.el | 45 | ||||
| -rw-r--r-- | tests/test-wttrin--display-weather.el | 8 | ||||
| -rw-r--r-- | wttrin.el | 6 |
3 files changed, 54 insertions, 5 deletions
diff --git a/tests/test-wttrin--display-weather-focus.el b/tests/test-wttrin--display-weather-focus.el new file mode 100644 index 0000000..cc08fdd --- /dev/null +++ b/tests/test-wttrin--display-weather-focus.el @@ -0,0 +1,45 @@ +;;; test-wttrin--display-weather-focus.el --- Async render doesn't steal focus -*- lexical-binding: t; -*- + +;; Copyright (C) 2024-2026 Craig Jennings + +;;; Commentary: +;; wttrin--display-weather runs from an async callback. If the user moved to +;; another buffer while the fetch was in flight, rendering the result must not +;; select *wttr.in* and steal the window. + +;;; Code: + +(require 'ert) +(require 'wttrin) +(require 'testutil-wttrin) + +(ert-deftest test-wttrin--display-weather-error-does-not-steal-focus () + "Error: rendering an async response does not select *wttr.in*. +If the user moved to another buffer while the fetch was in flight, displaying +the result must leave the selected window on that other buffer." + (let ((other (get-buffer-create "*wttrin-other*"))) + (unwind-protect + (progn + (set-window-buffer (selected-window) other) + (wttrin--display-weather "Paris" "Weather report: paris\n+10 C\n" + nil nil nil) + (should (eq (window-buffer (selected-window)) other))) + (when (get-buffer "*wttr.in*") (kill-buffer "*wttr.in*")) + (when (buffer-live-p other) (kill-buffer other))))) + +(ert-deftest test-wttrin--display-weather-normal-populates-buffer () + "Normal: rendering still fills *wttr.in* with the weather content." + (let ((other (get-buffer-create "*wttrin-other*"))) + (unwind-protect + (progn + (set-window-buffer (selected-window) other) + (wttrin--display-weather "Paris" "Weather report: paris\n+10 C\n" + nil nil nil) + (with-current-buffer "*wttr.in*" + (should (string-match-p "Weather report: Paris" + (buffer-string))))) + (when (get-buffer "*wttr.in*") (kill-buffer "*wttr.in*")) + (when (buffer-live-p other) (kill-buffer other))))) + +(provide 'test-wttrin--display-weather-focus) +;;; test-wttrin--display-weather-focus.el ends here diff --git a/tests/test-wttrin--display-weather.el b/tests/test-wttrin--display-weather.el index 99ea067..3b9cdee 100644 --- a/tests/test-wttrin--display-weather.el +++ b/tests/test-wttrin--display-weather.el @@ -51,7 +51,10 @@ Weather report: Paris, France ;;; Normal Cases (ert-deftest test-wttrin--display-weather-normal-valid-data-creates-buffer () - "Test that valid weather data creates and displays buffer correctly." + "Test that valid weather data creates and fills the buffer correctly. +Display is the interactive command's job (wttrin-query selects the buffer at +invocation); this async render only updates content, so it does not select a +window." (test-wttrin--display-weather-setup) (unwind-protect (testutil-wttrin-with-clean-weather-buffer @@ -60,9 +63,6 @@ Weather report: Paris, France ;; Buffer should exist (should (get-buffer "*wttr.in*")) - ;; Buffer should be displayed - (should (get-buffer-window "*wttr.in*")) - ;; Buffer should have content (with-current-buffer "*wttr.in*" (should (> (buffer-size) 0)) @@ -1176,7 +1176,11 @@ coordinates but can name the place)." "Cannot retrieve weather data. Perhaps the location was misspelled?")) (wttrin--add-to-location-history display) (let ((buffer (get-buffer-create (format "*wttr.in*")))) - (switch-to-buffer buffer) + ;; Render into the buffer without selecting it. This runs from an + ;; async callback; the command (wttrin-query) already showed the buffer + ;; at invocation time, so re-selecting here would steal focus if the + ;; user moved away while the fetch was in flight. + (set-buffer buffer) ;; wttrin-mode calls kill-all-local-variables, so it must run ;; before setting any buffer-local state (xterm-color, location) |
