diff options
| author | Craig Jennings <c@cjennings.net> | 2026-05-31 16:20:34 -0500 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-05-31 16:20:34 -0500 |
| commit | b72e794be60c5d4e94c61e5af8c08245773e3393 (patch) | |
| tree | c1a3f3cdce0ffed773213c0180badffc58843e14 /tests | |
| parent | 79db37565a356dead9a4c663361e77627d83d864 (diff) | |
| download | dotemacs-b72e794be60c5d4e94c61e5af8c08245773e3393.tar.gz dotemacs-b72e794be60c5d4e94c61e5af8c08245773e3393.zip | |
feat(ai-vterm): gate the F9 launcher to GUI frames
AI-vterm launches a graphical vterm side window, so F9 / C-F9 / M-F9 now decline with a message in a terminal frame instead of opening a vterm. The guard checks the current frame at command time rather than at load. That matters under the daemon, which serves GUI and terminal frames both with display-graphic-p nil at load, so a load-time gate would have disabled the launcher in its GUI frames too.
Routed the three window-behavior tests through a GUI-frame stub, since a batch run is itself a terminal frame.
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/test-ai-vterm--display-saved.el | 4 | ||||
| -rw-r--r-- | tests/test-ai-vterm--reuse-edge-window.el | 10 | ||||
| -rw-r--r-- | tests/test-ai-vterm--single-window-toggle.el | 12 | ||||
| -rw-r--r-- | tests/test-ai-vterm--terminal-guard.el | 78 | ||||
| -rw-r--r-- | tests/testutil-vterm-buffers.el | 13 |
5 files changed, 104 insertions, 13 deletions
diff --git a/tests/test-ai-vterm--display-saved.el b/tests/test-ai-vterm--display-saved.el index 866ff11d..0cf59a29 100644 --- a/tests/test-ai-vterm--display-saved.el +++ b/tests/test-ai-vterm--display-saved.el @@ -155,8 +155,8 @@ once and no spurious extra window leaks." (let ((display-buffer-alist (cj/--ai-vterm-display-rule-list)) (window-count-before (count-windows))) (select-window agent-win) - (cj/ai-vterm) ; off - (cj/ai-vterm) ; on + (cj/test--call-as-gui #'cj/ai-vterm) ; off + (cj/test--call-as-gui #'cj/ai-vterm) ; on (should (<= (count-windows) window-count-before)) ;; Agent must be displayed exactly once. (let ((agent-windows diff --git a/tests/test-ai-vterm--reuse-edge-window.el b/tests/test-ai-vterm--reuse-edge-window.el index a7009423..9f621477 100644 --- a/tests/test-ai-vterm--reuse-edge-window.el +++ b/tests/test-ai-vterm--reuse-edge-window.el @@ -175,7 +175,7 @@ window count stays 2 (the native `quit-restore-window' puts 2 back)." (should (= (count-windows) 2)) (should (member agent-name (cj/test--displayed-buffer-names))) ;; Toggle off -> the displaced buffer (2) returns to the slot. - (cj/ai-vterm) + (cj/test--call-as-gui #'cj/ai-vterm) (should (= (count-windows) 2)) (let ((bufs (cj/test--displayed-buffer-names))) (should (member right-name bufs)) @@ -213,11 +213,11 @@ the same width." (display-buffer agent-buf) (should (= (count-windows) 2)) ;; off - (cj/ai-vterm) + (cj/test--call-as-gui #'cj/ai-vterm) (should (= (count-windows) 2)) (should-not (cj/--ai-vterm-displayed-agent-window)) ;; on again - (cj/ai-vterm) + (cj/test--call-as-gui #'cj/ai-vterm) (should (= (count-windows) 2)) (let ((win (cj/--ai-vterm-displayed-agent-window))) (should (windowp win)) @@ -259,9 +259,9 @@ most-recent agent, which would now be the other one." (display-buffer a2) ; | left | A2 | (should (eq (window-buffer (cj/--ai-vterm-displayed-agent-window)) a2)) - (cj/ai-vterm) ; off -> | left | right | + (cj/test--call-as-gui #'cj/ai-vterm) ; off -> | left | right | (should-not (cj/--ai-vterm-displayed-agent-window)) - (cj/ai-vterm) ; on -> must bring A2 back + (cj/test--call-as-gui #'cj/ai-vterm) ; on -> must bring A2 back (should (eq (window-buffer (cj/--ai-vterm-displayed-agent-window)) a2)))))) (when (get-buffer left-name) (kill-buffer left-name)) diff --git a/tests/test-ai-vterm--single-window-toggle.el b/tests/test-ai-vterm--single-window-toggle.el index 50f1504a..928656f2 100644 --- a/tests/test-ai-vterm--single-window-toggle.el +++ b/tests/test-ai-vterm--single-window-toggle.el @@ -48,12 +48,12 @@ batch use both." (let ((display-buffer-alist (cj/--ai-vterm-display-rule-list))) ;; Toggle off -- the dispatcher's force-swap should put the ;; window on a non-agent buffer. - (cj/ai-vterm) + (cj/test--call-as-gui #'cj/ai-vterm) (should (one-window-p)) (should-not (cj/--ai-vterm-displayed-agent-window)) (should (eq cj/--ai-vterm-last-was-bury t)) ;; Toggle on -- should restore agent in the same lone window. - (cj/ai-vterm) + (cj/test--call-as-gui #'cj/ai-vterm) (should (one-window-p)) (let ((win (cj/--ai-vterm-displayed-agent-window))) (should (windowp win)) @@ -80,7 +80,7 @@ agent-window state and can route through the display-saved path." (win (selected-window))) (set-window-buffer win agent-buf) (let ((display-buffer-alist (cj/--ai-vterm-display-rule-list))) - (cj/ai-vterm)) + (cj/test--call-as-gui #'cj/ai-vterm)) (should (window-live-p win)) (should-not (cj/--ai-vterm-buffer-p (window-buffer win))))) (cj/test--kill-agent-buffers)))) @@ -96,7 +96,7 @@ agent-window state and can route through the display-saved path." (let ((agent-buf (get-buffer-create agent-name))) (set-window-buffer (selected-window) agent-buf) (let ((display-buffer-alist (cj/--ai-vterm-display-rule-list))) - (cj/ai-vterm) + (cj/test--call-as-gui #'cj/ai-vterm) (should (eq cj/--ai-vterm-last-was-bury t))))) (cj/test--kill-agent-buffers)))) @@ -119,7 +119,7 @@ toggle-off." (display-buffer-alist (cj/--ai-vterm-display-rule-list))) (set-window-buffer agent-win agent-buf) (select-window agent-win) - (cj/ai-vterm) + (cj/test--call-as-gui #'cj/ai-vterm) (should-not cj/--ai-vterm-last-was-bury)))) (when (get-buffer left-name) (kill-buffer left-name)) (cj/test--kill-agent-buffers)))) @@ -176,7 +176,7 @@ the flag nil (no spurious set)." (display-buffer-alist (cj/--ai-vterm-display-rule-list))) (set-window-buffer agent-win agent-buf) (select-window agent-win) - (cj/ai-vterm) + (cj/test--call-as-gui #'cj/ai-vterm) (should-not cj/--ai-vterm-last-was-bury)))) (when (get-buffer "*test-sw-untouched-left*") (kill-buffer "*test-sw-untouched-left*")) diff --git a/tests/test-ai-vterm--terminal-guard.el b/tests/test-ai-vterm--terminal-guard.el new file mode 100644 index 00000000..5a7971bf --- /dev/null +++ b/tests/test-ai-vterm--terminal-guard.el @@ -0,0 +1,78 @@ +;;; test-ai-vterm--terminal-guard.el --- Tests for the terminal-frame guard -*- lexical-binding: t; -*- + +;;; Commentary: +;; AI-vterm launches a graphical vterm side window, so it is GUI-only. +;; `cj/--ai-vterm-refuse-in-terminal' signals a `user-error' when the +;; current frame is a terminal frame; each interactive entry point +;; (`cj/ai-vterm', `cj/ai-vterm-pick-project', `cj/ai-vterm-close') +;; calls it first so F9 and friends decline -- with a message -- in a +;; terminal frame instead of launching a vterm. The check is per-frame +;; at command time, not at load, so a daemon serving both GUI and +;; terminal frames keeps the launcher working in its GUI frames. +;; +;; `env-terminal-p' is mocked so the tests are deterministic regardless +;; of whether the run itself is graphical (batch runs are terminal). + +;;; Code: + +(require 'ert) +(require 'cl-lib) + +(add-to-list 'load-path (expand-file-name "modules" user-emacs-directory)) +(add-to-list 'load-path (expand-file-name "tests" user-emacs-directory)) +(require 'ai-vterm) + +;; ---------------------------- the guard helper ---------------------------- + +(ert-deftest test-ai-vterm--refuse-in-terminal-errors-in-terminal-frame () + "Error: terminal frame -> `user-error', so the command declines." + (cl-letf (((symbol-function 'env-terminal-p) (lambda () t))) + (should-error (cj/--ai-vterm-refuse-in-terminal) :type 'user-error))) + +(ert-deftest test-ai-vterm--refuse-in-terminal-passes-in-gui-frame () + "Normal: GUI frame -> returns nil, no error, command proceeds." + (cl-letf (((symbol-function 'env-terminal-p) (lambda () nil))) + (should-not (cj/--ai-vterm-refuse-in-terminal)))) + +;; ------------------- the three interactive entry points ------------------- + +(ert-deftest test-ai-vterm-f9-declines-in-terminal-without-dispatching () + "Error: F9 in a terminal frame errors and never reaches dispatch." + (let ((dispatched nil)) + (cl-letf (((symbol-function 'env-terminal-p) (lambda () t)) + ((symbol-function 'cj/--ai-vterm-dispatch) + (lambda () (setq dispatched t) '(pick-project)))) + (should-error (cj/ai-vterm) :type 'user-error) + (should-not dispatched)))) + +(ert-deftest test-ai-vterm-pick-project-declines-in-terminal-without-prompting () + "Error: C-F9 in a terminal frame errors and never reaches the picker." + (let ((prompted nil)) + (cl-letf (((symbol-function 'env-terminal-p) (lambda () t)) + ((symbol-function 'cj/--ai-vterm-pick-project) + (lambda () (setq prompted t) "/tmp"))) + (should-error (cj/ai-vterm-pick-project) :type 'user-error) + (should-not prompted)))) + +(ert-deftest test-ai-vterm-close-declines-in-terminal-without-targeting () + "Error: M-F9 in a terminal frame errors and never reaches close-target." + (let ((targeted nil)) + (cl-letf (((symbol-function 'env-terminal-p) (lambda () t)) + ((symbol-function 'cj/--ai-vterm-close-target) + (lambda () (setq targeted t) nil))) + (should-error (cj/ai-vterm-close) :type 'user-error) + (should-not targeted)))) + +(ert-deftest test-ai-vterm-f9-passes-guard-in-gui-frame () + "Normal: F9 in a GUI frame passes the guard and reaches dispatch." + (let ((dispatched nil)) + (cl-letf (((symbol-function 'env-terminal-p) (lambda () nil)) + ((symbol-function 'cj/--ai-vterm-dispatch) + (lambda () (setq dispatched t) '(pick-project))) + ((symbol-function 'cj/ai-vterm-pick-project) + (lambda (&optional _arg) nil))) + (cj/ai-vterm) + (should dispatched)))) + +(provide 'test-ai-vterm--terminal-guard) +;;; test-ai-vterm--terminal-guard.el ends here diff --git a/tests/testutil-vterm-buffers.el b/tests/testutil-vterm-buffers.el index 01a65d90..17f0a69a 100644 --- a/tests/testutil-vterm-buffers.el +++ b/tests/testutil-vterm-buffers.el @@ -9,6 +9,19 @@ ;;; Code: +(require 'cl-lib) + +(defun cj/test--call-as-gui (fn) + "Call FN with `env-terminal-p' stubbed to return nil (a GUI frame). + +The AI-vterm interactive commands refuse to run in a terminal frame +via `cj/--ai-vterm-refuse-in-terminal'. A batch test run is itself a +terminal frame, so tests that exercise the GUI-frame window behavior +of those commands call them through this helper to present a GUI +context." + (cl-letf (((symbol-function 'env-terminal-p) (lambda () nil))) + (funcall fn))) + (defun cj/test--kill-buffers-matching-prefix (prefix) "Kill all live buffers whose name starts with PREFIX." (dolist (b (buffer-list)) |
