update examples. fix bugs

This commit is contained in:
2026-02-03 12:53:52 -05:00
parent 9150c90ad1
commit 426a0c4715
15 changed files with 867 additions and 1148 deletions
+69 -105
View File
@@ -4,6 +4,7 @@
(:require [clojure.test :refer [deftest testing is are]]
[clojure.string :as str]
[tui.core :as tui]
[tui.events :as ev]
[tui.render :as render]
[tui.input :as input]
[tui.ansi :as ansi]))
@@ -145,98 +146,65 @@
;; =============================================================================
(deftest key-match-edge-cases-test
(testing "empty string pattern"
(is (not (input/key-match? [:key {:char \a}] ""))))
(testing "nil event returns false"
(is (not (ev/key= nil \q)))
(is (not (ev/key= nil :enter))))
(testing "multi-char string pattern only matches first char"
;; The current implementation only looks at first char
(is (input/key-match? [:key {:char \q}] "quit")))
(testing "non-key event returns false"
(is (not (ev/key= {:type :timer-tick} \q)))
(is (not (ev/key= {:type :http-result :status 200} :enter))))
(testing "nil message returns false"
(is (not (input/key-match? nil "q")))
(is (not (input/key-match? nil :enter))))
(testing "non-key message returns false"
(is (not (input/key-match? [:tick 123] "q")))
(is (not (input/key-match? [:http-success 200] :enter)))
(is (not (input/key-match? "not a vector" "q"))))
(testing "unknown key message structure"
(is (not (input/key-match? [:key {:unknown true}] "q")))
(is (not (input/key-match? [:key {}] "q")))))
(deftest key-str-edge-cases-test
(testing "nil message returns empty string"
(is (= "" (input/key->str nil))))
(testing "non-key message returns string representation"
;; Legacy format returns the second element as string
(is (string? (input/key->str [:tick 123])))
(is (string? (input/key->str [:custom :message]))))
(testing "key message with empty map"
(is (= "" (input/key->str [:key {}]))))
(testing "ctrl and alt combined"
;; This is an edge case - both modifiers
(is (= "ctrl+alt+x" (input/key->str [:key {:ctrl true :alt true :char \x}])))))
(testing "key event with missing key field"
(is (not (ev/key= {:type :key} \q)))
(is (not (ev/key= {:type :key :modifiers #{:ctrl}} \c #{:ctrl})))))
;; =============================================================================
;; COMMAND EDGE CASES
;; EVENT EDGE CASES
;; =============================================================================
(deftest batch-edge-cases-test
(testing "batch with all nils"
(is (= [:batch] (tui/batch nil nil nil))))
(is (nil? (ev/batch nil nil nil))))
(testing "batch with single command"
(is (= [:batch tui/quit] (tui/batch tui/quit))))
(testing "batch with single event"
(let [event (ev/batch {:type :msg1})]
(is (= :batch (:type event)))
(is (= 1 (count (:events event))))))
(testing "batch with no arguments"
(is (= [:batch] (tui/batch))))
(is (nil? (ev/batch))))
(testing "batch with many commands"
(let [cmd (tui/batch (tui/after 1 :t1) (tui/after 2 :t2) (tui/after 3 :t3) (tui/after 4 :t4) (tui/after 5 :t5))]
(is (= 6 (count cmd))) ; :batch + 5 commands
(is (= :batch (first cmd))))))
(testing "batch with many events"
(let [event (ev/batch {:type :t1} {:type :t2} {:type :t3} {:type :t4} {:type :t5})]
(is (= 5 (count (:events event))))
(is (= :batch (:type event))))))
(deftest sequentially-edge-cases-test
(testing "sequentially with all nils"
(is (= [:seq] (tui/sequentially nil nil nil))))
(deftest sequential-edge-cases-test
(testing "sequential with all nils"
(is (nil? (ev/sequential nil nil nil))))
(testing "sequentially with single command"
(is (= [:seq tui/quit] (tui/sequentially tui/quit))))
(testing "sequential with single event"
(let [event (ev/sequential {:type :msg1})]
(is (= :sequential (:type event)))
(is (= 1 (count (:events event))))))
(testing "sequentially with no arguments"
(is (= [:seq] (tui/sequentially)))))
(testing "sequential with no arguments"
(is (nil? (ev/sequential)))))
(deftest after-edge-cases-test
(testing "after with zero delay"
(let [cmd (tui/after 0 :immediate)]
(is (fn? cmd))
;; Zero delay executes immediately
(is (= :immediate (cmd)))))
(deftest delayed-event-edge-cases-test
(testing "delayed-event with zero delay"
(let [event (ev/delayed-event 0 {:type :immediate})]
(is (= :delayed-event (:type event)))
(is (= 0 (:ms event)))))
(testing "after with various delays creates function"
;; Don't invoke - just verify the function is created correctly
(is (fn? (tui/after 1 :t1)))
(is (fn? (tui/after 1000 :t2)))
(is (fn? (tui/after 999999999 :t3))))
(testing "delayed-event with various delays"
(is (= 1 (:ms (ev/delayed-event 1 {:type :t1}))))
(is (= 1000 (:ms (ev/delayed-event 1000 {:type :t2}))))
(is (= 999999999 (:ms (ev/delayed-event 999999999 {:type :t3})))))
(testing "after with complex message"
(let [cmd (tui/after 0 [:tick {:id 1 :data [1 2 3]}])]
(is (= [:tick {:id 1 :data [1 2 3]}] (cmd))))))
(deftest send-msg-edge-cases-test
(testing "send-msg with nil"
(let [cmd (tui/send-msg nil)]
(is (fn? cmd))
(is (nil? (cmd)))))
(testing "send-msg with complex message"
(let [msg {:type :complex :data [1 2 3] :nested {:a :b}}
cmd (tui/send-msg msg)]
(is (= msg (cmd))))))
(testing "delayed-event with complex message"
(let [event (ev/delayed-event 0 {:type :tick :id 1 :data [1 2 3]})]
(is (= {:type :tick :id 1 :data [1 2 3]} (:event event))))))
;; =============================================================================
;; ANSI EDGE CASES
@@ -319,27 +287,24 @@
;; UPDATE FUNCTION EDGE CASES
;; =============================================================================
(deftest update-with-unknown-messages-test
(testing "update function handles unknown messages gracefully"
(let [update-fn (fn [model msg]
(deftest update-with-unknown-events-test
(testing "update function handles unknown events gracefully"
(let [update-fn (fn [{:keys [model event]}]
(cond
(tui/key= msg "q") [model tui/quit]
:else [model nil]))]
(ev/key= event \q) {:model model :events [(ev/quit)]}
:else {:model model}))]
;; Unknown key
(let [[m cmd] (update-fn {:n 0} [:key {:char \x}])]
(is (= {:n 0} m))
(is (nil? cmd)))
(let [{:keys [model]} (update-fn {:model {:n 0} :event {:type :key :key \x}})]
(is (= {:n 0} model)))
;; Unknown message type
(let [[m cmd] (update-fn {:n 0} [:unknown :message])]
(is (= {:n 0} m))
(is (nil? cmd)))
;; Unknown event type
(let [{:keys [model]} (update-fn {:model {:n 0} :event {:type :unknown :message "test"}})]
(is (= {:n 0} model)))
;; Empty message
(let [[m cmd] (update-fn {:n 0} [])]
(is (= {:n 0} m))
(is (nil? cmd))))))
;; Event with no type
(let [{:keys [model]} (update-fn {:model {:n 0} :event {}})]
(is (= {:n 0} model))))))
(deftest model-with-complex-state-test
(testing "model with nested data structures"
@@ -348,23 +313,22 @@
:nested {:deep {:value 42}}
:selected #{}
:history []}
update-fn (fn [model msg]
(if (tui/key= msg :up)
[(-> model
(update :count inc)
(update :history conj (:count model)))
nil]
[model nil]))]
update-fn (fn [{:keys [model event]}]
(if (ev/key= event :up)
{:model (-> model
(update :count inc)
(update :history conj (:count model)))}
{:model model}))]
(let [[m1 _] (update-fn complex-model [:key :up])
[m2 _] (update-fn m1 [:key :up])]
(is (= 1 (:count m1)))
(is (= [0] (:history m1)))
(is (= 2 (:count m2)))
(is (= [0 1] (:history m2)))
(let [r1 (update-fn {:model complex-model :event {:type :key :key :up}})
r2 (update-fn {:model (:model r1) :event {:type :key :key :up}})]
(is (= 1 (:count (:model r1))))
(is (= [0] (:history (:model r1))))
(is (= 2 (:count (:model r2))))
(is (= [0 1] (:history (:model r2))))
;; Other fields unchanged
(is (= ["a" "b" "c"] (:items m2)))
(is (= 42 (get-in m2 [:nested :deep :value])))))))
(is (= ["a" "b" "c"] (:items (:model r2))))
(is (= 42 (get-in (:model r2) [:nested :deep :value])))))))
;; =============================================================================
;; VIEW FUNCTION EDGE CASES