1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-22 05:29:44 +02:00

Merge pull request #2704 from jesseduffield/int-matchers

This commit is contained in:
Jesse Duffield 2023-06-03 15:56:34 +10:00 committed by GitHub
commit 042ab2f99a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 240 additions and 160 deletions

View File

@ -39,13 +39,13 @@ import (
func CopyFile(src, dst string) (err error) {
in, err := os.Open(src)
if err != nil {
return
return //nolint: nakedret
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return
return //nolint: nakedret
}
defer func() {
if e := out.Close(); e != nil {
@ -55,21 +55,21 @@ func CopyFile(src, dst string) (err error) {
_, err = io.Copy(out, in)
if err != nil {
return
return //nolint: nakedret
}
err = out.Sync()
if err != nil {
return
return //nolint: nakedret
}
si, err := os.Stat(src)
if err != nil {
return
return //nolint: nakedret
}
err = os.Chmod(dst, si.Mode())
if err != nil {
return
return //nolint: nakedret
}
return //nolint: nakedret
@ -92,7 +92,7 @@ func CopyDir(src string, dst string) (err error) {
_, err = os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
return
return //nolint: nakedret
}
if err == nil {
// it exists so let's remove it
@ -103,12 +103,12 @@ func CopyDir(src string, dst string) (err error) {
err = os.MkdirAll(dst, si.Mode())
if err != nil {
return
return //nolint: nakedret
}
entries, err := ioutil.ReadDir(src)
if err != nil {
return
return //nolint: nakedret
}
for _, entry := range entries {
@ -118,7 +118,7 @@ func CopyDir(src string, dst string) (err error) {
if entry.IsDir() {
err = CopyDir(srcPath, dstPath)
if err != nil {
return
return //nolint: nakedret
}
} else {
// Skip symlinks.
@ -128,7 +128,7 @@ func CopyDir(src string, dst string) (err error) {
err = CopyFile(srcPath, dstPath)
if err != nil {
return
return //nolint: nakedret
}
}
}

View File

@ -72,13 +72,13 @@ type prefixWriter struct {
writer io.Writer
}
func (self *prefixWriter) Write(p []byte) (n int, err error) {
func (self *prefixWriter) Write(p []byte) (int, error) {
if !self.prefixWritten {
self.prefixWritten = true
// assuming we can write this prefix in one go
_, err = self.writer.Write([]byte(self.prefix))
n, err := self.writer.Write([]byte(self.prefix))
if err != nil {
return
return n, err
}
}
return self.writer.Write(p)

View File

@ -11,7 +11,7 @@ func (self *AlertDriver) getViewDriver() *ViewDriver {
}
// asserts that the alert view has the expected title
func (self *AlertDriver) Title(expected *Matcher) *AlertDriver {
func (self *AlertDriver) Title(expected *TextMatcher) *AlertDriver {
self.getViewDriver().Title(expected)
self.hasCheckedTitle = true
@ -20,7 +20,7 @@ func (self *AlertDriver) Title(expected *Matcher) *AlertDriver {
}
// asserts that the alert view has the expected content
func (self *AlertDriver) Content(expected *Matcher) *AlertDriver {
func (self *AlertDriver) Content(expected *TextMatcher) *AlertDriver {
self.getViewDriver().Content(expected)
self.hasCheckedContent = true

View File

@ -22,7 +22,7 @@ func retryWaitTimes() []int {
}
}
func (self *assertionHelper) matchString(matcher *Matcher, context string, getValue func() string) {
func (self *assertionHelper) matchString(matcher *TextMatcher, context string, getValue func() string) {
self.assertWithRetries(func() (bool, string) {
value := getValue()
return matcher.context(context).test(value)

View File

@ -9,19 +9,19 @@ func (self *CommitMessagePanelDriver) getViewDriver() *ViewDriver {
}
// asserts on the text initially present in the prompt
func (self *CommitMessagePanelDriver) InitialText(expected *Matcher) *CommitMessagePanelDriver {
func (self *CommitMessagePanelDriver) InitialText(expected *TextMatcher) *CommitMessagePanelDriver {
return self.Content(expected)
}
// asserts on the current context in the prompt
func (self *CommitMessagePanelDriver) Content(expected *Matcher) *CommitMessagePanelDriver {
func (self *CommitMessagePanelDriver) Content(expected *TextMatcher) *CommitMessagePanelDriver {
self.getViewDriver().Content(expected)
return self
}
// asserts that the confirmation view has the expected title
func (self *CommitMessagePanelDriver) Title(expected *Matcher) *CommitMessagePanelDriver {
func (self *CommitMessagePanelDriver) Title(expected *TextMatcher) *CommitMessagePanelDriver {
self.getViewDriver().Title(expected)
return self

View File

@ -39,7 +39,7 @@ func (self *Common) ConfirmDiscardLines() {
Confirm()
}
func (self *Common) SelectPatchOption(matcher *Matcher) {
func (self *Common) SelectPatchOption(matcher *TextMatcher) {
self.t.GlobalPress(self.t.keys.Universal.CreatePatchOptionsMenu)
self.t.ExpectPopup().Menu().Title(Equals("Patch options")).Select(matcher).Confirm()

View File

@ -11,7 +11,7 @@ func (self *ConfirmationDriver) getViewDriver() *ViewDriver {
}
// asserts that the confirmation view has the expected title
func (self *ConfirmationDriver) Title(expected *Matcher) *ConfirmationDriver {
func (self *ConfirmationDriver) Title(expected *TextMatcher) *ConfirmationDriver {
self.getViewDriver().Title(expected)
self.hasCheckedTitle = true
@ -20,7 +20,7 @@ func (self *ConfirmationDriver) Title(expected *Matcher) *ConfirmationDriver {
}
// asserts that the confirmation view has the expected content
func (self *ConfirmationDriver) Content(expected *Matcher) *ConfirmationDriver {
func (self *ConfirmationDriver) Content(expected *TextMatcher) *ConfirmationDriver {
self.getViewDriver().Content(expected)
self.hasCheckedContent = true

View File

@ -26,7 +26,7 @@ func (self *FileSystem) PathNotPresent(path string) {
}
// Asserts that the file at the given path has the given content
func (self *FileSystem) FileContent(path string, matcher *Matcher) {
func (self *FileSystem) FileContent(path string, matcher *TextMatcher) {
self.assertWithRetries(func() (bool, string) {
_, err := os.Stat(path)
if os.IsNotExist(err) {

View File

@ -0,0 +1,58 @@
package components
import (
"fmt"
)
type IntMatcher struct {
*Matcher[int]
}
func (self *IntMatcher) EqualsInt(target int) *IntMatcher {
self.appendRule(matcherRule[int]{
name: fmt.Sprintf("equals '%d'", target),
testFn: func(value int) (bool, string) {
return value == target, fmt.Sprintf("Expected '%d' to equal '%d'", value, target)
},
})
return self
}
func (self *IntMatcher) GreaterThan(target int) *IntMatcher {
self.appendRule(matcherRule[int]{
name: fmt.Sprintf("greater than '%d'", target),
testFn: func(value int) (bool, string) {
return value > target, fmt.Sprintf("Expected '%d' to greater than '%d'", value, target)
},
})
return self
}
func (self *IntMatcher) LessThan(target int) *IntMatcher {
self.appendRule(matcherRule[int]{
name: fmt.Sprintf("less than '%d'", target),
testFn: func(value int) (bool, string) {
return value < target, fmt.Sprintf("Expected '%d' to less than '%d'", value, target)
},
})
return self
}
func AnyInt() *IntMatcher {
return &IntMatcher{Matcher: &Matcher[int]{}}
}
func EqualsInt(target int) *IntMatcher {
return AnyInt().EqualsInt(target)
}
func GreaterThan(target int) *IntMatcher {
return AnyInt().GreaterThan(target)
}
func LessThan(target int) *IntMatcher {
return AnyInt().LessThan(target)
}

View File

@ -1,46 +1,39 @@
package components
import (
"fmt"
"regexp"
"strings"
"github.com/samber/lo"
)
// for making assertions on string values
type Matcher struct {
rules []matcherRule
type Matcher[T any] struct {
rules []matcherRule[T]
// this is printed when there's an error so that it's clear what the context of the assertion is
prefix string
}
type matcherRule struct {
type matcherRule[T any] struct {
// e.g. "contains 'foo'"
name string
// returns a bool that says whether the test passed and if it returns false, it
// also returns a string of the error message
testFn func(string) (bool, string)
testFn func(T) (bool, string)
}
func NewMatcher(name string, testFn func(string) (bool, string)) *Matcher {
rules := []matcherRule{{name: name, testFn: testFn}}
return &Matcher{rules: rules}
}
func (self *Matcher) name() string {
func (self *Matcher[T]) name() string {
if len(self.rules) == 0 {
return "anything"
}
return strings.Join(
lo.Map(self.rules, func(rule matcherRule, _ int) string { return rule.name }),
lo.Map(self.rules, func(rule matcherRule[T], _ int) string { return rule.name }),
", ",
)
}
func (self *Matcher) test(value string) (bool, string) {
func (self *Matcher[T]) test(value T) (bool, string) {
for _, rule := range self.rules {
ok, message := rule.testFn(value)
if ok {
@ -57,65 +50,7 @@ func (self *Matcher) test(value string) (bool, string) {
return true, ""
}
func (self *Matcher) Contains(target string) *Matcher {
return self.appendRule(matcherRule{
name: fmt.Sprintf("contains '%s'", target),
testFn: func(value string) (bool, string) {
// everything contains the empty string so we unconditionally return true here
if target == "" {
return true, ""
}
return strings.Contains(value, target), fmt.Sprintf("Expected '%s' to be found in '%s'", target, value)
},
})
}
func (self *Matcher) DoesNotContain(target string) *Matcher {
return self.appendRule(matcherRule{
name: fmt.Sprintf("does not contain '%s'", target),
testFn: func(value string) (bool, string) {
return !strings.Contains(value, target), fmt.Sprintf("Expected '%s' to NOT be found in '%s'", target, value)
},
})
}
func (self *Matcher) MatchesRegexp(target string) *Matcher {
return self.appendRule(matcherRule{
name: fmt.Sprintf("matches regular expression '%s'", target),
testFn: func(value string) (bool, string) {
matched, err := regexp.MatchString(target, value)
if err != nil {
return false, fmt.Sprintf("Unexpected error parsing regular expression '%s': %s", target, err.Error())
}
return matched, fmt.Sprintf("Expected '%s' to match regular expression /%s/", value, target)
},
})
}
func (self *Matcher) Equals(target string) *Matcher {
return self.appendRule(matcherRule{
name: fmt.Sprintf("equals '%s'", target),
testFn: func(value string) (bool, string) {
return target == value, fmt.Sprintf("Expected '%s' to equal '%s'", value, target)
},
})
}
const IS_SELECTED_RULE_NAME = "is selected"
// special rule that is only to be used in the TopLines and Lines methods, as a way of
// asserting that a given line is selected.
func (self *Matcher) IsSelected() *Matcher {
return self.appendRule(matcherRule{
name: IS_SELECTED_RULE_NAME,
testFn: func(value string) (bool, string) {
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.")
},
})
}
func (self *Matcher) appendRule(rule matcherRule) *Matcher {
func (self *Matcher[T]) appendRule(rule matcherRule[T]) *Matcher[T] {
self.rules = append(self.rules, rule)
return self
@ -123,43 +58,8 @@ func (self *Matcher) appendRule(rule matcherRule) *Matcher {
// adds context so that if the matcher test(s) fails, we understand what we were trying to test.
// E.g. prefix: "Unexpected content in view 'files'."
func (self *Matcher) context(prefix string) *Matcher {
func (self *Matcher[T]) context(prefix string) *Matcher[T] {
self.prefix = prefix
return self
}
// if the matcher has an `IsSelected` rule, it returns true, along with the matcher after that rule has been removed
func (self *Matcher) checkIsSelected() (bool, *Matcher) {
// copying into a new matcher in case we want to re-use the original later
newMatcher := &Matcher{}
*newMatcher = *self
check := lo.ContainsBy(newMatcher.rules, func(rule matcherRule) bool { return rule.name == IS_SELECTED_RULE_NAME })
newMatcher.rules = lo.Filter(newMatcher.rules, func(rule matcherRule, _ int) bool { return rule.name != IS_SELECTED_RULE_NAME })
return check, newMatcher
}
// this matcher has no rules meaning it always passes the test. Use this
// when you don't care what value you're dealing with.
func Anything() *Matcher {
return &Matcher{}
}
func Contains(target string) *Matcher {
return Anything().Contains(target)
}
func DoesNotContain(target string) *Matcher {
return Anything().DoesNotContain(target)
}
func MatchesRegexp(target string) *Matcher {
return Anything().MatchesRegexp(target)
}
func Equals(target string) *Matcher {
return Anything().Equals(target)
}

View File

@ -10,7 +10,7 @@ func (self *MenuDriver) getViewDriver() *ViewDriver {
}
// asserts that the popup has the expected title
func (self *MenuDriver) Title(expected *Matcher) *MenuDriver {
func (self *MenuDriver) Title(expected *TextMatcher) *MenuDriver {
self.getViewDriver().Title(expected)
self.hasCheckedTitle = true
@ -30,19 +30,19 @@ func (self *MenuDriver) Cancel() {
self.getViewDriver().PressEscape()
}
func (self *MenuDriver) Select(option *Matcher) *MenuDriver {
func (self *MenuDriver) Select(option *TextMatcher) *MenuDriver {
self.getViewDriver().NavigateToLine(option)
return self
}
func (self *MenuDriver) Lines(matchers ...*Matcher) *MenuDriver {
func (self *MenuDriver) Lines(matchers ...*TextMatcher) *MenuDriver {
self.getViewDriver().Lines(matchers...)
return self
}
func (self *MenuDriver) TopLines(matchers ...*Matcher) *MenuDriver {
func (self *MenuDriver) TopLines(matchers ...*TextMatcher) *MenuDriver {
self.getViewDriver().TopLines(matchers...)
return self

View File

@ -10,7 +10,7 @@ func (self *PromptDriver) getViewDriver() *ViewDriver {
}
// asserts that the popup has the expected title
func (self *PromptDriver) Title(expected *Matcher) *PromptDriver {
func (self *PromptDriver) Title(expected *TextMatcher) *PromptDriver {
self.getViewDriver().Title(expected)
self.hasCheckedTitle = true
@ -19,7 +19,7 @@ func (self *PromptDriver) Title(expected *Matcher) *PromptDriver {
}
// asserts on the text initially present in the prompt
func (self *PromptDriver) InitialText(expected *Matcher) *PromptDriver {
func (self *PromptDriver) InitialText(expected *TextMatcher) *PromptDriver {
self.getViewDriver().Content(expected)
return self
@ -55,13 +55,13 @@ func (self *PromptDriver) checkNecessaryChecksCompleted() {
}
}
func (self *PromptDriver) SuggestionLines(matchers ...*Matcher) *PromptDriver {
func (self *PromptDriver) SuggestionLines(matchers ...*TextMatcher) *PromptDriver {
self.t.Views().Suggestions().Lines(matchers...)
return self
}
func (self *PromptDriver) SuggestionTopLines(matchers ...*Matcher) *PromptDriver {
func (self *PromptDriver) SuggestionTopLines(matchers ...*TextMatcher) *PromptDriver {
self.t.Views().Suggestions().TopLines(matchers...)
return self
@ -75,7 +75,7 @@ func (self *PromptDriver) ConfirmFirstSuggestion() {
PressEnter()
}
func (self *PromptDriver) ConfirmSuggestion(matcher *Matcher) {
func (self *PromptDriver) ConfirmSuggestion(matcher *TextMatcher) {
self.t.press(self.t.keys.Universal.TogglePanel)
self.t.Views().Suggestions().
IsFocused().

View File

@ -12,7 +12,7 @@ func (self *SearchDriver) getViewDriver() *ViewDriver {
}
// asserts on the text initially present in the prompt
func (self *SearchDriver) InitialText(expected *Matcher) *SearchDriver {
func (self *SearchDriver) InitialText(expected *TextMatcher) *SearchDriver {
self.getViewDriver().Content(expected)
return self

View File

@ -80,11 +80,11 @@ func (self *TestDriver) ExpectPopup() *Popup {
return &Popup{t: self}
}
func (self *TestDriver) ExpectToast(matcher *Matcher) {
func (self *TestDriver) ExpectToast(matcher *TextMatcher) {
self.Views().AppStatus().Content(matcher)
}
func (self *TestDriver) ExpectClipboard(matcher *Matcher) {
func (self *TestDriver) ExpectClipboard(matcher *TextMatcher) {
self.assertWithRetries(func() (bool, string) {
text, err := clipboard.ReadAll()
if err != nil {

View File

@ -0,0 +1,116 @@
package components
import (
"fmt"
"regexp"
"strings"
"github.com/samber/lo"
)
type TextMatcher struct {
*Matcher[string]
}
func (self *TextMatcher) Contains(target string) *TextMatcher {
self.appendRule(matcherRule[string]{
name: fmt.Sprintf("contains '%s'", target),
testFn: func(value string) (bool, string) {
// everything contains the empty string so we unconditionally return true here
if target == "" {
return true, ""
}
return strings.Contains(value, target), fmt.Sprintf("Expected '%s' to be found in '%s'", target, value)
},
})
return self
}
func (self *TextMatcher) DoesNotContain(target string) *TextMatcher {
self.appendRule(matcherRule[string]{
name: fmt.Sprintf("does not contain '%s'", target),
testFn: func(value string) (bool, string) {
return !strings.Contains(value, target), fmt.Sprintf("Expected '%s' to NOT be found in '%s'", target, value)
},
})
return self
}
func (self *TextMatcher) MatchesRegexp(target string) *TextMatcher {
self.appendRule(matcherRule[string]{
name: fmt.Sprintf("matches regular expression '%s'", target),
testFn: func(value string) (bool, string) {
matched, err := regexp.MatchString(target, value)
if err != nil {
return false, fmt.Sprintf("Unexpected error parsing regular expression '%s': %s", target, err.Error())
}
return matched, fmt.Sprintf("Expected '%s' to match regular expression /%s/", value, target)
},
})
return self
}
func (self *TextMatcher) Equals(target string) *TextMatcher {
self.appendRule(matcherRule[string]{
name: fmt.Sprintf("equals '%s'", target),
testFn: func(value string) (bool, string) {
return target == value, fmt.Sprintf("Expected '%s' to equal '%s'", value, target)
},
})
return self
}
const IS_SELECTED_RULE_NAME = "is selected"
// special rule that is only to be used in the TopLines and Lines methods, as a way of
// asserting that a given line is selected.
func (self *TextMatcher) IsSelected() *TextMatcher {
self.appendRule(matcherRule[string]{
name: IS_SELECTED_RULE_NAME,
testFn: func(value string) (bool, string) {
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.")
},
})
return self
}
// if the matcher has an `IsSelected` rule, it returns true, along with the matcher after that rule has been removed
func (self *TextMatcher) checkIsSelected() (bool, *TextMatcher) {
// copying into a new matcher in case we want to re-use the original later
newMatcher := &TextMatcher{}
*newMatcher = *self
check := lo.ContainsBy(newMatcher.rules, func(rule matcherRule[string]) bool { return rule.name == IS_SELECTED_RULE_NAME })
newMatcher.rules = lo.Filter(newMatcher.rules, func(rule matcherRule[string], _ int) bool { return rule.name != IS_SELECTED_RULE_NAME })
return check, newMatcher
}
// this matcher has no rules meaning it always passes the test. Use this
// when you don't care what value you're dealing with.
func AnyString() *TextMatcher {
return &TextMatcher{Matcher: &Matcher[string]{}}
}
func Contains(target string) *TextMatcher {
return AnyString().Contains(target)
}
func DoesNotContain(target string) *TextMatcher {
return AnyString().DoesNotContain(target)
}
func MatchesRegexp(target string) *TextMatcher {
return AnyString().MatchesRegexp(target)
}
func Equals(target string) *TextMatcher {
return AnyString().Equals(target)
}

View File

@ -52,7 +52,7 @@ func (self *ViewDriver) getSelectedLineIdx() (int, error) {
}
// asserts that the view has the expected title
func (self *ViewDriver) Title(expected *Matcher) *ViewDriver {
func (self *ViewDriver) Title(expected *TextMatcher) *ViewDriver {
self.t.assertWithRetries(func() (bool, string) {
actual := self.getView().Title
return expected.context(fmt.Sprintf("%s title", self.context)).test(actual)
@ -64,7 +64,7 @@ func (self *ViewDriver) Title(expected *Matcher) *ViewDriver {
// asserts that the view has lines matching the given matchers. One matcher must be passed for each line.
// If you only care about the top n lines, use the TopLines method instead.
// If you only care about a subset of lines, use the ContainsLines method instead.
func (self *ViewDriver) Lines(matchers ...*Matcher) *ViewDriver {
func (self *ViewDriver) Lines(matchers ...*TextMatcher) *ViewDriver {
self.validateMatchersPassed(matchers)
self.LineCount(len(matchers))
@ -75,7 +75,7 @@ func (self *ViewDriver) Lines(matchers ...*Matcher) *ViewDriver {
// are passed, we only check the first three lines of the view.
// This method is convenient when you have a list of commits but you only want to
// assert on the first couple of commits.
func (self *ViewDriver) TopLines(matchers ...*Matcher) *ViewDriver {
func (self *ViewDriver) TopLines(matchers ...*TextMatcher) *ViewDriver {
self.validateMatchersPassed(matchers)
self.validateEnoughLines(matchers)
@ -83,7 +83,7 @@ func (self *ViewDriver) TopLines(matchers ...*Matcher) *ViewDriver {
}
// asserts that somewhere in the view there are consequetive lines matching the given matchers.
func (self *ViewDriver) ContainsLines(matchers ...*Matcher) *ViewDriver {
func (self *ViewDriver) ContainsLines(matchers ...*TextMatcher) *ViewDriver {
self.validateMatchersPassed(matchers)
self.validateEnoughLines(matchers)
@ -162,7 +162,7 @@ func (self *ViewDriver) DoesNotContainColoredText(fgColorStr string, text string
}
// asserts on the lines that are selected in the view. Don't use the `IsSelected` matcher with this because it's redundant.
func (self *ViewDriver) SelectedLines(matchers ...*Matcher) *ViewDriver {
func (self *ViewDriver) SelectedLines(matchers ...*TextMatcher) *ViewDriver {
self.validateMatchersPassed(matchers)
self.validateEnoughLines(matchers)
@ -197,13 +197,13 @@ func (self *ViewDriver) SelectedLines(matchers ...*Matcher) *ViewDriver {
return self
}
func (self *ViewDriver) validateMatchersPassed(matchers []*Matcher) {
func (self *ViewDriver) validateMatchersPassed(matchers []*TextMatcher) {
if len(matchers) < 1 {
self.t.fail("'Lines' methods require at least one matcher to be passed as an argument. If you are trying to assert that there are no lines, use .IsEmpty()")
}
}
func (self *ViewDriver) validateEnoughLines(matchers []*Matcher) {
func (self *ViewDriver) validateEnoughLines(matchers []*TextMatcher) {
view := self.getView()
self.t.assertWithRetries(func() (bool, string) {
@ -212,7 +212,7 @@ func (self *ViewDriver) validateEnoughLines(matchers []*Matcher) {
})
}
func (self *ViewDriver) assertLines(offset int, matchers ...*Matcher) *ViewDriver {
func (self *ViewDriver) assertLines(offset int, matchers ...*TextMatcher) *ViewDriver {
view := self.getView()
for matcherIndex, matcher := range matchers {
@ -252,7 +252,7 @@ func (self *ViewDriver) assertLines(offset int, matchers ...*Matcher) *ViewDrive
}
// asserts on the content of the view i.e. the stuff within the view's frame.
func (self *ViewDriver) Content(matcher *Matcher) *ViewDriver {
func (self *ViewDriver) Content(matcher *TextMatcher) *ViewDriver {
self.t.matchString(matcher, fmt.Sprintf("%s: Unexpected content.", self.context),
func() string {
return self.getView().Buffer()
@ -265,7 +265,7 @@ func (self *ViewDriver) Content(matcher *Matcher) *ViewDriver {
// asserts on the selected line of the view. If your view has multiple lines selected,
// but also has a concept of a cursor position, this will assert on the line that
// the cursor is on. Otherwise it will assert on the first line of the selection.
func (self *ViewDriver) SelectedLine(matcher *Matcher) *ViewDriver {
func (self *ViewDriver) SelectedLine(matcher *TextMatcher) *ViewDriver {
self.t.assertWithRetries(func() (bool, string) {
selectedLineIdx, err := self.getSelectedLineIdx()
if err != nil {
@ -410,7 +410,7 @@ func (self *ViewDriver) PressEscape() *ViewDriver {
// If this changes in future, we'll need to update this code to first attempt to find the item
// in the current page and failing that, jump to the top of the view and iterate through all of it,
// looking for the item.
func (self *ViewDriver) NavigateToLine(matcher *Matcher) *ViewDriver {
func (self *ViewDriver) NavigateToLine(matcher *TextMatcher) *ViewDriver {
self.IsFocused()
view := self.getView()
@ -510,8 +510,8 @@ func (self *ViewDriver) Self() *ViewDriver {
return self
}
func expectedContentFromMatchers(matchers []*Matcher) string {
return strings.Join(lo.Map(matchers, func(matcher *Matcher, _ int) string {
func expectedContentFromMatchers(matchers []*TextMatcher) string {
return strings.Join(lo.Map(matchers, func(matcher *TextMatcher, _ int) string {
return matcher.name()
}), "\n")
}

View File

@ -19,8 +19,8 @@ var RemoteNamedStar = NewIntegrationTest(NewIntegrationTestArgs{
// here we're just asserting that we haven't panicked upon starting lazygit
t.Views().Commits().
Lines(
Anything(),
Anything(),
AnyString(),
AnyString(),
)
},
})

View File

@ -28,6 +28,12 @@ var Patch = NewIntegrationTest(NewIntegrationTestArgs{
Contains("commit (initial): one"),
).
SelectNextItem().
Lines(
Contains("reset: moving to HEAD^^"),
Contains("commit: three").IsSelected(),
Contains("commit: two"),
Contains("commit (initial): one"),
).
PressEnter()
t.Views().SubCommits().