mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-08-07 22:02:56 +03:00
Better assertion logic for line selection
Previously if we marked a line with IsSelected() we would check if it was selected, but we would not check if other lines were unexpectedly selected. Now, if you use IsSelected(), we ensure that _only_ the lines you marked as such are the selected lines.
This commit is contained in:
@@ -9,6 +9,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TextMatcher struct {
|
type TextMatcher struct {
|
||||||
|
// If you add or change a field here, be sure to update the copy
|
||||||
|
// code in checkIsSelected()
|
||||||
*Matcher[string]
|
*Matcher[string]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,8 +97,8 @@ func (self *TextMatcher) IsSelected() *TextMatcher {
|
|||||||
// if the matcher has an `IsSelected` rule, it returns true, along with the matcher after that rule has been removed
|
// 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) {
|
func (self *TextMatcher) checkIsSelected() (bool, *TextMatcher) {
|
||||||
// copying into a new matcher in case we want to re-use the original later
|
// copying into a new matcher in case we want to re-use the original later
|
||||||
newMatcher := &TextMatcher{}
|
newMatcher := &TextMatcher{Matcher: &Matcher[string]{}}
|
||||||
*newMatcher = *self
|
*newMatcher.Matcher = *self.Matcher
|
||||||
|
|
||||||
check := lo.ContainsBy(newMatcher.rules, func(rule matcherRule[string]) bool { return rule.name == IS_SELECTED_RULE_NAME })
|
check := lo.ContainsBy(newMatcher.rules, func(rule matcherRule[string]) bool { return rule.name == IS_SELECTED_RULE_NAME })
|
||||||
|
|
||||||
|
@@ -211,29 +211,63 @@ func (self *ViewDriver) validateVisibleLineCount(matchers []*TextMatcher) {
|
|||||||
func (self *ViewDriver) assertLines(offset int, matchers ...*TextMatcher) *ViewDriver {
|
func (self *ViewDriver) assertLines(offset int, matchers ...*TextMatcher) *ViewDriver {
|
||||||
view := self.getView()
|
view := self.getView()
|
||||||
|
|
||||||
|
var expectedStartIdx, expectedEndIdx int
|
||||||
|
foundSelectionStart := false
|
||||||
|
foundSelectionEnd := false
|
||||||
|
expectedSelectedLines := []string{}
|
||||||
|
|
||||||
for matcherIndex, matcher := range matchers {
|
for matcherIndex, matcher := range matchers {
|
||||||
lineIdx := matcherIndex + offset
|
lineIdx := matcherIndex + offset
|
||||||
|
|
||||||
checkIsSelected, matcher := matcher.checkIsSelected()
|
checkIsSelected, matcher := matcher.checkIsSelected()
|
||||||
|
|
||||||
|
if checkIsSelected {
|
||||||
|
if foundSelectionEnd {
|
||||||
|
self.t.fail("The IsSelected matcher can only be used on a contiguous range of lines.")
|
||||||
|
}
|
||||||
|
if !foundSelectionStart {
|
||||||
|
expectedStartIdx = lineIdx
|
||||||
|
foundSelectionStart = true
|
||||||
|
}
|
||||||
|
expectedSelectedLines = append(expectedSelectedLines, matcher.name())
|
||||||
|
expectedEndIdx = lineIdx
|
||||||
|
} else if foundSelectionStart {
|
||||||
|
foundSelectionEnd = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for matcherIndex, matcher := range matchers {
|
||||||
|
lineIdx := matcherIndex + offset
|
||||||
|
expectSelected, matcher := matcher.checkIsSelected()
|
||||||
|
|
||||||
self.t.matchString(matcher, fmt.Sprintf("Unexpected content in view '%s'.", view.Name()),
|
self.t.matchString(matcher, fmt.Sprintf("Unexpected content in view '%s'.", view.Name()),
|
||||||
func() string {
|
func() string {
|
||||||
return view.BufferLines()[lineIdx]
|
return view.BufferLines()[lineIdx]
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if checkIsSelected {
|
// If any of the matchers care about the selection, we need to
|
||||||
|
// assert on the selection for each matcher.
|
||||||
|
if foundSelectionStart {
|
||||||
self.t.assertWithRetries(func() (bool, string) {
|
self.t.assertWithRetries(func() (bool, string) {
|
||||||
startIdx, endIdx := self.getSelectedRange()
|
startIdx, endIdx := self.getSelectedRange()
|
||||||
|
|
||||||
if lineIdx < startIdx || lineIdx > endIdx {
|
selected := lineIdx >= startIdx && lineIdx <= endIdx
|
||||||
if startIdx == endIdx {
|
|
||||||
return false, fmt.Sprintf("Unexpected selected line index in view '%s'. Expected %d, got %d", view.Name(), lineIdx, startIdx)
|
if (selected && expectSelected) || (!selected && !expectSelected) {
|
||||||
} else {
|
|
||||||
lines := self.getSelectedLines()
|
|
||||||
return false, fmt.Sprintf("Unexpected selected line index in view '%s'. Expected line %d to be in range %d to %d. Selected lines:\n---\n%s\n---\n\nExpected line: '%s'", view.Name(), lineIdx, startIdx, endIdx, strings.Join(lines, "\n"), matcher.name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true, ""
|
return true, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := self.getSelectedLines()
|
||||||
|
|
||||||
|
return false, fmt.Sprintf(
|
||||||
|
"Unexpected selection in view '%s'. Expected %s to be selected but got %s.\nExpected selected lines:\n---\n%s\n---\n\nActual selected lines:\n---\n%s\n---\n",
|
||||||
|
view.Name(),
|
||||||
|
formatLineRange(startIdx, endIdx),
|
||||||
|
formatLineRange(expectedStartIdx, expectedEndIdx),
|
||||||
|
strings.Join(lines, "\n"),
|
||||||
|
strings.Join(expectedSelectedLines, "\n"),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,6 +275,14 @@ func (self *ViewDriver) assertLines(offset int, matchers ...*TextMatcher) *ViewD
|
|||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func formatLineRange(from int, to int) string {
|
||||||
|
if from == to {
|
||||||
|
return "line " + fmt.Sprintf("%d", from)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "lines " + fmt.Sprintf("%d-%d", from, to)
|
||||||
|
}
|
||||||
|
|
||||||
// asserts on the content of the view i.e. the stuff within the view's frame.
|
// asserts on the content of the view i.e. the stuff within the view's frame.
|
||||||
func (self *ViewDriver) Content(matcher *TextMatcher) *ViewDriver {
|
func (self *ViewDriver) Content(matcher *TextMatcher) *ViewDriver {
|
||||||
self.t.matchString(matcher, fmt.Sprintf("%s: Unexpected content.", self.context),
|
self.t.matchString(matcher, fmt.Sprintf("%s: Unexpected content.", self.context),
|
||||||
|
@@ -161,6 +161,7 @@ var tests = []*components.IntegrationTest{
|
|||||||
interactive_rebase.EditTheConflCommit,
|
interactive_rebase.EditTheConflCommit,
|
||||||
interactive_rebase.FixupFirstCommit,
|
interactive_rebase.FixupFirstCommit,
|
||||||
interactive_rebase.FixupSecondCommit,
|
interactive_rebase.FixupSecondCommit,
|
||||||
|
interactive_rebase.MidRebaseRangeSelect,
|
||||||
interactive_rebase.Move,
|
interactive_rebase.Move,
|
||||||
interactive_rebase.MoveInRebase,
|
interactive_rebase.MoveInRebase,
|
||||||
interactive_rebase.MoveWithCustomCommentChar,
|
interactive_rebase.MoveWithCustomCommentChar,
|
||||||
|
Reference in New Issue
Block a user