From ccd20994884733860b7995f418b065bfee3c3c55 Mon Sep 17 00:00:00 2001 From: Phillip Mates Date: Tue, 18 Jul 2023 12:36:46 +0200 Subject: [PATCH] fix match-with for cljs (#203) * fix match-with for cljs --- CHANGELOG.md | 1 + bb.edn | 4 ++ src/cljc/matcher_combinators/matchers.cljc | 2 +- src/cljc/matcher_combinators/parser.cljc | 51 +++++++++++++- .../clj/matcher_combinators/matchers_test.clj | 9 +-- .../matcher_combinators/standalone_test.clj | 2 +- .../matcher_combinators/test_helpers.cljc | 3 + .../cljs_example_test.cljs | 67 +++++++++++++++++-- 8 files changed, 126 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7295ec8..53f9892 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ change log follows the conventions of ## 3.8.6 / unreleased - fix issue when using non-composite matchers (`m/regex`, `m/pred`, etc) inside `match-with`. +- fix issue where `match-with` misbehaves in ClojureScript ## 3.8.5 / 2023-03-24 - fix clj-kondo lint warnings for `match?` in Clojurescript diff --git a/bb.edn b/bb.edn index 5378168..bdf13e3 100644 --- a/bb.edn +++ b/bb.edn @@ -12,6 +12,10 @@ {:doc "Starts a nREPL" :task (apply clojure "-Adev -Sdeps '{:deps {cider/cider-nrepl {:mvn/version \"0.29.0\"}}}' -m nrepl.cmdline --middleware \"[cider.nrepl/cider-middleware]\"" *command-line-args*)} + dev:cljs + {:doc "Starts a ClojureScript nREPL" + :task (apply clojure "-M:cljs-test node-repl" *command-line-args*)} + test:clj {:doc "run Clojure clojure.test tests" :task (apply clojure "-A:dev:clj-test:test-runner" *command-line-args*)} diff --git a/src/cljc/matcher_combinators/matchers.cljc b/src/cljc/matcher_combinators/matchers.cljc index 5eaeda7..52288f8 100644 --- a/src/cljc/matcher_combinators/matchers.cljc +++ b/src/cljc/matcher_combinators/matchers.cljc @@ -130,7 +130,7 @@ [& matchers] (core/->AllOf matchers)) -#?(:cljs (defn- cljs-uri [expected] +#?(:cljs (defn cljs-uri [expected] (core/->CljsUriEquals expected))) (defn matcher-for diff --git a/src/cljc/matcher_combinators/parser.cljc b/src/cljc/matcher_combinators/parser.cljc index c35930a..1b0183d 100644 --- a/src/cljc/matcher_combinators/parser.cljc +++ b/src/cljc/matcher_combinators/parser.cljc @@ -13,56 +13,95 @@ function (-match [this actual] (core/match (matchers/pred this) actual)) + (-matcher-for + ([this] (matchers/pred this)) + ([this _t->m] (matchers/pred this))) ;; equals base types nil (-match [this actual] (core/match (matchers/equals this) actual)) + (-matcher-for + ([_this] matchers/equals) + ([this t->m] (matchers/lookup-matcher this t->m))) number (-match [this actual] (core/match (matchers/equals this) actual)) + (-matcher-for + ([_this] matchers/equals) + ([this t->m] (matchers/lookup-matcher this t->m))) string (-match [this actual] (core/match (matchers/equals this) actual)) + (-matcher-for + ([_this] matchers/equals) + ([this t->m] (matchers/lookup-matcher this t->m))) boolean (-match [this actual] (core/match (matchers/equals this) actual)) + (-matcher-for + ([_this] matchers/equals) + ([this t->m] (matchers/lookup-matcher this t->m))) Keyword (-match [this actual] (core/match (matchers/equals this) actual)) + (-matcher-for + ([_this] matchers/equals) + ([this t->m] (matchers/lookup-matcher this t->m))) Symbol (-match [this actual] (core/match (matchers/equals this) actual)) + (-matcher-for + ([_this] matchers/equals) + ([this t->m] (matchers/lookup-matcher this t->m))) UUID (-match [this actual] (core/match (matchers/equals this) actual)) + (-matcher-for + ([_this] matchers/equals) + ([this t->m] (matchers/lookup-matcher this t->m))) goog.Uri (-match [this actual] (core/match (matchers/cljs-uri this) actual)) + (-matcher-for + ([_this] matchers/cljs-uri) + ([this t->m] (matchers/lookup-matcher this t->m))) js/Date (-match [this actual] (core/match (matchers/equals this) actual)) + (-matcher-for + ([_this] matchers/equals) + ([this t->m] (matchers/lookup-matcher this t->m))) Var (-match [this actual] (core/match (matchers/equals this) actual)) + (-matcher-for + ([_this] matchers/equals) + ([this t->m] (matchers/lookup-matcher this t->m))) ;; equals nested types Cons (-match [this actual] (core/match (matchers/equals this) actual)) + (-matcher-for + ([_this] matchers/equals) + ([this t->m] (matchers/lookup-matcher this t->m))) Repeat (-match [this actual] (core/match (matchers/equals this) actual)) + (-matcher-for + ([_this] matchers/equals) + ([this t->m] (matchers/lookup-matcher this t->m))) default (-match [this actual] @@ -73,10 +112,18 @@ (or (satisfies? ISet this) (satisfies? ISequential this)) (core/match (matchers/equals this) actual))) + (-matcher-for + ([this] (if (satisfies? IMap this) + matchers/embeds + matchers/equals)) + ([this t->m] (matchers/lookup-matcher this t->m))) js/RegExp (-match [this actual] - (core/match (matchers/regex this) actual)))) + (core/match (matchers/regex this) actual)) + (-matcher-for + ([_this] matchers/equals) + ([this t->m] (matchers/lookup-matcher this t->m))))) #?(:clj (do (defmacro mimic-matcher [matcher t] @@ -105,6 +152,6 @@ core/Matcher (-matcher-for ([this] (matchers/pred this)) - ([this t->m] (matchers/pred this))) + ([this t->m] (matchers/lookup-matcher this t->m))) (-match [this actual] (core/match (matchers/pred this) actual))))) diff --git a/test/clj/matcher_combinators/matchers_test.clj b/test/clj/matcher_combinators/matchers_test.clj index 755928b..1203079 100644 --- a/test/clj/matcher_combinators/matchers_test.clj +++ b/test/clj/matcher_combinators/matchers_test.clj @@ -9,7 +9,7 @@ [matcher-combinators.matchers :as m] [matcher-combinators.result :as result] [matcher-combinators.test :refer [match?]] - [matcher-combinators.test-helpers :as test-helpers :refer [abs-value-matcher]]) + [matcher-combinators.test-helpers :as test-helpers :refer [no-match? abs-value-matcher]]) (:import [matcher_combinators.model Mismatch Missing InvalidMatcherContext InvalidMatcherType])) (defn any? [_x] true) @@ -245,9 +245,6 @@ (is (= m/regex (m/matcher-for #"abc"))))) -(defn no-match? [expected actual] - (not (c/indicates-match? (c/match expected actual)))) - (deftest match-with-matcher (testing "processes overrides in order" (let [matcher (m/match-with [pos? abs-value-matcher @@ -297,6 +294,10 @@ #{1}) #{1 2})) + (is (match? + (m/match-with [set? m/embeds] + #{(m/pred odd?)}) + #{1 2})) (is (match? (m/match-with [set? m/embeds] #{odd?}) diff --git a/test/clj/matcher_combinators/standalone_test.clj b/test/clj/matcher_combinators/standalone_test.clj index 9f510b2..70e920c 100644 --- a/test/clj/matcher_combinators/standalone_test.clj +++ b/test/clj/matcher_combinators/standalone_test.clj @@ -1,5 +1,5 @@ (ns matcher-combinators.standalone-test - (:require [clojure.test :refer [deftest is testing use-fixtures]] + (:require [clojure.test :refer [deftest is testing]] [matcher-combinators.matchers :as m] [matcher-combinators.standalone :as standalone])) diff --git a/test/cljc/matcher_combinators/test_helpers.cljc b/test/cljc/matcher_combinators/test_helpers.cljc index ca0f995..129a2f8 100644 --- a/test/cljc/matcher_combinators/test_helpers.cljc +++ b/test/cljc/matcher_combinators/test_helpers.cljc @@ -6,3 +6,6 @@ (fn [actual] (= (Math/abs expected) (Math/abs actual))) (str "equal to abs value of " expected))) + +(defn no-match? [expected actual] + (not (core/indicates-match? (core/match expected actual)))) diff --git a/test/cljs/matcher_combinators/cljs_example_test.cljs b/test/cljs/matcher_combinators/cljs_example_test.cljs index 5ff383f..bfcac70 100644 --- a/test/cljs/matcher_combinators/cljs_example_test.cljs +++ b/test/cljs/matcher_combinators/cljs_example_test.cljs @@ -1,14 +1,13 @@ (ns matcher-combinators.cljs-example-test - (:require [clojure.test :refer [deftest testing is are]] + (:require [clojure.test :refer [deftest testing is]] [clojure.test.check.generators :as gen] [clojure.test.check.properties :as prop] [clojure.test.check.clojure-test :refer [defspec]] [matcher-combinators.standalone :as standalone] [matcher-combinators.parser] [matcher-combinators.matchers :as m] - [matcher-combinators.core :as c] - [matcher-combinators.test] - [matcher-combinators.test-helpers :as helpers]) + [matcher-combinators.test :refer [match? thrown-match?]] + [matcher-combinators.test-helpers :refer [abs-value-matcher no-match?]]) (:import [goog.Uri])) (def gen-any-equatable @@ -45,7 +44,12 @@ [2 3 1]))) (testing "`(sort 2)` throws and causes a mismatch" (is (not (standalone/match? (m/via sort 2) - 2))))) + 2)))) + (testing "via + match-with allows pre-processing `actual` before applying matching" + (is (match? (m/match-with + [vector? (fn [expected] (m/via sort expected))] + {:payloads [1 2 3]}) + {:payloads (shuffle [3 2 1])})))) (deftest exception-matching (is (thrown-match? ExceptionInfo @@ -55,6 +59,12 @@ (deftest passing-match (is (match? {:a 2} {:a 2 :b 1}))) +(deftest pred-match + (is (match? #{odd?} + #{1})) + (is (match? #{(m/pred odd?)} + #{1}))) + (comment (deftest match?-no-actual-arg (testing "fails with nice message when you don't provide an `actual` arg to `match?`" @@ -68,3 +78,50 @@ (testing "fails with nice message when you don't provide an `actual` arg to `thrown-match?`" (is (thrown-match? ExceptionInfo {:a 1}) :in-wrong-place)))) + +(deftest match-with-test + (testing "Example numeric test-case" + (is (match? (m/match-with [number? (m/within-delta 0.05)] 1) 0.99))) + + (testing "maps" + (testing "passing case with equals override" + (is (match? (m/match-with [map? m/equals] + {:a :b}) + {:a :b}))) + (testing "failing case with equals override" + (is (no-match? (m/match-with [map? m/equals] + {:a :b}) + {:a :b :d :e}))) + (testing "passing case multiple scopes" + (is (match? + {:o (m/match-with [map? m/equals] + {:a + (m/match-with [map? m/embeds] + {:b :c})})} + {:o {:a {:b :c :d :e}} + :p :q}))) + (testing "using `absent` matcher" + (is (match? (m/match-with [map? m/equals] + {:a m/absent + :b :c}) + {:b :c})) + (is (match? (m/match-with [map? m/embeds] + {:a m/absent}) + {:b :c})))) + + (testing "sets" + (is (match? + (m/match-with [set? m/embeds] + #{1}) + #{1 2})) + + (is (match? + (m/match-with [set? m/embeds] + #{(m/pred odd?)}) + #{1 2}))) + + (let [matcher (m/match-with [pos? abs-value-matcher + integer? m/equals] + 5)] + (is (match? matcher 5)) + (is (match? matcher -5))))