1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-26 05:37:18 +02:00

Merge pull request #2474 from jesseduffield/improve-staging-tests

This commit is contained in:
Jesse Duffield 2023-02-25 11:45:29 +11:00 committed by GitHub
commit 8a69d994c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 418 additions and 116 deletions

3
go.mod
View File

@ -18,7 +18,7 @@ require (
github.com/integrii/flaggy v1.4.0 github.com/integrii/flaggy v1.4.0
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d
github.com/jesseduffield/gocui v0.3.1-0.20230219034834-06a1f1e95da5 github.com/jesseduffield/gocui v0.3.1-0.20230225001450-38a4deaa7f24
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5
github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e github.com/jesseduffield/minimal/gitignore v0.3.3-0.20211018110810-9cde264e6b1e
@ -46,7 +46,6 @@ require (
github.com/emirpasic/gods v1.12.0 // indirect github.com/emirpasic/gods v1.12.0 // indirect
github.com/fatih/color v1.9.0 // indirect github.com/fatih/color v1.9.0 // indirect
github.com/gdamore/encoding v1.0.0 // indirect github.com/gdamore/encoding v1.0.0 // indirect
github.com/gdamore/tcell v1.4.0 // indirect
github.com/go-git/gcfg v1.5.0 // indirect github.com/go-git/gcfg v1.5.0 // indirect
github.com/go-git/go-billy/v5 v5.0.0 // indirect github.com/go-git/go-billy/v5 v5.0.0 // indirect
github.com/go-logfmt/logfmt v0.5.0 // indirect github.com/go-logfmt/logfmt v0.5.0 // indirect

12
go.sum
View File

@ -34,11 +34,7 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/gdamore/tcell v1.4.0 h1:vUnHwJRvcPQa3tzi+0QI4U9JINXYJlOz9yiaiPQ2wMU=
github.com/gdamore/tcell v1.4.0/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0=
github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU= github.com/gdamore/tcell/v2 v2.4.0/go.mod h1:cTTuF84Dlj/RqmaCIV5p4w8uG1zWdk0SF6oBpwHp4fU=
github.com/gdamore/tcell/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/k=
github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw=
github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg= github.com/gdamore/tcell/v2 v2.6.0 h1:OKbluoP9VYmJwZwq/iLb4BxwKcwGthaa1YNBJIyCySg=
github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y= github.com/gdamore/tcell/v2 v2.6.0/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y=
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
@ -76,8 +72,8 @@ github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 h1:EQP2Tv8T
github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk= github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68/go.mod h1:+LLj9/WUPAP8LqCchs7P+7X0R98HiFujVFANdNaxhGk=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE= github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d h1:bO+OmbreIv91rCe8NmscRwhFSqkDJtzWCPV4Y+SQuXE=
github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o= github.com/jesseduffield/go-git/v5 v5.1.2-0.20221018185014-fdd53fef665d/go.mod h1:nGNEErzf+NRznT+N2SWqmHnDnF9aLgANB1CUNEan09o=
github.com/jesseduffield/gocui v0.3.1-0.20230219034834-06a1f1e95da5 h1:6mrOZa9I1bol14HhVK0tl4w9xvvGKLWPmShPRey1Lxg= github.com/jesseduffield/gocui v0.3.1-0.20230225001450-38a4deaa7f24 h1:1uSYSN8np7ym5IjuGi2hHVp/9GQebLbApfz7vudzaSM=
github.com/jesseduffield/gocui v0.3.1-0.20230219034834-06a1f1e95da5/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU= github.com/jesseduffield/gocui v0.3.1-0.20230225001450-38a4deaa7f24/go.mod h1:znJuCDnF2Ph40YZSlBwdX/4GEofnIoWLGdT4mK5zRAU=
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0= github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 h1:jmpr7KpX2+2GRiE91zTgfq49QvgiqB0nbmlwZ8UnOx0=
github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo= github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo=
github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY= github.com/jesseduffield/lazycore v0.0.0-20221012050358-03d2e40243c5 h1:CDuQmfOjAtb1Gms6a1p5L2P8RhbLUq5t8aL7PiQd2uY=
@ -117,7 +113,6 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
@ -141,7 +136,6 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw=
github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@ -203,7 +197,6 @@ golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -224,7 +217,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -27,7 +27,9 @@ func GetHunksFromDiff(diff string) []*PatchHunk {
var hunkLines []string //nolint:prealloc var hunkLines []string //nolint:prealloc
pastDiffHeader := false pastDiffHeader := false
for lineIdx, line := range strings.SplitAfter(diff, "\n") { lines := strings.SplitAfter(diff, "\n")
for lineIdx, line := range lines {
isHunkHeader := strings.HasPrefix(line, "@@ -") isHunkHeader := strings.HasPrefix(line, "@@ -")
if isHunkHeader { if isHunkHeader {
@ -44,6 +46,10 @@ func GetHunksFromDiff(diff string) []*PatchHunk {
continue continue
} }
if lineIdx == len(lines)-1 && line == "" { // skip the trailing newline
continue
}
hunkLines = append(hunkLines, line) hunkLines = append(hunkLines, line)
} }

View File

@ -134,7 +134,8 @@ func coloredString(textStyle style.TextStyle, str string, selected bool, include
} }
func parsePatch(patch string) ([]int, []int, []*PatchLine) { func parsePatch(patch string) ([]int, []int, []*PatchLine) {
lines := strings.Split(patch, "\n") // ignore trailing newline.
lines := strings.Split(strings.TrimSuffix(patch, "\n"), "\n")
hunkStarts := []int{} hunkStarts := []int{}
stageableLines := []int{} stageableLines := []int{}
pastFirstHunkHeader := false pastFirstHunkHeader := false
@ -179,6 +180,7 @@ func parsePatch(patch string) ([]int, []int, []*PatchLine) {
} }
patchLines[index] = &PatchLine{Kind: lineKind, Content: line} patchLines[index] = &PatchLine{Kind: lineKind, Content: line}
} }
return hunkStarts, stageableLines, patchLines return hunkStarts, stageableLines, patchLines
} }

