From 48fbdf69f3756f82471a9900fb795eee91609b98 Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Fri, 27 Feb 2026 17:09:46 +0200 Subject: [PATCH 01/10] Add @testing-library/user-event dep --- package-lock.json | 3 ++- package.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1e47bd0..302cbe0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,8 @@ "": { "name": "react-testing-library-cljs", "dependencies": { - "@testing-library/react": "16.3.2" + "@testing-library/react": "16.3.2", + "@testing-library/user-event": "^14.6.1" }, "devDependencies": { "global-jsdom": "^28.0.0", diff --git a/package.json b/package.json index 727a103..c61a60a 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "shadow-cljs": "3.3.6" }, "dependencies": { - "@testing-library/react": "16.3.2" + "@testing-library/react": "16.3.2", + "@testing-library/user-event": "14.6.1" } } From 23b9b40e39bee02fdf30f80c236ed82ee39a8914 Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Fri, 27 Feb 2026 17:11:36 +0200 Subject: [PATCH 02/10] Add user-event with wrapper for all functions --- .../user_event.cljs | 136 ++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/main/react_testing_library_cljs/user_event.cljs diff --git a/src/main/react_testing_library_cljs/user_event.cljs b/src/main/react_testing_library_cljs/user_event.cljs new file mode 100644 index 0000000..9a809f8 --- /dev/null +++ b/src/main/react_testing_library_cljs/user_event.cljs @@ -0,0 +1,136 @@ +(ns react-testing-library-cljs.user-event + "ClojureScript wrapper for @testing-library/user-event. + + Simulates real user interactions by firing the full sequence of events + (focus, keyboard, pointer, input, etc.) rather than a single synthetic event. + + Refer to https://testing-library.com/docs/user-event/intro for more details." + (:require + ["@testing-library/user-event" :default userEvent])) + +(defn setup + "Creates a UserEvent instance for simulating user interactions. + Should be called once per test. All interactions on the instance share + browser state (clipboard, keyboard, pointer, etc.). + + All methods on the returned instance return a Promise. + + See [userEvent.setup](https://testing-library.com/docs/user-event/setup)." + ([] (.setup userEvent)) + ([options] (.setup userEvent (clj->js options)))) + +(defn click + "Clicks an element. + + See [click](https://testing-library.com/docs/user-event/convenience/#click)." + ([user element] (.click user element)) + ([user element options] (.click user element (clj->js options)))) + +(defn dbl-click + "Double-clicks an element. + + See [dblClick](https://testing-library.com/docs/user-event/convenience/#dblclick)." + ([user element] (.dblClick user element)) + ([user element options] (.dblClick user element (clj->js options)))) + +(defn triple-click + "Triple-clicks an element (selects all text in an input). + + See [tripleClick](https://testing-library.com/docs/user-event/convenience/#tripleclick)." + ([user element] (.tripleClick user element)) + ([user element options] (.tripleClick user element (clj->js options)))) + +(defn hover + "Moves the pointer to an element. + + See [hover](https://testing-library.com/docs/user-event/convenience/#hover)." + ([user element] (.hover user element)) + ([user element options] (.hover user element (clj->js options)))) + +(defn unhover + "Moves the pointer away from an element. + + See [unhover](https://testing-library.com/docs/user-event/convenience/#unhover)." + ([user element] (.unhover user element)) + ([user element options] (.unhover user element (clj->js options)))) + +(defn type + "Types text into an element character by character, simulating the full + keyboard event sequence for each character. + + See [type](https://testing-library.com/docs/user-event/utility/#type)." + ([user element text] (.type user element text)) + ([user element text options] (.type user element text (clj->js options)))) + +(defn clear + "Selects all text in an element and deletes it. + + See [clear](https://testing-library.com/docs/user-event/utility/#clear)." + [user element] (.clear user element)) + +(defn select-options + "Selects one or more options in a `` element. + `values` can be a single value or a vector of values (option text, value, or element). + + See [deselectOptions](https://testing-library.com/docs/user-event/utility/#deselectoptions)." + ([user element values] (.deselectOptions user element (clj->js values))) + ([user element values options] (.deselectOptions user element (clj->js values) (clj->js options)))) + +(defn upload + "Simulates uploading a file via a file input. + `files` can be a single File or a vector of Files. + + See [upload](https://testing-library.com/docs/user-event/utility/#upload)." + ([user element files] (.upload user element files)) + ([user element files options] (.upload user element files (clj->js options)))) + +(defn tab + "Presses the Tab key, moving focus to the next focusable element. + + See [tab](https://testing-library.com/docs/user-event/convenience/#tab)." + ([user] (.tab user)) + ([user options] (.tab user (clj->js options)))) + +(defn keyboard + "Simulates keyboard events for the given key descriptor string + (e.g. `\"{Enter}\"`, `\"a\"`, `\"{{a}}\"`). + + See [keyboard](https://testing-library.com/docs/user-event/keyboard)." + ([user text] (.keyboard user text)) + ([user text options] (.keyboard user text (clj->js options)))) + +(defn pointer + "Simulates pointer device interactions. + + See [pointer](https://testing-library.com/docs/user-event/pointer)." + ([user input] (.pointer user (clj->js input))) + ([user input options] (.pointer user (clj->js input) (clj->js options)))) + +(defn copy + "Copies the current selection to the clipboard. + + See [copy](https://testing-library.com/docs/user-event/clipboard/#copy)." + ([user] (.copy user)) + ([user options] (.copy user (clj->js options)))) + +(defn cut + "Cuts the current selection to the clipboard. + + See [cut](https://testing-library.com/docs/user-event/clipboard/#cut)." + ([user] (.cut user)) + ([user options] (.cut user (clj->js options)))) + +(defn paste + "Pastes clipboard contents into the focused element. + + See [paste](https://testing-library.com/docs/user-event/clipboard/#paste)." + ([user] (.paste user)) + ([user options] (.paste user (clj->js options)))) From 7497259f91ea1f4d35c30e8a4cf0abfd546dd819 Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Fri, 27 Feb 2026 17:12:17 +0200 Subject: [PATCH 03/10] Add test for wrapper ns --- .../user_event_test.cljs | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 src/test/react_testing_library_cljs/user_event_test.cljs diff --git a/src/test/react_testing_library_cljs/user_event_test.cljs b/src/test/react_testing_library_cljs/user_event_test.cljs new file mode 100644 index 0000000..60e3d9f --- /dev/null +++ b/src/test/react_testing_library_cljs/user_event_test.cljs @@ -0,0 +1,94 @@ +(ns react-testing-library-cljs.user-event-test + (:require + [cljs.test :refer [deftest is async]] + ["@testing-library/react" :as rtl] + ["react" :as react] + [react-testing-library-cljs.screen :as screen] + [react-testing-library-cljs.user-event :as user-event])) + +(defn- render-el [element] + (rtl/render element)) + +(deftest click-test + (async done + (rtl/cleanup) + (let [clicks (atom 0)] + (render-el (react/createElement "button" + #js {:onClick #(swap! clicks inc)} + "Click me")) + (let [user (user-event/setup)] + (-> (user-event/click user (screen/get-by-role "button")) + (.then (fn [] + (is (= 1 @clicks)) + (done)))))))) + +(deftest type-test + (async done + (rtl/cleanup) + (render-el (react/createElement "input" + #js {:placeholder "type here" + :defaultValue ""})) + (let [user (user-event/setup) + input (screen/get-by-placeholder-text "type here")] + (-> (user-event/type user input "hello") + (.then (fn [] + (is (= "hello" (.-value input))) + (done))))))) + +(deftest clear-test + (async done + (rtl/cleanup) + (render-el (react/createElement "input" + #js {:placeholder "to clear" + :defaultValue "existing text"})) + (let [user (user-event/setup) + input (screen/get-by-placeholder-text "to clear")] + (-> (user-event/clear user input) + (.then (fn [] + (is (= "" (.-value input))) + (done))))))) + +(deftest tab-test + (async done + (rtl/cleanup) + (render-el (react/createElement "div" nil + (react/createElement "input" #js {:placeholder "first"}) + (react/createElement "input" #js {:placeholder "second"}))) + (let [user (user-event/setup) + first-input (screen/get-by-placeholder-text "first") + second-input (screen/get-by-placeholder-text "second")] + (.focus first-input) + (-> (user-event/tab user) + (.then (fn [] + (is (= second-input (.-activeElement js/document))) + (done))))))) + +(deftest keyboard-test + (async done + (rtl/cleanup) + (let [submitted (atom false)] + (render-el (react/createElement "form" + #js {:onSubmit (fn [e] + (.preventDefault e) + (reset! submitted true))} + (react/createElement "input" #js {:placeholder "press enter"}))) + (let [user (user-event/setup)] + (.focus (screen/get-by-placeholder-text "press enter")) + (-> (user-event/keyboard user "{Enter}") + (.then (fn [] + (is (true? @submitted)) + (done)))))))) + +(deftest select-options-test + (async done + (rtl/cleanup) + (render-el (react/createElement "select" + #js {:defaultValue ""} + (react/createElement "option" #js {:value "a"} "Option A") + (react/createElement "option" #js {:value "b"} "Option B"))) + (let [user (user-event/setup) + select (screen/get-by-role "combobox")] + (-> (user-event/select-options user select "Option A") + (.then (fn [] + (is (= "a" (.-value select))) + (done))))))) From 3b1751b1e3531bc452b97adf8bee03ec5642e0f0 Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Fri, 27 Feb 2026 17:12:25 +0200 Subject: [PATCH 04/10] Update package-lock --- package-lock.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/package-lock.json b/package-lock.json index 302cbe0..0c53759 100644 --- a/package-lock.json +++ b/package-lock.json @@ -314,6 +314,19 @@ } } }, + "node_modules/@testing-library/user-event": { + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", From 4765703a544c513e6a8f3eabab12577d31ef9783 Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Fri, 27 Feb 2026 17:33:39 +0200 Subject: [PATCH 05/10] Add deftest-async macros to wrap async test with promesa --- src/main/react_testing_library_cljs/async.clj | 21 +++++++++++++++++++ .../react_testing_library_cljs/async.cljs | 2 ++ 2 files changed, 23 insertions(+) create mode 100644 src/main/react_testing_library_cljs/async.clj create mode 100644 src/main/react_testing_library_cljs/async.cljs diff --git a/src/main/react_testing_library_cljs/async.clj b/src/main/react_testing_library_cljs/async.clj new file mode 100644 index 0000000..8c3012b --- /dev/null +++ b/src/main/react_testing_library_cljs/async.clj @@ -0,0 +1,21 @@ +(ns react-testing-library-cljs.async) + +(defmacro deftest-async + "Like `cljs.test/deftest` but for async tests that return a Promise. + Wraps the body in `cljs.test/async` and automatically calls `done` + when the returned Promise resolves. + + Requires `promesa` on the classpath. Use `promesa.core/p/let` to + sequence async operations without nesting `.then` calls: + + (deftest-async my-test + (p/let [_ (user-event/type user input \"hello\")] + (is (= \"hello\" (.-value input)))))" + [name & body] + `(cljs.test/deftest ~name + (cljs.test/async done# + (-> (do ~@body) + (.then done#) + (.catch (fn [err#] + (cljs.test/is (nil? err#) (.-message err#)) + (done#))))))) diff --git a/src/main/react_testing_library_cljs/async.cljs b/src/main/react_testing_library_cljs/async.cljs new file mode 100644 index 0000000..24a19fc --- /dev/null +++ b/src/main/react_testing_library_cljs/async.cljs @@ -0,0 +1,2 @@ +(ns react-testing-library-cljs.async + (:require-macros [react-testing-library-cljs.async])) From b5a04fb66699c1f195178c4373b573dcaa8ca41e Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Fri, 27 Feb 2026 17:34:03 +0200 Subject: [PATCH 06/10] Recreate user-event test with deftest-async --- shadow-cljs.edn | 3 +- .../user_event_promesa_test.cljs | 78 +++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 src/test/react_testing_library_cljs/user_event_promesa_test.cljs diff --git a/shadow-cljs.edn b/shadow-cljs.edn index 5c20e00..9670ca2 100644 --- a/shadow-cljs.edn +++ b/shadow-cljs.edn @@ -1,4 +1,5 @@ -{:dependencies [[reagent "2.0.1"]] +{:dependencies [[reagent "2.0.1"] + [funcool/promesa "11.0.678"]] :source-paths ["src/main" "src/test"] :builds {:test {:target :node-test diff --git a/src/test/react_testing_library_cljs/user_event_promesa_test.cljs b/src/test/react_testing_library_cljs/user_event_promesa_test.cljs new file mode 100644 index 0000000..a700716 --- /dev/null +++ b/src/test/react_testing_library_cljs/user_event_promesa_test.cljs @@ -0,0 +1,78 @@ +(ns react-testing-library-cljs.user-event-promesa-test + (:require + [cljs.test :refer [is]] + ["@testing-library/react" :as rtl] + ["react" :as react] + [promesa.core :as p] + [react-testing-library-cljs.async :refer-macros [deftest-async]] + [react-testing-library-cljs.screen :as screen] + [react-testing-library-cljs.user-event :as user-event])) + +(defn- render-el [element] + (rtl/render element)) + +(deftest-async click-test + (rtl/cleanup) + (let [clicks (atom 0)] + (render-el (react/createElement "button" + #js {:onClick #(swap! clicks inc)} + "Click me")) + (let [user (user-event/setup)] + (p/let [_ (user-event/click user (screen/get-by-role "button"))] + (is (= 1 @clicks)))))) + +(deftest-async type-test + (rtl/cleanup) + (render-el (react/createElement "input" + #js {:placeholder "type here" + :defaultValue ""})) + (let [user (user-event/setup) + input (screen/get-by-placeholder-text "type here")] + (p/let [_ (user-event/type user input "hello")] + (is (= "hello" (.-value input)))))) + +(deftest-async clear-test + (rtl/cleanup) + (render-el (react/createElement "input" + #js {:placeholder "to clear" + :defaultValue "existing text"})) + (let [user (user-event/setup) + input (screen/get-by-placeholder-text "to clear")] + (p/let [_ (user-event/clear user input)] + (is (= "" (.-value input)))))) + +(deftest-async tab-test + (rtl/cleanup) + (render-el (react/createElement "div" nil + (react/createElement "input" #js {:placeholder "first"}) + (react/createElement "input" #js {:placeholder "second"}))) + (let [user (user-event/setup) + first-input (screen/get-by-placeholder-text "first") + second-input (screen/get-by-placeholder-text "second")] + (.focus first-input) + (p/let [_ (user-event/tab user)] + (is (= second-input (.-activeElement js/document)))))) + +(deftest-async keyboard-test + (rtl/cleanup) + (let [submitted (atom false)] + (render-el (react/createElement "form" + #js {:onSubmit (fn [e] + (.preventDefault e) + (reset! submitted true))} + (react/createElement "input" #js {:placeholder "press enter"}))) + (let [user (user-event/setup)] + (.focus (screen/get-by-placeholder-text "press enter")) + (p/let [_ (user-event/keyboard user "{Enter}")] + (is (true? @submitted)))))) + +(deftest-async select-options-test + (rtl/cleanup) + (render-el (react/createElement "select" + #js {:defaultValue ""} + (react/createElement "option" #js {:value "a"} "Option A") + (react/createElement "option" #js {:value "b"} "Option B"))) + (let [user (user-event/setup) + select (screen/get-by-role "combobox")] + (p/let [_ (user-event/select-options user select "Option A")] + (is (= "a" (.-value select)))))) From bbc929fd4af20a809e6d428636cc9ad868b9e5de Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Fri, 27 Feb 2026 17:37:50 +0200 Subject: [PATCH 07/10] Replace p/let by p/do --- .../user_event_promesa_test.cljs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/test/react_testing_library_cljs/user_event_promesa_test.cljs b/src/test/react_testing_library_cljs/user_event_promesa_test.cljs index a700716..2d34cb2 100644 --- a/src/test/react_testing_library_cljs/user_event_promesa_test.cljs +++ b/src/test/react_testing_library_cljs/user_event_promesa_test.cljs @@ -18,7 +18,8 @@ #js {:onClick #(swap! clicks inc)} "Click me")) (let [user (user-event/setup)] - (p/let [_ (user-event/click user (screen/get-by-role "button"))] + (p/do + (user-event/click user (screen/get-by-role "button")) (is (= 1 @clicks)))))) (deftest-async type-test @@ -28,7 +29,8 @@ :defaultValue ""})) (let [user (user-event/setup) input (screen/get-by-placeholder-text "type here")] - (p/let [_ (user-event/type user input "hello")] + (p/do + (user-event/type user input "hello") (is (= "hello" (.-value input)))))) (deftest-async clear-test @@ -38,7 +40,8 @@ :defaultValue "existing text"})) (let [user (user-event/setup) input (screen/get-by-placeholder-text "to clear")] - (p/let [_ (user-event/clear user input)] + (p/do + (user-event/clear user input) (is (= "" (.-value input)))))) (deftest-async tab-test @@ -50,7 +53,8 @@ first-input (screen/get-by-placeholder-text "first") second-input (screen/get-by-placeholder-text "second")] (.focus first-input) - (p/let [_ (user-event/tab user)] + (p/do + (user-event/tab user) (is (= second-input (.-activeElement js/document)))))) (deftest-async keyboard-test @@ -63,7 +67,8 @@ (react/createElement "input" #js {:placeholder "press enter"}))) (let [user (user-event/setup)] (.focus (screen/get-by-placeholder-text "press enter")) - (p/let [_ (user-event/keyboard user "{Enter}")] + (p/do + (user-event/keyboard user "{Enter}") (is (true? @submitted)))))) (deftest-async select-options-test @@ -74,5 +79,6 @@ (react/createElement "option" #js {:value "b"} "Option B"))) (let [user (user-event/setup) select (screen/get-by-role "combobox")] - (p/let [_ (user-event/select-options user select "Option A")] + (p/do + (user-event/select-options user select "Option A") (is (= "a" (.-value select)))))) From e7b9076e94edea58ab1059581774898b0fbf4fce Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Fri, 27 Feb 2026 17:50:36 +0200 Subject: [PATCH 08/10] Move promesa.core/do to macros --- src/main/react_testing_library_cljs/async.clj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/react_testing_library_cljs/async.clj b/src/main/react_testing_library_cljs/async.clj index 8c3012b..e1f408b 100644 --- a/src/main/react_testing_library_cljs/async.clj +++ b/src/main/react_testing_library_cljs/async.clj @@ -14,7 +14,7 @@ [name & body] `(cljs.test/deftest ~name (cljs.test/async done# - (-> (do ~@body) + (-> (promesa.core/do ~@body) (.then done#) (.catch (fn [err#] (cljs.test/is (nil? err#) (.-message err#)) From d6f807ca133222307c766bc3d556d87b9ad76be6 Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Fri, 27 Feb 2026 17:56:52 +0200 Subject: [PATCH 09/10] Update the deftest-async --- .../user_event_promesa_test.cljs | 47 +++++++------------ 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/src/test/react_testing_library_cljs/user_event_promesa_test.cljs b/src/test/react_testing_library_cljs/user_event_promesa_test.cljs index 2d34cb2..da52db5 100644 --- a/src/test/react_testing_library_cljs/user_event_promesa_test.cljs +++ b/src/test/react_testing_library_cljs/user_event_promesa_test.cljs @@ -17,45 +17,34 @@ (render-el (react/createElement "button" #js {:onClick #(swap! clicks inc)} "Click me")) - (let [user (user-event/setup)] - (p/do - (user-event/click user (screen/get-by-role "button")) - (is (= 1 @clicks)))))) + (p/do + (user-event/click (user-event/setup) (screen/get-by-role "button")) + (is (= 1 @clicks))))) (deftest-async type-test (rtl/cleanup) (render-el (react/createElement "input" #js {:placeholder "type here" :defaultValue ""})) - (let [user (user-event/setup) - input (screen/get-by-placeholder-text "type here")] - (p/do - (user-event/type user input "hello") - (is (= "hello" (.-value input)))))) + (user-event/type (user-event/setup) (screen/get-by-placeholder-text "type here") "hello") + (is (= "hello" (.-value (screen/get-by-placeholder-text "type here"))))) (deftest-async clear-test (rtl/cleanup) (render-el (react/createElement "input" #js {:placeholder "to clear" :defaultValue "existing text"})) - (let [user (user-event/setup) - input (screen/get-by-placeholder-text "to clear")] - (p/do - (user-event/clear user input) - (is (= "" (.-value input)))))) + (user-event/clear (user-event/setup) (screen/get-by-placeholder-text "to clear")) + (is (= "" (.-value (screen/get-by-placeholder-text "to clear"))))) (deftest-async tab-test (rtl/cleanup) (render-el (react/createElement "div" nil (react/createElement "input" #js {:placeholder "first"}) (react/createElement "input" #js {:placeholder "second"}))) - (let [user (user-event/setup) - first-input (screen/get-by-placeholder-text "first") - second-input (screen/get-by-placeholder-text "second")] - (.focus first-input) - (p/do - (user-event/tab user) - (is (= second-input (.-activeElement js/document)))))) + (.focus (screen/get-by-placeholder-text "first")) + (user-event/tab (user-event/setup)) + (is (= (screen/get-by-placeholder-text "second") (.-activeElement js/document)))) (deftest-async keyboard-test (rtl/cleanup) @@ -65,11 +54,10 @@ (.preventDefault e) (reset! submitted true))} (react/createElement "input" #js {:placeholder "press enter"}))) - (let [user (user-event/setup)] - (.focus (screen/get-by-placeholder-text "press enter")) - (p/do - (user-event/keyboard user "{Enter}") - (is (true? @submitted)))))) + (.focus (screen/get-by-placeholder-text "press enter")) + (p/do + (user-event/keyboard (user-event/setup) "{Enter}") + (is (true? @submitted))))) (deftest-async select-options-test (rtl/cleanup) @@ -77,8 +65,5 @@ #js {:defaultValue ""} (react/createElement "option" #js {:value "a"} "Option A") (react/createElement "option" #js {:value "b"} "Option B"))) - (let [user (user-event/setup) - select (screen/get-by-role "combobox")] - (p/do - (user-event/select-options user select "Option A") - (is (= "a" (.-value select)))))) + (user-event/select-options (user-event/setup) (screen/get-by-role "combobox") "Option A") + (is (= "a" (.-value (screen/get-by-role "combobox"))))) From 4ba1d35146fe474dcf3398b4b00c9f1b88202ecb Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Fri, 27 Feb 2026 18:00:15 +0200 Subject: [PATCH 10/10] Improve docstring --- src/main/react_testing_library_cljs/async.clj | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/react_testing_library_cljs/async.clj b/src/main/react_testing_library_cljs/async.clj index e1f408b..1f87f3c 100644 --- a/src/main/react_testing_library_cljs/async.clj +++ b/src/main/react_testing_library_cljs/async.clj @@ -2,15 +2,24 @@ (defmacro deftest-async "Like `cljs.test/deftest` but for async tests that return a Promise. - Wraps the body in `cljs.test/async` and automatically calls `done` - when the returned Promise resolves. + Wraps the body in `promesa.core/do`, so each top-level form is awaited + before the next runs. Automatically calls `done` when the Promise resolves. - Requires `promesa` on the classpath. Use `promesa.core/p/let` to - sequence async operations without nesting `.then` calls: + Requires `promesa` on the classpath. (deftest-async my-test - (p/let [_ (user-event/type user input \"hello\")] - (is (= \"hello\" (.-value input)))))" + (render-el (react/createElement \"input\" #js {:placeholder \"type here\"})) + (user-event/type (user-event/setup) (screen/get-by-placeholder-text \"type here\") \"hello\") + (is (= \"hello\" (.-value (screen/get-by-placeholder-text \"type here\"))))) + + Use `promesa.core/do` explicitly when async calls share a `let` binding: + + (deftest-async my-test + (let [clicks (atom 0)] + (render-el (react/createElement \"button\" #js {:onClick #(swap! clicks inc)} \"Click\")) + (p/do + (user-event/click (user-event/setup) (screen/get-by-role \"button\")) + (is (= 1 @clicks)))))" [name & body] `(cljs.test/deftest ~name (cljs.test/async done#