1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-25 12:24:47 +02:00

a more complex custom command test

This commit is contained in:
Jesse Duffield 2022-08-14 20:13:39 +10:00
parent 9c0d860980
commit 53979f7cec
17 changed files with 263 additions and 43 deletions

View File

@ -71,3 +71,11 @@ func (self *GuiDriver) LogUI(message string) {
func (self *GuiDriver) CheckedOutRef() *models.Branch { func (self *GuiDriver) CheckedOutRef() *models.Branch {
return self.gui.helpers.Refs.GetCheckedOutRef() return self.gui.helpers.Refs.GetCheckedOutRef()
} }
func (self *GuiDriver) MainView() *gocui.View {
return self.gui.mainView()
}
func (self *GuiDriver) SecondaryView() *gocui.View {
return self.gui.secondaryView()
}

View File

@ -7,6 +7,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
"golang.org/x/exp/constraints"
) )
// through this struct we assert on the state of the lazygit gui // through this struct we assert on the state of the lazygit gui
@ -19,6 +20,43 @@ func NewAssert(gui integrationTypes.GuiDriver) *Assert {
return &Assert{gui: gui} return &Assert{gui: gui}
} }
// for making assertions on string values
type matcher[T any] struct {
testFn func(T) (bool, string)
prefix string
}
func (self *matcher[T]) test(value T) (bool, string) {
ok, message := self.testFn(value)
if ok {
return true, ""
}
if self.prefix != "" {
return false, self.prefix + " " + message
}
return false, message
}
func (self *matcher[T]) context(prefix string) *matcher[T] {
self.prefix = prefix
return self
}
func Contains(target string) *matcher[string] {
return &matcher[string]{testFn: func(value string) (bool, string) {
return strings.Contains(value, target), fmt.Sprintf("Expected '%s' to contain '%s'", value, target)
}}
}
func Equals[T constraints.Ordered](target T) *matcher[T] {
return &matcher[T]{testFn: func(value T) (bool, string) {
return target == value, fmt.Sprintf("Expected '%T' to equal '%T'", value, target)
}}
}
func (self *Assert) WorkingTreeFileCount(expectedCount int) { func (self *Assert) WorkingTreeFileCount(expectedCount int) {
self.assertWithRetries(func() (bool, string) { self.assertWithRetries(func() (bool, string) {
actualCount := len(self.gui.Model().Files) actualCount := len(self.gui.Model().Files)
@ -41,22 +79,16 @@ func (self *Assert) CommitCount(expectedCount int) {
}) })
} }
func (self *Assert) HeadCommitMessage(expectedMessage string) { func (self *Assert) MatchHeadCommitMessage(matcher *matcher[string]) {
self.assertWithRetries(func() (bool, string) { self.assertWithRetries(func() (bool, string) {
if len(self.gui.Model().Commits) == 0 { return len(self.gui.Model().Commits) == 0, "Expected at least one commit to be present"
return false, "Expected at least one commit to be present"
}
headCommit := self.gui.Model().Commits[0]
if headCommit.Name != expectedMessage {
return false, fmt.Sprintf(
"Expected commit message to be '%s', but got '%s'",
expectedMessage, headCommit.Name,
)
}
return true, ""
}) })
self.matchString(matcher, "Unexpected commit message.",
func() string {
return self.gui.Model().Commits[0].Name
},
)
} }
func (self *Assert) CurrentViewName(expectedViewName string) { func (self *Assert) CurrentViewName(expectedViewName string) {
@ -81,10 +113,70 @@ func (self *Assert) InListContext() {
}) })
} }
func (self *Assert) SelectedLineContains(text string) { func (self *Assert) MatchSelectedLine(matcher *matcher[string]) {
self.matchString(matcher, "Unexpected selected line.",
func() string {
return self.gui.CurrentContext().GetView().SelectedLine()
},
)
}
func (self *Assert) InPrompt() {
self.assertWithRetries(func() (bool, string) { self.assertWithRetries(func() (bool, string) {
line := self.gui.CurrentContext().GetView().SelectedLine() currentView := self.gui.CurrentContext().GetView()
return strings.Contains(line, text), fmt.Sprintf("Expected selected line to contain '%s', but got '%s'", text, line) return currentView.Name() == "confirmation" && currentView.Editable, fmt.Sprintf("Expected prompt popup to be focused")
})
}
func (self *Assert) InConfirm() {
self.assertWithRetries(func() (bool, string) {
currentView := self.gui.CurrentContext().GetView()
return currentView.Name() == "confirmation" && !currentView.Editable, fmt.Sprintf("Expected confirmation popup to be focused")
})
}
func (self *Assert) InAlert() {
// basically the same thing as a confirmation popup with the current implementation
self.assertWithRetries(func() (bool, string) {
currentView := self.gui.CurrentContext().GetView()
return currentView.Name() == "confirmation" && !currentView.Editable, fmt.Sprintf("Expected alert popup to be focused")
})
}
func (self *Assert) InMenu() {
self.assertWithRetries(func() (bool, string) {
return self.gui.CurrentContext().GetView().Name() == "menu", fmt.Sprintf("Expected popup menu to be focused")
})
}
func (self *Assert) MatchCurrentViewTitle(matcher *matcher[string]) {
self.matchString(matcher, "Unexpected current view title.",
func() string {
return self.gui.CurrentContext().GetView().Title
},
)
}
func (self *Assert) MatchMainViewContent(matcher *matcher[string]) {
self.matchString(matcher, "Unexpected main view content.",
func() string {
return self.gui.MainView().Buffer()
},
)
}
func (self *Assert) MatchSecondaryViewContent(matcher *matcher[string]) {
self.matchString(matcher, "Unexpected secondary view title.",
func() string {
return self.gui.SecondaryView().Buffer()
},
)
}
func (self *Assert) matchString(matcher *matcher[string], context string, getValue func() string) {
self.assertWithRetries(func() (bool, string) {
value := getValue()
return matcher.context(context).test(value)
}) })
} }