View File

@ -119,6 +119,7 @@ var conflictStrings = []string{
"When you have resolved this problem", "When you have resolved this problem",
"fix conflicts", "fix conflicts",
"Resolve all conflicts manually", "Resolve all conflicts manually",
"Merge conflict in file",
} }
func isMergeConflictErr(errStr string) bool { func isMergeConflictErr(errStr string) bool {

View File

@ -49,6 +49,15 @@ func (self *GuiDriver) CurrentContext() types.Context {
return self.gui.c.CurrentContext() return self.gui.c.CurrentContext()
} }
func (self *GuiDriver) ContextForView(viewName string) types.Context {
context, ok := self.gui.contextForView(viewName)
if !ok {
return nil
}
return context
}
func (self *GuiDriver) Fail(message string) { func (self *GuiDriver) Fail(message string) {
currentView := self.gui.g.CurrentView() currentView := self.gui.g.CurrentView()
fullMessage := fmt.Sprintf( fullMessage := fmt.Sprintf(

View File

@ -61,6 +61,11 @@ func (self *matcher) Contains(target string) *matcher {
return self.appendRule(matcherRule{ return self.appendRule(matcherRule{
name: fmt.Sprintf("contains '%s'", target), name: fmt.Sprintf("contains '%s'", target),
testFn: func(value string) (bool, string) { 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 strings.Contains(value, target), fmt.Sprintf("Expected '%s' to be found in '%s'", target, value)
}, },
}) })

View File

@ -7,7 +7,6 @@ import (
"github.com/atotto/clipboard" "github.com/atotto/clipboard"
"github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/types"
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
) )
@ -84,9 +83,7 @@ func (self *TestDriver) Shell() *Shell {
// in the current page and failing that, jump to the top of the view and iterate through all of it, // in the current page and failing that, jump to the top of the view and iterate through all of it,
// looking for the item. // looking for the item.
func (self *TestDriver) navigateToListItem(matcher *matcher) { func (self *TestDriver) navigateToListItem(matcher *matcher) {
self.inListContext() currentContext := self.gui.CurrentContext()
currentContext := self.gui.CurrentContext().(types.IListContext)
view := currentContext.GetView() view := currentContext.GetView()
@ -133,14 +130,6 @@ func (self *TestDriver) navigateToListItem(matcher *matcher) {
} }
} }
func (self *TestDriver) inListContext() {
self.assertWithRetries(func() (bool, string) {
currentContext := self.gui.CurrentContext()
_, ok := currentContext.(types.IListContext)
return ok, fmt.Sprintf("Expected current context to be a list context, but got %s", currentContext.GetKey())
})
}
// for making assertions on lazygit views // for making assertions on lazygit views
func (self *TestDriver) Views() *Views { func (self *TestDriver) Views() *Views {
return &Views{t: self} return &Views{t: self}

View File

@ -30,6 +30,10 @@ func (self *fakeGuiDriver) CurrentContext() types.Context {
return nil return nil
} }
func (self *fakeGuiDriver) ContextForView(viewName string) types.Context {
return nil
}
func (self *fakeGuiDriver) Fail(message string) { func (self *fakeGuiDriver) Fail(message string) {
self.failureMessage = message self.failureMessage = message
} }

View File

@ -10,9 +10,10 @@ import (
type ViewDriver struct { type ViewDriver struct {
// context is prepended to any error messages e.g. 'context: "current view"' // context is prepended to any error messages e.g. 'context: "current view"'
context string context string
getView func() *gocui.View getView func() *gocui.View
t *TestDriver t *TestDriver
getSelectedLinesFn func() ([]string, error)
} }
// asserts that the view has the expected title // asserts that the view has the expected title
@ -50,6 +51,74 @@ func (self *ViewDriver) Lines(matchers ...*matcher) *ViewDriver {
return self.assertLines(matchers...) return self.assertLines(matchers...)
} }
func (self *ViewDriver) getSelectedLines() ([]string, error) {
if self.getSelectedLinesFn == nil {
view := self.t.gui.View(self.getView().Name())
return []string{view.SelectedLine()}, nil
}
return self.getSelectedLinesFn()
}
func (self *ViewDriver) SelectedLines(matchers ...*matcher) *ViewDriver {
self.t.assertWithRetries(func() (bool, string) {
selectedLines, err := self.getSelectedLines()
if err != nil {
return false, err.Error()
}
selectedContent := strings.Join(selectedLines, "\n")
expectedContent := expectedContentFromMatchers(matchers)
if len(selectedLines) != len(matchers) {
return false, fmt.Sprintf("Expected the following to be selected:\n-----\n%s\n-----\nBut got:\n-----\n%s\n-----", expectedContent, selectedContent)
}
for i, line := range selectedLines {
ok, message := matchers[i].test(line)
if !ok {
return false, fmt.Sprintf("Error: %s. Expected the following to be selected:\n-----\n%s\n-----\nBut got:\n-----\n%s\n-----", message, expectedContent, selectedContent)
}
}
return true, ""
})
return self
}
func (self *ViewDriver) ContainsLines(matchers ...*matcher) *ViewDriver {
self.t.assertWithRetries(func() (bool, string) {
content := self.getView().Buffer()
lines := strings.Split(content, "\n")
for i := 0; i < len(lines)-len(matchers)+1; i++ {
matches := true
for j, matcher := range matchers {
ok, _ := matcher.test(lines[i+j])
if !ok {
matches = false
break
}
}
if matches {
return true, ""
}
}
expectedContent := expectedContentFromMatchers(matchers)
return false, fmt.Sprintf(
"Expected the following to be contained in the staging panel:\n-----\n%s\n-----\nBut got:\n-----\n%s\n-----",
expectedContent,
content,
)
})
return self
}
func (self *ViewDriver) assertLines(matchers ...*matcher) *ViewDriver { func (self *ViewDriver) assertLines(matchers ...*matcher) *ViewDriver {
view := self.getView() view := self.getView()
@ -86,9 +155,35 @@ func (self *ViewDriver) Content(matcher *matcher) *ViewDriver {
// asserts on the selected line of the view // asserts on the selected line of the view
func (self *ViewDriver) SelectedLine(matcher *matcher) *ViewDriver { func (self *ViewDriver) SelectedLine(matcher *matcher) *ViewDriver {
self.t.assertWithRetries(func() (bool, string) {
selectedLines, err := self.getSelectedLines()
if err != nil {
return false, err.Error()
}
if len(selectedLines) == 0 {
return false, "No line selected. Expected exactly one line to be selected"
} else if len(selectedLines) > 1 {
return false, fmt.Sprintf(
"Multiple lines selected. Expected only a single line to be selected. Selected lines:\n---\n%s\n---\n\nExpected line: %s",
strings.Join(selectedLines, "\n"),
matcher.name(),
)
}
value := selectedLines[0]
return matcher.context(fmt.Sprintf("%s: Unexpected selected line.", self.context)).test(value)
})
self.t.matchString(matcher, fmt.Sprintf("%s: Unexpected selected line.", self.context), self.t.matchString(matcher, fmt.Sprintf("%s: Unexpected selected line.", self.context),
func() string { func() string {
return self.getView().SelectedLine() selectedLines, err := self.getSelectedLines()
if err != nil {
self.t.gui.Fail(err.Error())
return "<failed to obtain selected line>"
}
return selectedLines[0]
}, },
) )
@ -253,3 +348,9 @@ func (self *ViewDriver) Tap(f func()) *ViewDriver {
return self return self
} }
func expectedContentFromMatchers(matchers []*matcher) string {
return strings.Join(lo.Map(matchers, func(matcher *matcher, _ int) string {
return matcher.name()
}), "\n")
}

View File

@ -2,8 +2,11 @@ package components
import ( import (
"fmt" "fmt"
"strings"
"github.com/go-errors/errors"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui/context"
) )
type Views struct { type Views struct {
@ -36,98 +39,129 @@ func (self *Views) Secondary() *ViewDriver {
} }
} }
func (self *Views) byName(viewName string) *ViewDriver { func (self *Views) regularView(viewName string) *ViewDriver {
return self.newStaticViewDriver(viewName, nil)
}
func (self *Views) patchExplorerViewByName(viewName string) *ViewDriver {
return self.newStaticViewDriver(viewName, func() ([]string, error) {
ctx := self.t.gui.ContextForView(viewName).(*context.PatchExplorerContext)
state := ctx.GetState()
if state == nil {
return nil, errors.New("Expected patch explorer to be activated")
}
selectedContent := state.PlainRenderSelected()
// the above method returns a string with a trailing newline so we need to remove that before splitting
selectedLines := strings.Split(strings.TrimSuffix(selectedContent, "\n"), "\n")
return selectedLines, nil
})
}
// 'static' because it'll always refer to the same view, as opposed to the 'main' view which could actually be
// one of several views, or the 'current' view which depends on focus.
func (self *Views) newStaticViewDriver(viewName string, getSelectedLinesFn func() ([]string, error)) *ViewDriver {
return &ViewDriver{ return &ViewDriver{
context: fmt.Sprintf("%s view", viewName), context: fmt.Sprintf("%s view", viewName),
getView: func() *gocui.View { return self.t.gui.View(viewName) }, getView: func() *gocui.View { return self.t.gui.View(viewName) },
t: self.t, getSelectedLinesFn: getSelectedLinesFn,
t: self.t,
} }
} }
func (self *Views) Commits() *ViewDriver { func (self *Views) Commits() *ViewDriver {
return self.byName("commits") return self.regularView("commits")
} }
func (self *Views) Files() *ViewDriver { func (self *Views) Files() *ViewDriver {
return self.byName("files") return self.regularView("files")
} }
func (self *Views) Status() *ViewDriver { func (self *Views) Status() *ViewDriver {
return self.byName("status") return self.regularView("status")
} }
func (self *Views) Submodules() *ViewDriver { func (self *Views) Submodules() *ViewDriver {
return self.byName("submodules") return self.regularView("submodules")
} }
func (self *Views) Information() *ViewDriver { func (self *Views) Information() *ViewDriver {
return self.byName("information") return self.regularView("information")
} }
func (self *Views) AppStatus() *ViewDriver { func (self *Views) AppStatus() *ViewDriver {
return self.byName("appStatus") return self.regularView("appStatus")
} }
func (self *Views) Branches() *ViewDriver { func (self *Views) Branches() *ViewDriver {
return self.byName("localBranches") return self.regularView("localBranches")
} }
func (self *Views) Remotes() *ViewDriver { func (self *Views) Remotes() *ViewDriver {
return self.byName("remotes") return self.regularView("remotes")
} }
func (self *Views) RemoteBranches() *ViewDriver { func (self *Views) RemoteBranches() *ViewDriver {
return self.byName("remoteBranches") return self.regularView("remoteBranches")
} }
func (self *Views) Tags() *ViewDriver { func (self *Views) Tags() *ViewDriver {
return self.byName("tags") return self.regularView("tags")
} }
func (self *Views) ReflogCommits() *ViewDriver { func (self *Views) ReflogCommits() *ViewDriver {
return self.byName("reflogCommits") return self.regularView("reflogCommits")
} }
func (self *Views) SubCommits() *ViewDriver { func (self *Views) SubCommits() *ViewDriver {
return self.byName("subCommits") return self.regularView("subCommits")
} }
func (self *Views) CommitFiles() *ViewDriver { func (self *Views) CommitFiles() *ViewDriver {
return self.byName("commitFiles") return self.regularView("commitFiles")
} }
func (self *Views) Stash() *ViewDriver { func (self *Views) Stash() *ViewDriver {
return self.byName("stash") return self.regularView("stash")
} }
func (self *Views) Staging() *ViewDriver { func (self *Views) Staging() *ViewDriver {
return self.byName("staging") return self.patchExplorerViewByName("staging")
} }
func (self *Views) StagingSecondary() *ViewDriver { func (self *Views) StagingSecondary() *ViewDriver {
return self.byName("stagingSecondary") return self.patchExplorerViewByName("stagingSecondary")
}
func (self *Views) PatchBuilding() *ViewDriver {
return self.patchExplorerViewByName("patchBuilding")
}
func (self *Views) PatchBuildingSecondary() *ViewDriver {
// this is not a patch explorer view because you can't actually focus it: it
// just renders content
return self.regularView("patchBuildingSecondary")
} }
func (self *Views) Menu() *ViewDriver { func (self *Views) Menu() *ViewDriver {
return self.byName("menu") return self.regularView("menu")
} }
func (self *Views) Confirmation() *ViewDriver { func (self *Views) Confirmation() *ViewDriver {
return self.byName("confirmation") return self.regularView("confirmation")
} }
func (self *Views) CommitMessage() *ViewDriver { func (self *Views) CommitMessage() *ViewDriver {
return self.byName("commitMessage") return self.regularView("commitMessage")
} }
func (self *Views) Suggestions() *ViewDriver { func (self *Views) Suggestions() *ViewDriver {
return self.byName("suggestions") return self.regularView("suggestions")
} }
func (self *Views) MergeConflicts() *ViewDriver { func (self *Views) MergeConflicts() *ViewDriver {
return self.byName("mergeConflicts") return self.regularView("mergeConflicts")
} }
func (self *Views) Search() *ViewDriver { func (self *Views) Search() *ViewDriver {
return self.byName("search") return self.regularView("search")
} }

View File

@ -50,31 +50,80 @@ var DiffContextChange = NewIntegrationTest(NewIntegrationTestArgs{
t.Views().Staging(). t.Views().Staging().
IsFocused(). IsFocused().
Content(Contains("@@ -1,6 +1,6 @@").DoesNotContain(" 7a")). Press(keys.Main.ToggleSelectHunk).
SelectedLine(Contains("-3a")). SelectedLines(
Contains(`@@ -1,6 +1,6 @@`),
Contains(` 1a`),
Contains(` 2a`),
Contains(`-3a`),
Contains(`+3b`),
Contains(` 4a`),
Contains(` 5a`),
Contains(` 6a`),
).
Press(keys.Universal.IncreaseContextInDiffView). Press(keys.Universal.IncreaseContextInDiffView).
// still on the same line SelectedLines(
SelectedLine(Contains("-3a")). Contains(`@@ -1,7 +1,7 @@`),
// '7a' is now visible Contains(` 1a`),
Content(Contains("@@ -1,7 +1,7 @@").Contains(" 7a")). Contains(` 2a`),
Contains(`-3a`),
Contains(`+3b`),
Contains(` 4a`),
Contains(` 5a`),
Contains(` 6a`),
Contains(` 7a`),
).
Press(keys.Universal.DecreaseContextInDiffView). Press(keys.Universal.DecreaseContextInDiffView).
SelectedLine(Contains("-3a")). SelectedLines(
Content(Contains("@@ -1,6 +1,6 @@").DoesNotContain(" 7a")). Contains(`@@ -1,6 +1,6 @@`),
Contains(` 1a`),
Contains(` 2a`),
Contains(`-3a`),
Contains(`+3b`),
Contains(` 4a`),
Contains(` 5a`),
Contains(` 6a`),
).
Press(keys.Universal.DecreaseContextInDiffView). Press(keys.Universal.DecreaseContextInDiffView).
SelectedLine(Contains("-3a")). SelectedLines(
Content(Contains("@@ -1,5 +1,5 @@").DoesNotContain(" 6a")). Contains(`@@ -1,5 +1,5 @@`),
Contains(` 1a`),
Contains(` 2a`),
Contains(`-3a`),
Contains(`+3b`),
Contains(` 4a`),
Contains(` 5a`),
).
Press(keys.Universal.DecreaseContextInDiffView). Press(keys.Universal.DecreaseContextInDiffView).
// arguably we should still be on -3a, but at the moment the logic puts us on on +3b SelectedLines(
SelectedLine(Contains("+3b")). Contains(`@@ -2,3 +2,3 @@`),
Content(Contains("@@ -2,3 +2,3 @@").DoesNotContain(" 5a")). Contains(` 2a`),
Contains(`-3a`),
Contains(`+3b`),
Contains(` 4a`),
).
PressPrimaryAction(). PressPrimaryAction().
Content(DoesNotContain("+3b")).
Press(keys.Universal.TogglePanel) Press(keys.Universal.TogglePanel)
t.Views().StagingSecondary(). t.Views().StagingSecondary().
IsFocused(). IsFocused().
Content(Contains("@@ -3,2 +3,3 @@\n 3a\n+3b\n 4a")). Press(keys.Main.ToggleSelectHunk).
SelectedLines(
Contains(`@@ -2,3 +2,3 @@`),
Contains(` 2a`),
Contains(`-3a`),
Contains(`+3b`),
Contains(` 4a`),
).
Press(keys.Universal.IncreaseContextInDiffView). Press(keys.Universal.IncreaseContextInDiffView).
Content(Contains("@@ -2,4 +2,5 @@\n 2a\n 3a\n+3b\n 4a\n 5a")) SelectedLines(
Contains(`@@ -1,5 +1,5 @@`),
Contains(` 1a`),
Contains(` 2a`),
Contains(`-3a`),
Contains(`+3b`),
Contains(` 4a`),
Contains(` 5a`),
)
}, },
}) })

View File

@ -29,13 +29,13 @@ var DiscardAllChanges = NewIntegrationTest(NewIntegrationTestArgs{
t.Views().Staging(). t.Views().Staging().
IsFocused(). IsFocused().
SelectedLine(Contains("+three")). SelectedLines(Contains("+three")).
// discard the line // discard the line
Press(keys.Universal.Remove). Press(keys.Universal.Remove).
Tap(func() { Tap(func() {
t.Actions().ConfirmDiscardLines() t.Actions().ConfirmDiscardLines()
}). }).
SelectedLine(Contains("+four")). SelectedLines(Contains("+four")).
// discard the other line // discard the other line
Press(keys.Universal.Remove). Press(keys.Universal.Remove).
Tap(func() { Tap(func() {
@ -49,6 +49,6 @@ var DiscardAllChanges = NewIntegrationTest(NewIntegrationTestArgs{
}). }).
// assert we are still in the staging panel, but now looking at the changes of the other file // assert we are still in the staging panel, but now looking at the changes of the other file
IsFocused(). IsFocused().
SelectedLine(Contains("+3")) SelectedLines(Contains("+3"))
}, },
}) })

