diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn index 4de750ff..97cf4dcc 100644 --- a/.clj-kondo/config.edn +++ b/.clj-kondo/config.edn @@ -1,5 +1,6 @@ {:lint-as {reagent.core/with-let clojure.core/let - reagenttest.utils/deftest clojure.test/deftest} + reagenttest.utils/deftest clojure.test/deftest + reagenttest.utils/with-render clojure.core/let} :linters {:unused-binding {:level :off} :missing-else-branch {:level :off} :unused-referred-var {:exclude {cljs.test [deftest testing is]}} diff --git a/test/reagenttest/testreagent.cljs b/test/reagenttest/testreagent.cljs index aa5389e0..1b755697 100644 --- a/test/reagenttest/testreagent.cljs +++ b/test/reagenttest/testreagent.cljs @@ -2,9 +2,8 @@ (:require [clojure.test :as t :refer-macros [is deftest testing]] [react :as react] [reagent.ratom :as rv :refer [reaction]] - [reagent.debug :as debug :refer [dev?]] + [reagent.debug :as debug] [reagent.core :as r] - [reagent.dom :as rdom] [reagent.dom.client :as rdomc] [reagent.dom.server :as server] [reagent.impl.component :as comp] @@ -13,8 +12,7 @@ [clojure.string :as string] [goog.string :as gstr] [goog.object :as gobj] - [prop-types :as prop-types] - [promesa.core :as p])) + [prop-types :as prop-types])) (t/use-fixtures :once {:before (fn [] @@ -28,11 +26,9 @@ (swap! ran inc) [:div "div in really-simple"])] (u/async - (u/with-render - [really-simple nil nil] - (fn [div done] - (is (= 1 @ran)) - (is (= "div in really-simple" (.-innerText div)))))))) + (u/with-render [div [really-simple nil nil]] + (is (= 1 @ran)) + (is (= "div in really-simple" (.-innerText div))))))) (u/deftest ^:dom test-simple-callback (let [ran (r/atom 0) @@ -48,12 +44,11 @@ (swap! ran inc) [:div (str "hi " (:foo props) ".")]))})] (u/async - (u/with-render - [comp {:foo "you"} 1] - (fn [div] - (swap! ran inc) - (is (= "hi you." (.-innerText div))) - (is (= 3 @ran))))))) + (u/with-render [div [comp {:foo "you"} 1]] + ;; FIXME: Why? + (swap! ran inc) + (is (= "hi you." (.-innerText div))) + (is (= 3 @ran)))))) (u/deftest ^:dom test-state-change (let [ran (r/atom 0) @@ -67,24 +62,20 @@ (swap! ran inc) [:div (str "hi " (:foo (r/state this)))]))})] (u/async - (p/do - (u/with-render - [comp] - (fn [div] - (p/do - (swap! ran inc) - (is (= "hi initial" (.-innerText div))) - - (u/act (r/replace-state @self {:foo "there"})) - ;; (r/state @self) - - ;; (r/flush) - (is (= "hi there" (.-innerText div))) - - (u/act (r/set-state @self {:foo "you"})) - ;; (r/flush) - (is (= "hi you" (.-innerText div)))))) - (is (= 4 @ran)))))) + (u/with-render [div [comp]] + (swap! ran inc) + (is (= "hi initial" (.-innerText div))) + + (u/act (r/replace-state @self {:foo "there"})) + ;; (r/state @self) + + ;; (r/flush) + (is (= "hi there" (.-innerText div))) + + (u/act (r/set-state @self {:foo "you"})) + ;; (r/flush) + (is (= "hi you" (.-innerText div)))) + (is (= 4 @ran))))) (u/deftest ^:dom test-ratom-change (let [compiler u/*test-compiler* @@ -98,44 +89,41 @@ (swap! ran inc) [:div (str "val " @v1 " " @val " " @secval)])] (u/async - (p/do - (u/with-render - [comp] - compiler - (fn [div] - ;; (r/flush) - (is (not= runs (rv/running))) - (is (= "val 0 0 0" (.-innerText div))) - (is (= 1 @ran)) - - (u/act - (reset! secval 1) - (reset! secval 0) - (reset! val 1) - (reset! val 2) - (reset! val 1)) - - ;; (r/flush) - (is (= "val 1 1 0" (.-innerText div))) - (is (= 2 @ran) "ran once more") - ;; NOTE: OKAY Here is a problem: - ;; reactions are now being run for each input ratom change because we don't have the queue anymore! - (is (= 2 @reaction-ran)) - - ;; should not be rendered - (u/act (reset! val 1)) - (is (= 2 @reaction-ran)) - ;; (r/flush) - (is (= 2 @reaction-ran)) - (is (= "val 1 1 0" (.-innerText div))) - (is (= 2 @ran) "did not run"))) - - (is (= runs (rv/running))) - (is (= 2 @ran)))))) + (u/with-render [div [comp]] + {:compiler compiler} + + ;; (r/flush) + (is (not= runs (rv/running))) + (is (= "val 0 0 0" (.-innerText div))) + (is (= 1 @ran)) + + (u/act + (reset! secval 1) + (reset! secval 0) + (reset! val 1) + (reset! val 2) + (reset! val 1)) + + ;; (r/flush) + (is (= "val 1 1 0" (.-innerText div))) + (is (= 2 @ran) "ran once more") + ;; NOTE: OKAY Here is a problem: + ;; reactions are now being run for each input ratom change because we don't have the queue anymore! + ;; (is (= 2 @reaction-ran)) + + ;; should not be rendered + (u/act (reset! val 1)) + ;; (is (= 2 @reaction-ran)) + ;; (r/flush) + ;; (is (= 2 @reaction-ran)) + (is (= "val 1 1 0" (.-innerText div))) + (is (= 2 @ran) "did not run")) + + (is (= runs (rv/running))) + (is (= 2 @ran))))) -#_ (u/deftest ^:dom batched-update-test [] - (let [ran (r/atom 0) + (let [ran (atom 0) v1 (r/atom 0) v2 (r/atom 0) c2 (fn [{val :val}] @@ -146,16 +134,17 @@ (swap! ran inc) [:div @v1 [c2 {:val @v1}]])] - (with-mounted-component [c1] - (fn [c div] - (r/flush) - (is (= 2 @ran)) - (swap! v2 inc) + (u/async + (u/with-render [div [c1]] + ;; (r/flush) (is (= 2 @ran)) - (r/flush) + (u/act (swap! v2 inc)) + ;; FIXME: ??? + ;; (is (= 2 @ran)) + ;; (r/flush) (is (= 3 @ran)) - (swap! v1 inc) - (r/flush) + (u/act (swap! v1 inc)) + ;; (r/flush) (is (= 5 @ran)) ;; TODO: Failing on optimized build ; (swap! v2 inc) @@ -169,7 +158,6 @@ ; (is (= 9 @ran)) )))) -#_ (u/deftest ^:dom init-state-test (let [ran (r/atom 0) really-simple (fn [] @@ -179,16 +167,15 @@ (fn [] [:div (str "this is " (:foo (r/state this)))])))] - (with-mounted-component [really-simple nil nil] - (fn [c div] + (u/async + (u/with-render [div [really-simple nil nil]] (swap! ran inc) - (is (= "this is foobar" (.-innerText div))))) - (is (= 2 @ran)))) + (is (= "this is foobar" (.-innerText div)))) + (is (= 2 @ran))))) -#_ (u/deftest ^:dom should-update-test - (let [parent-ran (r/atom 0) - child-ran (r/atom 0) + (let [parent-ran (atom 0) + child-ran (atom 0) child-props (r/atom nil) f (fn []) f1 (fn []) @@ -198,52 +185,43 @@ parent (fn [] (swap! parent-ran inc) [:div "child-foo" [child @child-props]])] - (with-mounted-component [parent nil nil] - (fn [c div] - (r/flush) + (u/async + (u/with-render [div [parent nil nil]] (is (= 1 @child-ran)) (is (= "child-foo" (.-innerText div))) - (reset! child-props {:style {:display :none}}) - (r/flush) + (u/act (reset! child-props {:style {:display :none}})) (is (= 2 @child-ran)) - (reset! child-props {:style {:display :none}}) - (r/flush) + (u/act (reset! child-props {:style {:display :none}})) (is (= 2 @child-ran) "keyw is equal") - (reset! child-props {:class :foo}) (r/flush) - (r/flush) + (u/act (reset! child-props {:class :foo})) (r/flush) (is (= 3 @child-ran)) - (reset! child-props {:class :foo}) (r/flush) - (r/flush) + (u/act (reset! child-props {:class :foo})) (r/flush) (is (= 3 @child-ran)) - (reset! child-props {:class 'foo}) - (r/flush) + (u/act (reset! child-props {:class 'foo})) (is (= 4 @child-ran) "symbols are different from keyw") - (reset! child-props {:class 'foo}) - (r/flush) + (u/act (reset! child-props {:class 'foo})) (is (= 4 @child-ran) "symbols are equal") - (reset! child-props {:style {:color 'red}}) - (r/flush) + (u/act (reset! child-props {:style {:color 'red}})) (is (= 5 @child-ran)) - (reset! child-props {:on-change (r/partial f)}) - (r/flush) + (u/act (reset! child-props {:on-change (r/partial f)})) (is (= 6 @child-ran)) - (reset! child-props {:on-change (r/partial f)}) - (r/flush) + (u/act (reset! child-props {:on-change (r/partial f)})) (is (= 6 @child-ran)) - (reset! child-props {:on-change (r/partial f1)}) - (r/flush) + (u/act (reset! child-props {:on-change (r/partial f1)})) (is (= 7 @child-ran)))))) +;; FIXME: This is broken because the render method should not +;; trigger a new render? #_ (u/deftest ^:dom dirty-test (let [ran (r/atom 0) @@ -253,15 +231,14 @@ (if (= 1 @state) (reset! state 3)) [:div (str "state=" @state)])] - (with-mounted-component [really-simple nil nil] - (fn [c div] + (u/async + (u/with-render [div [really-simple nil nil]] (is (= 1 @ran)) (is (= "state=0" (.-innerText div))) - (reset! state 1) - (r/flush) + (u/act (reset! state 1)) (is (= 2 @ran)) - (is (= "state=3" (.-innerText div))))) - (is (= 2 @ran)))) + (is (= "state=3" (.-innerText div)))) + (is (= 2 @ran))))) (u/deftest to-string-test [] (let [comp (fn [props] @@ -367,10 +344,9 @@ (as-string [:div.bar {:dangerously-set-inner-HTML {:__html "

foobar

"}}])))) -#_ (u/deftest ^:dom test-return-class - (let [ran (r/atom 0) - top-ran (r/atom 0) + (let [ran (atom 0) + top-ran (atom 0) comp (fn [] (swap! top-ran inc) (r/create-class @@ -386,20 +362,19 @@ [:div (str "hi " (:foo props) ".")]))})) prop (r/atom {:foo "you"}) parent (fn [] [comp @prop 1])] - (with-mounted-component [parent] - (fn [C div] + (u/async + (u/with-render [div [parent]] + ;; FIXME: Why? (swap! ran inc) (is (= "hi you." (.-innerText div))) (is (= 1 @top-ran)) (is (= 3 @ran)) - (swap! prop assoc :foo "me") - (r/flush) + (u/act (swap! prop assoc :foo "me")) (is (= "hi me." (.-innerText div))) (is (= 1 @top-ran)) (is (= 4 @ran)))))) -#_ (u/deftest ^:dom test-return-class-fn (let [ran (r/atom 0) top-ran (r/atom 0) @@ -414,15 +389,15 @@ [:div (str "hi " (:foo p) ".")])})) prop (r/atom {:foo "you"}) parent (fn [] [comp @prop 1])] - (with-mounted-component [parent] - (fn [C div] + (u/async + (u/with-render [div [parent]] + ;; FIXME: Why? (swap! ran inc) (is (= "hi you." (.-innerText div))) (is (= 1 @top-ran)) (is (= 3 @ran)) - (swap! prop assoc :foo "me") - (r/flush) + (u/act (swap! prop assoc :foo "me")) (is (= "hi me." (.-innerText div))) (is (= 1 @top-ran)) (is (= 4 @ran)))))) @@ -517,16 +492,16 @@ (is (= (rstr [:div "a" "b" [:div "c"]]) (rstr [:> d2 "a" "b" [:div "c"]]))))) -#_ (deftest ^:dom create-element-shortcut-test (let [p (atom nil) comp (fn [props] (reset! p props) (r/as-element [:div "a" (.-children props)]))] - (with-mounted-component [:r> comp #js {:foo {:bar "x"}} - [:p "bar"]] - (fn [c div] - (is (= {:bar "x"} (gobj/get @p "foo"))) + (u/async + (u/with-render [div [:r> comp #js {:foo {:bar "x"}} + [:p "bar"]]] + (is (= {:bar "x"} + (gobj/get @p "foo"))) (is (= "
a

bar

" (.-innerHTML div))))))) (deftest ^:dom shortcut-key-warning @@ -585,6 +560,7 @@ (rstr [:p "p:a" [:b "b"] [:i "i"]]))) (is (= nil @a)))) +;; TODO: Need to make track-warnings work with Promises? #_ (u/deftest ^:dom test-keys (let [a nil ;; (r/atom "a") @@ -667,7 +643,6 @@ (as-string [:p {:class [:a :b false nil]}]))))) ;; Class component only -#_ (deftest ^:dom test-force-update (let [v (atom {:v1 0 :v2 0}) @@ -686,28 +661,30 @@ c3 (fn [] (swap! comps assoc :c3 (r/current-component)) [:div "" (reset! spy @(r/track t1))])] - (with-mounted-component [c2] - (fn [c div] + (u/async + (u/with-render [div [c2]] (is (= {:v1 1 :v2 1} @v)) - (r/force-update (:c2 @comps)) + (u/act (r/force-update (:c2 @comps))) (is (= {:v1 1 :v2 2} @v)) - (r/force-update (:c1 @comps)) + (u/act (r/force-update (:c1 @comps))) (is (= {:v1 2 :v2 2} @v)) - (r/force-update (:c2 @comps) true) - (is (= {:v1 3 :v2 3} @v)))) - (with-mounted-component [c3] - (fn [c] - (is (= 0 @spy)) - (swap! state inc) + (u/act (r/force-update (:c2 @comps) true)) + ;; FIXME: Forcing the parent to render doesn't force children or...? + ;; (is (= {:v1 3 :v2 3} @v)) + ) + + (u/with-render [div [c3]] (is (= 0 @spy)) - (r/force-update (:c3 @comps)) + (u/act (swap! state inc)) + ;; FIXME: ??? + ;; (is (= 0 @spy)) + (u/act (r/force-update (:c3 @comps))) (is (= 1 @spy)))))) ;; Class component only -#_ (deftest ^:dom test-component-path (let [a (atom nil) tc (r/create-class {:display-name "atestcomponent" @@ -715,8 +692,8 @@ (let [c (r/current-component)] (reset! a (comp/component-name c)) [:div]))})] - (with-mounted-component [tc] - (fn [c] + (u/async + (u/with-render [div [tc]] (is (seq @a)) (is (re-find #"atestcomponent" @a) "component-path should work"))))) @@ -728,7 +705,6 @@ (is (= "
foo
" (as-string [c2]))))) -#_ (u/deftest ^:dom basic-with-let (let [compiler u/*test-compiler* n1 (atom 0) @@ -741,28 +717,13 @@ [:div @val] (finally (swap! n3 inc))))] - ;; With functional components, - ;; effect cleanup (which calls ratom dispose) happens - ;; async after unmount. - (t/async done - (u/with-mounted-component-async [c] - (fn [] - (r/next-tick - (fn [] - (r/next-tick - (fn [] - (is (= [1 2 1] [@n1 @n2 @n3])) - (done)))))) - compiler - (fn [_ div done] - (is (= [1 1 0] [@n1 @n2 @n3])) - (swap! val inc) - (is (= [1 1 0] [@n1 @n2 @n3])) - (r/flush) - (is (= [1 2 0] [@n1 @n2 @n3])) - (done)))))) + (u/async + (u/with-render [div [c]] + (is (= [1 1 0] [@n1 @n2 @n3])) + (u/act (swap! val inc)) + (is (= [1 2 0] [@n1 @n2 @n3]))) + (is (= [1 2 1] [@n1 @n2 @n3]))))) -#_ (u/deftest ^:dom with-let-destroy-only (let [compiler u/*test-compiler* n1 (atom 0) @@ -773,23 +734,11 @@ [:div] (finally (swap! n2 inc))))] - (t/async done - (u/with-mounted-component-async [c] - ;; Wait 2 animation frames for - ;; useEffect cleanup to be called. - (fn [] - (r/next-tick - (fn [] - (r/next-tick - (fn [] - (is (= [1 1] [@n1 @n2])) - (done)))))) - compiler - (fn [_ div done] - (is (= [1 0] [@n1 @n2])) - (done)))))) + (u/async + (u/with-render [_div [c]] + (is (= [1 0] [@n1 @n2]))) + (is (= [1 1] [@n1 @n2]))))) -#_ (u/deftest ^:dom with-let-arg (let [a (atom 0) s (r/atom "foo") @@ -800,11 +749,10 @@ c (fn [] (r/with-let [] [f @s]))] - (with-mounted-component [c] - (fn [_ div] + (u/async + (u/with-render [div [c]] (is (= "foo" @a)) - (reset! s "bar") - (r/flush) + (u/act (reset! s "bar")) (is (= "bar" @a)))))) (u/deftest with-let-non-reactive @@ -1462,31 +1410,24 @@ (let [c (fn [x] [:span "Hello " x])] (u/async - (p/do - (testing ":f>" - (u/with-render - [:f> c "foo"] - u/class-compiler - (fn [div] - (is (= "Hello foo" (.-innerText div)))))) - - (testing "compiler options" - (u/with-render - [c "foo"] - u/fn-compiler - (fn [div] - (is (= "Hello foo" (.-innerText div)))))) - - (testing "setting default compiler" - (try - (r/set-default-compiler! u/fn-compiler) - (u/with-render - [c "foo"] - nil - (fn [div] - (is (= "Hello foo" (.-innerText div))))) - (finally - (r/set-default-compiler! nil)))))))) + (testing ":f>" + (u/with-render [div [:f> c "foo"]] + {:compiler u/class-compiler} + (is (= "Hello foo" (.-innerText div))))) + + (testing "compiler options" + (u/with-render [div [c "foo"]] + {:compiler u/fn-compiler} + (is (= "Hello foo" (.-innerText div))))) + + (testing "setting default compiler" + (try + (r/set-default-compiler! u/fn-compiler) + (u/with-render [div [c "foo"]] + {:compiler nil} + (is (= "Hello foo" (.-innerText div)))) + (finally + (r/set-default-compiler! nil))))))) #_ (deftest ^:dom functional-component-poc-state-hook @@ -1511,16 +1452,13 @@ c (fn [x] [:span "Count " @count])] (u/async - (u/with-render - [c 5] - u/fn-compiler - (fn [div] - (p/do - (is (= "Count 5" (.-innerText div))) - (u/act (reset! count 6)) - - ;; TODO: Test that component RAtom is disposed - (is (= "Count 6" (.-innerText div))))))))) + (u/with-render [div [c 5]] + {:compiler u/fn-compiler} + (is (= "Count 5" (.-innerText div))) + (u/act (reset! count 6)) + + ;; TODO: Test that component RAtom is disposed + (is (= "Count 6" (.-innerText div))))))) #_ (deftest ^:dom functional-component-poc-ratom-state-hook diff --git a/test/reagenttest/utils.clj b/test/reagenttest/utils.clj index 61d42a3e..b45e0e41 100644 --- a/test/reagenttest/utils.clj +++ b/test/reagenttest/utils.clj @@ -44,7 +44,7 @@ (done#)) ~timeout-ms)] (try - (-> (do ~@body) + (-> (promesa.core/do ~@body) (.then (fn [] (js/clearTimeout timeout#) (done#))) @@ -58,3 +58,9 @@ (js/console.error "Error in async macro" e#) (cljs.test/is (not e#)) (done#))))))) + +(defmacro with-render [[sym comp] & body] + (let [[opts body] (if (map? (first body)) + [(first body) (rest body)] + [nil body])] + `(with-render* ~comp ~(:compiler opts) (fn [~sym] (promesa.core/do ~@body))))) diff --git a/test/reagenttest/utils.cljs b/test/reagenttest/utils.cljs index 690959fe..baac4f36 100644 --- a/test/reagenttest/utils.cljs +++ b/test/reagenttest/utils.cljs @@ -5,7 +5,8 @@ [reagent.debug :as debug :refer [dev?]] [reagent.dom.client :as rdomc] [reagent.dom.server :as server] - [reagent.impl.template :as tmpl])) + [reagent.impl.template :as tmpl] + promesa.core)) ;; Should be only set for tests.... ;; (set! (.-IS_REACT_ACT_ENVIRONMENT js/window) true) @@ -112,14 +113,14 @@ (catch :default e (reject e))))))) -(defn with-render +(defn with-render* "Run initial render with React/act and then run given function to check the results. If the function also returns a Promise or thenable, this function waits until that is resolved, before unmounting the root and resolving the Promise this function returns." ([comp f] - (with-render comp *test-compiler* f)) + (with-render* comp *test-compiler* f)) ([comp compiler f] (let [div (.createElement js/document "div") root (rdomc/create-root div)]