Skip to content

Commit 4932c10

Browse files
committed
Add (async done body) macro for cljs.test parity
Adds core-async to the built-in test macros: (async done body) expands to (js/Promise. (fn [done] body)). The deftest body returns the Promise and test-var awaits it, so no ^:async marker on the outer fn is needed. Lets users copy-paste cljs.test code that uses the (async done ...) idiom without rewriting it. Also added to builtin-refer-is-macro? so :refer [async] doesn't emit a runtime import. Smoke test gets a regression. CHANGELOG and TODO entries updated (#8 closed).
1 parent b6551c0 commit 4932c10

6 files changed

Lines changed: 44 additions & 13 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55
## Unreleased
66

77
- Add built-in `cljs.test` / `clojure.test` support: `deftest`, `is`, `testing`,
8-
`are`, `deftest-`, `use-fixtures`, `run-tests` (no-args defaults to current ns;
9-
also accepts quoted ns symbols, e.g. `(run-tests 'my.ns)`). Per-namespace
10-
fixtures, counter isolation per `run-tests` call, `^:async` test support.
11-
Runtime ships at `squint-cljs/src/squint/test.mjs`.
8+
`are`, `deftest-`, `use-fixtures`, `async`, `run-tests` (no-args defaults to
9+
current ns; also accepts quoted ns symbols, e.g. `(run-tests 'my.ns)`).
10+
Per-namespace fixtures, counter isolation per `run-tests` call, `^:async`
11+
test support, and `(async done body)` form for tests that resolve when
12+
`done` is called. Runtime ships at `squint-cljs/src/squint/test.mjs`.
1213
- `with-meta` now preserves callability when applied to a function (returns a
1314
forwarding wrapper instead of a plain object via `copy()`).
1415
- Fix [#783](https://github.com/squint-cljs/squint/issues/783): auto-load macros from `.cljc` files via `:require` (no need for `:require-macros`)

bb/tasks.clj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@
8787
(shell "node" "node_cli.js" "compile" src)
8888
(let [out (:out (shell {:out :string} "node" out-file))]
8989
(fs/delete out-file)
90-
(assert (str/includes? out "Ran 9 tests containing 19 assertions") out)
90+
(assert (str/includes? out "Ran 10 tests containing 20 assertions") out)
9191
(assert (str/includes? out "1 failures, 0 errors") out)
9292
;; :begin-test-ns must fire for each ns visited by run-tests
9393
(assert (str/includes? out "Testing ns.a") out)

doc/cljs-test-todo.md

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,26 @@ everyone uses, but it's overridable). Same idea as `report`.
7070

7171
## Coverage gaps — features that exist but are partial
7272

73-
### 7. `:begin-test-ns` / `:end-test-ns` are never emitted
74-
We support the report types in `report`'s `case`, but `run-tests` never
73+
### 7. `:begin-test-ns` / `:end-test-ns` are never emitted ✅ DONE
74+
~~We support the report types in `report`'s `case`, but `run-tests` never
7575
fires them. Real `clojure.test` brackets each ns it processes with these
76-
events; reporters use them to group output.
76+
events; reporters use them to group output.~~
7777

78-
### 8. No `(t/async done body)` form
79-
Real `cljs.test` async tests use `(async done (do ... (done)))`. We use
78+
Fixed: `run-vars-with-once-fixtures` now brackets each ns's tests with
79+
`:begin-test-ns` / `:end-test-ns` reports, so `(t/run-tests 'my.ns)`
80+
prints `Testing my.ns` like cljs.test. Anonymous (nil-ns) groups are
81+
skipped. Smoke tests assert the lines appear.
82+
83+
### 8. No `(t/async done body)` form ✅ DONE
84+
~~Real `cljs.test` async tests use `(async done (do ... (done)))`. We use
8085
`^:async` on the deftest plus a Promise-returning body. Functionally
8186
equivalent but different surface — code copied from a CLJS project
82-
won't run.
87+
won't run.~~
88+
89+
Added: `core-async` macro expands `(async done body)` to
90+
`(js/Promise. (fn [done] body))`. `test-var` already awaits any
91+
Promise-returning test fn, so no additional plumbing was needed —
92+
the `^:async` form keeps working too. Regression tests in both repos.
8393

8494
### 9. Test discovery is registration-based, not metadata-based
8595
`clojure.test/run-tests` walks `ns-publics` and filters by

src/squint/compiler_common.cljc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@
316316
alias))))
317317

318318
(def ^:private builtin-test-macro-names
319-
'#{deftest deftest- is testing are use-fixtures})
319+
'#{deftest deftest- is testing are async use-fixtures})
320320

321321
(defn builtin-refer-is-macro?
322322
"Returns true when a `:refer`'d symbol is known to be a compiler built-in

src/squint/internal/test.cljc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@
9898
:once `(clojure.test/set-once-fixtures! ~ns-name [~@fns])
9999
:each `(clojure.test/set-each-fixtures! ~ns-name [~@fns]))))
100100

101+
(defn core-async [_&form _&env done & body]
102+
;; (async done body...) — common cljs.test idiom for async tests.
103+
;; Expands to a Promise constructor whose `resolve` is bound to `done`
104+
;; in the body's scope. The deftest body returns this Promise and
105+
;; test-var awaits it; no ^:async marker needed on the outer fn.
106+
(assert (symbol? done) "first argument to async must be a symbol")
107+
`(js/Promise. (fn [~done] ~@body)))
108+
101109
(defn core-run-tests [_&form &env & args]
102110
;; Match cljs.test: with no args, default to the compile-time current ns.
103111
;; Quoted namespace symbols `'my.ns` get converted to plain strings at
@@ -123,5 +131,6 @@
123131
'is core-is
124132
'testing core-testing
125133
'are core-are
134+
'async core-async
126135
'use-fixtures core-use-fixtures
127136
'run-tests core-run-tests})

test-resources/cljs_test_smoke.cljs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
(ns cljs-test-smoke
2-
(:require [cljs.test :as t :refer [deftest is testing are]]))
2+
(:require [cljs.test :as t :refer [deftest is testing are async]]))
33

44
(deftest math-test
55
(testing "basic math"
@@ -99,6 +99,16 @@
9999
(is (nil? (:begin-test-ns counters))
100100
"no bogus :begin-test-ns key added")))))
101101

102+
(deftest async-done-form-test
103+
;; (async done ...) is the cljs.test idiom — body runs, done resolves
104+
;; the wrapping promise, test-var awaits it.
105+
(async done
106+
(js/setTimeout
107+
(fn []
108+
(is (= 42 (* 6 7)))
109+
(done))
110+
5)))
111+
102112
(deftest run-tests-quoted-symbol-test
103113
(testing "(run-tests 'my.ns) macro converts quoted symbol to a string"
104114
(let [saved-env (t/get-current-env)]
@@ -119,6 +129,7 @@
119129
(t/test-var thrown-test)
120130
(t/test-var expected-failure-test)
121131
(js-await (t/test-var async-test))
132+
(js-await (t/test-var async-done-form-test))
122133
(t/test-var per-ns-each-fixtures-test)
123134
(t/test-var per-ns-once-fixtures-test)
124135
(t/test-var run-tests-counter-isolation-test)

0 commit comments

Comments
 (0)