├── .gitignore ├── .lvimrc ├── CHANGES.md ├── README.md ├── UNLICENSE ├── deps.edn ├── other-tests └── other │ └── other_test.cljs ├── pom.xml ├── scripts ├── format ├── outdated ├── repl └── test ├── src ├── cljs_test_runner │ └── main.clj └── giants_shoulders │ └── repl.clj └── test ├── cljs-opts-test.edn ├── doo-opts-test.edn └── example ├── ignore_me.cljs ├── nope_test.clj ├── partial_test.cljc └── yes_test.cljs /.gitignore: -------------------------------------------------------------------------------- 1 | .cpcache/ 2 | target/ 3 | cljs-test-runner-out/ 4 | .nrepl-port 5 | .clj-kondo/ 6 | -------------------------------------------------------------------------------- /.lvimrc: -------------------------------------------------------------------------------- 1 | nnoremap rc :call conjure#connect("default", "127.0.0.1:5005", "cljc?$", "clj") 2 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # cljs-test-runner changes 2 | 3 | ## 3.8.1 4 | 5 | - Merged [#42](https://github.com/Olical/cljs-test-runner/pull/42) - Upgrade doo 6 | and add Firefox support 7 | - Upgraded all other dependencies, cleaned up 8 | [developer tooling](https://github.com/Olical/clojure-giants-shoulders), 9 | formatted source code. 10 | 11 | ## 3.8.0 12 | 13 | - Switch to a doo fork 14 | ([#36](https://github.com/Olical/cljs-test-runner/issues/36)) to support 15 | `:target :bundle`. 16 | 17 | ## 3.7.0 18 | 19 | - Merged [#32](https://github.com/Olical/cljs-test-runner/pull/32) - Add support 20 | for Lumo. 21 | 22 | ## 3.6.0 23 | 24 | - Merged [#31](https://github.com/Olical/cljs-test-runner/pull/31) - Fix 25 | optimisation level quirks. 26 | 27 | ## 3.5.0 28 | 29 | - Merged [#28](https://github.com/Olical/cljs-test-runner/pull/28) - Read inline 30 | EDN compiler opts. 31 | 32 | ## 3.4.0 33 | 34 | - Merged [#25](https://github.com/Olical/cljs-test-runner/pull/25) - Support for 35 | [planck](https://github.com/planck-repl/planck). 36 | 37 | ## 3.3.0 38 | 39 | - Merged [#23](https://github.com/Olical/cljs-test-runner/pull/23) - Avoid crash 40 | when specifying a namespace that doesn't exist. 41 | - Move to Clojure 1.10.0 by default. 42 | - Clean up some old code left over from a refactor around var filtering. 43 | - Add a `Makefile` wrapper that can start a prepl server for development. 44 | 45 | ## 3.2.1 46 | 47 | - Merged [#21](https://github.com/Olical/cljs-test-runner/pull/21) - Filter 48 | tests under :advanced. 49 | 50 | ## 3.2.0 51 | 52 | - Merged [#19](https://github.com/Olical/cljs-test-runner/pull/19) - Adds 53 | `chrome-headless` to the envs list. 54 | - Added some more documentation and caveats about running with `:simple` and 55 | `:whitespace` optimisation levels. 56 | 57 | ## 3.1.0 58 | 59 | - Merged [#17](https://github.com/Olical/cljs-test-runner/pull/17) - Fixes 60 | running tests with advanced compilation as mentioned in 61 | [#16](https://github.com/Olical/cljs-test-runner/issues/16). 62 | 63 | ## 3.0.0 64 | 65 | - Merged [#15](https://github.com/Olical/cljs-test-runner/pull/15) - Improves 66 | `--dir` so it accumulated but replaces the default if you use it. 67 | 68 | ## 2.1.0 69 | 70 | - Added `--doo-opts` (`-D`) which is analogous to `--compile-opts`, as 71 | recommended by [@johnmn3](https://github.com/johnmn3) in 72 | [#9](https://github.com/Olical/cljs-test-runner/issues/9). 73 | 74 | ## 2.0.0 75 | 76 | - `-e` became `-x` as a shortcut for `--env`. 77 | - `-h` became `-H`, like the Cognitect test-runner. 78 | - Added `--namespace` (`-n`) so you can test a single namespace by it's symbol. 79 | - Added `--namespace-regex` (`-r`) so you can test namespaces matching a regex. 80 | This default to any namespace ending in `-test`. 81 | - Added filtering of tests by symbol or metadata keywords. 82 | - Added `-V` / `--verbose` for turning the ClojureScript compiler verbose flag 83 | on when you want it. 84 | - Made all options repeatable, so now you can specify 85 | `-d test -d other-test-dir` as well as `-v some-test -v some-other-test`. 86 | - Output the rendered ClojureScript test runner to the output directory, so you 87 | only have to git ignore `cljs-test-runner-out`. 88 | - Added `--compile-opts` thanks to [@kthu](https://github.com/kthu) in 89 | [#7](https://github.com/Olical/cljs-test-runner/pull/7). 90 | - Any argument that takes a keyword will now parse both `:foo` and `foo` as the 91 | same. 92 | 93 | ## 1.0.0 94 | 95 | - `--watch` support thanks to [@eval](https://github.com/eval) in 96 | [#2](https://github.com/Olical/cljs-test-runner/pull/2). 97 | 98 | ## 0.1.1 99 | 100 | - Print errors that originate from the ClojureScript compiler or doo. Things 101 | like missing namespaces were failing silently. 102 | 103 | ## 0.1.0 104 | 105 | - Initial release. 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cljs-test-runner [![Clojars Project](https://img.shields.io/clojars/v/olical/cljs-test-runner.svg)](https://clojars.org/olical/cljs-test-runner) 2 | 3 | Run all of your [ClojureScript][ClojureScript] tests with one simple command. 4 | 5 | Inspired by Cognitect's [test-runner][test-runner] for [Clojure][Clojure], it is 6 | designed to be used in conjunction with the Clojure CLI tool and a `deps.edn` 7 | file. 8 | 9 | Under the hood it's building a test runner file, compiling everything and then 10 | executing the compiled tests with [doo][doo] (using 11 | [ingesolvoll's fork][doo-fork]). Discovery of test namespaces is automatic, so 12 | no configuration is required. 13 | 14 | ## Usage 15 | 16 | In simple cases, you'll be able to execute your tests with something as succinct 17 | as the following line. 18 | 19 | ```bash 20 | $ clojure -Sdeps '{:deps {olical/cljs-test-runner {:mvn/version "3.8.1"}}}' -m cljs-test-runner.main 21 | ``` 22 | 23 | > Note: The generated test code is placed in the directory 24 | > `cljs-test-runner-out` by default (configure with `--out`), you should add 25 | > that to your `.gitignore` file. 26 | 27 | It's likely that your tests will require dependencies and configuration that 28 | would become unwieldy in this format. You will need to add the dependency and 29 | `--main` (`-m`) parameter to your `deps.edn` file. 30 | 31 | I recommend you put this under an alias such as `test` or `cljs-test` if that's 32 | already taken by your Clojure tests. 33 | 34 | ```clojure 35 | {:deps {org.clojure/clojure {:mvn/version "1.10.1"} 36 | org.clojure/clojurescript {:mvn/version "1.10.520"}} 37 | :aliases {:test {:extra-deps {olical/cljs-test-runner {:mvn/version "3.8.1"}} 38 | :main-opts ["-m" "cljs-test-runner.main"]}}} 39 | ``` 40 | 41 | The following will then find, compile and execute your tests through 42 | [node][node]. 43 | 44 | ```bash 45 | $ clojure -Atest 46 | 47 | Testing example.partial-test 48 | 49 | Testing example.yes-test 50 | 51 | Ran 2 tests containing 2 assertions. 52 | 0 failures, 0 errors. 53 | ``` 54 | 55 | ## Configuration 56 | 57 | You can configure the test runner with a few different flags, the most important 58 | one is `--env` (`-x`) which allows you to swap from node to [phantom][phantom] 59 | or chrome-headless if required. I would recommend sticking to node and using 60 | something like [jsdom][jsdom], but this does come down to preference and 61 | technical requirements. 62 | 63 | ```bash 64 | $ clojure -Atest -x phantom 65 | ``` 66 | 67 | If you need to use `foreign-libs` or any cljs compiler flags that are not 68 | mirrored in cljs-test-runner's flags, you can put them into an EDN file and 69 | point to that file using the `--compile-opts` flag. 70 | 71 | You can use `--help` to see the current flags and their default values. 72 | 73 | ```bash 74 | $ clojure -Atest --help 75 | -d, --dir DIRNAME test The directory containing your test files 76 | -n, --namespace SYMBOL Symbol indicating a specific namespace to test. 77 | -r, --namespace-regex REGEX .*\-test$ Regex for namespaces to test. Only namespaces ending in '-test' are evaluated by default. 78 | -v, --var SYMBOL Symbol indicating the fully qualified name of a specific test. 79 | -i, --include SYMBOL Run only tests that have this metadata keyword. 80 | -e, --exclude SYMBOL Exclude tests with this metadata keyword. 81 | -o, --out DIRNAME cljs-test-runner-out The output directory for compiled test code 82 | -x, --env ENV node Run your tests in node, phantom, chrome-headless, lumo or planck. 83 | -w, --watch DIRNAME Directory to watch for changes (alongside the test directory). May be repeated. 84 | -c, --compile-opts PATH EDN file containing opts to be passed to the ClojureScript compiler. 85 | -D, --doo-opts PATH EDN file containing opts to be passed to doo. 86 | -V, --verbose Flag passed directly to the ClojureScript compiler to enable verbose compiler output. 87 | -H, --help 88 | ``` 89 | 90 | ## Advanced compilation 91 | 92 | To use Closure Compiler advanced optimisation levels you will need to create an 93 | EDN file like this: 94 | 95 | ```edn 96 | {:optimizations :advanced} 97 | ``` 98 | 99 | Now when you run the following, your tests will be executed with advanced 100 | compilation: 101 | 102 | ```bash 103 | clj -m cljs-test-runner.main -c ./config/advanced-compilation.edn 104 | ``` 105 | 106 | You can also directly inline the EDN using the `-c` flag: 107 | 108 | ```bash 109 | clj -m cljs-test-runner.main -c '{:optimizations :advanced}' 110 | ``` 111 | 112 | There is a known issue with `:whitespace`, I just haven't invested the time into 113 | working out what it is. For now, stick to `:none`, `:simple` or `:advanced`, the 114 | original issue for optimisation levels breaking things is [#16][#16]. 115 | 116 | ## Bundle target 117 | 118 | The new [bundle][bundle] target requires a 2-step process for compilation. One 119 | for building an `index.js` consumable by bundlers like webpack, and a second 120 | step for actually running the bundler. This requires you to specify 2 different 121 | targets. 122 | 123 | In essence, this requires you to include the following in your `doo.edn`: 124 | 125 | `{:output-to "resources/public/js/main.js"}` 126 | 127 | Your CLJS compiler options should be according to the standard guide. 128 | 129 | ## Gotchas 130 | 131 | ### Paths 132 | 133 | Make sure the directory (or directories!) containing your tests are on your Java 134 | class path. Specify this with a top level `:paths` key in your `deps.edn` file. 135 | 136 | ### Lumo / Planck 137 | 138 | To use Lumo or Planck, add the generated test runner to the `:paths` in your 139 | `deps.edn`: 140 | 141 | ```edn 142 | :paths ["src" "test" "cljs-test-runner-out/gen"] 143 | ``` 144 | 145 | and set the environment: 146 | 147 | -x lumo 148 | 149 | or 150 | 151 | -x planck 152 | 153 | ## Unlicenced 154 | 155 | Find the full [unlicense][unlicense] in the `UNLICENSE` file, but here's a 156 | snippet. 157 | 158 | > This is free and unencumbered software released into the public domain. 159 | > 160 | > Anyone is free to copy, modify, publish, use, compile, sell, or distribute 161 | > this software, either in source code form or as a compiled binary, for any 162 | > purpose, commercial or non-commercial, and by any means. 163 | 164 | Do what you want. Learn as much as you can. Unlicense more software. 165 | 166 | [clojure]: https://clojure.org/ 167 | [clojurescript]: https://clojurescript.org/ 168 | [test-runner]: https://github.com/cognitect-labs/test-runner 169 | [doo]: https://github.com/bensu/doo 170 | [doo-fork]: https://github.com/ingesolvoll/doo 171 | [node]: https://nodejs.org 172 | [phantom]: http://phantomjs.org/ 173 | [jsdom]: https://github.com/jsdom/jsdom 174 | [unlicense]: http://unlicense.org/ 175 | [#16]: https://github.com/Olical/cljs-test-runner/issues/16 176 | [bundle]: https://clojurescript.org/guides/webpack 177 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {org.clojure/clojure {:mvn/version "1.11.1"} 2 | org.clojure/clojurescript {:mvn/version "1.11.132"} 3 | org.clojure/tools.namespace {:mvn/version "1.4.5"} 4 | org.clojure/tools.cli {:mvn/version "1.0.219"} 5 | ingesolvoll/doo {:mvn/version "0.2.1"}} 6 | :aliases {:dev {:extra-paths ["test" "other-tests" "cljs-test-runner-out/gen"]} 7 | :repl {:extra-deps {cider/cider-nrepl {:mvn/version "0.45.0"} 8 | djblue/portal {:mvn/version "0.51.1"} 9 | com.bhauman/rebel-readline {:mvn/version "0.1.4"} 10 | com.taoensso/timbre {:mvn/version "6.3.1"}}} 11 | :outdated {:deps {com.github.liquidz/antq {:mvn/version "2.8.1173"}} 12 | :main-opts ["-m" "antq.core"]} 13 | :format {:deps {cljfmt/cljfmt {:mvn/version "0.9.2"}} 14 | :main-opts ["-m" "cljfmt.main" "fix"]}}} 15 | -------------------------------------------------------------------------------- /other-tests/other/other_test.cljs: -------------------------------------------------------------------------------- 1 | (ns other.other-test 2 | (:require [cljs.test :as t])) 3 | 4 | (t/deftest other-should-run 5 | (t/testing "this should run in the other dir" 6 | (t/is (= 1 1)))) 7 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | olical 5 | cljs-test-runner 6 | 3.8.1 7 | cljs-test-runner 8 | Run all of your ClojureScript tests with one simple command. 9 | https://github.com/Olical/cljs-test-runner 10 | 11 | 12 | Unlicense 13 | https://unlicense.org/ 14 | 15 | 16 | 17 | https://github.com/Olical/cljs-test-runner 18 | 19 | 20 | 21 | org.clojure 22 | clojure 23 | 1.11.1 24 | 25 | 26 | org.clojure 27 | tools.cli 28 | 1.0.219 29 | 30 | 31 | ingesolvoll 32 | doo 33 | 0.2.1 34 | 35 | 36 | org.clojure 37 | clojurescript 38 | 1.11.132 39 | 40 | 41 | org.clojure 42 | tools.namespace 43 | 1.4.5 44 | 45 | 46 | 47 | src 48 | 49 | 50 | src 51 | 52 | 53 | 54 | 55 | 56 | clojars 57 | https://repo.clojars.org/ 58 | 59 | 60 | 61 | 62 | clojars 63 | Clojars repository 64 | https://clojars.org/repo 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /scripts/format: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | clojure -M:format "$@" 4 | -------------------------------------------------------------------------------- /scripts/outdated: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | clojure -M:outdated "$@" 4 | -------------------------------------------------------------------------------- /scripts/repl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | clojure -X:test:repl giants-shoulders.repl/start! $@ 4 | -------------------------------------------------------------------------------- /scripts/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | clj -Adev -m cljs-test-runner.main 4 | -------------------------------------------------------------------------------- /src/cljs_test_runner/main.clj: -------------------------------------------------------------------------------- 1 | (ns cljs-test-runner.main 2 | "Discover and run ClojureScript tests in node (by default)." 3 | (:require [clojure.tools.namespace.find :as find] 4 | [clojure.java.io :as io] 5 | [clojure.string :as str] 6 | [clojure.edn :as edn] 7 | [clojure.tools.cli :as cli] 8 | [cljs.build.api :as cljs] 9 | [doo.core :as doo]) 10 | (:import (clojure.lang DynamicClassLoader))) 11 | 12 | (def ns-filter-cljs 13 | "Namespace filtering code from cognitect-labs/test-runner but modified and as a string. Inserted into the test runner ClojureScript code when it's rendered. Forgive me performing string based meta programming." 14 | " 15 | (defn var->sym [var] 16 | (symbol (:ns (meta var)) (:name (meta var)))) 17 | 18 | (defn var-filter 19 | [{:keys [var include exclude]}] 20 | (let [test-specific (if var 21 | (comp var var->sym) 22 | (constantly true)) 23 | test-inclusion (if include 24 | #((apply some-fn include) (meta %)) 25 | (constantly true)) 26 | test-exclusion (if exclude 27 | #((complement (apply some-fn exclude)) (meta %)) 28 | (constantly true))] 29 | #(and (test-specific %) 30 | (test-inclusion %) 31 | (test-exclusion %)))) 32 | 33 | (defn filter-vars! [ns-syms filter-fn] 34 | (doseq [ns-sym ns-syms] 35 | (doseq [[_ var] ns-sym] 36 | (when (:test (meta var)) 37 | (when (not (filter-fn var)) 38 | (set! (.-cljs$lang$test @var) nil)))))) 39 | ") 40 | 41 | (defn format-value 42 | "Return a string with a quote at the front. For use in this silly CLJS meta programming." 43 | [s] 44 | (str (when (symbol? s) "'") s)) 45 | 46 | (defn format-filter 47 | "Format filter values as a possible set or nil." 48 | [coll] 49 | (if coll 50 | (str "#{" (str/join " " (map format-value coll)) "}") 51 | "nil")) 52 | 53 | (defn render-test-runner-cljs 54 | "Renders a ClojureScript test runner from a seq of namespaces." 55 | [nses {:keys [var include exclude]}] 56 | (str 57 | "(ns cljs-test-runner.gen 58 | (:require [doo.runner :refer-macros [doo-tests]] [" (str/join "] [" nses) "]))" 59 | ns-filter-cljs 60 | "(filter-vars! [" (str/join " " (map #(str "(ns-publics " (format-value %) ")") nses)) "] 61 | (var-filter {:var " (format-filter var) " 62 | :include " (format-filter include) " 63 | :exclude " (format-filter exclude) "}))" 64 | "\n" 65 | "(doo-tests " (str/join " " (map format-value nses)) ")")) 66 | 67 | (defn ns-filter-fn 68 | "Given possible namespace symbols and regexs, return a function that returns true if it's given namespace matches one of the rules." 69 | [{:keys [ns-symbols ns-regexs]}] 70 | (let [ns-regexs (or ns-regexs #{#".*\-test$"})] 71 | (fn [n] 72 | (if ns-symbols 73 | (ns-symbols n) 74 | (some #(re-matches % (name n)) ns-regexs))))) 75 | 76 | (defn exit 77 | "Exit the program cleanly." 78 | ([status] 79 | (exit status nil)) 80 | ([status msg] 81 | (when msg 82 | (println msg)) 83 | (System/exit status))) 84 | 85 | (defn error-msg 86 | "Render an error message." 87 | [errors] 88 | (str "The following errors occurred while parsing your command:\n\n" 89 | (str/join \newline errors))) 90 | 91 | (defn find-namespaces-in-dirs 92 | "Given a set of directory paths, find every ClojureScript namespace within those directories and return it as one sequence." 93 | [dirs] 94 | (mapcat #(find/find-namespaces-in-dir (io/file %) find/cljs) dirs)) 95 | 96 | (defn load-opts 97 | "Load compiler options from input. Supports either an inline EDN map or assumed 98 | to be a file path. If input is nil, returns empty map" 99 | [path-or-data] 100 | (cond 101 | (nil? path-or-data) 102 | {} 103 | 104 | (= (first (str/trim path-or-data)) \{) 105 | (edn/read-string path-or-data) 106 | 107 | :else 108 | (edn/read-string (slurp path-or-data)))) 109 | 110 | (defn add-loader-url 111 | "Add url string or URL to the highest level DynamicClassLoader url set." 112 | [url] 113 | (let [u (if (string? url) (java.net.URL. url) url) 114 | loader (loop [loader (.getContextClassLoader (Thread/currentThread))] 115 | (let [parent (.getParent loader)] 116 | (if (instance? DynamicClassLoader parent) 117 | (recur parent) 118 | loader)))] 119 | (if (instance? DynamicClassLoader loader) 120 | (.addURL ^DynamicClassLoader loader u) 121 | (throw (IllegalAccessError. "Context classloader is not a DynamicClassLoader"))))) 122 | 123 | (defn test-cljs-namespaces-in-dir 124 | "Execute all ClojureScript tests in a directory." 125 | [{:keys [env dir out watch ns-symbols ns-regexs var include exclude verbose compile-opts doo-opts]}] 126 | (when-let [nses (seq (filter (ns-filter-fn {:ns-symbols ns-symbols 127 | :ns-regexs ns-regexs}) 128 | (find-namespaces-in-dirs dir)))] 129 | (let [test-runner-cljs (-> nses 130 | (render-test-runner-cljs {:var var 131 | :include include 132 | :exclude exclude})) 133 | exit-code (atom 1) 134 | gen-path (str/join "/" [out "gen"]) 135 | src-path (str/join "/" [gen-path "cljs_test_runner" "gen.cljs"]) 136 | out-path (str/join "/" [out "cljs_test_runner.gen.js"]) 137 | {:keys [target doo-env]} (case env 138 | :node {:target :nodejs 139 | :doo-env :node} 140 | :phantom {:target :browser 141 | :doo-env :phantom} 142 | :chrome-headless {:target :browser 143 | :doo-env :chrome-headless} 144 | :firefox-headless {:target :browser 145 | :doo-env :firefox-headless} 146 | :lumo {:doo-env :lumo} 147 | :planck {:doo-env :planck})] 148 | (io/make-parents src-path) 149 | (spit src-path test-runner-cljs) 150 | (add-loader-url (io/as-url (io/file gen-path))) 151 | (try 152 | (let [build-opts (merge {:output-to out-path 153 | :output-dir out 154 | :target target 155 | :main "cljs-test-runner.gen" 156 | :optimizations :none 157 | :verbose verbose} 158 | compile-opts) 159 | run-tests-fn #(doo/run-script doo-env build-opts doo-opts) 160 | watch-opts (assoc build-opts :watch-fn run-tests-fn)] 161 | (if (contains? #{:lumo :planck} env) 162 | (->> (run-tests-fn) :exit (reset! exit-code)) 163 | (if (seq watch) 164 | (cljs/watch (apply cljs/inputs (into watch (cons gen-path dir))) watch-opts) 165 | (do (cljs/build gen-path build-opts) 166 | (->> (run-tests-fn) :exit (reset! exit-code)))))) 167 | (catch Exception e 168 | (println e)) 169 | (finally 170 | (exit @exit-code)))))) 171 | 172 | (defn parse-kw 173 | "Parse a keyword from a string, dropping the initial : if required." 174 | [s] 175 | (if (.startsWith s ":") (read-string s) (keyword s))) 176 | 177 | (defn accumulate 178 | "Used in CLI options to accumulate multiple occurrences of a flag." 179 | [m k v] 180 | (update-in m [k] (fnil conj #{}) v)) 181 | 182 | (def cli-options 183 | "Options for use with clojure.tools.cli." 184 | [["-d" "--dir DIRNAME" "The directory containing your test files" 185 | :default-desc "test" 186 | :assoc-fn accumulate 187 | :default-fn (constantly #{"test"})] 188 | ["-n" "--namespace SYMBOL" "Symbol indicating a specific namespace to test." 189 | :id :ns-symbols 190 | :parse-fn symbol 191 | :assoc-fn accumulate] 192 | ["-r" "--namespace-regex REGEX" "Regex for namespaces to test. Only namespaces ending in '-test' are evaluated by default." 193 | :id :ns-regexs 194 | :default-desc ".*\\-test$" 195 | :parse-fn re-pattern 196 | :assoc-fn accumulate] 197 | ["-v" "--var SYMBOL" "Symbol indicating the fully qualified name of a specific test." 198 | :parse-fn symbol 199 | :assoc-fn accumulate] 200 | ["-i" "--include SYMBOL" "Run only tests that have this metadata keyword." 201 | :parse-fn parse-kw 202 | :assoc-fn accumulate] 203 | ["-e" "--exclude SYMBOL" "Exclude tests with this metadata keyword." 204 | :parse-fn parse-kw 205 | :assoc-fn accumulate] 206 | ["-o" "--out DIRNAME" "The output directory for compiled test code" 207 | :default "cljs-test-runner-out"] 208 | ["-x" "--env ENV" "Run your tests in node, phantom, chrome-headless, lumo or planck." 209 | :default :node 210 | :default-desc "node" 211 | :parse-fn parse-kw] 212 | ["-w" "--watch DIRNAME" "Directory to watch for changes (alongside the test directory). May be repeated." 213 | :assoc-fn accumulate] 214 | ["-c" "--compile-opts PATH" "EDN opts or EDN file containing opts to be passed to the ClojureScript compiler." 215 | :parse-fn load-opts] 216 | ["-D" "--doo-opts PATH" "EDN file containing opts to be passed to doo." 217 | :parse-fn load-opts] 218 | ["-V" "--verbose" "Flag passed directly to the ClojureScript compiler to enable verbose compiler output."] 219 | ["-H" "--help"]]) 220 | 221 | (defn -main 222 | "Creates a ClojureScript test runner and executes it with node (by default)." 223 | [& args] 224 | (let [cl (.getContextClassLoader (Thread/currentThread))] 225 | (.setContextClassLoader (Thread/currentThread) (clojure.lang.DynamicClassLoader. cl))) 226 | (let [{:keys [options errors summary]} (cli/parse-opts args cli-options)] 227 | (cond 228 | (:help options) (exit 0 summary) 229 | errors (exit 1 (error-msg errors)) 230 | :else (test-cljs-namespaces-in-dir options)))) 231 | 232 | (comment 233 | (defn run 234 | "Runs the test suite with the give arguments without letting the process die at the end." 235 | [& args] 236 | (with-redefs [exit println] 237 | (println 238 | (with-out-str 239 | (apply -main args))))) 240 | 241 | ;; all 242 | (run) 243 | 244 | ;; ns symbol 245 | (run "-n" "example.yes-test") 246 | 247 | ;; ns regexs 248 | (run "-r" ".*yes.*") 249 | 250 | ;; var symbol 251 | (run "-v" "example.yes-test/should-run") 252 | 253 | ;; include 254 | (run "-i" "integration") 255 | 256 | ;; exclude 257 | (run "-e" "integration") 258 | 259 | ;; more dirs 260 | (run "-d" "other-tests") 261 | 262 | ;; doo-opts 263 | (run "-D" "test/doo-opts-test.edn") 264 | 265 | ;; cljs-opts 266 | (run "-c" "test/cljs-opts-test.edn") 267 | 268 | ;; help 269 | (run "-H")) 270 | -------------------------------------------------------------------------------- /src/giants_shoulders/repl.clj: -------------------------------------------------------------------------------- 1 | (ns giants-shoulders.repl 2 | (:require [nrepl.server :as nrepl] 3 | [cider.nrepl :as cider] 4 | [taoensso.timbre :as log] 5 | [portal.api :as portal] 6 | [rebel-readline.core :as rr] 7 | [rebel-readline.clojure.line-reader :as rr-clr] 8 | [rebel-readline.clojure.service.local :as rr-csl] 9 | [rebel-readline.clojure.main :as rr-cm] 10 | [clojure.main :as clj-main])) 11 | 12 | (defn start! 13 | "Start a development REPL, intended to be invoked from ./scripts/repl" 14 | [{:keys [portal]}] 15 | 16 | (log/info "Starting nREPL server") 17 | (let [{:keys [port] :as _server} (nrepl/start-server :handler cider/cider-nrepl-handler)] 18 | (log/info "nREPL server started on port" port) 19 | (log/info "Writing port to .nrepl-port") 20 | (spit ".nrepl-port" port)) 21 | 22 | (when portal 23 | (log/info "Opening portal, use (tap> ...) to inspect values") 24 | (portal/open) 25 | (add-tap #'portal/submit)) 26 | 27 | (log/info "Starting interactive REPL") 28 | (rr/with-line-reader 29 | (rr-clr/create (rr-csl/create)) 30 | (clj-main/repl 31 | :prompt (fn []) 32 | :read (rr-cm/create-repl-read))) 33 | 34 | (log/info "Shutting down") 35 | 36 | (when portal 37 | (log/info "Closing portal") 38 | (portal/close)) 39 | 40 | (shutdown-agents) 41 | (System/exit 0)) 42 | -------------------------------------------------------------------------------- /test/cljs-opts-test.edn: -------------------------------------------------------------------------------- 1 | {:optimizations :advanced} 2 | -------------------------------------------------------------------------------- /test/doo-opts-test.edn: -------------------------------------------------------------------------------- 1 | {:paths {:node "node --trace-gc --trace-gc-verbose"}} 2 | -------------------------------------------------------------------------------- /test/example/ignore_me.cljs: -------------------------------------------------------------------------------- 1 | (ns example.ignore-me) 2 | 3 | (throw (js/Error. "Should not run or load, it does not end with -test.")) 4 | -------------------------------------------------------------------------------- /test/example/nope_test.clj: -------------------------------------------------------------------------------- 1 | (ns example.nope-test 2 | (:require [clojure.test :as t])) 3 | 4 | (t/deftest should-not-run 5 | (t/testing "this should not run" 6 | (t/is (= 1 2)))) 7 | -------------------------------------------------------------------------------- /test/example/partial_test.cljc: -------------------------------------------------------------------------------- 1 | (ns example.partial-test 2 | (:require [#?(:clj clojure.test 3 | :cljs cljs.test) :as t])) 4 | 5 | (t/deftest should-run 6 | (t/testing "this should run" 7 | (t/is (= 1 1)))) 8 | -------------------------------------------------------------------------------- /test/example/yes_test.cljs: -------------------------------------------------------------------------------- 1 | (ns example.yes-test 2 | (:require [cljs.test :as t])) 3 | 4 | (t/deftest should-run 5 | (t/testing "this should run" 6 | (t/is (= 1 1)))) 7 | 8 | (t/deftest ^:integration maybe-run 9 | (t/testing "this may run" 10 | (t/is (= 2 2)))) 11 | --------------------------------------------------------------------------------