From 565b786d2396e149d6f2ffd70548d3dbeda81459 Mon Sep 17 00:00:00 2001 From: Michiel Borkent Date: Wed, 15 Jul 2026 12:18:43 +0200 Subject: [PATCH] CLJS-3483: async tests should report uncaught exception --- src/main/cljs/cljs/test.cljc | 6 + src/test/cljs/cljs/async_await_test.cljs | 299 ++++++++++++----------- 2 files changed, 158 insertions(+), 147 deletions(-) diff --git a/src/main/cljs/cljs/test.cljc b/src/main/cljs/cljs/test.cljc index ec0bb29b3..624361202 100644 --- a/src/main/cljs/cljs/test.cljc +++ b/src/main/cljs/cljs/test.cljc @@ -243,6 +243,12 @@ (let [done-sym (gensym "done")] [`(cljs.test/async ~done-sym (try ~@body + (catch :default e# + (cljs.test/do-report + {:type :error + :message "Uncaught exception, not in assertion." + :expected nil + :actual e#})) (finally (~done-sym))))]) body)] (when ana/*load-tests* diff --git a/src/test/cljs/cljs/async_await_test.cljs b/src/test/cljs/cljs/async_await_test.cljs index 367183df2..97734e6de 100644 --- a/src/test/cljs/cljs/async_await_test.cljs +++ b/src/test/cljs/cljs/async_await_test.cljs @@ -1,6 +1,6 @@ (ns cljs.async-await-test (:refer-global :only [Date Promise]) - (:require [clojure.test :refer [deftest is async]] + (:require [clojure.test :refer [deftest is] :as t] [cljs.core :as cc :refer [await] :rename {await aw}] [goog.object :as gobj]) (:require-macros [cljs.macro-test.macros :refer [await!] :as macros])) @@ -14,12 +14,10 @@ (+ n x y (f)))) (deftest ^:async defn-test - (try - (let [v (await (foo 10))] - (is (= 61 v))) - (let [v (await (apply foo [10]))] - (is (= 61 v))) - (catch :default _ (is false)))) + (let [v (await (foo 10))] + (is (= 61 v))) + (let [v (await (apply foo [10]))] + (is (= 61 v)))) (defn ^:async variadic-foo [n & ns] (let [x (await (js/Promise.resolve n)) @@ -30,179 +28,155 @@ (+ n x y (f)))) (deftest ^:async variadic-defn-test - (try - (let [v (await (variadic-foo 10))] - (is (= 41 v))) - (let [v (await (variadic-foo 10 1 2 3))] - (is (= 47 v))) - (let [v (await (apply variadic-foo [10 1 2 3]))] - (is (= 47 v))) - (catch :default _ (is false)))) + (let [v (await (variadic-foo 10))] + (is (= 41 v))) + (let [v (await (variadic-foo 10 1 2 3))] + (is (= 47 v))) + (let [v (await (apply variadic-foo [10 1 2 3]))] + (is (= 47 v)))) (defn ^:async multi-arity-foo ([n] (await n)) ([n x] (+ (await n) x))) (deftest ^:async multi-arity-defn-test - (try - (let [v (await (multi-arity-foo 10))] - (is (= 10 v))) - (let [v (await (multi-arity-foo 10 20))] - (is (= 30 v))) - (let [v (await (apply multi-arity-foo [10]))] - (is (= 10 v))) - (let [v (await (apply multi-arity-foo [10 20]))] - (is (= 30 v))) - (catch :default _ (is false)))) + (let [v (await (multi-arity-foo 10))] + (is (= 10 v))) + (let [v (await (multi-arity-foo 10 20))] + (is (= 30 v))) + (let [v (await (apply multi-arity-foo [10]))] + (is (= 10 v))) + (let [v (await (apply multi-arity-foo [10 20]))] + (is (= 30 v)))) (defn ^:async multi-arity-variadic-foo ([n] (await n)) ([n & xs] (apply + (await n) xs))) (deftest ^:async multi-arity-variadic-test - (try - (let [v (await (multi-arity-variadic-foo 10))] - (is (= 10 v))) - (let [v (await (multi-arity-variadic-foo 10 20))] - (is (= 30 v))) - (let [v (await (apply multi-arity-variadic-foo [10]))] - (is (= 10 v))) - (let [v (await (apply multi-arity-variadic-foo [10 20]))] - (is (= 30 v))) - (catch :default _ (is false)))) + (let [v (await (multi-arity-variadic-foo 10))] + (is (= 10 v))) + (let [v (await (multi-arity-variadic-foo 10 20))] + (is (= 30 v))) + (let [v (await (apply multi-arity-variadic-foo [10]))] + (is (= 10 v))) + (let [v (await (apply multi-arity-variadic-foo [10 20]))] + (is (= 30 v)))) (deftest ^:async fn-test - (try - (let [f (^:async fn [x] (+ x (await (js/Promise.resolve 20)))) - v (await (f 10)) - v2 (await (apply f [10]))] - (is (= 30 v v2))) - (catch :default _ (is false)))) + (let [f (^:async fn [x] (+ x (await (js/Promise.resolve 20)))) + v (await (f 10)) + v2 (await (apply f [10]))] + (is (= 30 v v2)))) (deftest ^:async varargs-fn-test - (try - (let [f (^:async fn [x & xs] (apply + x (await (js/Promise.resolve 20)) xs)) - v (await (f 10)) - v2 (await (apply f [10])) - v3 (await (f 5 5)) - v4 (await (apply f [5 5]))] - (is (= 30 v v2 v3 v4))) - (catch :default _ (is false)))) + (let [f (^:async fn [x & xs] (apply + x (await (js/Promise.resolve 20)) xs)) + v (await (f 10)) + v2 (await (apply f [10])) + v3 (await (f 5 5)) + v4 (await (apply f [5 5]))] + (is (= 30 v v2 v3 v4)))) (deftest ^:async variadic-fn-test - (try (let [f (^:async fn - ([x] (await (js/Promise.resolve x))) - ([x y] (cons (await (js/Promise.resolve x)) [y])))] - (is (= [1 1 [1 2] [1 2]] - [(await (f 1)) - (await (apply f [1])) - (await (f 1 2)) - (await (apply f [1 2]))]))) - (catch :default _ (is false)))) + (let [f (^:async fn + ([x] (await (js/Promise.resolve x))) + ([x y] (cons (await (js/Promise.resolve x)) [y])))] + (is (= [1 1 [1 2] [1 2]] + [(await (f 1)) + (await (apply f [1])) + (await (f 1 2)) + (await (apply f [1 2]))])))) (deftest ^:async variadic-varargs-fn-test - (try (let [f (^:async fn - ([x] (await (js/Promise.resolve x))) - ([x & xs] (cons (await (js/Promise.resolve x)) xs)))] - (is (= [1 1 [1 2 3] [1 2 3]] - [(await (f 1)) - (await (apply f [1])) - (await (f 1 2 3)) - (await (apply f [1 2 3]))]))) - (catch :default _ (is false)))) + (let [f (^:async fn + ([x] (await (js/Promise.resolve x))) + ([x & xs] (cons (await (js/Promise.resolve x)) xs)))] + (is (= [1 1 [1 2 3] [1 2 3]] + [(await (f 1)) + (await (apply f [1])) + (await (f 1 2 3)) + (await (apply f [1 2 3]))])))) (deftest ^:async await-in-throw-test (let [f (^:async fn [x] (inc (if (odd? x) (throw (await (js/Promise.resolve "dude"))) x)))] - (try - (let [x (await (f 2))] - (is (= 3 x))) - (let [x (try (await (f 1)) - (catch :default e e))] - (is (= "dude" x))) - (catch :default _ (is false))))) + (let [x (await (f 2))] + (is (= 3 x))) + (let [x (try (await (f 1)) + (catch :default e e))] + (is (= "dude" x))))) (deftest ^:async await-in-do-test - (try - (let [a (atom 0) - f (^:async fn [] (let [_ (do (swap! a inc) - (swap! a + (await (js/Promise.resolve 2))))] - @a)) - v (await (f))] - (is (= 3 v))) - (catch :default _ (is false)))) + (let [a (atom 0) + f (^:async fn [] (let [_ (do (swap! a inc) + (swap! a + (await (js/Promise.resolve 2))))] + @a)) + v (await (f))] + (is (= 3 v)))) (deftest ^:async await-let-fn-test - (try - (let [f (^:async fn [] (let [v - ;; force letfn in expr position - (letfn [(^:async f [] (inc (await (js/Promise.resolve 10))))] - (inc (await (f))))] - (identity v))) - v (await (f))] - (is (= 12 v))) - (catch :default _ (is false)))) + (let [f (^:async fn [] (let [v + ;; force letfn in expr position + (letfn [(^:async f [] (inc (await (js/Promise.resolve 10))))] + (inc (await (f))))] + (identity v))) + v (await (f))] + (is (= 12 v)))) (deftest ^:async await-in-loop-test - (try - (let [f (^:async fn [] (let [x - ;; force loop in expr position - (loop [xs (map #(js/Promise.resolve %) [1 2 3]) - ys []] - (if (seq xs) - (let [x (first xs) - v (await x)] - (recur (rest xs) (conj ys v))) - ys))] - (identity x))) - v (await (f))] - (is (= [1 2 3] v))) - (catch :default _ (is false)))) + (let [f (^:async fn [] (let [x + ;; force loop in expr position + (loop [xs (map #(js/Promise.resolve %) [1 2 3]) + ys []] + (if (seq xs) + (let [x (first xs) + v (await x)] + (recur (rest xs) (conj ys v))) + ys))] + (identity x))) + v (await (f))] + (is (= [1 2 3] v)))) (deftest ^:async await-in-nested - (try - (let [f (^:async fn [] - (let [b1 1 - b2 (let [x 2] - (+ x - ;; outer let doesn't have awaits - ;; but inner let does, so outer let should become async - (let [x (await (js/Promise.resolve 1))] x))) - b3 (case :foo :foo (case :foo :foo (await (js/Promise.resolve 1)))) - b4 (int ;; wrapped in int to avoid false positive warning: - ;; all arguments must be numbers, got [number - ;; ignore] instead - (try (throw (throw (await (js/Promise.resolve 1)))) (catch :default _ 1 ))) - a (atom 0) - b5 (do (swap! a inc) (swap! a inc) - ;; do with single expr, wrapped in identity to avoid merging with upper do - (identity (do (swap! a (await (js/Promise.resolve inc))))) - ;; do with multiple exprs, wrapped identity to avoid merging with upper do - (identity (do (swap! a inc) (swap! a (await (js/Promise.resolve inc))))) - @a) - b6 (try (identity (try 1 (finally (await nil)))) - (finally nil)) - b7 (letfn [(f [x] x)] - (f (letfn [(f [x] x)] - (f (await 1)))))] - (await (+ b1 b2 b3 b4 b5 b6 b7))))] - (is (= 13 (await (f))))) - (catch :default _ (is false)))) + (let [f (^:async fn [] + (let [b1 1 + b2 (let [x 2] + (+ x + ;; outer let doesn't have awaits + ;; but inner let does, so outer let should become async + (let [x (await (js/Promise.resolve 1))] x))) + b3 (case :foo :foo (case :foo :foo (await (js/Promise.resolve 1)))) + b4 (int ;; wrapped in int to avoid false positive warning: + ;; all arguments must be numbers, got [number + ;; ignore] instead + (try (throw (throw (await (js/Promise.resolve 1)))) (catch :default _ 1 ))) + a (atom 0) + b5 (do (swap! a inc) (swap! a inc) + ;; do with single expr, wrapped in identity to avoid merging with upper do + (identity (do (swap! a (await (js/Promise.resolve inc))))) + ;; do with multiple exprs, wrapped identity to avoid merging with upper do + (identity (do (swap! a inc) (swap! a (await (js/Promise.resolve inc))))) + @a) + b6 (try (identity (try 1 (finally (await nil)))) + (finally nil)) + b7 (letfn [(f [x] x)] + (f (letfn [(f [x] x)] + (f (await 1)))))] + (await (+ b1 b2 b3 b4 b5 b6 b7))))] + (is (= 13 (await (f)))))) (deftest ^:async await-with-aliases-or-renamed-and-via-macros-test - (try - (let [a (await! (js/Promise.resolve 1)) - b (macros/await! (js/Promise.resolve 1)) - c (cc/await (js/Promise.resolve 1)) - d (aw (js/Promise.resolve 1)) - e (cljs.core/await (js/Promise.resolve 1)) - f (clojure.core/await (js/Promise.resolve 1))] - (is (= 1 a)) - (is (= 1 b)) - (is (= 1 c)) - (is (= 1 d)) - (is (= 1 e)) - (is (= 1 f))) - (catch :default _ (is false)))) + (let [a (await! (js/Promise.resolve 1)) + b (macros/await! (js/Promise.resolve 1)) + c (cc/await (js/Promise.resolve 1)) + d (aw (js/Promise.resolve 1)) + e (cljs.core/await (js/Promise.resolve 1)) + f (clojure.core/await (js/Promise.resolve 1))] + (is (= 1 a)) + (is (= 1 b)) + (is (= 1 c)) + (is (= 1 d)) + (is (= 1 e)) + (is (= 1 f)))) (deftest ^:async await-with-ctor (let [f (^:async fn [] (Date. (await (Promise/resolve 0))))] @@ -235,6 +209,37 @@ (let [res (await (async-destructure {:foo 1}))] (is (= [1 "hello!"] res)))) +(def throw-in-async-error-test (volatile! false)) + +(deftest ^:async simulated-async-error-test + (when @throw-in-async-error-test + (throw (js/Error. "simulated"))) + (is (not @throw-in-async-error-test))) + +(def async-error-reports (atom [])) + +(defmethod t/report [::error-capture :error] [m] + (swap! async-error-reports conj m)) + +(deftest ^:async async-test-error-reporting-test + (reset! async-error-reports []) + (let [outer-env (t/get-current-env) + reports (try + (t/set-env! (t/empty-env ::error-capture)) + (vreset! throw-in-async-error-test true) + (await (js/Promise. + (fn [resolve _] + (t/run-block + (concat (t/test-var-block (var simulated-async-error-test)) + [(fn [] (resolve @async-error-reports))]))))) + (finally + (vreset! throw-in-async-error-test false) + (t/set-env! outer-env)))] + (is (= 1 (count reports))) + (let [{:keys [message actual]} (first reports)] + (is (= "Uncaught exception, not in assertion." message)) + (is (= "simulated" (ex-message actual)))))) + (comment (clojure.test/run-tests) )