@@ -8,39 +8,42 @@ import (
88"github.com/samber/lo"
99)
1010
11+ type TextMatcher struct {
12+ * Matcher [string ]
13+ }
14+
15+ type IntMatcher struct {
16+ * Matcher [int ]
17+ }
18+
1119// for making assertions on string values
12- type Matcher struct {
13- rules []matcherRule
20+ type Matcher [ T any ] struct {
21+ rules []matcherRule [ T ]
1422
1523// this is printed when there's an error so that it's clear what the context of the assertion is
1624prefix string
1725}
1826
19- type matcherRule struct {
27+ type matcherRule [ T any ] struct {
2028// e.g. "contains 'foo'"
2129name string
2230// returns a bool that says whether the test passed and if it returns false, it
2331// also returns a string of the error message
24- testFn func (string ) (bool , string )
25- }
26-
27- func NewMatcher (name string , testFn func (string ) (bool , string )) * Matcher {
28- rules := []matcherRule {{name : name , testFn : testFn }}
29- return & Matcher {rules : rules }
32+ testFn func (T ) (bool , string )
3033}
3134
32- func (self * Matcher ) name () string {
35+ func (self * Matcher [ T ] ) name () string {
3336if len (self .rules ) == 0 {
3437return "anything"
3538}
3639
3740return strings .Join (
38- lo .Map (self .rules , func (rule matcherRule , _ int ) string { return rule .name }),
41+ lo .Map (self .rules , func (rule matcherRule [ T ] , _ int ) string { return rule .name }),
3942", " ,
4043)
4144}
4245
43- func (self * Matcher ) test (value string ) (bool , string ) {
46+ func (self * Matcher [ T ] ) test (value T ) (bool , string ) {
4447for _ , rule := range self .rules {
4548ok , message := rule .testFn (value )
4649if ok {
@@ -57,8 +60,8 @@ func (self *Matcher) test(value string) (bool, string) {
5760return true , ""
5861}
5962
60- func (self * Matcher ) Contains (target string ) * Matcher {
61- return self .appendRule (matcherRule {
63+ func (self * TextMatcher ) Contains (target string ) * TextMatcher {
64+ self .appendRule (matcherRule [ string ] {
6265name : fmt .Sprintf ("contains '%s'" , target ),
6366testFn : func (value string ) (bool , string ) {
6467// everything contains the empty string so we unconditionally return true here
@@ -69,19 +72,23 @@ func (self *Matcher) Contains(target string) *Matcher {
6972return strings .Contains (value , target ), fmt .Sprintf ("Expected '%s' to be found in '%s'" , target , value )
7073},
7174})
75+
76+ return self
7277}
7378
74- func (self * Matcher ) DoesNotContain (target string ) * Matcher {
75- return self .appendRule (matcherRule {
79+ func (self * TextMatcher ) DoesNotContain (target string ) * TextMatcher {
80+ self .appendRule (matcherRule [ string ] {
7681name : fmt .Sprintf ("does not contain '%s'" , target ),
7782testFn : func (value string ) (bool , string ) {
7883return ! strings .Contains (value , target ), fmt .Sprintf ("Expected '%s' to NOT be found in '%s'" , target , value )
7984},
8085})
86+
87+ return self
8188}
8289
83- func (self * Matcher ) MatchesRegexp (target string ) * Matcher {
84- return self .appendRule (matcherRule {
90+ func (self * TextMatcher ) MatchesRegexp (target string ) * TextMatcher {
91+ self .appendRule (matcherRule [ string ] {
8592name : fmt .Sprintf ("matches regular expression '%s'" , target ),
8693testFn : func (value string ) (bool , string ) {
8794matched , err := regexp .MatchString (target , value )
@@ -91,75 +98,130 @@ func (self *Matcher) MatchesRegexp(target string) *Matcher {
9198return matched , fmt .Sprintf ("Expected '%s' to match regular expression /%s/" , value , target )
9299},
93100})
101+
102+ return self
94103}
95104
96- func (self * Matcher ) Equals (target string ) * Matcher {
97- return self .appendRule (matcherRule {
105+ func (self * TextMatcher ) Equals (target string ) * TextMatcher {
106+ self .appendRule (matcherRule [ string ] {
98107name : fmt .Sprintf ("equals '%s'" , target ),
99108testFn : func (value string ) (bool , string ) {
100109return target == value , fmt .Sprintf ("Expected '%s' to equal '%s'" , value , target )
101110},
102111})
112+
113+ return self
103114}
104115
105116const IS_SELECTED_RULE_NAME = "is selected"
106117
107118// special rule that is only to be used in the TopLines and Lines methods, as a way of
108119// asserting that a given line is selected.
109- func (self * Matcher ) IsSelected () * Matcher {
110- return self .appendRule (matcherRule {
120+ func (self * TextMatcher ) IsSelected () * TextMatcher {
121+ self .appendRule (matcherRule [ string ] {
111122name : IS_SELECTED_RULE_NAME ,
112123testFn : func (value string ) (bool , string ) {
113124panic ("Special IsSelected matcher is not supposed to have its testFn method called. This rule should only be used within the .Lines() and .TopLines() method on a ViewAsserter." )
114125},
115126})
127+
128+ return self
116129}
117130
118- func (self * Matcher ) appendRule (rule matcherRule ) * Matcher {
131+ func (self * Matcher [ T ] ) appendRule (rule matcherRule [ T ] ) * Matcher [ T ] {
119132self .rules = append (self .rules , rule )
120133
121134return self
122135}
123136
124137// adds context so that if the matcher test(s) fails, we understand what we were trying to test.
125138// E.g. prefix: "Unexpected content in view 'files'."
126- func (self * Matcher ) context (prefix string ) * Matcher {
139+ func (self * Matcher [ T ] ) context (prefix string ) * Matcher [ T ] {
127140self .prefix = prefix
128141
129142return self
130143}
131144
132145// if the matcher has an `IsSelected` rule, it returns true, along with the matcher after that rule has been removed
133- func (self * Matcher ) checkIsSelected () (bool , * Matcher ) {
146+ func (self * TextMatcher ) checkIsSelected () (bool , * TextMatcher ) {
134147// copying into a new matcher in case we want to re-use the original later
135- newMatcher := & Matcher {}
148+ newMatcher := & TextMatcher {}
136149* newMatcher = * self
137150
138- check := lo .ContainsBy (newMatcher .rules , func (rule matcherRule ) bool { return rule .name == IS_SELECTED_RULE_NAME })
151+ check := lo .ContainsBy (newMatcher .rules , func (rule matcherRule [ string ] ) bool { return rule .name == IS_SELECTED_RULE_NAME })
139152
140- newMatcher .rules = lo .Filter (newMatcher .rules , func (rule matcherRule , _ int ) bool { return rule .name != IS_SELECTED_RULE_NAME })
153+ newMatcher .rules = lo .Filter (newMatcher .rules , func (rule matcherRule [ string ] , _ int ) bool { return rule .name != IS_SELECTED_RULE_NAME })
141154
142155return check , newMatcher
143156}
144157
158+ func (self * IntMatcher ) EqualsInt (target int ) * IntMatcher {
159+ self .appendRule (matcherRule [int ]{
160+ name : fmt .Sprintf ("equals '%d'" , target ),
161+ testFn : func (value int ) (bool , string ) {
162+ return value == target , fmt .Sprintf ("Expected '%d' to equal '%d'" , value , target )
163+ },
164+ })
165+
166+ return self
167+ }
168+
169+ func (self * IntMatcher ) GreaterThan (target int ) * IntMatcher {
170+ self .appendRule (matcherRule [int ]{
171+ name : fmt .Sprintf ("greater than '%d'" , target ),
172+ testFn : func (value int ) (bool , string ) {
173+ return value > target , fmt .Sprintf ("Expected '%d' to greater than '%d'" , value , target )
174+ },
175+ })
176+
177+ return self
178+ }
179+
180+ func (self * IntMatcher ) LessThan (target int ) * IntMatcher {
181+ self .appendRule (matcherRule [int ]{
182+ name : fmt .Sprintf ("less than '%d'" , target ),
183+ testFn : func (value int ) (bool , string ) {
184+ return value < target , fmt .Sprintf ("Expected '%d' to less than '%d'" , value , target )
185+ },
186+ })
187+
188+ return self
189+ }
190+
145191// this matcher has no rules meaning it always passes the test. Use this
146192// when you don't care what value you're dealing with.
147- func Anything () * Matcher {
148- return & Matcher {}
193+ func AnyString () * TextMatcher {
194+ return & TextMatcher {Matcher : & Matcher [string ]{}}
195+ }
196+
197+ func Contains (target string ) * TextMatcher {
198+ return AnyString ().Contains (target )
199+ }
200+
201+ func DoesNotContain (target string ) * TextMatcher {
202+ return AnyString ().DoesNotContain (target )
203+ }
204+
205+ func MatchesRegexp (target string ) * TextMatcher {
206+ return AnyString ().MatchesRegexp (target )
207+ }
208+
209+ func Equals (target string ) * TextMatcher {
210+ return AnyString ().Equals (target )
149211}
150212
151- func Contains ( target string ) * Matcher {
152- return Anything (). Contains ( target )
213+ func AnyInt ( ) * IntMatcher {
214+ return & IntMatcher { Matcher : & Matcher [ int ]{}}
153215}
154216
155- func DoesNotContain (target string ) * Matcher {
156- return Anything ().DoesNotContain (target )
217+ func EqualsInt (target int ) * IntMatcher {
218+ return AnyInt ().EqualsInt (target )
157219}
158220
159- func MatchesRegexp (target string ) * Matcher {
160- return Anything ().MatchesRegexp (target )
221+ func GreaterThan (target int ) * IntMatcher {
222+ return AnyInt ().GreaterThan (target )
161223}
162224
163- func Equals (target string ) * Matcher {
164- return Anything ().Equals (target )
225+ func LessThan (target int ) * IntMatcher {
226+ return AnyInt ().LessThan (target )
165227}
0 commit comments