diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2022-08-08 21:32:58 +1000 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2022-08-11 21:24:16 +1000 |
commit | 225c563c630e8771c2c6741c78e8a427b3283f58 (patch) | |
tree | 315d535574b1e1aab2696c258427b3c5dcc12b30 | |
parent | 77881a9c7d24bb11bc74abff35d94397fd4ccb67 (diff) |
another integration test
46 files changed, 326 insertions, 26 deletions
diff --git a/pkg/gui/assert.go b/pkg/gui/assert.go index 43ecc26d8..253278220 100644 --- a/pkg/gui/assert.go +++ b/pkg/gui/assert.go @@ -2,8 +2,10 @@ package gui import ( "fmt" + "strings" "time" + guiTypes "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/integration/types" ) @@ -67,8 +69,23 @@ func (self *AssertImpl) CurrentBranchName(expectedViewName string) { }) } +func (self *AssertImpl) InListContext() { + self.assertWithRetries(func() (bool, string) { + currentContext := self.gui.currentContext() + _, ok := currentContext.(guiTypes.IListContext) + return ok, fmt.Sprintf("Expected current context to be a list context, but got %s", currentContext.GetKey()) + }) +} + +func (self *AssertImpl) SelectedLineContains(text string) { + self.assertWithRetries(func() (bool, string) { + line := self.gui.currentContext().GetView().SelectedLine() + return strings.Contains(line, text), fmt.Sprintf("Expected selected line to contain '%s', but got '%s'", text, line) + }) +} + func (self *AssertImpl) assertWithRetries(test func() (bool, string)) { - waitTimes := []int{0, 100, 200, 400, 800, 1600} + waitTimes := []int{0, 1, 5, 10, 200, 500, 1000} var message string for _, waitTime := range waitTimes { @@ -81,6 +98,10 @@ func (self *AssertImpl) assertWithRetries(test func() (bool, string)) { } } + self.Fail(message) +} + +func (self *AssertImpl) Fail(message string) { self.gui.g.Close() // need to give the gui time to close time.Sleep(time.Millisecond * 100) diff --git a/pkg/gui/input.go b/pkg/gui/input.go index c6424c077..111d0de8e 100644 --- a/pkg/gui/input.go +++ b/pkg/gui/input.go @@ -1,29 +1,45 @@ package gui import ( + "fmt" + "strings" "time" "github.com/gdamore/tcell/v2" "github.com/jesseduffield/gocui" "github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/gui/keybindings" + guiTypes "github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/integration/types" ) type InputImpl struct { - g *gocui.Gui - keys config.KeybindingConfig + gui *Gui + keys config.KeybindingConfig + assert types.Assert + pushKeyDelay int +} + +func NewInputImpl(gui *Gui, keys config.KeybindingConfig, assert types.Assert, pushKeyDelay int) *InputImpl { + return &InputImpl{ + gui: gui, + keys: keys, + assert: assert, + pushKeyDelay: pushKeyDelay, + } } var _ types.Input = &InputImpl{} -func (self *InputImpl) PushKeys(keyStrs ...string) { +func (self *InputImpl) PressKeys(keyStrs ...string) { for _, keyStr := range keyStrs { - self.pushKey(keyStr) + self.pressKey(keyStr) } } -func (self *InputImpl) pushKey(keyStr string) { +func (self *InputImpl) pressKey(keyStr string) { + self.Wait(self.pushKeyDelay) + key := keybindings.GetKey(keyStr) var r rune @@ -36,58 +52,115 @@ func (self *InputImpl) pushKey(keyStr string) { tcellKey = tcell.Key(v) } - self.g.ReplayedEvents.Keys <- gocui.NewTcellKeyEventWrapper( + self.gui.g.ReplayedEvents.Keys <- gocui.NewTcellKeyEventWrapper( tcell.NewEventKey(tcellKey, r, tcell.ModNone), 0, ) } func (self *InputImpl) SwitchToStatusWindow() { - self.pushKey(self.keys.Universal.JumpToBlock[0]) + self.pressKey(self.keys.Universal.JumpToBlock[0]) } func (self *InputImpl) SwitchToFilesWindow() { - self.pushKey(self.keys.Universal.JumpToBlock[1]) + self.pressKey(self.keys.Universal.JumpToBlock[1]) } func (self *InputImpl) SwitchToBranchesWindow() { - self.pushKey(self.keys.Universal.JumpToBlock[2]) + self.pressKey(self.keys.Universal.JumpToBlock[2]) } func (self *InputImpl) SwitchToCommitsWindow() { - self.pushKey(self.keys.Universal.JumpToBlock[3]) + self.pressKey(self.keys.Universal.JumpToBlock[3]) } func (self *InputImpl) SwitchToStashWindow() { - self.pushKey(self.keys.Universal.JumpToBlock[4]) + self.pressKey(self.keys.Universal.JumpToBlock[4]) } func (self *InputImpl) Type(content string) { for _, char := range content { - self.pushKey(string(char)) + self.pressKey(string(char)) } } func (self *InputImpl) Confirm() { - self.pushKey(self.keys.Universal.Confirm) + self.pressKey(self.keys.Universal.Confirm) } func (self *InputImpl) Cancel() { - self.pushKey(self.keys.Universal.Return) + self.pressKey(self.keys.Universal.Return) } func (self *InputImpl) Select() { - self.pushKey(self.keys.Universal.Select) + self.pressKey(self.keys.Universal.Select) } func (self *InputImpl) NextItem() { - self.pushKey(self.keys.Universal.NextItem) + self.pressKey(self.keys.Universal.NextItem) } func (self *InputImpl) PreviousItem() { - self.pushKey(self.keys.Universal.PrevItem) + self.pressKey(self.keys.Universal.PrevItem) +} + +func (self *InputImpl) ContinueMerge() { + self.PressKeys(self.keys.Universal.CreateRebaseOptionsMenu) + self.assert.SelectedLineContains("continue") + self.Confirm() +} + +func (self *InputImpl) ContinueRebase() { + self.ContinueMerge() } func (self *InputImpl) Wait(milliseconds int) { time.Sleep(time.Duration(milliseconds) * time.Millisecond) } + +func (self *InputImpl) log(message string) { + self.gui.c.LogAction(message) +} + +// NOTE: this currently assumes that ViewBufferLines returns all the lines that can be accessed. +// If this changes in future, we'll need to update this code to first attempt to find the item +// in the current page and failing that, jump to the top of the view and iterate through all of it, +// looking for the item. +func (self *InputImpl) NavigateToListItemContainingText(text string) { + self.assert.InListContext() + + currentContext := self.gui.currentContext().(guiTypes.IListContext) + 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 + } + } + + self.assert.Fail(fmt.Sprintf("Could not find item containing text: %s", text)) +} diff --git a/pkg/gui/test_mode.go b/pkg/gui/test_mode.go index 88e42e440..7f57a4be8 100644 --- a/pkg/gui/test_mode.go +++ b/pkg/gui/test_mode.go @@ -21,10 +21,15 @@ func (gui *Gui) handleTestMode() { go func() { time.Sleep(time.Millisecond * 100) + shell := &integration.ShellImpl{} + assert := &AssertImpl{gui: gui} + keys := gui.Config.GetUserConfig().Keybinding + input := NewInputImpl(gui, keys, assert, integration.KeyPressDelay()) + test.Run( - &integration.ShellImpl{}, - &InputImpl{g: gui.g, keys: gui.Config.GetUserConfig().Keybinding}, - &AssertImpl{gui: gui}, + shell, + input, + assert, gui.c.UserConfig.Keybinding, ) diff --git a/pkg/integration/env.go b/pkg/integration/env.go index 7cdc72267..0c0d0b196 100644 --- a/pkg/integration/env.go +++ b/pkg/integration/env.go @@ -2,6 +2,7 @@ package integration import ( "os" + "strconv" "github.com/jesseduffield/generics/slices" "github.com/jesseduffield/lazygit/pkg/integration/types" @@ -31,6 +32,21 @@ func PlayingIntegrationTest() bool { return IntegrationTestName() != "" } +// this is the delay in milliseconds between keypresses +// defaults to zero +func KeyPressDelay() int { + delayStr := os.Getenv("KEY_PRESS_DELAY") + if delayStr == "" { + return 0 + } + + delay, err := strconv.Atoi(delayStr) + if err != nil { + panic(err) + } + return delay +} + // OLD integration test format stuff func Replaying() bool { diff --git a/pkg/integration/integration_tests/branch/suggestions.go b/pkg/integration/integration_tests/branch/suggestions.go index ff37ec9be..47c360514 100644 --- a/pkg/integration/integration_tests/branch/suggestions.go +++ b/pkg/integration/integration_tests/branch/suggestions.go @@ -23,12 +23,12 @@ var Suggestions = types.NewTest(types.NewTestArgs{ Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) { input.SwitchToBranchesWindow() - input.PushKeys(keys.Branches.CheckoutBranchByName) + input.PressKeys(keys.Branches.CheckoutBranchByName) assert.CurrentViewName("confirmation") input.Type("branch-to") - input.PushKeys(keys.Universal.TogglePanel) + input.PressKeys(keys.Universal.TogglePanel) assert.CurrentViewName("suggestions") // we expect the first suggestion to be the branch we want because it most diff --git a/pkg/integration/integration_tests/commit/commit.go b/pkg/integration/integration_tests/commit/commit.go index 777653bc0..f0af9462a 100644 --- a/pkg/integration/integration_tests/commit/commit.go +++ b/pkg/integration/integration_tests/commit/commit.go @@ -20,7 +20,7 @@ var Commit = types.NewTest(types.NewTestArgs{ input.Select() input.NextItem() input.Select() - input.PushKeys(keys.Files.CommitChanges) + input.PressKeys(keys.Files.CommitChanges) commitMessage := "my commit message" input.Type(commitMessage) diff --git a/pkg/integration/integration_tests/commit/new_branch.go b/pkg/integration/integration_tests/commit/new_branch.go index d70515280..218e95180 100644 --- a/pkg/integration/integration_tests/commit/new_branch.go +++ b/pkg/integration/integration_tests/commit/new_branch.go @@ -23,7 +23,7 @@ var NewBranch = types.NewTest(types.NewTestArgs{ assert.CurrentViewName("commits") input.NextItem() - input.PushKeys(keys.Universal.New) + input.PressKeys(keys.Universal.New) assert.CurrentViewName("confirmation") diff --git a/pkg/integration/integration_tests/interactive_rebase/one.go b/pkg/integration/integration_tests/interactive_rebase/one.go new file mode 100644 index 000000000..92780b24d --- /dev/null +++ b/pkg/integration/integration_tests/interactive_rebase/one.go @@ -0,0 +1,41 @@ +package interactive_rebase + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +var One = types.NewTest(types.NewTestArgs{ + Description: "Begins an interactive rebase, then fixups, drops, and squashes some commits", + ExtraCmdArgs: "", + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell types.Shell) { + shell. + CreateNCommits(5) // these will appears at commit 05, 04, 04, down to 01 + }, + Run: func(shell types.Shell, input types.Input, assert types.Assert, keys config.KeybindingConfig) { + input.SwitchToCommitsWindow() + assert.CurrentViewName("commits") + + input.NavigateToListItemContainingText("commit 02") + input.PressKeys(keys.Universal.Edit) + assert.SelectedLineContains("YOU ARE HERE") + + input.PreviousItem() + input.PressKeys(keys.Commits.MarkCommitAsFixup) + assert.SelectedLineContains("fixup") + + input.PreviousItem() + input.PressKeys(keys.Universal.Remove) + assert.SelectedLineContains("drop") + + input.PreviousItem() + input.PressKeys(keys.Commits.SquashDown) + assert.SelectedLineContains("squash") + + input.ContinueRebase() + + assert.CommitCount(2) + }, +}) diff --git a/pkg/integration/integration_tests/tests.go b/pkg/integration/integration_tests/tests.go index 1470ef178..752929698 100644 --- a/pkg/integration/integration_tests/tests.go +++ b/pkg/integration/integration_tests/tests.go @@ -3,6 +3,8 @@ package integration_tests import ( "github.com/jesseduffield/lazygit/pkg/integration/integration_tests/branch" "github.com/jesseduffield/lazygit/pkg/integration/integration_tests/commit" + "github.com/jesseduffield/lazygit/pkg/integration/integration_tests/interactive_rebase" + "github.com/jesseduffield/lazygit/pkg/integration/types" ) @@ -13,4 +15,5 @@ var Tests = []types.Test{ commit.Commit, commit.NewBranch, branch.Suggestions, + interactive_rebase.One, } diff --git a/pkg/integration/shell.go b/pkg/integration/shell.go index 69bccb66c..70da9c27b 100644 --- a/pkg/integration/shell.go +++ b/pkg/integration/shell.go @@ -40,6 +40,10 @@ func (s *ShellImpl) NewBranch(name string) types.Shell { return s.RunCommand("git checkout -b " + name) } +func (s *ShellImpl) GitAdd(path string) types.Shell { + return s.RunCommand(fmt.Sprintf("git add \"%s\"", path)) +} + func (s *ShellImpl) GitAddAll() types.Shell { return s.RunCommand("git add -A") } @@ -51,3 +55,21 @@ func (s *ShellImpl) Commit(message string) types.Shell { func (s *ShellImpl) EmptyCommit(message string) types.Shell { return s.RunCommand(fmt.Sprintf("git commit --allow-empty -m \"%s\"", message)) } + +func (s *ShellImpl) CreateFileAndAdd(fileName string, fileContents string) types.Shell { + return s. + CreateFile(fileName, fileContents). + GitAdd(fileName) +} + +func (s *ShellImpl) CreateNCommits(n int) types.Shell { + for i := 1; i <= n; i++ { + s.CreateFileAndAdd( + fmt.Sprintf("file%02d.txt", i), + fmt.Sprintf("file%02d content", i), + ). + Commit(fmt.Sprintf("commit %02d", i)) + } + + return s +} diff --git a/pkg/integration/types/types.go b/pkg/integration/types/types.go index 29b303f35..60fd4f353 100644 --- a/pkg/integration/types/types.go +++ b/pkg/integration/types/types.go @@ -31,9 +31,16 @@ type Shell interface { RunCommand(command string) Shell CreateFile(name string, content string) Shell NewBranch(branchName string) Shell + GitAdd(path string) Shell GitAddAll() Shell Commit(message string) Shell EmptyCommit(message string) Shell + // convenience method for creating a file and adding it + CreateFileAndAdd(fileName string, fileContents string) Shell + // 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 + CreateNCommits(n int) Shell } // through this interface our test interacts with the lazygit gui @@ -41,7 +48,7 @@ type Shell interface { type Input interface { // key is something like 'w' or '<space>'. It's best not to pass a direct value, // but instead to go through the default user config to get a more meaningful key name - PushKeys(keys ...string) + PressKeys(keys ...string) // for typing into a popup prompt Type(content string) // for when you want to allow lazygit to process something before continuing @@ -62,6 +69,15 @@ type Input interface { NextItem() // i.e. pressing up arrow PreviousItem() + // this will look for a list item in the current panel and if it finds it, it will + // enter the keypresses required to navigate to it. + // The test will fail if: + // - the user is not in a list item + // - no list item is found containing the given text + // - multiple list items are found containing the given text in the initial page of items + NavigateToListItemContainingText(text string) + ContinueRebase() + ContinueMerge() } // through this interface we assert on the state of the lazygit gui @@ -71,6 +87,10 @@ type Assert interface { HeadCommitMessage(string) CurrentViewName(expectedViewName string) CurrentBranchName(expectedBranchName string) + InListContext() + SelectedLineContains(text string) + // for when you just want to fail the test yourself + Fail(errorMessage string) } type TestImpl struct { diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/COMMIT_EDITMSG b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/COMMIT_EDITMSG new file mode 100644 index 000000000..9e0c89102 --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/COMMIT_EDITMSG @@ -0,0 +1,30 @@ +# This is a combination of 3 commits. +# This is the 1st commit message: + +commit 02 + +# The commit message #2 will be skipped: + +# commit 03 + +# This is the commit message #3: + +commit 05 + +# Please enter the commit message for your changes. Lines starting +# with '#' will be ignored, and an empty message aborts the commit. +# +# Date: Mon Aug 8 21:32:34 2022 +1000 +# +# interactive rebase in progress; onto a1a6f7b +# Last commands done (4 commands done): +# drop 84b1ae9 commit 04 +# squash aa2585a commit 05 +# No commands remaining. +# You are currently rebasing branch 'master' on 'a1a6f7b'. +# +# Changes to be committed: +# new file: file02.txt +# new file: file03.txt +# new file: file05.txt +# diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/FETCH_HEAD b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/FETCH_HEAD new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/FETCH_HEAD diff --git a/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/HEAD b/test/integration_new/interactive_rebase/one/expected/repo/.git_keep/HEAD new file mode 100644 index 000000000..cb089cd89 |