-
Notifications
You must be signed in to change notification settings - Fork 21
/
cljs_test.cljc
115 lines (103 loc) · 4.6 KB
/
cljs_test.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
(ns matcher-combinators.cljs-test
"Internal use. Require `matcher-combinators.test` instead of this
namespace."
#?(:cljs
(:require-macros [matcher-combinators.cljs-test]))
(:require [matcher-combinators.core :as core]
[matcher-combinators.printer :as printer]
[matcher-combinators.parser]
[matcher-combinators.result :as result]
[cljs.test :as t :refer-macros [deftest is]]))
(defrecord CljsMismatch [summary match-result])
(defn tagged-for-pretty-printing [actual-summary result]
(->CljsMismatch actual-summary result))
#?(:cljs
(extend-protocol IPrintWithWriter
CljsMismatch
(-pr-writer [cljs-mismatch writer _]
(-write writer (printer/as-string (-> cljs-mismatch :match-result ::result/value))))))
(defn with-file+line-info [report]
#?(:cljs (merge (t/file-and-line (js/Error.) 4) report)))
;; This technique was copied from https://github.com/tonsky/datascript
;; below is the reasoning from the datascript repo:
;; The matcher-combinators.cljs-test namespace exists only for the side
;; effect of extending the cljs.test/assert-expr multimethod.
;; This has to be done on the clj side of cljs compilation, and
;; so we have a separate namespace that is only loaded by cljs
;; via a :require-macros clause in datascript.test.core. This
;; means we have a clj namespace that should only be loaded by
;; cljs compilation.
#?(:clj (do
(defmethod t/assert-expr 'match? [_ msg form]
`(let [args# (list ~@(rest form))
[matcher# actual#] args#]
(cond
(not (= 2 (count args#)))
(t/do-report
{:type :fail
:message ~msg
:expected (symbol "`match?` expects 2 arguments: a `matcher` and the `actual`")
:actual (symbol (str (count args#) " were provided: " '~form))})
(core/matcher? matcher#)
(let [result# (core/match matcher# actual#)]
(t/do-report
(if (core/indicates-match? result#)
{:type :pass
:message ~msg
:expected '~form
:actual (list 'match? matcher# actual#)}
(with-file+line-info
{:type :fail
:message ~msg
:expected '~form
:actual (tagged-for-pretty-printing (list '~'not (list 'match? matcher# actual#))
result#)
:markup (::result/value result#)}))))
:else
(t/do-report
{:type :fail
:message ~msg
:expected (str "The first argument of match? needs to be a matcher (implement the match protocol)")
:actual '~form}))))
(defmethod t/assert-expr 'match-with? [_ msg form]
`(clojure.test/do-report
{:type :fail
:message ~msg
:expected (symbol "`match-with?` not yet implemented for cljs")
:actual '~form}))
(defmethod t/assert-expr 'thrown-match? [_ msg form]
;; (is (thrown-with-match? exception-class matcher expr))
;; Asserts that evaluating expr throws an exception of class c.
;; Also asserts that the exception data satisfies the provided matcher.
(let [klass (nth form 1)
matcher (nth form 2)
body (nthnext form 3)]
`(try ~@body
(let [args# (list ~@(rest form))]
(if (not (= 3 (count args#)))
(clojure.test/do-report
{:type :fail
:message ~msg
:expected (symbol "`thrown-match?` expects 3 arguments: an exception class, a `matcher`, and the `actual`")
:actual (symbol (str (count args#) " were provided: " '~form))})
(t/do-report {:type :fail
:message ~msg
:expected '~form
:actual (symbol "the expected exception wasn't thrown")})))
(catch ~klass e#
(let [result# (core/match ~matcher (ex-data e#))]
(t/do-report
(if (core/indicates-match? result#)
{:type :pass
:message ~msg
:expected '~form
:actual (list 'thrown-match? ~klass ~matcher '~body)}
(with-file+line-info
{:type :fail
:message ~msg
:expected '~form
:actual (tagged-for-pretty-printing (list '~'not (list 'thrown-match? ~klass ~matcher '~body))
result#)
:ex-class ~klass
:markup (::result/value result#)}))))
e#))))))