View File

@ -50,42 +50,108 @@ var StageHunks = NewIntegrationTest(NewIntegrationTestArgs{
t.Views().Staging(). t.Views().Staging().
IsFocused(). IsFocused().
SelectedLine(Contains("-3a")). SelectedLines(
Contains("-3a"),
).
Press(keys.Universal.NextBlock). Press(keys.Universal.NextBlock).
SelectedLine(Contains("-13a")). SelectedLines(
Contains("-13a"),
).
Press(keys.Main.ToggleSelectHunk). Press(keys.Main.ToggleSelectHunk).
SelectedLines(
Contains("@@ -10,6 +10,6 @@"),
Contains(" 10a"),
Contains(" 11a"),
Contains(" 12a"),
Contains("-13a"),
Contains("+13b"),
Contains(" 14a"),
Contains(" 15a"),
Contains(`\ No newline at end of file`),
).
// when in hunk mode, pressing up/down moves us up/down by a hunk // when in hunk mode, pressing up/down moves us up/down by a hunk
SelectPreviousItem(). SelectPreviousItem().
SelectedLine(Contains("-3a")). SelectedLines(
Contains(`@@ -1,6 +1,6 @@`),
Contains(` 1a`),
Contains(` 2a`),
Contains(`-3a`),
Contains(`+3b`),
Contains(` 4a`),
Contains(` 5a`),
Contains(` 6a`),
).
SelectNextItem(). SelectNextItem().
SelectedLine(Contains("-13a")). SelectedLines(
Contains("@@ -10,6 +10,6 @@"),
Contains(" 10a"),
Contains(" 11a"),
Contains(" 12a"),
Contains("-13a"),
Contains("+13b"),
Contains(" 14a"),
Contains(" 15a"),
Contains(`\ No newline at end of file`),
).
// stage the second hunk // stage the second hunk
PressPrimaryAction(). PressPrimaryAction().
Content(Contains("-3a\n+3b")). ContainsLines(
Contains("-3a"),
Contains("+3b"),
).
Tap(func() { Tap(func() {
t.Views().StagingSecondary(). t.Views().StagingSecondary().
Content(Contains("-13a\n+13b")) ContainsLines(
Contains("-13a"),
Contains("+13b"),
)
}). }).
Press(keys.Universal.TogglePanel) Press(keys.Universal.TogglePanel)
t.Views().StagingSecondary(). t.Views().StagingSecondary().
IsFocused(). IsFocused().
SelectedLine(Contains("-13a")).
// after toggling panel, we're back to only having selected a single line // after toggling panel, we're back to only having selected a single line
SelectedLines(
Contains("-13a"),
).
PressPrimaryAction(). PressPrimaryAction().
SelectedLine(Contains("+13b")). SelectedLines(
Contains("+13b"),
).
PressPrimaryAction(). PressPrimaryAction().
IsEmpty() IsEmpty()
t.Views().Staging(). t.Views().Staging().
IsFocused(). IsFocused().
SelectedLine(Contains("-3a")). SelectedLines(
Contains("-3a"),
).
Press(keys.Main.ToggleSelectHunk). Press(keys.Main.ToggleSelectHunk).
SelectedLines(
Contains(`@@ -1,6 +1,6 @@`),
Contains(` 1a`),
Contains(` 2a`),
Contains(`-3a`),
Contains(`+3b`),
Contains(` 4a`),
Contains(` 5a`),
Contains(` 6a`),
).
Press(keys.Universal.Remove). Press(keys.Universal.Remove).
Tap(func() { Tap(func() {
t.Actions().ConfirmDiscardLines() t.Actions().ConfirmDiscardLines()
}). }).
SelectedLine(Contains("-13a")). Content(DoesNotContain("-3a").DoesNotContain("+3b")).
Content(DoesNotContain("-3a").DoesNotContain("+3b")) SelectedLines(
Contains("@@ -10,6 +10,6 @@"),
Contains(" 10a"),
Contains(" 11a"),
Contains(" 12a"),
Contains("-13a"),
Contains("+13b"),
Contains(" 14a"),
Contains(" 15a"),
Contains(`\ No newline at end of file`),
)
}, },
}) })

