Skip to content

Instantly share code, notes, and snippets.

@PEZ
Last active November 27, 2024 12:50
Show Gist options
  • Save PEZ/5f1101ee69ccedf042c3bea5116a8cd0 to your computer and use it in GitHub Desktop.
Save PEZ/5f1101ee69ccedf042c3bea5116a8cd0 to your computer and use it in GitHub Desktop.
Baldr, clojure.test and cljs/test with Mocha-inspired positive reporting
(ns pez.baldr
(:require #?(:cljs [cljs.test :as t]
:clj [clojure.test :as t])
[clojure.string :as string]))
(defn- default [text]
(str "\033[39m" text "\033[22m"))
(defn- gray [text]
(str "\033[90m" text "\033[39m"))
(defn- green [text]
(str "\033[1;32m" text "\033[22m"))
(defn- red [text]
(str "\033[31m" text "\033[39m"))
(def ^:private initial-state {:contexts nil
:failure-prints []})
(defonce ^:private !state (atom initial-state))
(defn- indent [level]
(apply str (repeat (* 2 level) " ")))
(defn- get-report [m contexts state {:keys [color bullet bullet-color]}]
(let [seen-contexts (:contexts state)
common-contexts (take-while true? (map = (reverse seen-contexts) (reverse contexts)))
common-prefix-length (count common-contexts)
new-contexts (reverse (take (- (count contexts) common-prefix-length) contexts))
message (or (:message m) (pr-str (:expected m)))]
{:new-state (assoc state :contexts contexts)
:printouts (cond-> []
(seq new-contexts) (into (map-indexed (fn [idx ctx]
(str (indent (+ common-prefix-length idx 2))
(default ctx)))
new-contexts))
:always (conj (str (indent (+ 2 (count contexts)))
(str (bullet-color bullet) " " (color message)))))}))
(defn report! [m config]
(let [contexts #?(:cljs (:testing-contexts (t/get-current-env))
:clj t/*testing-contexts*)
{:keys [new-state printouts]} (get-report m contexts @!state config)]
(reset! !state new-state)
(doseq [printout printouts]
(println printout))))
(defn- dispatch-value [type]
#?(:clj type
:cljs [::t/default type]))
(defmethod t/report (dispatch-value :begin-test-var) [_m]
(swap! !state merge (select-keys initial-state [:contexts])))
(def ^:private original-summary (get-method t/report (dispatch-value :summary)))
(defmethod t/report (dispatch-value :summary) [m]
(when (seq (:failure-prints @!state))
(println))
(doseq [[i failure-print] (map-indexed vector (:failure-prints @!state))]
(println (red (str (inc i) ") " (string/trim failure-print)))))
(reset! !state initial-state)
(original-summary m))
(def ^:private original-pass (get-method t/report (dispatch-value :pass)))
(defmethod t/report (dispatch-value :pass) [m]
(report! m {:color gray
:bullet "βœ“"
:bullet-color green})
(original-pass m))
(def ^:private original-fail (get-method t/report (dispatch-value :fail)))
(defmethod t/report (dispatch-value :fail) [m]
(let [failure-printout (with-out-str (original-fail m))]
(swap! !state update :failure-prints conj failure-printout))
(report! m {:color red
:bullet (str (count (:failure-prints @!state)) ")")
:bullet-color red}))
(def ^:private original-error (get-method t/report (dispatch-value :error)))
(defmethod t/report (dispatch-value :error) [m]
(let [error-printout (with-out-str (original-error m))]
(swap! !state update :failure-prints conj error-printout))
(report! m {:color red
:bullet (str (count (:failure-prints @!state)) ")")
:bullet-color red}))
(defmethod t/report (dispatch-value :begin-test-var) [m]
(println (str (indent 1) (default (:var m)))))
@PEZ
Copy link
Author

PEZ commented Nov 24, 2024

It's not really done yet, but it mostly seems to work. Looks like so:

image

Where the headers are the (testing "...") messages, and the bullets either the is message or, lacking a message, the expected clause from the is form.

Like Mocha, it will collect failures and errors, indexing them, and report them last, as part of the summary:

image image

I tried to make it a .cljc module, because there's nothing ClojureScript specific about it, but I didn't get it to work. I think that to use it with Clojure, just replace all references to cljs.test.

My use case for this does not have any async tests, so probably this reporting won't work with async tests?

I may make a library out of this, if that is of interest for anyone.

Feedback welcome! I am probably doing it all wrong. πŸ˜„

@PEZ
Copy link
Author

PEZ commented Nov 25, 2024

Update: Now I understand why I didn't succeed with making it .cljc at first. It wasn't totally obvious. But now Baldr works in both Clojure and ClojureScript. πŸŽ‰

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment