1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-07-30 03:23:08 +03:00

Merge branch 'master' into stash-untracked-changes

This commit is contained in:
Andrew Hynes
2022-10-06 22:59:06 -02:30
committed by GitHub
822 changed files with 4921 additions and 2627 deletions

View File

@ -46,7 +46,13 @@ func (self *matcher) context(prefix string) *matcher {
func Contains(target string) *matcher {
return &matcher{testFn: func(value string) (bool, string) {
return strings.Contains(value, target), fmt.Sprintf("Expected '%s' to contain '%s'", value, target)
return strings.Contains(value, target), fmt.Sprintf("Expected '%s' to be found in '%s'", target, value)
}}
}
func NotContains(target string) *matcher {
return &matcher{testFn: func(value string) (bool, string) {
return !strings.Contains(value, target), fmt.Sprintf("Expected '%s' to NOT be found in '%s'", target, value)
}}
}
@ -89,6 +95,14 @@ func (self *Assert) StashCount(expectedCount int) {
})
}
func (self *Assert) AtLeastOneCommit() {
self.assertWithRetries(func() (bool, string) {
actualCount := len(self.gui.Model().Commits)
return actualCount > 0, "Expected at least one commit present"
})
}
func (self *Assert) MatchHeadCommitMessage(matcher *matcher) {
self.assertWithRetries(func() (bool, string) {
return len(self.gui.Model().Commits) > 0, "Expected at least one commit to be present"
@ -108,6 +122,13 @@ func (self *Assert) CurrentViewName(expectedViewName string) {
})
}
func (self *Assert) CurrentWindowName(expectedWindowName string) {
self.assertWithRetries(func() (bool, string) {
actual := self.gui.CurrentContext().GetView().Name()
return actual == expectedWindowName, fmt.Sprintf("Expected current window name to be '%s', but got '%s'", expectedWindowName, actual)
})
}
func (self *Assert) CurrentBranchName(expectedViewName string) {
self.assertWithRetries(func() (bool, string) {
actual := self.gui.CheckedOutRef().Name
@ -167,6 +188,22 @@ func (self *Assert) MatchCurrentViewTitle(matcher *matcher) {
)
}
func (self *Assert) MatchViewContent(viewName string, matcher *matcher) {
self.matchString(matcher, fmt.Sprintf("Unexpected content in view '%s'.", viewName),
func() string {
return self.gui.View(viewName).Buffer()
},
)
}
func (self *Assert) MatchCurrentViewContent(matcher *matcher) {
self.matchString(matcher, "Unexpected content in current view.",
func() string {
return self.gui.CurrentContext().GetView().Buffer()
},
)
}
func (self *Assert) MatchMainViewContent(matcher *matcher) {
self.matchString(matcher, "Unexpected main view content.",
func() string {
@ -191,7 +228,7 @@ func (self *Assert) matchString(matcher *matcher, context string, getValue func(
}
func (self *Assert) assertWithRetries(test func() (bool, string)) {
waitTimes := []int{0, 1, 5, 10, 200, 500, 1000}
waitTimes := []int{0, 1, 5, 10, 200, 500, 1000, 2000, 4000}
var message string
for _, waitTime := range waitTimes {

View File

@ -42,22 +42,27 @@ func (self *Input) pressKey(keyStr string) {
func (self *Input) SwitchToStatusWindow() {
self.pressKey(self.keys.Universal.JumpToBlock[0])
self.assert.CurrentWindowName("status")
}
func (self *Input) SwitchToFilesWindow() {
self.pressKey(self.keys.Universal.JumpToBlock[1])
self.assert.CurrentWindowName("files")
}
func (self *Input) SwitchToBranchesWindow() {
self.pressKey(self.keys.Universal.JumpToBlock[2])
self.assert.CurrentWindowName("localBranches")
}
func (self *Input) SwitchToCommitsWindow() {
self.pressKey(self.keys.Universal.JumpToBlock[3])
self.assert.CurrentWindowName("commits")
}
func (self *Input) SwitchToStashWindow() {
self.pressKey(self.keys.Universal.JumpToBlock[4])
self.assert.CurrentWindowName("stash")
}
func (self *Input) Type(content string) {
@ -71,6 +76,17 @@ func (self *Input) Confirm() {
self.pressKey(self.keys.Universal.Confirm)
}
func (self *Input) ProceedWhenAsked(matcher *matcher) {
self.assert.InConfirm()
self.assert.MatchCurrentViewContent(matcher)
self.Confirm()
}
// i.e. same as Confirm
func (self *Input) Enter() {
self.pressKey(self.keys.Universal.Confirm)
}
// i.e. pressing escape
func (self *Input) Cancel() {
self.pressKey(self.keys.Universal.Return)
@ -132,35 +148,43 @@ func (self *Input) NavigateToListItemContainingText(text string) {
view := currentContext.GetView()
// first we look for a duplicate on the current screen. We won't bother looking beyond that though.
matchCount := 0
matchIndex := -1
for i, line := range view.ViewBufferLines() {
if strings.Contains(line, text) {
matchCount++
matchIndex = i
}
}
if matchCount > 1 {
self.assert.Fail(fmt.Sprintf("Found %d matches for %s, expected only a single match", matchCount, text))
}
if matchCount == 1 {
selectedLineIdx := view.SelectedLineIdx()
if selectedLineIdx == matchIndex {
return
}
if selectedLineIdx < matchIndex {
for i := selectedLineIdx; i < matchIndex; i++ {
self.NextItem()
}
return
} else {
for i := selectedLineIdx; i > matchIndex; i-- {
self.PreviousItem()
}
return
}
}
var matchIndex int
self.assert.Fail(fmt.Sprintf("Could not find item containing text: %s", text))
self.assert.assertWithRetries(func() (bool, string) {
matchCount := 0
matchIndex = -1
// first we look for a duplicate on the current screen. We won't bother looking beyond that though.
for i, line := range view.ViewBufferLines() {
if strings.Contains(line, text) {
matchCount++
matchIndex = i
}
}
if matchCount > 1 {
return false, fmt.Sprintf("Found %d matches for %s, expected only a single match", matchCount, text)
} else if matchCount == 0 {
return false, fmt.Sprintf("Could not find item containing text: %s", text)
} else {
return true, ""
}
})
selectedLineIdx := view.SelectedLineIdx()
if selectedLineIdx == matchIndex {
self.assert.MatchSelectedLine(Contains(text))
return
}
if selectedLineIdx < matchIndex {
for i := selectedLineIdx; i < matchIndex; i++ {
self.NextItem()
}
self.assert.MatchSelectedLine(Contains(text))
return
} else {
for i := selectedLineIdx; i > matchIndex; i-- {
self.PreviousItem()
}
self.assert.MatchSelectedLine(Contains(text))
return
}
}

View File

@ -42,6 +42,7 @@ func RunTests(
testWrapper func(test *IntegrationTest, f func() error),
mode Mode,
keyPressDelay int,
maxAttempts int,
) error {
projectRootDir := utils.GetLazygitRootDirectory()
err := os.Chdir(projectRootDir)
@ -58,12 +59,24 @@ func RunTests(
for _, test := range tests {
test := test
paths := NewPaths(
filepath.Join(testDir, test.Name()),
)
testWrapper(test, func() error { //nolint: thelper
return runTest(test, paths, projectRootDir, logf, runCmd, mode, keyPressDelay)
paths := NewPaths(
filepath.Join(testDir, test.Name()),
)
for i := 0; i < maxAttempts; i++ {
err := runTest(test, paths, projectRootDir, logf, runCmd, mode, keyPressDelay)
if err != nil {
if i == maxAttempts-1 {
return err
}
logf("retrying test %s", test.Name())
} else {
break
}
}
return nil
})
}
@ -126,16 +139,7 @@ func buildLazygit() error {
}
func createFixture(test *IntegrationTest, paths Paths) error {
originalDir, err := os.Getwd()
if err != nil {
return err
}
if err := os.Chdir(paths.ActualRepo()); err != nil {
panic(err)
}
shell := NewShell()
shell := NewShell(paths.ActualRepo())
shell.RunCommand("git init -b master")
shell.RunCommand(`git config user.email "CI@example.com"`)
shell.RunCommand(`git config user.name "CI"`)
@ -143,10 +147,6 @@ func createFixture(test *IntegrationTest, paths Paths) error {
test.SetupRepo(shell)
if err := os.Chdir(originalDir); err != nil {
panic(err)
}
return nil
}

View File

@ -2,8 +2,8 @@ package components
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"github.com/jesseduffield/lazygit/pkg/secureexec"
"github.com/mgutz/str"
@ -12,16 +12,20 @@ import (
// this is for running shell commands, mostly for the sake of setting up the repo
// but you can also run the commands from within lazygit to emulate things happening
// in the background.
type Shell struct{}
type Shell struct {
// working directory the shell is invoked in
dir string
}
func NewShell() *Shell {
return &Shell{}
func NewShell(dir string) *Shell {
return &Shell{dir: dir}
}
func (s *Shell) RunCommand(cmdStr string) *Shell {
args := str.ToArgv(cmdStr)
cmd := secureexec.Command(args[0], args[1:]...)
cmd.Env = os.Environ()
cmd.Dir = s.dir
output, err := cmd.CombinedOutput()
if err != nil {
@ -32,9 +36,20 @@ func (s *Shell) RunCommand(cmdStr string) *Shell {
}
func (s *Shell) CreateFile(path string, content string) *Shell {
err := ioutil.WriteFile(path, []byte(content), 0o644)
fullPath := filepath.Join(s.dir, path)
err := os.WriteFile(fullPath, []byte(content), 0o644)
if err != nil {
panic(fmt.Sprintf("error creating file: %s\n%s", path, err))
panic(fmt.Sprintf("error creating file: %s\n%s", fullPath, err))
}
return s
}
func (s *Shell) UpdateFile(path string, content string) *Shell {
fullPath := filepath.Join(s.dir, path)
err := os.WriteFile(fullPath, []byte(content), 0o644)
if err != nil {
panic(fmt.Sprintf("error updating file: %s\n%s", fullPath, err))
}
return s
@ -44,6 +59,14 @@ func (s *Shell) NewBranch(name string) *Shell {
return s.RunCommand("git checkout -b " + name)
}
func (s *Shell) Checkout(name string) *Shell {
return s.RunCommand("git checkout " + name)
}
func (s *Shell) Merge(name string) *Shell {
return s.RunCommand("git merge --commit --no-ff " + name)
}
func (s *Shell) GitAdd(path string) *Shell {
return s.RunCommand(fmt.Sprintf("git add \"%s\"", path))
}
@ -67,6 +90,13 @@ func (s *Shell) CreateFileAndAdd(fileName string, fileContents string) *Shell {
GitAdd(fileName)
}
// convenience method for updating a file and adding it
func (s *Shell) UpdateFileAndAdd(fileName string, fileContents string) *Shell {
return s.
UpdateFile(fileName, fileContents).
GitAdd(fileName)
}
// creates commits 01, 02, 03, ..., n with a new file in each
// The reason for padding with zeroes is so that it's easier to do string
// matches on the commit messages when there are many of them

View File

@ -168,13 +168,13 @@ func (self *Snapshotter) compareSnapshots() error {
if expectedRepo != actualRepo {
// get the log file and print it
bytes, err := ioutil.ReadFile(filepath.Join(self.paths.Config(), "development.log"))
bytes, err := os.ReadFile(filepath.Join(self.paths.Config(), "development.log"))
if err != nil {
return err
}
self.logf("%s", string(bytes))
return errors.New(getDiff(f.Name(), actualRepo, expectedRepo))
return errors.New(getDiff(f.Name(), expectedRepo, actualRepo))
}
}
@ -250,7 +250,7 @@ func generateSnapshot(dir string) (string, error) {
return nil
}
bytes, err := ioutil.ReadFile(path)
bytes, err := os.ReadFile(path)
if err != nil {
return err
}

View File

@ -93,7 +93,7 @@ func (self *IntegrationTest) SetupRepo(shell *Shell) {
// I want access to all contexts, the model, the ability to press a key, the ability to log,
func (self *IntegrationTest) Run(gui integrationTypes.GuiDriver) {
shell := NewShell()
shell := NewShell("/tmp/lazygit-test")
assert := NewAssert(gui)
keys := gui.Keys()
input := NewInput(gui, keys, assert, KeyPressDelay())

View File

@ -56,6 +56,10 @@ func (self *fakeGuiDriver) SecondaryView() *gocui.View {
return nil
}
func (self *fakeGuiDriver) View(viewName string) *gocui.View {
return nil
}
func TestAssertionFailure(t *testing.T) {
test := NewIntegrationTest(NewIntegrationTestArgs{
Description: unitTestDescription,