mirror of
				https://github.com/jesseduffield/lazygit.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	Improve staging panel integration tests
This commit is contained in:
		
							
								
								
									
										3
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								go.mod
									
									
									
									
									
								
							| @@ -18,7 +18,7 @@ require ( | ||||
| 	github.com/integrii/flaggy v1.4.0 | ||||
| 	github.com/jesseduffield/generics v0.0.0-20220320043834-727e535cbe68 | ||||
| 	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/lazycore v0.0.0-20221012050358-03d2e40243c5 | ||||
| 	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/fatih/color v1.9.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/go-billy/v5 v5.0.0 // indirect | ||||
| 	github.com/go-logfmt/logfmt v0.5.0 // indirect | ||||
|   | ||||
							
								
								
									
										12
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								go.sum
									
									
									
									
									
								
							| @@ -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/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= | ||||
| 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.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/go.mod h1:be9omFATkdr0D9qewWW3d+MEvl5dha+Etb5y65J2H8Y= | ||||
| 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/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/gocui v0.3.1-0.20230219034834-06a1f1e95da5 h1:6mrOZa9I1bol14HhVK0tl4w9xvvGKLWPmShPRey1Lxg= | ||||
| 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 h1:1uSYSN8np7ym5IjuGi2hHVp/9GQebLbApfz7vudzaSM= | ||||
| 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/go.mod h1:aA97kHeNA+sj2Hbki0pvLslmE4CbDyhBeSSTUUnOuVo= | ||||
| 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.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= | ||||
| 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.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= | ||||
| 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/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.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= | ||||
| 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/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-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-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-20200302150141-5c8b2ff67527/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.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.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/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
|   | ||||
| @@ -27,7 +27,9 @@ func GetHunksFromDiff(diff string) []*PatchHunk { | ||||
| 	var hunkLines []string //nolint:prealloc | ||||
| 	pastDiffHeader := false | ||||
|  | ||||
| 	for lineIdx, line := range strings.SplitAfter(diff, "\n") { | ||||
| 	lines := strings.SplitAfter(diff, "\n") | ||||
|  | ||||
| 	for lineIdx, line := range lines { | ||||
| 		isHunkHeader := strings.HasPrefix(line, "@@ -") | ||||
|  | ||||
| 		if isHunkHeader { | ||||
| @@ -44,6 +46,10 @@ func GetHunksFromDiff(diff string) []*PatchHunk { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		if lineIdx == len(lines)-1 && line == "" { // skip the trailing newline | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		hunkLines = append(hunkLines, line) | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -134,7 +134,8 @@ func coloredString(textStyle style.TextStyle, str string, selected bool, include | ||||
| } | ||||
|  | ||||
| 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{} | ||||
| 	stageableLines := []int{} | ||||
| 	pastFirstHunkHeader := false | ||||
| @@ -179,6 +180,7 @@ func parsePatch(patch string) ([]int, []int, []*PatchLine) { | ||||
| 		} | ||||
| 		patchLines[index] = &PatchLine{Kind: lineKind, Content: line} | ||||
| 	} | ||||
|  | ||||
| 	return hunkStarts, stageableLines, patchLines | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -49,6 +49,15 @@ func (self *GuiDriver) CurrentContext() types.Context { | ||||
| 	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) { | ||||
| 	currentView := self.gui.g.CurrentView() | ||||
| 	fullMessage := fmt.Sprintf( | ||||
|   | ||||
| @@ -61,6 +61,11 @@ 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) | ||||
| 		}, | ||||
| 	}) | ||||
|   | ||||
| @@ -7,7 +7,6 @@ import ( | ||||
|  | ||||
| 	"github.com/atotto/clipboard" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/config" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/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, | ||||
| // looking for the item. | ||||
| func (self *TestDriver) navigateToListItem(matcher *matcher) { | ||||
| 	self.inListContext() | ||||
|  | ||||
| 	currentContext := self.gui.CurrentContext().(types.IListContext) | ||||
| 	currentContext := self.gui.CurrentContext() | ||||
|  | ||||
| 	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 | ||||
| func (self *TestDriver) Views() *Views { | ||||
| 	return &Views{t: self} | ||||
|   | ||||
| @@ -30,6 +30,10 @@ func (self *fakeGuiDriver) CurrentContext() types.Context { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (self *fakeGuiDriver) ContextForView(viewName string) types.Context { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (self *fakeGuiDriver) Fail(message string) { | ||||
| 	self.failureMessage = message | ||||
| } | ||||
|   | ||||
| @@ -10,9 +10,10 @@ import ( | ||||
|  | ||||
| type ViewDriver struct { | ||||
| 	// context is prepended to any error messages e.g. 'context: "current view"' | ||||
| 	context string | ||||
| 	getView func() *gocui.View | ||||
| 	t       *TestDriver | ||||
| 	context            string | ||||
| 	getView            func() *gocui.View | ||||
| 	t                  *TestDriver | ||||
| 	getSelectedLinesFn func() ([]string, error) | ||||
| } | ||||
|  | ||||
| // asserts that the view has the expected title | ||||
| @@ -50,6 +51,74 @@ func (self *ViewDriver) Lines(matchers ...*matcher) *ViewDriver { | ||||
| 	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 { | ||||
| 	view := self.getView() | ||||
|  | ||||
| @@ -86,9 +155,35 @@ func (self *ViewDriver) Content(matcher *matcher) *ViewDriver { | ||||
|  | ||||
| // asserts on the selected line of the view | ||||
| 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), | ||||
| 		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 | ||||
| } | ||||
|  | ||||
| func expectedContentFromMatchers(matchers []*matcher) string { | ||||
| 	return strings.Join(lo.Map(matchers, func(matcher *matcher, _ int) string { | ||||
| 		return matcher.name() | ||||
| 	}), "\n") | ||||
| } | ||||
|   | ||||
| @@ -2,8 +2,11 @@ package components | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/go-errors/errors" | ||||
| 	"github.com/jesseduffield/gocui" | ||||
| 	"github.com/jesseduffield/lazygit/pkg/gui/context" | ||||
| ) | ||||
|  | ||||
| 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{ | ||||
| 		context: fmt.Sprintf("%s view", viewName), | ||||
| 		getView: func() *gocui.View { return self.t.gui.View(viewName) }, | ||||
| 		t:       self.t, | ||||
| 		context:            fmt.Sprintf("%s view", viewName), | ||||
| 		getView:            func() *gocui.View { return self.t.gui.View(viewName) }, | ||||
| 		getSelectedLinesFn: getSelectedLinesFn, | ||||
| 		t:                  self.t, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (self *Views) Commits() *ViewDriver { | ||||
| 	return self.byName("commits") | ||||
| 	return self.regularView("commits") | ||||
| } | ||||
|  | ||||
| func (self *Views) Files() *ViewDriver { | ||||
| 	return self.byName("files") | ||||
| 	return self.regularView("files") | ||||
| } | ||||
|  | ||||
| func (self *Views) Status() *ViewDriver { | ||||
| 	return self.byName("status") | ||||
| 	return self.regularView("status") | ||||
| } | ||||
|  | ||||
| func (self *Views) Submodules() *ViewDriver { | ||||
| 	return self.byName("submodules") | ||||
| 	return self.regularView("submodules") | ||||
| } | ||||
|  | ||||
| func (self *Views) Information() *ViewDriver { | ||||
| 	return self.byName("information") | ||||
| 	return self.regularView("information") | ||||
| } | ||||
|  | ||||
| func (self *Views) AppStatus() *ViewDriver { | ||||
| 	return self.byName("appStatus") | ||||
| 	return self.regularView("appStatus") | ||||
| } | ||||
|  | ||||
| func (self *Views) Branches() *ViewDriver { | ||||
| 	return self.byName("localBranches") | ||||
| 	return self.regularView("localBranches") | ||||
| } | ||||
|  | ||||
| func (self *Views) Remotes() *ViewDriver { | ||||
| 	return self.byName("remotes") | ||||
| 	return self.regularView("remotes") | ||||
| } | ||||
|  | ||||
| func (self *Views) RemoteBranches() *ViewDriver { | ||||
| 	return self.byName("remoteBranches") | ||||
| 	return self.regularView("remoteBranches") | ||||
| } | ||||
|  | ||||
| func (self *Views) Tags() *ViewDriver { | ||||
| 	return self.byName("tags") | ||||
| 	return self.regularView("tags") | ||||
| } | ||||
|  | ||||
| func (self *Views) ReflogCommits() *ViewDriver { | ||||
| 	return self.byName("reflogCommits") | ||||
| 	return self.regularView("reflogCommits") | ||||
| } | ||||
|  | ||||
| func (self *Views) SubCommits() *ViewDriver { | ||||
| 	return self.byName("subCommits") | ||||
| 	return self.regularView("subCommits") | ||||
| } | ||||
|  | ||||
| func (self *Views) CommitFiles() *ViewDriver { | ||||
| 	return self.byName("commitFiles") | ||||
| 	return self.regularView("commitFiles") | ||||
| } | ||||
|  | ||||
| func (self *Views) Stash() *ViewDriver { | ||||
| 	return self.byName("stash") | ||||
| 	return self.regularView("stash") | ||||
| } | ||||
|  | ||||
| func (self *Views) Staging() *ViewDriver { | ||||
| 	return self.byName("staging") | ||||
| 	return self.patchExplorerViewByName("staging") | ||||
| } | ||||
|  | ||||
| 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 { | ||||
| 	return self.byName("menu") | ||||
| 	return self.regularView("menu") | ||||
| } | ||||
|  | ||||
| func (self *Views) Confirmation() *ViewDriver { | ||||
| 	return self.byName("confirmation") | ||||
| 	return self.regularView("confirmation") | ||||
| } | ||||
|  | ||||
| func (self *Views) CommitMessage() *ViewDriver { | ||||
| 	return self.byName("commitMessage") | ||||
| 	return self.regularView("commitMessage") | ||||
| } | ||||
|  | ||||
| func (self *Views) Suggestions() *ViewDriver { | ||||
| 	return self.byName("suggestions") | ||||
| 	return self.regularView("suggestions") | ||||
| } | ||||
|  | ||||
| func (self *Views) MergeConflicts() *ViewDriver { | ||||
| 	return self.byName("mergeConflicts") | ||||
| 	return self.regularView("mergeConflicts") | ||||
| } | ||||
|  | ||||
| func (self *Views) Search() *ViewDriver { | ||||
| 	return self.byName("search") | ||||
| 	return self.regularView("search") | ||||
| } | ||||
|   | ||||
| @@ -50,31 +50,80 @@ var DiffContextChange = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
|  | ||||
| 		t.Views().Staging(). | ||||
| 			IsFocused(). | ||||
| 			Content(Contains("@@ -1,6 +1,6 @@").DoesNotContain(" 7a")). | ||||
| 			SelectedLine(Contains("-3a")). | ||||
| 			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.IncreaseContextInDiffView). | ||||
| 			// still on the same line | ||||
| 			SelectedLine(Contains("-3a")). | ||||
| 			// '7a' is now visible | ||||
| 			Content(Contains("@@ -1,7 +1,7 @@").Contains(" 7a")). | ||||
| 			SelectedLines( | ||||
| 				Contains(`@@ -1,7 +1,7 @@`), | ||||
| 				Contains(` 1a`), | ||||
| 				Contains(` 2a`), | ||||
| 				Contains(`-3a`), | ||||
| 				Contains(`+3b`), | ||||
| 				Contains(` 4a`), | ||||
| 				Contains(` 5a`), | ||||
| 				Contains(` 6a`), | ||||
| 				Contains(` 7a`), | ||||
| 			). | ||||
| 			Press(keys.Universal.DecreaseContextInDiffView). | ||||
| 			SelectedLine(Contains("-3a")). | ||||
| 			Content(Contains("@@ -1,6 +1,6 @@").DoesNotContain(" 7a")). | ||||
| 			SelectedLines( | ||||
| 				Contains(`@@ -1,6 +1,6 @@`), | ||||
| 				Contains(` 1a`), | ||||
| 				Contains(` 2a`), | ||||
| 				Contains(`-3a`), | ||||
| 				Contains(`+3b`), | ||||
| 				Contains(` 4a`), | ||||
| 				Contains(` 5a`), | ||||
| 				Contains(` 6a`), | ||||
| 			). | ||||
| 			Press(keys.Universal.DecreaseContextInDiffView). | ||||
| 			SelectedLine(Contains("-3a")). | ||||
| 			Content(Contains("@@ -1,5 +1,5 @@").DoesNotContain(" 6a")). | ||||
| 			SelectedLines( | ||||
| 				Contains(`@@ -1,5 +1,5 @@`), | ||||
| 				Contains(` 1a`), | ||||
| 				Contains(` 2a`), | ||||
| 				Contains(`-3a`), | ||||
| 				Contains(`+3b`), | ||||
| 				Contains(` 4a`), | ||||
| 				Contains(` 5a`), | ||||
| 			). | ||||
| 			Press(keys.Universal.DecreaseContextInDiffView). | ||||
| 			// arguably we should still be on -3a, but at the moment the logic puts us on on +3b | ||||
| 			SelectedLine(Contains("+3b")). | ||||
| 			Content(Contains("@@ -2,3 +2,3 @@").DoesNotContain(" 5a")). | ||||
| 			SelectedLines( | ||||
| 				Contains(`@@ -2,3 +2,3 @@`), | ||||
| 				Contains(` 2a`), | ||||
| 				Contains(`-3a`), | ||||
| 				Contains(`+3b`), | ||||
| 				Contains(` 4a`), | ||||
| 			). | ||||
| 			PressPrimaryAction(). | ||||
| 			Content(DoesNotContain("+3b")). | ||||
| 			Press(keys.Universal.TogglePanel) | ||||
|  | ||||
| 		t.Views().StagingSecondary(). | ||||
| 			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). | ||||
| 			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`), | ||||
| 			) | ||||
| 	}, | ||||
| }) | ||||
|   | ||||
| @@ -29,13 +29,13 @@ var DiscardAllChanges = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
|  | ||||
| 		t.Views().Staging(). | ||||
| 			IsFocused(). | ||||
| 			SelectedLine(Contains("+three")). | ||||
| 			SelectedLines(Contains("+three")). | ||||
| 			// discard the line | ||||
| 			Press(keys.Universal.Remove). | ||||
| 			Tap(func() { | ||||
| 				t.Actions().ConfirmDiscardLines() | ||||
| 			}). | ||||
| 			SelectedLine(Contains("+four")). | ||||
| 			SelectedLines(Contains("+four")). | ||||
| 			// discard the other line | ||||
| 			Press(keys.Universal.Remove). | ||||
| 			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 | ||||
| 			IsFocused(). | ||||
| 			SelectedLine(Contains("+3")) | ||||
| 			SelectedLines(Contains("+3")) | ||||
| 	}, | ||||
| }) | ||||
|   | ||||
| @@ -50,42 +50,108 @@ var StageHunks = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
|  | ||||
| 		t.Views().Staging(). | ||||
| 			IsFocused(). | ||||
| 			SelectedLine(Contains("-3a")). | ||||
| 			SelectedLines( | ||||
| 				Contains("-3a"), | ||||
| 			). | ||||
| 			Press(keys.Universal.NextBlock). | ||||
| 			SelectedLine(Contains("-13a")). | ||||
| 			SelectedLines( | ||||
| 				Contains("-13a"), | ||||
| 			). | ||||
| 			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 | ||||
| 			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(). | ||||
| 			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 | ||||
| 			PressPrimaryAction(). | ||||
| 			Content(Contains("-3a\n+3b")). | ||||
| 			ContainsLines( | ||||
| 				Contains("-3a"), | ||||
| 				Contains("+3b"), | ||||
| 			). | ||||
| 			Tap(func() { | ||||
| 				t.Views().StagingSecondary(). | ||||
| 					Content(Contains("-13a\n+13b")) | ||||
| 					ContainsLines( | ||||
| 						Contains("-13a"), | ||||
| 						Contains("+13b"), | ||||
| 					) | ||||
| 			}). | ||||
| 			Press(keys.Universal.TogglePanel) | ||||
|  | ||||
| 		t.Views().StagingSecondary(). | ||||
| 			IsFocused(). | ||||
| 			SelectedLine(Contains("-13a")). | ||||
| 			// after toggling panel, we're back to only having selected a single line | ||||
| 			SelectedLines( | ||||
| 				Contains("-13a"), | ||||
| 			). | ||||
| 			PressPrimaryAction(). | ||||
| 			SelectedLine(Contains("+13b")). | ||||
| 			SelectedLines( | ||||
| 				Contains("+13b"), | ||||
| 			). | ||||
| 			PressPrimaryAction(). | ||||
| 			IsEmpty() | ||||
|  | ||||
| 		t.Views().Staging(). | ||||
| 			IsFocused(). | ||||
| 			SelectedLine(Contains("-3a")). | ||||
| 			SelectedLines( | ||||
| 				Contains("-3a"), | ||||
| 			). | ||||
| 			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). | ||||
| 			Tap(func() { | ||||
| 				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`), | ||||
| 			) | ||||
| 	}, | ||||
| }) | ||||
|   | ||||
| @@ -26,16 +26,18 @@ var StageLines = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
|  | ||||
| 		t.Views().Staging(). | ||||
| 			IsFocused(). | ||||
| 			SelectedLine(Contains("+three")). | ||||
| 			SelectedLines(Contains("+three")). | ||||
| 			// stage 'three' | ||||
| 			PressPrimaryAction(). | ||||
| 			// 'three' moves over to the staging secondary panel | ||||
| 			Content(DoesNotContain("+three")). | ||||
| 			Tap(func() { | ||||
| 				t.Views().StagingSecondary(). | ||||
| 					Content(Contains("+three")) | ||||
| 					ContainsLines( | ||||
| 						Contains("+three"), | ||||
| 					) | ||||
| 			}). | ||||
| 			SelectedLine(Contains("+four")). | ||||
| 			SelectedLines(Contains("+four")). | ||||
| 			// stage 'four' | ||||
| 			PressPrimaryAction(). | ||||
| 			// 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 | ||||
| 		t.Views().StagingSecondary(). | ||||
| 			IsFocused(). | ||||
| 			Content(Contains("+three\n+four")). | ||||
| 			SelectedLine(Contains("+three")). | ||||
| 			ContainsLines( | ||||
| 				Contains("+three"), | ||||
| 				Contains("+four"), | ||||
| 			). | ||||
| 			SelectedLines(Contains("+three")). | ||||
| 			PressPrimaryAction(). | ||||
| 			Content(DoesNotContain("+three")). | ||||
| 			Tap(func() { | ||||
| 				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 | ||||
| 			Press(keys.Universal.Remove). | ||||
| 			IsEmpty() | ||||
| @@ -61,8 +68,11 @@ var StageLines = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
| 		// stage one line and then manually toggle to the staging secondary panel | ||||
| 		t.Views().Staging(). | ||||
| 			IsFocused(). | ||||
| 			Content(Contains("+three\n+four")). | ||||
| 			SelectedLine(Contains("+three")). | ||||
| 			ContainsLines( | ||||
| 				Contains("+three"), | ||||
| 				Contains("+four"), | ||||
| 			). | ||||
| 			SelectedLines(Contains("+three")). | ||||
| 			PressPrimaryAction(). | ||||
| 			Content(DoesNotContain("+three")). | ||||
| 			Tap(func() { | ||||
| @@ -77,7 +87,7 @@ var StageLines = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
| 			Press(keys.Universal.TogglePanel) | ||||
|  | ||||
| 		t.Views().Staging(). | ||||
| 			SelectedLine(Contains("+four")). | ||||
| 			SelectedLines(Contains("+four")). | ||||
| 			// discard the line | ||||
| 			Press(keys.Universal.Remove). | ||||
| 			Tap(func() { | ||||
| @@ -90,7 +100,9 @@ var StageLines = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
|  | ||||
| 		t.Views().StagingSecondary(). | ||||
| 			IsFocused(). | ||||
| 			Content(Contains("+three\n")). | ||||
| 			ContainsLines( | ||||
| 				Contains("+three"), | ||||
| 			). | ||||
| 			// return to file | ||||
| 			PressEscape() | ||||
|  | ||||
|   | ||||
| @@ -26,50 +26,81 @@ var StageRanges = NewIntegrationTest(NewIntegrationTestArgs{ | ||||
|  | ||||
| 		t.Views().Staging(). | ||||
| 			IsFocused(). | ||||
| 			SelectedLine(Contains("+three")). | ||||
| 			SelectedLines( | ||||
| 				Contains("+three"), | ||||
| 			). | ||||
| 			Press(keys.Main.ToggleDragSelect). | ||||
| 			SelectNextItem(). | ||||
| 			SelectedLine(Contains("+four")). | ||||
| 			SelectNextItem(). | ||||
| 			SelectedLine(Contains("+five")). | ||||
| 			NavigateToListItem(Contains("+five")). | ||||
| 			SelectedLines( | ||||
| 				Contains("+three"), | ||||
| 				Contains("+four"), | ||||
| 				Contains("+five"), | ||||
| 			). | ||||
| 			// stage the three lines we've just selected | ||||
| 			PressPrimaryAction(). | ||||
| 			Content(Contains(" five\n+six")). | ||||
| 			SelectedLines( | ||||
| 				Contains("+six"), | ||||
| 			). | ||||
| 			ContainsLines( | ||||
| 				Contains(" five"), | ||||
| 				Contains("+six"), | ||||
| 			). | ||||
| 			Tap(func() { | ||||
| 				t.Views().StagingSecondary(). | ||||
| 					Content(Contains("+three\n+four\n+five")) | ||||
| 					ContainsLines( | ||||
| 						Contains("+three"), | ||||
| 						Contains("+four"), | ||||
| 						Contains("+five"), | ||||
| 					) | ||||
| 			}). | ||||
| 			Press(keys.Universal.TogglePanel) | ||||
|  | ||||
| 		t.Views().StagingSecondary(). | ||||
| 			IsFocused(). | ||||
| 			SelectedLine(Contains("+three")). | ||||
| 			SelectedLines( | ||||
| 				Contains("+three"), | ||||
| 			). | ||||
| 			Press(keys.Main.ToggleDragSelect). | ||||
| 			SelectNextItem(). | ||||
| 			SelectedLine(Contains("+four")). | ||||
| 			SelectNextItem(). | ||||
| 			SelectedLine(Contains("+five")). | ||||
| 			NavigateToListItem(Contains("+five")). | ||||
| 			SelectedLines( | ||||
| 				Contains("+three"), | ||||
| 				Contains("+four"), | ||||
| 				Contains("+five"), | ||||
| 			). | ||||
| 			// unstage the three selected lines | ||||
| 			PressPrimaryAction(). | ||||
| 			// nothing left in our staging secondary panel | ||||
| 			IsEmpty(). | ||||
| 			Tap(func() { | ||||
| 				t.Views().Staging(). | ||||
| 					Content(Contains("+three\n+four\n+five\n+six")) | ||||
| 					ContainsLines( | ||||
| 						Contains("+three"), | ||||
| 						Contains("+four"), | ||||
| 						Contains("+five"), | ||||
| 						Contains("+six"), | ||||
| 					) | ||||
| 			}) | ||||
|  | ||||
| 		t.Views().Staging(). | ||||
| 			IsFocused(). | ||||
| 			// coincidentally we land at '+four' here. Maybe we should instead land | ||||
| 			// at '+three'? given it's at the start of the hunk? | ||||
| 			SelectedLine(Contains("+four")). | ||||
| 			SelectedLines( | ||||
| 				Contains("+four"), | ||||
| 			). | ||||
| 			Press(keys.Main.ToggleDragSelect). | ||||
| 			SelectNextItem(). | ||||
| 			SelectedLine(Contains("+five")). | ||||
| 			SelectedLines( | ||||
| 				Contains("+four"), | ||||
| 				Contains("+five"), | ||||
| 			). | ||||
| 			Press(keys.Universal.Remove). | ||||
| 			Tap(func() { | ||||
| 				t.Actions().ConfirmDiscardLines() | ||||
| 			}). | ||||
| 			Content(Contains("+three\n+six")) | ||||
| 			ContainsLines( | ||||
| 				Contains("+three"), | ||||
| 				Contains("+six"), | ||||
| 			) | ||||
| 	}, | ||||
| }) | ||||
|   | ||||
| @@ -20,6 +20,7 @@ type GuiDriver interface { | ||||
| 	PressKey(string) | ||||
| 	Keys() config.KeybindingConfig | ||||
| 	CurrentContext() types.Context | ||||
| 	ContextForView(viewName string) types.Context | ||||
| 	Fail(message string) | ||||
| 	// These two log methods are for the sake of debugging while testing. There's no need to actually | ||||
| 	// commit any logging. | ||||
|   | ||||
							
								
								
									
										3
									
								
								vendor/github.com/jesseduffield/gocui/view.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								vendor/github.com/jesseduffield/gocui/view.go
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1302,6 +1302,9 @@ func (v *View) SelectedLineIdx() int { | ||||
|  | ||||
| // expected to only be used in tests | ||||
| func (v *View) SelectedLine() string { | ||||
| 	v.writeMutex.Lock() | ||||
| 	defer v.writeMutex.Unlock() | ||||
|  | ||||
| 	if len(v.lines) == 0 { | ||||
| 		return "" | ||||
| 	} | ||||
|   | ||||
							
								
								
									
										4
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							| @@ -39,8 +39,6 @@ github.com/fsnotify/fsnotify | ||||
| # github.com/gdamore/encoding v1.0.0 | ||||
| ## explicit; go 1.9 | ||||
| github.com/gdamore/encoding | ||||
| # github.com/gdamore/tcell v1.4.0 | ||||
| ## explicit; go 1.12 | ||||
| # github.com/gdamore/tcell/v2 v2.6.0 | ||||
| ## explicit; go 1.12 | ||||
| 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/internal/frame | ||||
| 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 | ||||
| github.com/jesseduffield/gocui | ||||
| # github.com/jesseduffield/kill v0.0.0-20220618033138-bfbe04675d10 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user