From e379ff10cc07e8e22061b7752e89e0c0cce7b3af Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Thu, 16 Jul 2026 09:09:52 +0300 Subject: [PATCH 1/5] Migrate async tests to native ^:async; keep deftest-async covered Move the project's own async tests from the deftest-async macro + promesa to native ClojureScript async (^:async / await), now that shadow-cljs 3.4.11 (cljs 1.12.145) supports it: - screen_async_test: rewritten native; the redundant proof file native_async_test.cljs is removed (its coverage lives here now). - within_native_async_test -> within_async_test: native. - user_event_promesa_test -> user_event_async_test: native. deftest-async and promesa are intentionally retained: native ^:async requires shadow-cljs >= 3.4.x, so the macro remains the async-test path for older toolchains. Add async_macro_test.cljs as a smoke test so the macro stays covered in CI even though nothing else uses it now. --- .../async_macro_test.cljs | 30 +++++++++++++++ .../native_async_test.cljs | 27 ------------- .../screen_async_test.cljs | 14 +++---- ...a_test.cljs => user_event_async_test.cljs} | 38 +++++++++---------- ...async_test.cljs => within_async_test.cljs} | 2 +- 5 files changed, 56 insertions(+), 55 deletions(-) create mode 100644 src/test/react_testing_library_cljs/async_macro_test.cljs delete mode 100644 src/test/react_testing_library_cljs/native_async_test.cljs rename src/test/react_testing_library_cljs/{user_event_promesa_test.cljs => user_event_async_test.cljs} (69%) rename src/test/react_testing_library_cljs/{within_native_async_test.cljs => within_async_test.cljs} (96%) diff --git a/src/test/react_testing_library_cljs/async_macro_test.cljs b/src/test/react_testing_library_cljs/async_macro_test.cljs new file mode 100644 index 0000000..1011a25 --- /dev/null +++ b/src/test/react_testing_library_cljs/async_macro_test.cljs @@ -0,0 +1,30 @@ +(ns react-testing-library-cljs.async-macro-test + "Smoke test guarding the `deftest-async` macro, the compatibility path for + toolchains that cannot run native `^:async` tests (shadow-cljs < 3.4.x). + Requires `promesa` on the classpath." + (:require + ["@testing-library/react" :as rtl] + ["react" :as react] + [cljs.test :refer [is]] + [promesa.core :as p] + [react-testing-library-cljs.async :refer-macros [deftest-async]] + [react-testing-library-cljs.screen :as screen])) + +(defn- render-el [element] + (rtl/render element)) + +(deftest-async find-by-text-test + (rtl/cleanup) + (render-el (react/createElement "div" nil "Macro Hello")) + (p/let [el (screen/find-by-text "Macro Hello")] + (is (some? el)) + (is (= "Macro Hello" (.-textContent el))))) + +(deftest-async find-all-by-text-test + (rtl/cleanup) + (render-el (react/createElement "div" nil + (react/createElement "span" nil "Item") + (react/createElement "span" nil "Item"))) + (p/let [els (screen/find-all-by-text "Item")] + (is (vector? els)) + (is (= 2 (count els))))) diff --git a/src/test/react_testing_library_cljs/native_async_test.cljs b/src/test/react_testing_library_cljs/native_async_test.cljs deleted file mode 100644 index ed2d431..0000000 --- a/src/test/react_testing_library_cljs/native_async_test.cljs +++ /dev/null @@ -1,27 +0,0 @@ -(ns react-testing-library-cljs.native-async-test - "Native ClojureScript async tests (`^:async` / `await`). - Requires shadow-cljs >= 3.4.x (ClojureScript >= 1.12.145)." - (:require - ["@testing-library/react" :as rtl] - ["react" :as react] - [cljs.test :refer [deftest is]] - [react-testing-library-cljs.screen :as screen])) - -(defn- render-el [element] - (rtl/render element)) - -(deftest ^:async find-by-text-native-test - (rtl/cleanup) - (render-el (react/createElement "div" nil "Native Hello")) - (let [el (await (screen/find-by-text "Native Hello"))] - (is (some? el)) - (is (= "Native Hello" (.-textContent el))))) - -(deftest ^:async find-all-by-text-native-test - (rtl/cleanup) - (render-el (react/createElement "div" nil - (react/createElement "span" nil "Row") - (react/createElement "span" nil "Row"))) - (let [els (await (screen/find-all-by-text "Row"))] - (is (vector? els)) - (is (= 2 (count els))))) diff --git a/src/test/react_testing_library_cljs/screen_async_test.cljs b/src/test/react_testing_library_cljs/screen_async_test.cljs index 624f515..b582804 100644 --- a/src/test/react_testing_library_cljs/screen_async_test.cljs +++ b/src/test/react_testing_library_cljs/screen_async_test.cljs @@ -1,28 +1,28 @@ (ns react-testing-library-cljs.screen-async-test + "Native ClojureScript async tests (`^:async` / `await`) for `screen`. + Requires shadow-cljs >= 3.4.x (ClojureScript >= 1.12.145)." (:require ["@testing-library/react" :as rtl] ["react" :as react] - [cljs.test :refer [is]] - [promesa.core :as p] - [react-testing-library-cljs.async :refer-macros [deftest-async]] + [cljs.test :refer [deftest is]] [react-testing-library-cljs.screen :as screen])) (defn- render-el [element] (rtl/render element)) -(deftest-async find-by-text-test +(deftest ^:async find-by-text-test (rtl/cleanup) (render-el (react/createElement "div" nil "Async Hello")) - (p/let [el (screen/find-by-text "Async Hello")] + (let [el (await (screen/find-by-text "Async Hello"))] (is (some? el)) (is (= "Async Hello" (.-textContent el))))) -(deftest-async find-all-by-text-test +(deftest ^:async find-all-by-text-test (rtl/cleanup) (render-el (react/createElement "div" nil (react/createElement "span" nil "Item") (react/createElement "span" nil "Item"))) - (p/let [els (screen/find-all-by-text "Item")] + (let [els (await (screen/find-all-by-text "Item"))] (is (vector? els)) (is (= 2 (count els))) (is (= "Item" (.-textContent (first els)))))) diff --git a/src/test/react_testing_library_cljs/user_event_promesa_test.cljs b/src/test/react_testing_library_cljs/user_event_async_test.cljs similarity index 69% rename from src/test/react_testing_library_cljs/user_event_promesa_test.cljs rename to src/test/react_testing_library_cljs/user_event_async_test.cljs index 023dc0d..88f796f 100644 --- a/src/test/react_testing_library_cljs/user_event_promesa_test.cljs +++ b/src/test/react_testing_library_cljs/user_event_async_test.cljs @@ -1,52 +1,51 @@ -(ns react-testing-library-cljs.user-event-promesa-test +(ns react-testing-library-cljs.user-event-async-test + "Native ClojureScript async tests (`^:async` / `await`) for user-event. + Requires shadow-cljs >= 3.4.x (ClojureScript >= 1.12.145)." (:require ["@testing-library/react" :as rtl] ["react" :as react] - [cljs.test :refer [is]] - [promesa.core :as p] - [react-testing-library-cljs.async :refer-macros [deftest-async]] + [cljs.test :refer [deftest is]] [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 +(deftest ^:async click-test (rtl/cleanup) (let [clicks (atom 0)] (render-el (react/createElement "button" #js {:onClick #(swap! clicks inc)} "Click me")) - (p/do - (user-event/click (user-event/setup) (screen/get-by-role "button")) - (is (= 1 @clicks))))) + (await (user-event/click (user-event/setup) (screen/get-by-role "button"))) + (is (= 1 @clicks)))) -(deftest-async type-test +(deftest ^:async type-test (rtl/cleanup) (render-el (react/createElement "input" #js {:placeholder "type here" :defaultValue ""})) - (user-event/type (user-event/setup) (screen/get-by-placeholder-text "type here") "hello") + (await (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 +(deftest ^:async clear-test (rtl/cleanup) (render-el (react/createElement "input" #js {:placeholder "to clear" :defaultValue "existing text"})) - (user-event/clear (user-event/setup) (screen/get-by-placeholder-text "to clear")) + (await (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 +(deftest ^:async tab-test (rtl/cleanup) (render-el (react/createElement "div" nil (react/createElement "input" #js {:placeholder "first"}) (react/createElement "input" #js {:placeholder "second"}))) (.focus (screen/get-by-placeholder-text "first")) - (user-event/tab (user-event/setup)) + (await (user-event/tab (user-event/setup))) (is (= (screen/get-by-placeholder-text "second") (.-activeElement js/document)))) -(deftest-async keyboard-test +(deftest ^:async keyboard-test (rtl/cleanup) (let [submitted (atom false)] (render-el (react/createElement "form" @@ -55,15 +54,14 @@ (reset! submitted true))} (react/createElement "input" #js {:placeholder "press enter"}))) (.focus (screen/get-by-placeholder-text "press enter")) - (p/do - (user-event/keyboard (user-event/setup) "{Enter}") - (is (true? @submitted))))) + (await (user-event/keyboard (user-event/setup) "{Enter}")) + (is (true? @submitted)))) -(deftest-async select-options-test +(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"))) - (user-event/select-options (user-event/setup) (screen/get-by-role "combobox") "Option A") + (await (user-event/select-options (user-event/setup) (screen/get-by-role "combobox") "Option A")) (is (= "a" (.-value (screen/get-by-role "combobox"))))) diff --git a/src/test/react_testing_library_cljs/within_native_async_test.cljs b/src/test/react_testing_library_cljs/within_async_test.cljs similarity index 96% rename from src/test/react_testing_library_cljs/within_native_async_test.cljs rename to src/test/react_testing_library_cljs/within_async_test.cljs index 44f7cda..b203e04 100644 --- a/src/test/react_testing_library_cljs/within_native_async_test.cljs +++ b/src/test/react_testing_library_cljs/within_async_test.cljs @@ -1,4 +1,4 @@ -(ns react-testing-library-cljs.within-native-async-test +(ns react-testing-library-cljs.within-async-test "Native ClojureScript async tests (`^:async` / `await`) for `within`. Requires shadow-cljs >= 3.4.x (ClojureScript >= 1.12.145)." (:require From 2553381f269f03e89abce48db37a7234df90eea2 Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Thu, 16 Jul 2026 09:42:49 +0300 Subject: [PATCH 2/5] Rename async-macro-test -> deftest-async-macro-test --- .../{async_macro_test.cljs => deftest_async_macro_test.cljs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/test/react_testing_library_cljs/{async_macro_test.cljs => deftest_async_macro_test.cljs} (100%) diff --git a/src/test/react_testing_library_cljs/async_macro_test.cljs b/src/test/react_testing_library_cljs/deftest_async_macro_test.cljs similarity index 100% rename from src/test/react_testing_library_cljs/async_macro_test.cljs rename to src/test/react_testing_library_cljs/deftest_async_macro_test.cljs From baec2ff1f0d0fb0efd2d7e99d1b45ae37c08fe7c Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Thu, 16 Jul 2026 09:42:49 +0300 Subject: [PATCH 3/5] Keep user-event async tests covered via deftest-async + promesa Restore user_event_promesa_test.cljs alongside the native user_event_async_test.cljs, so the deftest-async + promesa compat path is exercised against the full range of user-event interactions (click, type, clear, tab, keyboard, select-options), not just screen queries. --- .../user_event_promesa_test.cljs | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 src/test/react_testing_library_cljs/user_event_promesa_test.cljs 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..72d401a --- /dev/null +++ b/src/test/react_testing_library_cljs/user_event_promesa_test.cljs @@ -0,0 +1,73 @@ +(ns react-testing-library-cljs.user-event-promesa-test + "user-event async tests using the `deftest-async` macro + promesa, the + compatibility path for toolchains without native `^:async` support + (shadow-cljs < 3.4.x). The native equivalents live in + `user-event-async-test`." + (:require + ["@testing-library/react" :as rtl] + ["react" :as react] + [cljs.test :refer [is]] + [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")) + (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 ""})) + (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"})) + (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"}))) + (.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) + (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"}))) + (.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) + (render-el (react/createElement "select" + #js {:defaultValue ""} + (react/createElement "option" #js {:value "a"} "Option A") + (react/createElement "option" #js {:value "b"} "Option B"))) + (user-event/select-options (user-event/setup) (screen/get-by-role "combobox") "Option A") + (is (= "a" (.-value (screen/get-by-role "combobox"))))) From b4e87c3dd723a4b5d92889a4c02a9d97301431af Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Thu, 16 Jul 2026 09:52:42 +0300 Subject: [PATCH 4/5] Rework async test ns docstrings to follow the Clojure style guide Make each first line a complete, capitalized sentence (per the bbatsov Clojure style guide), fix the lowercase opening in user_event_promesa_test, keep two-space continuation indentation, and wrap var references in backticks. --- .../deftest_async_macro_test.cljs | 7 +++---- src/test/react_testing_library_cljs/screen_async_test.cljs | 4 ++-- .../react_testing_library_cljs/user_event_async_test.cljs | 4 ++-- .../user_event_promesa_test.cljs | 5 ++--- src/test/react_testing_library_cljs/within_async_test.cljs | 4 ++-- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/test/react_testing_library_cljs/deftest_async_macro_test.cljs b/src/test/react_testing_library_cljs/deftest_async_macro_test.cljs index 1011a25..e278773 100644 --- a/src/test/react_testing_library_cljs/deftest_async_macro_test.cljs +++ b/src/test/react_testing_library_cljs/deftest_async_macro_test.cljs @@ -1,7 +1,6 @@ -(ns react-testing-library-cljs.async-macro-test - "Smoke test guarding the `deftest-async` macro, the compatibility path for - toolchains that cannot run native `^:async` tests (shadow-cljs < 3.4.x). - Requires `promesa` on the classpath." +(ns react-testing-library-cljs.deftest-async-macro-test + "Smoke-tests the `deftest-async` macro, the pre-3.4 compatibility path. + Requires `promesa`; native `^:async` tests need shadow-cljs 3.4+ instead." (:require ["@testing-library/react" :as rtl] ["react" :as react] diff --git a/src/test/react_testing_library_cljs/screen_async_test.cljs b/src/test/react_testing_library_cljs/screen_async_test.cljs index b582804..5019512 100644 --- a/src/test/react_testing_library_cljs/screen_async_test.cljs +++ b/src/test/react_testing_library_cljs/screen_async_test.cljs @@ -1,6 +1,6 @@ (ns react-testing-library-cljs.screen-async-test - "Native ClojureScript async tests (`^:async` / `await`) for `screen`. - Requires shadow-cljs >= 3.4.x (ClojureScript >= 1.12.145)." + "Exercises `screen`'s async queries with native async (`^:async` / `await`). + Requires shadow-cljs 3.4+ (ClojureScript 1.12.145+)." (:require ["@testing-library/react" :as rtl] ["react" :as react] diff --git a/src/test/react_testing_library_cljs/user_event_async_test.cljs b/src/test/react_testing_library_cljs/user_event_async_test.cljs index 88f796f..4a83566 100644 --- a/src/test/react_testing_library_cljs/user_event_async_test.cljs +++ b/src/test/react_testing_library_cljs/user_event_async_test.cljs @@ -1,6 +1,6 @@ (ns react-testing-library-cljs.user-event-async-test - "Native ClojureScript async tests (`^:async` / `await`) for user-event. - Requires shadow-cljs >= 3.4.x (ClojureScript >= 1.12.145)." + "Exercises user-event interactions with native async (`^:async` / `await`). + Requires shadow-cljs 3.4+ (ClojureScript 1.12.145+)." (:require ["@testing-library/react" :as rtl] ["react" :as react] 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 72d401a..c20f4b8 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 @@ -1,7 +1,6 @@ (ns react-testing-library-cljs.user-event-promesa-test - "user-event async tests using the `deftest-async` macro + promesa, the - compatibility path for toolchains without native `^:async` support - (shadow-cljs < 3.4.x). The native equivalents live in + "Exercises user-event interactions with the `deftest-async` macro + `promesa`. + This is the pre-3.4 compatibility path; the native equivalents live in `user-event-async-test`." (:require ["@testing-library/react" :as rtl] diff --git a/src/test/react_testing_library_cljs/within_async_test.cljs b/src/test/react_testing_library_cljs/within_async_test.cljs index b203e04..e1493a7 100644 --- a/src/test/react_testing_library_cljs/within_async_test.cljs +++ b/src/test/react_testing_library_cljs/within_async_test.cljs @@ -1,6 +1,6 @@ (ns react-testing-library-cljs.within-async-test - "Native ClojureScript async tests (`^:async` / `await`) for `within`. - Requires shadow-cljs >= 3.4.x (ClojureScript >= 1.12.145)." + "Exercises `within`'s async queries with native async (`^:async` / `await`). + Requires shadow-cljs 3.4+ (ClojureScript 1.12.145+)." (:require ["@testing-library/react" :as rtl] ["react" :as react] From 2cd1cc6c4452555d51c718a6c85e6a75c5517146 Mon Sep 17 00:00:00 2001 From: Oleg Tsvetkov Date: Thu, 16 Jul 2026 10:08:37 +0300 Subject: [PATCH 5/5] Update deftest-async docstring: complete first sentence, native pointer Make the first line a complete sentence per the Clojure style guide, and note that native ^:async / await is preferred on shadow-cljs 3.4+ while this macro remains the compatibility path for older toolchains. --- src/main/react_testing_library_cljs/async.clj | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/react_testing_library_cljs/async.clj b/src/main/react_testing_library_cljs/async.clj index 1f87f3c..b1e42e6 100644 --- a/src/main/react_testing_library_cljs/async.clj +++ b/src/main/react_testing_library_cljs/async.clj @@ -1,9 +1,13 @@ (ns react-testing-library-cljs.async) (defmacro deftest-async - "Like `cljs.test/deftest` but for async tests that return a Promise. + "Defines a `cljs.test/deftest` for an async test whose body returns a Promise. 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. + before the next runs, then calls `done` when the Promise resolves. + + This is the compatibility path for toolchains without native ClojureScript + async functions. On shadow-cljs 3.4+ (ClojureScript 1.12.145+) prefer a + native `^:async` deftest with `await` instead. Requires `promesa` on the classpath.