mirror of
https://github.com/penpot/penpot.git
synced 2026-05-23 00:43:41 +00:00
✨ Improve common JS test runner
Add focused common JavaScript test execution with log-level control and a quiet test wrapper matching the frontend workflow. Update developer docs and testing memories to reflect the common/frontend test split, and document why runner helper extraction is deferred. Signed-off-by: Codex <codex@openai.com>
This commit is contained in:
parent
e252bcf901
commit
cec90416c2
@ -12,10 +12,13 @@ clojure -M:dev:test
|
||||
pnpm run test:jvm --focus common-tests.logic.variants-switch-test
|
||||
clojure -M:dev:test --focus common-tests.logic.variants-switch-test/test-basic-switch
|
||||
pnpm run test:js
|
||||
pnpm run test:quiet
|
||||
pnpm run test:quiet -- --focus common-tests.logic.comp-sync-test
|
||||
pnpm run test:quiet -- --focus common-tests.logic.comp-sync-test/test-sync-when-changing-attribute --log-level warn
|
||||
pnpm run watch:test
|
||||
```
|
||||
|
||||
Focused JS tests are selected by editing `test/common_tests/runner.cljs`, then running `pnpm run test:js`. Multiple JVM `--focus` flags compose as a union.
|
||||
Use `test:quiet` for non-interactive JS runs; it buffers `build:test` output and forwards runner args. Common JS runner args support `--focus <namespace-or-var>` and `--log-level trace|debug|info|warn|error`. After `pnpm run build:test`, direct compiled runner focus is faster: `node target/tests/test.js --focus common-tests.logic.comp-sync-test/test-sync-when-changing-attribute --log-level warn`. New common JS test namespaces must be required/listed in `common_tests/runner.cljc`; new vars in existing namespaces need no runner change. Multiple JVM `--focus` flags compose as a union.
|
||||
|
||||
## Test helpers
|
||||
|
||||
|
||||
@ -15,6 +15,8 @@ From `frontend/`:
|
||||
- After `pnpm run build:test`, direct compiled runner focus is faster: `node target/tests/test.js --focus frontend-tests.logic.components-and-tokens/change-spacing-token-in-main-updates-copy-layout`.
|
||||
- Watch tests: `pnpm run watch:test`.
|
||||
|
||||
New frontend test namespaces must be required/listed in `frontend_tests/runner.cljs`; new vars in existing namespaces need no runner change.
|
||||
|
||||
## Playwright integration tests
|
||||
|
||||
Do not add, modify, or run Playwright integration tests under `frontend/playwright` unless explicitly asked. When explicitly asked, use `pnpm run test:e2e` or `pnpm run test:e2e --grep "pattern"` from `frontend/`; ensure dependencies are installed through `./scripts/setup` if the environment is not prepared.
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
"watch:test": "concurrently \"clojure -M:dev:shadow-cljs watch test\" \"nodemon -C -d 2 -w target/tests/ --exec 'node target/tests/test.js'\"",
|
||||
"build:test": "clojure -M:dev:shadow-cljs compile test",
|
||||
"test:js": "pnpm run build:test && node target/tests/test.js",
|
||||
"test:quiet": "node ./scripts/test-quiet.js",
|
||||
"test:jvm": "clojure -M:dev:test"
|
||||
}
|
||||
}
|
||||
|
||||
25
common/scripts/test-quiet.js
Normal file
25
common/scripts/test-quiet.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
|
||||
const progress = (msg) => process.stderr.write(`${msg}\n`);
|
||||
|
||||
progress("Building test bundle...");
|
||||
const build = spawnSync("pnpm", ["run", "build:test"], {
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
maxBuffer: 64 * 1024 * 1024,
|
||||
});
|
||||
|
||||
if (build.status !== 0) {
|
||||
progress("Building test bundle failed");
|
||||
if (build.stdout?.length) process.stdout.write(build.stdout);
|
||||
if (build.stderr?.length) process.stderr.write(build.stderr);
|
||||
process.exit(build.status ?? 1);
|
||||
}
|
||||
|
||||
progress("Running tests...");
|
||||
const result = spawnSync(
|
||||
"node",
|
||||
["target/tests/test.js", ...process.argv.slice(2)],
|
||||
{ stdio: "inherit" },
|
||||
);
|
||||
|
||||
process.exit(result.status ?? 1);
|
||||
@ -7,6 +7,10 @@
|
||||
(ns common-tests.runner
|
||||
(:require
|
||||
#?(:clj [common-tests.fressian-test])
|
||||
#?(:cljs [app.common.logging :as l])
|
||||
#?(:cljs [clojure.string :as str])
|
||||
#?(:cljs [clojure.tools.cli :refer [parse-opts]])
|
||||
#?(:cljs [goog.object :as gobj])
|
||||
[clojure.test :as t]
|
||||
[common-tests.attrs-test]
|
||||
[common-tests.buffer-test]
|
||||
@ -78,22 +82,12 @@
|
||||
[common-tests.undo-stack-test]
|
||||
[common-tests.uuid-test]))
|
||||
|
||||
#?(:cljs (enable-console-print!))
|
||||
|
||||
#?(:cljs
|
||||
(defmethod cljs.test/report [:cljs.test/default :end-run-tests] [m]
|
||||
(if (cljs.test/successful? m)
|
||||
(.exit js/process 0)
|
||||
(.exit js/process 1))))
|
||||
|
||||
(defn -main
|
||||
[& args]
|
||||
(t/run-tests
|
||||
(def test-namespaces
|
||||
[#?(:clj 'common-tests.fressian-test)
|
||||
'common-tests.attrs-test
|
||||
'common-tests.buffer-test
|
||||
'common-tests.colors-test
|
||||
'common-tests.data-test
|
||||
#?(:clj 'common-tests.fressian-test)
|
||||
'common-tests.files-changes-test
|
||||
'common-tests.files-builder-test
|
||||
'common-tests.files-migrations-test
|
||||
@ -157,4 +151,169 @@
|
||||
'common-tests.types.token-test
|
||||
'common-tests.types.tokens-lib-test
|
||||
'common-tests.undo-stack-test
|
||||
'common-tests.uuid-test))
|
||||
'common-tests.uuid-test])
|
||||
|
||||
#?(:cljs
|
||||
(assert (every? find-ns-obj test-namespaces)
|
||||
"test-namespaces contains a namespace that isn't required in runner.cljc"))
|
||||
|
||||
#?(:cljs (enable-console-print!))
|
||||
|
||||
#?(:cljs
|
||||
(defmethod cljs.test/report [:cljs.test/default :end-run-tests] [m]
|
||||
(if (cljs.test/successful? m)
|
||||
(.exit js/process 0)
|
||||
(.exit js/process 1))))
|
||||
|
||||
#?(:cljs
|
||||
(do
|
||||
;; This runner intentionally mirrors frontend-tests.runner. Both runners need
|
||||
;; forwarded CLI args, focused namespace/var execution, fixture preservation,
|
||||
;; and app log-level setup. A shared helper could own those mechanics, but we
|
||||
;; keep the logic local while there are only two test targets because sharing
|
||||
;; it would add cross-module test classpath coupling.
|
||||
(def ^:private log-levels
|
||||
#{:trace :debug :info :warn :error})
|
||||
|
||||
(def cli-options
|
||||
[["-f" "--focus FOCUS" "Run one test namespace or one test var, e.g. common-tests.logic.comp-sync-test/test-sync-when-changing-attribute"]
|
||||
["-l" "--log-level LEVEL" "Set app logger level: trace|debug|info|warn|error"
|
||||
:parse-fn keyword
|
||||
:validate [log-levels "must be one of trace, debug, info, warn, error"]]
|
||||
["-h" "--help"]])
|
||||
|
||||
(defn- argv
|
||||
[]
|
||||
(let [args (->> (.-argv js/process)
|
||||
(array-seq)
|
||||
(drop 2))]
|
||||
(cond-> args
|
||||
(= "--" (first args)) rest)))
|
||||
|
||||
(defn- usage
|
||||
[summary]
|
||||
(str "Usage: pnpm run test:js -- [options]\n\n"
|
||||
"Options:\n"
|
||||
summary "\n\n"
|
||||
"Focus examples:\n"
|
||||
" pnpm run test:js -- --focus common-tests.logic.comp-sync-test\n"
|
||||
" pnpm run test:js -- --focus common-tests.logic.comp-sync-test/test-sync-when-changing-attribute\n\n"
|
||||
"Log level example (quiets app logging during the run):\n"
|
||||
" pnpm run test:js -- --focus common-tests.logic.comp-sync-test --log-level warn"))
|
||||
|
||||
(defn- fail!
|
||||
[message]
|
||||
(js/console.error message)
|
||||
(.exit js/process 1))
|
||||
|
||||
(defn- parse-focus
|
||||
[focus]
|
||||
(let [[ns-name test-name & extra] (str/split focus #"/")]
|
||||
(cond
|
||||
(or (str/blank? ns-name) (seq extra))
|
||||
(fail! (str "Invalid --focus value: " focus))
|
||||
|
||||
(some? test-name)
|
||||
{:ns (symbol ns-name) :test test-name}
|
||||
|
||||
:else
|
||||
{:ns (symbol ns-name)})))
|
||||
|
||||
(defn- fixture-value
|
||||
[ns-obj fixture-name]
|
||||
(let [value (gobj/get ns-obj (munge fixture-name))]
|
||||
(when-not (undefined? value)
|
||||
value)))
|
||||
|
||||
(defn- ns-test-vars
|
||||
[ns-sym]
|
||||
(when-let [ns-obj (find-ns-obj ns-sym)]
|
||||
(->> (js-keys ns-obj)
|
||||
(keep (fn [key]
|
||||
(some-> (gobj/get ns-obj key)
|
||||
(.-cljs$lang$var))))
|
||||
(filter (comp :test meta))
|
||||
(sort-by (comp :line meta)))))
|
||||
|
||||
(defn- ns-fixtures
|
||||
[ns-sym vars]
|
||||
(when-let [ns-obj (find-ns-obj ns-sym)]
|
||||
(let [ns-key (or (some-> vars first meta :ns) ns-sym)
|
||||
once-fixtures (fixture-value ns-obj "cljs-test-once-fixtures")
|
||||
each-fixtures (fixture-value ns-obj "cljs-test-each-fixtures")]
|
||||
{:once (when once-fixtures {ns-key once-fixtures})
|
||||
:each (when each-fixtures {ns-key each-fixtures})})))
|
||||
|
||||
(defn- selected-tests
|
||||
[{:keys [ns test]}]
|
||||
(when-not (some #{ns} test-namespaces)
|
||||
(fail! (str "Unknown test namespace: " ns)))
|
||||
(let [vars (vec (ns-test-vars ns))]
|
||||
(when (empty? vars)
|
||||
(fail! (str "No tests found in namespace: " ns)))
|
||||
(if test
|
||||
(let [test-sym (symbol test)
|
||||
test-var (some #(when (= test-sym (:name (meta %))) %) vars)]
|
||||
(if test-var
|
||||
{:vars [test-var]
|
||||
:fixtures (ns-fixtures ns [test-var])}
|
||||
(fail! (str "Unknown test var: " ns "/" test))))
|
||||
{:vars vars
|
||||
:fixtures (ns-fixtures ns vars)})))
|
||||
|
||||
(defn- merge-fixtures
|
||||
[fixtures]
|
||||
{:once (apply merge (keep :once fixtures))
|
||||
:each (apply merge (keep :each fixtures))})
|
||||
|
||||
(defn- run-test-vars!
|
||||
[tests]
|
||||
(let [vars (vec (mapcat :vars tests))
|
||||
fixtures (merge-fixtures (map :fixtures tests))
|
||||
env (assoc (t/empty-env)
|
||||
:once-fixtures (:once fixtures)
|
||||
:each-fixtures (:each fixtures))
|
||||
summary (volatile! {:test 0 :pass 0 :fail 0 :error 0 :type :summary})]
|
||||
(t/run-block
|
||||
(concat [(fn [] (t/set-env! env))]
|
||||
(t/test-vars-block vars)
|
||||
[(fn []
|
||||
(vswap! summary
|
||||
(partial merge-with +)
|
||||
(:report-counters (t/get-and-clear-env!))))
|
||||
(fn []
|
||||
(t/set-env! env)
|
||||
(t/do-report @summary)
|
||||
(t/report (assoc @summary :type :end-run-tests))
|
||||
(t/clear-env!))]))))
|
||||
|
||||
(defn- run-focused-test!
|
||||
[focus]
|
||||
(run-test-vars! [(selected-tests (parse-focus focus))]))
|
||||
|
||||
(defn- run-all-tests!
|
||||
[]
|
||||
(run-test-vars! (map #(selected-tests {:ns %}) test-namespaces)))))
|
||||
|
||||
(defn -main
|
||||
[& _args]
|
||||
#?(:cljs
|
||||
(let [{:keys [options errors summary]} (parse-opts (argv) cli-options)]
|
||||
(cond
|
||||
(seq errors)
|
||||
(fail! (str/join "\n" errors))
|
||||
|
||||
(:help options)
|
||||
(do
|
||||
(println (usage summary))
|
||||
(.exit js/process 0))
|
||||
|
||||
:else
|
||||
(do
|
||||
(when-let [level (:log-level options)]
|
||||
(l/setup! {:app level}))
|
||||
(if (:focus options)
|
||||
(run-focused-test! (:focus options))
|
||||
(run-all-tests!)))))
|
||||
:clj
|
||||
(apply t/run-tests test-namespaces)))
|
||||
|
||||
@ -277,6 +277,31 @@ You can also mark tests in the code by adding metadata:
|
||||
Please refer to the [kaocha manual](https://cljdoc.org/d/lambdaisland/kaocha/1.91.1392/doc/6-focusing-and-skipping)
|
||||
for how to define custom metadata and other ways of selecting tests.
|
||||
|
||||
Common tests can also be run in the JavaScript runtime with shadow-cljs. From
|
||||
<code class="language-text">common</code>:
|
||||
|
||||
```bash
|
||||
# To run all common JavaScript tests once
|
||||
pnpm run test:js
|
||||
|
||||
# Quiet run for non-interactive output
|
||||
pnpm run test:quiet
|
||||
|
||||
# To run a single common JavaScript tests module
|
||||
pnpm run test:quiet -- --focus common-tests.logic.comp-sync-test
|
||||
|
||||
# To run a single common JavaScript test and quiet app-level logging
|
||||
pnpm run test:quiet -- --focus common-tests.logic.comp-sync-test/test-sync-when-changing-attribute --log-level warn
|
||||
```
|
||||
|
||||
The common JavaScript runner accepts the same forwarded runner arguments through
|
||||
<code class="language-text">pnpm run test:js -- ...</code>,
|
||||
<code class="language-text">pnpm run test:quiet -- ...</code>, or directly through
|
||||
<code class="language-text">node target/tests/test.js ...</code> after
|
||||
<code class="language-text">pnpm run build:test</code>. Supported runner arguments
|
||||
are <code class="language-text">--focus namespace-or-var</code> and
|
||||
<code class="language-text">--log-level trace|debug|info|warn|error</code>.
|
||||
|
||||
**NOTE**: in <code class="language-text">frontend</code> we still can't use kaocha to run the tests. We are on
|
||||
it, but for now we use shadow-cljs with <code class="language-text">package.json</code> scripts:
|
||||
|
||||
@ -284,17 +309,20 @@ it, but for now we use shadow-cljs with <code class="language-text">package.json
|
||||
# To run all frontend tests once
|
||||
pnpm run test
|
||||
|
||||
# Quiet run for non-interactive output
|
||||
pnpm run test:quiet
|
||||
|
||||
# To run all frontend tests and keep watching for changes
|
||||
pnpm run watch:test
|
||||
|
||||
# To run a single frontend tests module
|
||||
pnpm run test -- --focus frontend-tests.logic.components-and-tokens
|
||||
pnpm run test:quiet -- --focus frontend-tests.logic.components-and-tokens
|
||||
|
||||
# To run a single frontend test
|
||||
pnpm run test -- --focus frontend-tests.logic.components-and-tokens/change-token-in-main
|
||||
pnpm run test:quiet -- --focus frontend-tests.logic.components-and-tokens/change-token-in-main
|
||||
|
||||
# To quiet app-level logging during the run (trace|debug|info|warn|error)
|
||||
pnpm run test -- --focus frontend-tests.logic.components-and-tokens --log-level warn
|
||||
pnpm run test:quiet -- --focus frontend-tests.logic.components-and-tokens --log-level warn
|
||||
```
|
||||
|
||||
For non-interactive runs (CI, scripted invocations, agent loops), use the quiet
|
||||
@ -304,11 +332,6 @@ test-runner output streams through normally. Short progress hints
|
||||
(`Building wasm...`, `Running tests...`) go to `stderr`, so capturing `stdout`
|
||||
gives you just the test results.
|
||||
|
||||
```bash
|
||||
# Quiet run (same arguments as `pnpm run test`)
|
||||
pnpm run test:quiet -- --focus frontend-tests.logic.components-and-tokens
|
||||
```
|
||||
|
||||
#### Test output
|
||||
|
||||
The default kaocha reporter outputs a summary for the test run. There is a pair
|
||||
|
||||
@ -94,6 +94,11 @@
|
||||
(assert (every? find-ns-obj test-namespaces)
|
||||
"test-namespaces contains a namespace that isn't required in runner.cljs")
|
||||
|
||||
;; This runner intentionally mirrors common-tests.runner. Both runners need
|
||||
;; forwarded CLI args, focused namespace/var execution, fixture preservation,
|
||||
;; and app log-level setup. A shared helper could own those mechanics, but we
|
||||
;; keep the logic local while there are only two test targets because sharing
|
||||
;; it would add cross-module test classpath coupling.
|
||||
(def ^:private log-levels
|
||||
#{:trace :debug :info :warn :error})
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user