Skip to content

Commit 0af38ad

Browse files
committed
Support matchers on integers in integration tests
1 parent e98935f commit 0af38ad

File tree

13 files changed

+137
-75
lines changed

13 files changed

+137
-75
lines changed

pkg/integration/components/alert_driver.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func (self *AlertDriver) getViewDriver() *ViewDriver {
1111
}
1212

1313
// asserts that the alert view has the expected title
14-
func (self *AlertDriver) Title(expected *Matcher) *AlertDriver {
14+
func (self *AlertDriver) Title(expected *TextMatcher) *AlertDriver {
1515
self.getViewDriver().Title(expected)
1616

1717
self.hasCheckedTitle = true
@@ -20,7 +20,7 @@ func (self *AlertDriver) Title(expected *Matcher) *AlertDriver {
2020
}
2121

2222
// asserts that the alert view has the expected content
23-
func (self *AlertDriver) Content(expected *Matcher) *AlertDriver {
23+
func (self *AlertDriver) Content(expected *TextMatcher) *AlertDriver {
2424
self.getViewDriver().Content(expected)
2525

2626
self.hasCheckedContent = true

pkg/integration/components/assertion_helper.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func retryWaitTimes() []int {
2222
}
2323
}
2424

25-
func (self *assertionHelper) matchString(matcher *Matcher, context string, getValue func() string) {
25+
func (self *assertionHelper) matchString(matcher *TextMatcher, context string, getValue func() string) {
2626
self.assertWithRetries(func() (bool, string) {
2727
value := getValue()
2828
return matcher.context(context).test(value)

pkg/integration/components/commit_message_panel_driver.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,19 @@ func (self *CommitMessagePanelDriver) getViewDriver() *ViewDriver {
99
}
1010

1111
// asserts on the text initially present in the prompt
12-
func (self *CommitMessagePanelDriver) InitialText(expected *Matcher) *CommitMessagePanelDriver {
12+
func (self *CommitMessagePanelDriver) InitialText(expected *TextMatcher) *CommitMessagePanelDriver {
1313
return self.Content(expected)
1414
}
1515

1616
// asserts on the current context in the prompt
17-
func (self *CommitMessagePanelDriver) Content(expected *Matcher) *CommitMessagePanelDriver {
17+
func (self *CommitMessagePanelDriver) Content(expected *TextMatcher) *CommitMessagePanelDriver {
1818
self.getViewDriver().Content(expected)
1919

2020
return self
2121
}
2222

2323
// asserts that the confirmation view has the expected title
24-
func (self *CommitMessagePanelDriver) Title(expected *Matcher) *CommitMessagePanelDriver {
24+
func (self *CommitMessagePanelDriver) Title(expected *TextMatcher) *CommitMessagePanelDriver {
2525
self.getViewDriver().Title(expected)
2626

2727
return self

pkg/integration/components/common.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func (self *Common) ConfirmDiscardLines() {
3939
Confirm()
4040
}
4141

42-
func (self *Common) SelectPatchOption(matcher *Matcher) {
42+
func (self *Common) SelectPatchOption(matcher *TextMatcher) {
4343
self.t.GlobalPress(self.t.keys.Universal.CreatePatchOptionsMenu)
4444

4545
self.t.ExpectPopup().Menu().Title(Equals("Patch options")).Select(matcher).Confirm()

pkg/integration/components/confirmation_driver.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ func (self *ConfirmationDriver) getViewDriver() *ViewDriver {
1111
}
1212

1313
// asserts that the confirmation view has the expected title
14-
func (self *ConfirmationDriver) Title(expected *Matcher) *ConfirmationDriver {
14+
func (self *ConfirmationDriver) Title(expected *TextMatcher) *ConfirmationDriver {
1515
self.getViewDriver().Title(expected)
1616

1717
self.hasCheckedTitle = true
@@ -20,7 +20,7 @@ func (self *ConfirmationDriver) Title(expected *Matcher) *ConfirmationDriver {
2020
}
2121

2222
// asserts that the confirmation view has the expected content
23-
func (self *ConfirmationDriver) Content(expected *Matcher) *ConfirmationDriver {
23+
func (self *ConfirmationDriver) Content(expected *TextMatcher) *ConfirmationDriver {
2424
self.getViewDriver().Content(expected)
2525

2626
self.hasCheckedContent = true

pkg/integration/components/file_system.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func (self *FileSystem) PathNotPresent(path string) {
2626
}
2727

2828
// Asserts that the file at the given path has the given content
29-
func (self *FileSystem) FileContent(path string, matcher *Matcher) {
29+
func (self *FileSystem) FileContent(path string, matcher *TextMatcher) {
3030
self.assertWithRetries(func() (bool, string) {
3131
_, err := os.Stat(path)
3232
if os.IsNotExist(err) {

pkg/integration/components/matcher.go

Lines changed: 100 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -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
1624
prefix string
1725
}
1826

19-
type matcherRule struct {
27+
type matcherRule[T any] struct {
2028
// e.g. "contains 'foo'"
2129
name 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 {
3336
if len(self.rules) == 0 {
3437
return "anything"
3538
}
3639

3740
return 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) {
4447
for _, rule := range self.rules {
4548
ok, message := rule.testFn(value)
4649
if ok {
@@ -57,8 +60,8 @@ func (self *Matcher) test(value string) (bool, string) {
5760
return 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]{
6265
name: fmt.Sprintf("contains '%s'", target),
6366
testFn: 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 {
6972
return 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]{
7681
name: fmt.Sprintf("does not contain '%s'", target),
7782
testFn: func(value string) (bool, string) {
7883
return !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]{
8592
name: fmt.Sprintf("matches regular expression '%s'", target),
8693
testFn: func(value string) (bool, string) {
8794
matched, err := regexp.MatchString(target, value)
@@ -91,75 +98,130 @@ func (self *Matcher) MatchesRegexp(target string) *Matcher {
9198
return 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]{
98107
name: fmt.Sprintf("equals '%s'", target),
99108
testFn: func(value string) (bool, string) {
100109
return target == value, fmt.Sprintf("Expected '%s' to equal '%s'", value, target)
101110
},
102111
})
112+
113+
return self
103114
}
104115

105116
const 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]{
111122
name: IS_SELECTED_RULE_NAME,
112123
testFn: func(value string) (bool, string) {
113124
panic("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] {
119132
self.rules = append(self.rules, rule)
120133

121134
return 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] {
127140
self.prefix = prefix
128141

129142
return 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

142155
return 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
}

pkg/integration/components/menu_driver.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ func (self *MenuDriver) getViewDriver() *ViewDriver {
1010
}
1111

1212
// asserts that the popup has the expected title
13-
func (self *MenuDriver) Title(expected *Matcher) *MenuDriver {
13+
func (self *MenuDriver) Title(expected *TextMatcher) *MenuDriver {
1414
self.getViewDriver().Title(expected)
1515

1616
self.hasCheckedTitle = true
@@ -30,19 +30,19 @@ func (self *MenuDriver) Cancel() {
3030
self.getViewDriver().PressEscape()
3131
}
3232

33-
func (self *MenuDriver) Select(option *Matcher) *MenuDriver {
33+
func (self *MenuDriver) Select(option *TextMatcher) *MenuDriver {
3434
self.getViewDriver().NavigateToLine(option)
3535

3636
return self
3737
}
3838

39-
func (self *MenuDriver) Lines(matchers ...*Matcher) *MenuDriver {
39+
func (self *MenuDriver) Lines(matchers ...*TextMatcher) *MenuDriver {
4040
self.getViewDriver().Lines(matchers...)
4141

4242
return self
4343
}
4444

45-
func (self *MenuDriver) TopLines(matchers ...*Matcher) *MenuDriver {
45+
func (self *MenuDriver) TopLines(matchers ...*TextMatcher) *MenuDriver {
4646
self.getViewDriver().TopLines(matchers...)
4747

4848
return self

0 commit comments

Comments
 (0)