Add built-in cljs.test/clojure.test support#802
Merged
Conversation
Makes squint recognize (:require [cljs.test :refer [deftest is testing are]]) and friends out of the box, so user code can use the standard cljs.test API without vendoring macros or a runtime namespace into their project. - src/squint/test.cljs: runtime (report, run-tests, test-var, fixtures, ^:async), pre-compiled to src/squint/test.mjs and shipped via package.json. - src/squint/internal/test.cljc: deftest/is/testing/are/deftest-/use-fixtures as compiler-built-in macros, registered through built-in-macro-nss. - compiler_common: resolve-ns and resolve-macro-ns learn to map cljs.test/clojure.test to the squint runtime; symbol emit falls back to the built-in ns->libname mapping so macro-generated qualified refs auto-import; :refer'd macro names are filtered out of runtime imports while still being tracked in :refers for lookup. - bb tasks: compile-test-runtime regenerates test.mjs on build/test; test-cljs-test compiles test-resources/cljs_test_smoke.cljs and asserts on the summary output, wired into test-squint for CI.
When a macro expansion produces a qualified ref like clojure.test/report, the symbol emitter generates an auto-import for the namespace. In REPL mode this emitted `var X = await import(...)` but forgot the matching `globalThis.<ns>.X = X` assignment that the regular ns form uses, so later emissions of `globalThis.user.X.report(...)` blew up with "Cannot read properties of undefined". Also add test.mjs to the playground's runtime file copy list so prod builds have the file available under public/public/src/squint/.
The auto-import can land before the ns form has emitted its own
`globalThis.<ns> = globalThis.<ns> || {}` initializer, so emit the
same idempotent guard alongside the assignment.
data: URLs aren't hierarchical, so relative import specifiers inside the compiled module (e.g. ./squint-local/core.js in dev) fail to resolve. A Blob URL inherits the document origin, letting those relative imports resolve against the page URL.
Blob URLs have an opaque origin, so a ./squint-local/... import inside the compiled module has nothing hierarchical to resolve against. Compute an absolute URL once (relative to main_js.mjs) and substitute it in — works under both blob and data scheme.
Under Vite, import.meta.url inside main_js.mjs can resolve to an unexpected bundle path (e.g. .../index.js), which made the computed localBase miss the trailing "squint-local/" segment entirely. The result was concatenated URLs like .../index.jscore.js. Use the page origin directly so the rewrite always targets the js/squint-local symlink.
- deftest registers each test fn under its compile-time ns name. - run-tests dispatches on arg type: strings look up ns's tests, fns run directly, no args defaults (via the new macro) to the current compile-time ns — matching cljs.test/run-tests. - Auto-initialise the env and emit :summary, returning counters. - Macro output carries :squint.compiler/skip-macro so the generated runtime call doesn't re-enter macro dispatch.
with_meta previously copied through an object spread, which lost callability for functions. Introduce IWithMeta__withMeta, following the existing IApply__apply pattern, and give functions a default implementation that wraps in a new callable carrying meta — keeping fn? true and avoiding mutation. With that in place the squint deftest macro can use plain with-meta like cherry's (no _squintTestName side channel). :name is stored as a string so neither compiler has to emit a quoted symbol for it.
The single-arg form still reads *target*, but call sites that live inside a Promise chain (e.g. cherry's scan-macros) can't rely on the dynamic binding — it's already unwound by the time .then fires. Pass the target explicitly there.
(cljs.test/foo) with no require would silently dispatch to the built-in test macros. Require the user's :aliases to actually contain the alias (bare or alias-munged) before consulting the fallback. Reordered so the contains? checks only run when there's a built-in candidate to gate.
The Symbol emit method needs resolve-ns; rather than carrying a forward (declare resolve-ns), define resolve-import-map, resolve-macro-ns, builtin-refer-is-macro? and resolve-ns ahead of the emit method. Pure code reorganisation — no behaviour change.
No type implements it; with_meta only ever ran the type-dispatched default branches. Keep the function-wrap fix (the original reason for touching with_meta) but skip the symbol-protocol indirection until there's an actual consumer.
Regression coverage for the with_meta fn-wrap path: fn? must stay true, (meta ...) must round-trip the attached map, calling the result must forward to the original, and the original must not be mutated.
Inventory of behavior our built-in cljs.test diverges from canonical clojure.test/cljs.test. Grouped by impact (correctness, extensibility, coverage, polish) with a fix sketch per item so they can be tackled incrementally.
Each-fixtures and once-fixtures are now keyed by namespace string in the env, matching cljs.test semantics. - core-deftest stashes :ns alongside :name on the fn's meta so test-var can find the right fixture vector for a fn alone. - core-use-fixtures captures the compile-time ns and emits the ns-aware setter call. - set-each-fixtures!/set-once-fixtures! and their getters get a 2-arity (ns-str, fixtures); the 1-arity stays as a back-compat shim that targets the nil-keyed bucket so existing direct callers keep working. - test-var prefers per-ns each-fixtures, falling back to the nil bucket; run-tests groups its test-vars by ns so each ns's once-fixtures wrap only that ns's tests. - Smoke test grows two regression tests: per-ns each-fixtures fire in isolation, and per-ns once-fixtures wrap their group only.
Previously an inner (run-tests ...) inflated the outer caller's counters and emitted a summary showing cumulative totals. Match clojure.test by saving the caller's :report-counters on entry, running the tests against fresh counters, then restoring the saved counters before returning. The returned counters map reflects only this run's tests. Smoke test gets a regression: inner run-tests must not change outer counters; its returned summary must equal its own scope.
`(report {:type X})` was unconditionally incrementing
`[:report-counters X]`, leaving bogus keys like `:summary` and
`:begin-test-ns` in the counters map. Match clojure.test by
restricting the increment to result types.
Smoke test gets a regression: after a few non-result reports, only
the result keys exist on `:report-counters`.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Makes squint recognize (:require [cljs.test :refer [deftest is testing are]]) and friends out of the box, so user code can use the standard cljs.test API without vendoring macros or a runtime namespace into their project.