(in-package #:lime.ui.test) (def-suite lime-ui-suite :description "Renderer-neutral palette session tests.") (in-suite lime-ui-suite) (defun candidate (id label &optional detail result) (make-ui-candidate :id id :label label :detail detail :action (lambda () (or result (ui-close-result))))) (test direct-search-filters-and-wraps-selection (let ((session (make-ui-session (list (candidate "code" "Code" "Lime development") (candidate "play" "Play" "Game"))))) (ui-session-handle-key session #\g) (is (equal (mapcar #'ui-candidate-id (ui-session-candidates session)) '("play"))) (is (= (ui-session-selected-index session) 0)) (ui-session-handle-key session :backspace) (ui-session-handle-key session :up) (is (= (ui-session-selected-index session) 1)))) (test direct-search-supports-ordered-fuzzy-program-input (let ((session (make-ui-session (list (candidate "waterfox" "waterfox") (candidate "web-browser" "web-browser") (candidate "terminal" "terminal"))))) (dolist (character '(#\w #\t #\f)) (ui-session-handle-key session character)) (is (equal (mapcar #'ui-candidate-id (ui-session-candidates session)) '("waterfox"))))) (test a-candidate-can-replace-the-palette-with-an-explicit-next-step (let* ((follow-up (candidate "take" "Move windows here" nil (ui-close-result))) (session (make-ui-session (list (candidate "docs" "Docs" "Lime development" (ui-replace-result (list follow-up) "Docs has windows leased on another Head.")))))) (let ((result (ui-session-handle-key session :return))) (is (eq (ui-result-kind result) :replace)) (is (equal (ui-session-status session) "Docs has windows leased on another Head.")) (is (equal (mapcar #'ui-candidate-id (ui-session-candidates session)) '("take")))))) (test escape-closes-a-session-without-running-an-action (let ((session (make-ui-session (list (candidate "code" "Code"))))) (is (eq (ui-result-kind (ui-session-handle-key session :escape)) :close)))) (test a-candidate-can-collect-a-name-through-a-renderer-neutral-prompt (let ((submitted-name nil)) (let ((session (make-ui-session (list (make-ui-candidate :id :new-activity :label "New named Activity..." :action (lambda () (ui-prompt-result "New Activity" (lambda (name) (setf submitted-name name) (ui-close-result "Activity created."))))))))) (is (eq (ui-result-kind (ui-session-handle-key session :return)) :prompt)) (is (eq (ui-session-mode session) :prompt)) (is (equal (ui-session-prompt session) "New Activity")) (is (eq (ui-result-kind (ui-session-handle-key session :return)) :keep-open)) (is (equal (ui-session-status session) "Input is required.")) (dolist (character '(#\L #\i #\m #\e)) (ui-session-handle-key session character)) (is (eq (ui-result-kind (ui-session-handle-key session :return)) :close)) (is (equal submitted-name "Lime"))))) (test key-path-mode-navigates-a-tree-and-can-return-to-search (let ((ran-p nil)) (let* ((run-node (make-ui-key-node :id :run :key #\r :label "Run" :action (lambda () (setf ran-p t) (ui-close-result)))) (tools-node (make-ui-key-node :id :tools :key #\t :label "Tools" :children (list run-node))) (session (make-ui-session (list (candidate "search" "Search")) :key-path (list tools-node)))) (is (eq (ui-result-kind (ui-session-handle-key session :tab)) :keep-open)) (is (eq (ui-session-mode session) :key-path)) (is (equal (mapcar #'ui-key-node-id (ui-session-key-nodes session)) '(:tools))) (ui-session-handle-key session #\t) (is (equal (mapcar #'ui-key-node-id (ui-session-key-path session)) '(:tools))) (is (equal (mapcar #'ui-key-node-id (ui-session-key-nodes session)) '(:run))) (ui-session-handle-key session :backspace) (is-false (ui-session-key-path session)) (is (eq (ui-result-kind (ui-session-handle-key session #\t)) :keep-open)) (is (eq (ui-result-kind (ui-session-handle-key session #\r)) :close)) (is-true ran-p) (ui-session-handle-key session :tab) (is (eq (ui-session-mode session) :search))))) (test command-map-can-open-without-a-search-list-or-running-an-action (let* ((runs 0) (leaf (make-ui-key-node :key #\h :label "Hide" :action (lambda () (incf runs) (ui-close-result)))) (root (make-ui-key-node :key #\w :label "Windows" :children (list leaf))) (session (make-ui-session (list (candidate :all "All commands")) :key-path (list root) :mode :key-path))) (is (eq :key-path (ui-session-mode session))) (is (equal (list root) (ui-session-key-nodes session))) (is-false (ui-session-candidates session)) (is (eq :keep-open (ui-result-kind (ui-session-handle-key session :return)))) (is (= 0 runs)) (ui-session-handle-key session #\z) (is (search "not available" (ui-session-status session))) (is (string= "" (ui-session-query session))) (ui-session-handle-key session #\w) (is (equal (list root) (ui-session-key-path session))) (is (equal (list leaf) (ui-session-key-nodes session))) (ui-session-handle-key session :page-down) (is (= 1 (ui-session-key-page session))) (ui-session-handle-key session :backspace) (is (= 0 (ui-session-key-page session))) (is-false (ui-session-key-path session)) (ui-session-handle-key session #\w) (is (eq :close (ui-result-kind (ui-session-handle-key session #\h)))) (is (= 1 runs)))) (test search-from-the-menu-restores-the-root-catalog-after-a-temporary-picker (let* ((all (candidate :all "All commands")) (choice (candidate :partial "Open partial view")) (leaf (make-ui-key-node :key #\v :label "Views" :action (lambda () (ui-replace-result (list choice))))) (session (make-ui-session (list all) :key-path (list leaf) :mode :key-path))) (ui-session-handle-key session #\v) (is (eq :search (ui-session-mode session))) (is (string= "Type to filter; Enter to choose; Esc to cancel." (ui-session-status session))) (is (equal (list choice) (ui-session-candidates session))) (ui-session-handle-key session :tab) (is (eq :key-path (ui-session-mode session))) (ui-session-handle-key session #\Space) (is (eq :search (ui-session-mode session))) (is (equal (list all) (ui-session-candidates session))) (ui-session-handle-key session :tab) (ui-session-handle-key session :tab) (is (equal (list all) (ui-session-candidates session))))) (test a-bound-space-in-a-custom-menu-wins-over-the-search-shortcut (let* ((ran nil) (node (make-ui-key-node :key #\Space :label "Custom command" :action (lambda () (setf ran t) (ui-close-result)))) (session (make-ui-session nil :key-path (list node) :mode :key-path))) (is (eq :close (ui-result-kind (ui-session-handle-key session #\Space)))) (is-true ran))) (test menu-name-prompts-keep-spaces-and-dont-let-tab-run-a-command (let* ((name nil) (node (make-ui-key-node :key #\n :label "New activity" :action (lambda () (ui-prompt-result "Name" (lambda (input) (setf name input) (ui-close-result)))))) (session (make-ui-session nil :key-path (list node) :mode :key-path))) (ui-session-handle-key session #\n) (is (eq :prompt (ui-session-mode session))) (is (string= "Enter to confirm; Esc to cancel." (ui-session-status session))) (dolist (key '(#\M #\y #\Space #\W #\M)) (ui-session-handle-key session key)) (ui-session-handle-key session :tab) (is (eq :prompt (ui-session-mode session))) (is-false name) (is (eq :close (ui-result-kind (ui-session-handle-key session :return)))) (is (string= "My WM" name)))) (test search-and-keyless-launcher-sessions-keep-their-old-entry-behavior (let ((session (make-ui-session (list (candidate :terminal "st terminal"))))) (is (eq :search (ui-session-mode session))) (ui-session-handle-key session #\s) (ui-session-handle-key session #\Space) (is (string= "s " (ui-session-query session))) (ui-session-handle-key session :tab) (is (eq :search (ui-session-mode session))) (is (eq :close (ui-result-kind (ui-session-handle-key session :escape)))))) (test pointer-candidate-activation-shares-transitions-and-rejects-stale-hits (let* ((runs 0) (choice (make-ui-candidate :label "Choice" :action (lambda () (incf runs) (ui-close-result)))) (picker (make-ui-candidate :label "Picker" :action (lambda () (ui-replace-result (list choice))))) (session (make-ui-session (list picker)))) (is (eq :keep-open (ui-result-kind (ui-session-activate-candidate session choice)))) (is (= 0 runs)) (is (eq :replace (ui-result-kind (ui-session-activate-candidate session picker)))) (is (equal (list choice) (ui-session-candidates session))) (is (eq :keep-open (ui-result-kind (ui-session-activate-candidate session picker)))) (is (eq :close (ui-result-kind (ui-session-activate-candidate session choice)))) (is (= 1 runs)) (ui-session-handle-key session #\z) (is-false (ui-session-candidates session)) (is (eq :keep-open (ui-result-kind (ui-session-activate-candidate session choice)))) (is (= 1 runs)) (ui-session-apply-result session (ui-prompt-result "Name" (lambda (name) (declare (ignore name)) (ui-close-result)))) (is (eq :keep-open (ui-result-kind (ui-session-activate-candidate session choice)))) (is (eq :prompt (ui-session-mode session))))) (defun run-tests () (run! 'lime-ui-suite))