View File

@ -26,16 +26,18 @@ var StageLines = NewIntegrationTest(NewIntegrationTestArgs{
t.Views().Staging(). t.Views().Staging().
IsFocused(). IsFocused().
SelectedLine(Contains("+three")). SelectedLines(Contains("+three")).
// stage 'three' // stage 'three'
PressPrimaryAction(). PressPrimaryAction().
// 'three' moves over to the staging secondary panel // 'three' moves over to the staging secondary panel
Content(DoesNotContain("+three")). Content(DoesNotContain("+three")).
Tap(func() { Tap(func() {
t.Views().StagingSecondary(). t.Views().StagingSecondary().
Content(Contains("+three")) ContainsLines(
Contains("+three"),
)
}). }).
SelectedLine(Contains("+four")). SelectedLines(Contains("+four")).
// stage 'four' // stage 'four'
PressPrimaryAction(). PressPrimaryAction().
// nothing left in our staging panel // nothing left in our staging panel
@ -45,15 +47,20 @@ var StageLines = NewIntegrationTest(NewIntegrationTestArgs{
// do the same thing as above, moving the lines back to the staging panel // do the same thing as above, moving the lines back to the staging panel
t.Views().StagingSecondary(). t.Views().StagingSecondary().
IsFocused(). IsFocused().
Content(Contains("+three\n+four")). ContainsLines(
SelectedLine(Contains("+three")). Contains("+three"),
Contains("+four"),
).
SelectedLines(Contains("+three")).
PressPrimaryAction(). PressPrimaryAction().
Content(DoesNotContain("+three")). Content(DoesNotContain("+three")).
Tap(func() { Tap(func() {
t.Views().Staging(). t.Views().Staging().
Content(Contains("+three")) ContainsLines(
Contains("+three"),
)
}). }).
SelectedLine(Contains("+four")). SelectedLines(Contains("+four")).
// pressing 'remove' has the same effect as pressing space when in the staging secondary panel // pressing 'remove' has the same effect as pressing space when in the staging secondary panel
Press(keys.Universal.Remove). Press(keys.Universal.Remove).
IsEmpty() IsEmpty()
@ -61,8 +68,11 @@ var StageLines = NewIntegrationTest(NewIntegrationTestArgs{
// stage one line and then manually toggle to the staging secondary panel // stage one line and then manually toggle to the staging secondary panel
t.Views().Staging(). t.Views().Staging().
IsFocused(). IsFocused().
Content(Contains("+three\n+four")). ContainsLines(
SelectedLine(Contains("+three")). Contains("+three"),
Contains("+four"),
).
SelectedLines(Contains("+three")).
PressPrimaryAction(). PressPrimaryAction().
Content(DoesNotContain("+three")). Content(DoesNotContain("+three")).
Tap(func() { Tap(func() {
@ -77,7 +87,7 @@ var StageLines = NewIntegrationTest(NewIntegrationTestArgs{
Press(keys.Universal.TogglePanel) Press(keys.Universal.TogglePanel)
t.Views().Staging(). t.Views().Staging().
SelectedLine(Contains("+four")). SelectedLines(Contains("+four")).
// discard the line // discard the line
Press(keys.Universal.Remove). Press(keys.Universal.Remove).
Tap(func() { Tap(func() {
@ -90,7 +100,9 @@ var StageLines = NewIntegrationTest(NewIntegrationTestArgs{
t.Views().StagingSecondary(). t.Views().StagingSecondary().
IsFocused(). IsFocused().
Content(Contains("+three\n")). ContainsLines(
Contains("+three"),
).
// return to file // return to file
PressEscape() PressEscape()

View File

@ -26,50 +26,81 @@ var StageRanges = NewIntegrationTest(NewIntegrationTestArgs{
t.Views().Staging(). t.Views().Staging().
IsFocused(). IsFocused().
SelectedLine(Contains("+three")). SelectedLines(
Contains("+three"),
).
Press(keys.Main.ToggleDragSelect). Press(keys.Main.ToggleDragSelect).
SelectNextItem(). NavigateToListItem(Contains("+five")).
SelectedLine(Contains("+four")). SelectedLines(
SelectNextItem(). Contains("+three"),
SelectedLine(Contains("+five")). Contains("+four"),
Contains("+five"),
).
// stage the three lines we've just selected // stage the three lines we've just selected
PressPrimaryAction(). PressPrimaryAction().
Content(Contains(" five\n+six")). SelectedLines(
Contains("+six"),
).
ContainsLines(
Contains(" five"),
Contains("+six"),
).
Tap(func() { Tap(func() {
t.Views().StagingSecondary(). t.Views().StagingSecondary().
Content(Contains("+three\n+four\n+five")) ContainsLines(
Contains("+three"),
Contains("+four"),
Contains("+five"),
)
}). }).
Press(keys.Universal.TogglePanel) Press(keys.Universal.TogglePanel)
t.Views().StagingSecondary(). t.Views().StagingSecondary().
IsFocused(). IsFocused().
SelectedLine(Contains("+three")). SelectedLines(
Contains("+three"),
).
Press(keys.Main.ToggleDragSelect). Press(keys.Main.ToggleDragSelect).
SelectNextItem(). NavigateToListItem(Contains("+five")).
SelectedLine(Contains("+four")). SelectedLines(
SelectNextItem(). Contains("+three"),
SelectedLine(Contains("+five")). Contains("+four"),
Contains("+five"),
).
// unstage the three selected lines // unstage the three selected lines
PressPrimaryAction(). PressPrimaryAction().
// nothing left in our staging secondary panel // nothing left in our staging secondary panel
IsEmpty(). IsEmpty().
Tap(func() { Tap(func() {
t.Views().Staging(). t.Views().Staging().
Content(Contains("+three\n+four\n+five\n+six")) ContainsLines(
Contains("+three"),
Contains("+four"),
Contains("+five"),
Contains("+six"),
)
}) })
t.Views().Staging(). t.Views().Staging().
IsFocused(). IsFocused().
// coincidentally we land at '+four' here. Maybe we should instead land // coincidentally we land at '+four' here. Maybe we should instead land
// at '+three'? given it's at the start of the hunk? // at '+three'? given it's at the start of the hunk?
SelectedLine(Contains("+four")). SelectedLines(
Contains("+four"),
).
Press(keys.Main.ToggleDragSelect). Press(keys.Main.ToggleDragSelect).
SelectNextItem(). SelectNextItem().
SelectedLine(Contains("+five")). SelectedLines(
Contains("+four"),
Contains("+five"),
).
Press(keys.Universal.Remove). Press(keys.Universal.Remove).
Tap(func() { Tap(func() {
t.Actions().ConfirmDiscardLines() t.Actions().ConfirmDiscardLines()
}). }).
Content(Contains("+three\n+six")) ContainsLines(
Contains("+three"),
Contains("+six"),
)
}, },
}) })

View File

@ -20,6 +20,7 @@ type GuiDriver interface {
PressKey(string) PressKey(string)
Keys() config.KeybindingConfig Keys() config.KeybindingConfig
CurrentContext() types.Context CurrentContext() types.Context
ContextForView(viewName string) types.Context
Fail(message string) Fail(message string)
// These two log methods are for the sake of debugging while testing. There's no need to actually // These two log methods are for the sake of debugging while testing. There's no need to actually
// commit any logging. // commit any logging.

View File

@ -1302,6 +1302,9 @@ func (v *View) SelectedLineIdx() int {
// expected to only be used in tests // expected to only be used in tests
func (v *View) SelectedLine() string { func (v *View) SelectedLine() string {
v.writeMutex.Lock()
defer v.writeMutex.Unlock()
if len(v.lines) == 0 { if len(v.lines) == 0 {
return "" return ""
} }

4
vendor/modules.txt vendored
View File

@ -39,8 +39,6 @@ github.com/fsnotify/fsnotify
# github.com/gdamore/encoding v1.0.0 # github.com/gdamore/encoding v1.0.0
## explicit; go 1.9 ## explicit; go 1.9
github.com/gdamore/encoding github.com/gdamore/encoding
# github.com/gdamore/tcell v1.4.0
## explicit; go 1.12
# github.com/gdamore/tcell/v2 v2.6.0 # github.com/gdamore/tcell/v2 v2.6.0
## explicit; go 1.12 ## explicit; go 1.12
github.com/gdamore/tcell/v2 github.com/gdamore/tcell/v2
@ -174,7 +172,7 @@ github.com/jesseduffield/go-git/v5/utils/merkletrie/filesystem
github.com/jesseduffield/go-git/v5/utils/merkletrie/index github.com/jesseduffield/go-git/v5/utils/merkletrie/index
github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame github.com/jesseduffield/go-git/v5/utils/merkletrie/internal/frame
github.com/jesseduffield/go-git/v5/utils/merkletrie/noder github.com/jesseduffield/go-git/v5/utils/merkletrie/noder
# github.com/jesseduffield/gocui v0.3.1-0.20230219034834-06a1f1e95da5 # github.com/jesseduffield/gocui v0.3.1-0.20230225001450-38a4deaa7f24
## explicit; go 1.12 ## explicit; go 1.12
github.com/jesseduffield/gocui github.com/jesseduffield/gocui
# github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 # github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10