View File

@ -93,7 +93,7 @@ func (self *Input) PreviousItem() {
func (self *Input) ContinueMerge() { func (self *Input) ContinueMerge() {
self.PressKeys(self.keys.Universal.CreateRebaseOptionsMenu) self.PressKeys(self.keys.Universal.CreateRebaseOptionsMenu)
self.assert.SelectedLineContains("continue") self.assert.MatchSelectedLine(Contains("continue"))
self.Confirm() self.Confirm()
} }

View File

@ -3,6 +3,7 @@ package components
import ( import (
"testing" "testing"
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
@ -47,6 +48,14 @@ func (self *fakeGuiDriver) CheckedOutRef() *models.Branch {
return nil return nil
} }
func (self *fakeGuiDriver) MainView() *gocui.View {
return nil
}
func (self *fakeGuiDriver) SecondaryView() *gocui.View {
return nil
}
func TestAssertionFailure(t *testing.T) { func TestAssertionFailure(t *testing.T) {
test := NewIntegrationTest(NewIntegrationTestArgs{ test := NewIntegrationTest(NewIntegrationTestArgs{
Description: unitTestDescription, Description: unitTestDescription,

View File

@ -2,19 +2,19 @@ package commit
import ( import (
"github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/integration/components" . "github.com/jesseduffield/lazygit/pkg/integration/components"
) )
var Commit = components.NewIntegrationTest(components.NewIntegrationTestArgs{ var Commit = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Staging a couple files and committing", Description: "Staging a couple files and committing",
ExtraCmdArgs: "", ExtraCmdArgs: "",
Skip: false, Skip: false,
SetupConfig: func(config *config.AppConfig) {}, SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *components.Shell) { SetupRepo: func(shell *Shell) {
shell.CreateFile("myfile", "myfile content") shell.CreateFile("myfile", "myfile content")
shell.CreateFile("myfile2", "myfile2 content") shell.CreateFile("myfile2", "myfile2 content")
}, },
Run: func(shell *components.Shell, input *components.Input, assert *components.Assert, keys config.KeybindingConfig) { Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
assert.CommitCount(0) assert.CommitCount(0)
input.Select() input.Select()
@ -27,6 +27,6 @@ var Commit = components.NewIntegrationTest(components.NewIntegrationTestArgs{
input.Confirm() input.Confirm()
assert.CommitCount(1) assert.CommitCount(1)
assert.HeadCommitMessage(commitMessage) assert.MatchHeadCommitMessage(Equals(commitMessage))
}, },
}) })

View File

@ -2,21 +2,21 @@ package commit
import ( import (
"github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/integration/components" . "github.com/jesseduffield/lazygit/pkg/integration/components"
) )
var NewBranch = components.NewIntegrationTest(components.NewIntegrationTestArgs{ var NewBranch = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Creating a new branch from a commit", Description: "Creating a new branch from a commit",
ExtraCmdArgs: "", ExtraCmdArgs: "",
Skip: false, Skip: false,
SetupConfig: func(config *config.AppConfig) {}, SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *components.Shell) { SetupRepo: func(shell *Shell) {
shell. shell.
EmptyCommit("commit 1"). EmptyCommit("commit 1").
EmptyCommit("commit 2"). EmptyCommit("commit 2").
EmptyCommit("commit 3") EmptyCommit("commit 3")
}, },
Run: func(shell *components.Shell, input *components.Input, assert *components.Assert, keys config.KeybindingConfig) { Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
assert.CommitCount(3) assert.CommitCount(3)
input.SwitchToCommitsWindow() input.SwitchToCommitsWindow()
@ -32,7 +32,7 @@ var NewBranch = components.NewIntegrationTest(components.NewIntegrationTestArgs{
input.Confirm() input.Confirm()
assert.CommitCount(2) assert.CommitCount(2)
assert.HeadCommitMessage("commit 2") assert.MatchHeadCommitMessage(Contains("commit 2"))
assert.CurrentBranchName(branchName) assert.CurrentBranchName(branchName)
}, },
}) })

View File

@ -2,14 +2,14 @@ package custom_commands
import ( import (
"github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/integration/components" . "github.com/jesseduffield/lazygit/pkg/integration/components"
) )
var Basic = components.NewIntegrationTest(components.NewIntegrationTestArgs{ var Basic = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Using a custom command to create a new file", Description: "Using a custom command to create a new file",
ExtraCmdArgs: "", ExtraCmdArgs: "",
Skip: false, Skip: false,
SetupRepo: func(shell *components.Shell) {}, SetupRepo: func(shell *Shell) {},
SetupConfig: func(cfg *config.AppConfig) { SetupConfig: func(cfg *config.AppConfig) {
cfg.UserConfig.CustomCommands = []config.CustomCommand{ cfg.UserConfig.CustomCommands = []config.CustomCommand{
{ {
@ -20,15 +20,15 @@ var Basic = components.NewIntegrationTest(components.NewIntegrationTestArgs{
} }
}, },
Run: func( Run: func(
shell *components.Shell, shell *Shell,
input *components.Input, input *Input,
assert *components.Assert, assert *Assert,
keys config.KeybindingConfig, keys config.KeybindingConfig,
) { ) {
assert.WorkingTreeFileCount(0) assert.WorkingTreeFileCount(0)
input.PressKeys("a") input.PressKeys("a")
assert.WorkingTreeFileCount(1) assert.WorkingTreeFileCount(1)
assert.SelectedLineContains("myfile") assert.MatchSelectedLine(Contains("myfile"))
}, },
}) })

View File

@ -0,0 +1,84 @@
package custom_commands
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var MultiplePrompts = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Using a custom command with multiple prompts",
ExtraCmdArgs: "",
Skip: false,
SetupRepo: func(shell *Shell) {},
SetupConfig: func(cfg *config.AppConfig) {
cfg.UserConfig.CustomCommands = []config.CustomCommand{
{
Key: "a",
Context: "files",
Command: `echo "{{index .PromptResponses 1}}" > {{index .PromptResponses 0}}`,
Prompts: []config.CustomCommandPrompt{
{
Type: "input",
Title: "Enter a file name",
},
{
Type: "menu",
Title: "Choose file content",
Options: []config.CustomCommandMenuOption{
{
Name: "foo",
Description: "Foo",
Value: "FOO",
},
{
Name: "bar",
Description: "Bar",
Value: "BAR",
},
{
Name: "baz",
Description: "Baz",
Value: "BAZ",
},
},
},
{
Type: "confirm",
Title: "Are you sure?",
Body: "Are you REALLY sure you want to make this file? Up to you buddy.",
},
},
},
}
},
Run: func(
shell *Shell,
input *Input,
assert *Assert,
keys config.KeybindingConfig,
) {
assert.WorkingTreeFileCount(0)
input.PressKeys("a")
assert.InPrompt()
assert.MatchCurrentViewTitle(Equals("Enter a file name"))
input.Type("myfile")
input.Confirm()
assert.InMenu()
assert.MatchCurrentViewTitle(Equals("Choose file content"))
assert.MatchSelectedLine(Contains("foo"))
input.NextItem()
assert.MatchSelectedLine(Contains("bar"))
input.Confirm()
assert.InConfirm()
assert.MatchCurrentViewTitle(Equals("Are you sure?"))
input.Confirm()
assert.WorkingTreeFileCount(1)
assert.MatchSelectedLine(Contains("myfile"))
assert.MatchMainViewContent(Contains("BAR"))
},
})

View File

@ -2,37 +2,37 @@ package interactive_rebase
import ( import (
"github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/integration/components" . "github.com/jesseduffield/lazygit/pkg/integration/components"
) )
var One = components.NewIntegrationTest(components.NewIntegrationTestArgs{ var One = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Begins an interactive rebase, then fixups, drops, and squashes some commits", Description: "Begins an interactive rebase, then fixups, drops, and squashes some commits",
ExtraCmdArgs: "", ExtraCmdArgs: "",
Skip: false, Skip: false,
SetupConfig: func(config *config.AppConfig) {}, SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *components.Shell) { SetupRepo: func(shell *Shell) {
shell. shell.
CreateNCommits(5) // these will appears at commit 05, 04, 04, down to 01 CreateNCommits(5) // these will appears at commit 05, 04, 04, down to 01
}, },
Run: func(shell *components.Shell, input *components.Input, assert *components.Assert, keys config.KeybindingConfig) { Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
input.SwitchToCommitsWindow() input.SwitchToCommitsWindow()
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
input.NavigateToListItemContainingText("commit 02") input.NavigateToListItemContainingText("commit 02")
input.PressKeys(keys.Universal.Edit) input.PressKeys(keys.Universal.Edit)
assert.SelectedLineContains("YOU ARE HERE") assert.MatchSelectedLine(Contains("YOU ARE HERE"))
input.PreviousItem() input.PreviousItem()
input.PressKeys(keys.Commits.MarkCommitAsFixup) input.PressKeys(keys.Commits.MarkCommitAsFixup)
assert.SelectedLineContains("fixup") assert.MatchSelectedLine(Contains("fixup"))
input.PreviousItem() input.PreviousItem()
input.PressKeys(keys.Universal.Remove) input.PressKeys(keys.Universal.Remove)
assert.SelectedLineContains("drop") assert.MatchSelectedLine(Contains("drop"))
input.PreviousItem() input.PreviousItem()
input.PressKeys(keys.Commits.SquashDown) input.PressKeys(keys.Commits.SquashDown)
assert.SelectedLineContains("squash") assert.MatchSelectedLine(Contains("squash"))
input.ContinueRebase() input.ContinueRebase()

View File

@ -17,4 +17,5 @@ var Tests = []*components.IntegrationTest{
branch.Suggestions, branch.Suggestions,
interactive_rebase.One, interactive_rebase.One,
custom_commands.Basic, custom_commands.Basic,
custom_commands.MultiplePrompts,
} }

View File

@ -1,6 +1,7 @@
package types package types
import ( import (
"github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/commands/models" "github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/config" "github.com/jesseduffield/lazygit/pkg/config"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
@ -28,4 +29,9 @@ type GuiDriver interface {
// logs in the actual UI (in the commands panel) // logs in the actual UI (in the commands panel)
LogUI(message string) LogUI(message string)
CheckedOutRef() *models.Branch CheckedOutRef() *models.Branch
// the view that appears to the right of the side panel
MainView() *gocui.View
// the other view that sometimes appears to the right of the side panel
// e.g. when we're showing both staged and unstaged changes
SecondaryView() *gocui.View
} }

View File

@ -0,0 +1 @@
ref: refs/heads/master

View File

@ -0,0 +1,10 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[user]
email = CI@example.com
name = CI

View File

@ -0,0 +1 @@
Unnamed repository; edit this file 'description' to name the repository.

View File

@ -0,0 +1,7 @@
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
.DS_Store