diff options
author | Jesse Duffield <jessedduffield@gmail.com> | 2022-12-27 15:22:31 +1100 |
---|---|---|
committer | Jesse Duffield <jessedduffield@gmail.com> | 2022-12-27 21:26:18 +1100 |
commit | 09e80e5f2a56e5d13262e6d01c68cb4054bad6f4 (patch) | |
tree | 84e9280ce674ead35e15348d0e6f6fa54d0ebdea /pkg/integration | |
parent | be30cbb37509f09c53265aa39d89cac1f6cd65c2 (diff) |
better namespacing for assertions
Diffstat (limited to 'pkg/integration')
36 files changed, 328 insertions, 294 deletions
diff --git a/pkg/integration/components/alert_asserter.go b/pkg/integration/components/alert_asserter.go index 9dc2d32ab..8ca027b59 100644 --- a/pkg/integration/components/alert_asserter.go +++ b/pkg/integration/components/alert_asserter.go @@ -7,7 +7,7 @@ type AlertAsserter struct { hasCheckedContent bool } -func (self *AlertAsserter) getViewAsserter() *ViewAsserter { +func (self *AlertAsserter) getViewAsserter() *Views { return self.assert.Views().ByName("confirmation") } diff --git a/pkg/integration/components/assert.go b/pkg/integration/components/assert.go index b4be669c5..42ef90d6f 100644 --- a/pkg/integration/components/assert.go +++ b/pkg/integration/components/assert.go @@ -1,12 +1,6 @@ package components import ( - "fmt" - "os" - "time" - - "github.com/jesseduffield/gocui" - "github.com/jesseduffield/lazygit/pkg/gui/types" integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" ) @@ -14,212 +8,30 @@ import ( type Assert struct { gui integrationTypes.GuiDriver + *assertionHelper } func NewAssert(gui integrationTypes.GuiDriver) *Assert { return &Assert{gui: gui} } -func (self *Assert) WorkingTreeFileCount(expectedCount int) { - self.assertWithRetries(func() (bool, string) { - actualCount := len(self.gui.Model().Files) - - return actualCount == expectedCount, fmt.Sprintf( - "Expected %d changed working tree files, but got %d", - expectedCount, actualCount, - ) - }) -} - -func (self *Assert) CommitCount(expectedCount int) { - self.assertWithRetries(func() (bool, string) { - actualCount := len(self.gui.Model().Commits) - - return actualCount == expectedCount, fmt.Sprintf( - "Expected %d commits present, but got %d", - expectedCount, actualCount, - ) - }) -} - -func (self *Assert) StashCount(expectedCount int) { - self.assertWithRetries(func() (bool, string) { - actualCount := len(self.gui.Model().StashEntries) - - return actualCount == expectedCount, fmt.Sprintf( - "Expected %d stash entries, but got %d", - expectedCount, actualCount, - ) - }) -} - -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) HeadCommitMessage(matcher *matcher) { - self.assertWithRetries(func() (bool, string) { - return len(self.gui.Model().Commits) > 0, "Expected at least one commit to be present" - }) - - self.matchString(matcher, "Unexpected commit message.", - func() string { - return self.gui.Model().Commits[0].Name - }, - ) -} - -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 - return actual == expectedViewName, fmt.Sprintf("Expected current branch name to be '%s', but got '%s'", expectedViewName, actual) - }) -} - -func (self *Assert) 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()) - }) -} - -func (self *Assert) InPrompt() { - self.assertWithRetries(func() (bool, string) { - currentView := self.gui.CurrentContext().GetView() - return currentView.Name() == "confirmation" && currentView.Editable, "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, "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, "Expected alert popup to be focused" - }) -} - -func (self *Assert) InCommitMessagePanel() { - self.assertWithRetries(func() (bool, string) { - currentView := self.gui.CurrentContext().GetView() - return currentView.Name() == "commitMessage", "Expected commit message panel to be focused" - }) -} - -func (self *Assert) InMenu() { - self.assertWithRetries(func() (bool, string) { - return self.gui.CurrentContext().GetView().Name() == "menu", "Expected popup menu to be focused" - }) -} - -func (self *Assert) NotInPopup() { - self.assertWithRetries(func() (bool, string) { - currentViewName := self.gui.CurrentContext().GetView().Name() - return currentViewName != "menu" && currentViewName != "confirmation" && currentViewName != "commitMessage", "Expected popup not to be focused" - }) -} - -func (self *Assert) matchString(matcher *matcher, context string, getValue func() string) { - self.assertWithRetries(func() (bool, string) { - value := getValue() - return matcher.context(context).test(value) - }) -} - -func (self *Assert) assertWithRetries(test func() (bool, string)) { - waitTimes := []int{0, 1, 1, 1, 1, 1, 5, 10, 20, 40, 100, 200, 500, 1000, 2000, 4000} - - var message string - for _, waitTime := range waitTimes { - time.Sleep(time.Duration(waitTime) * time.Millisecond) - - var ok bool - ok, message = test() - if ok { - return - } - } - - self.Fail(message) -} - -// for when you just want to fail the test yourself -func (self *Assert) Fail(message string) { - self.gui.Fail(message) -} - -// This does _not_ check the files panel, it actually checks the filesystem -func (self *Assert) FileSystemPathPresent(path string) { - self.assertWithRetries(func() (bool, string) { - _, err := os.Stat(path) - return err == nil, fmt.Sprintf("Expected path '%s' to exist, but it does not", path) - }) -} - -// This does _not_ check the files panel, it actually checks the filesystem -func (self *Assert) FileSystemPathNotPresent(path string) { - self.assertWithRetries(func() (bool, string) { - _, err := os.Stat(path) - return os.IsNotExist(err), fmt.Sprintf("Expected path '%s' to not exist, but it does", path) - }) -} - +// for making assertions on lazygit views func (self *Assert) Views() *ViewAsserterGetter { - return &ViewAsserterGetter{ - assert: self, - } -} - -type ViewAsserterGetter struct { - assert *Assert + return &ViewAsserterGetter{assert: self} } -func (self *ViewAsserterGetter) Current() *ViewAsserter { - return &ViewAsserter{ - context: "current view", - getView: func() *gocui.View { return self.assert.gui.CurrentContext().GetView() }, - assert: self.assert, - } +// for making assertions on the lazygit model +func (self *Assert) Model() *Model { + return &Model{assertionHelper: self.assertionHelper, gui: self.gui} } -func (self *ViewAsserterGetter) Main() *ViewAsserter { - return &ViewAsserter{ - context: "main view", - getView: func() *gocui.View { return self.assert.gui.MainView() }, - assert: self.assert, - } +// for making assertions on the file system +func (self *Assert) FileSystem() *FileSystem { + return &FileSystem{assertionHelper: self.assertionHelper} } -func (self *ViewAsserterGetter) Secondary() *ViewAsserter { - return &ViewAsserter{ - context: "secondary view", - getView: func() *gocui.View { return self.assert.gui.SecondaryView() }, - assert: self.assert, - } -} - -func (self *ViewAsserterGetter) ByName(viewName string) *ViewAsserter { - return &ViewAsserter{ - context: fmt.Sprintf("%s view", viewName), - getView: func() *gocui.View { return self.assert.gui.View(viewName) }, - assert: self.assert, - } +// for when you just want to fail the test yourself. +// This runs callbacks to ensure we render the error after closing the gui. +func (self *Assert) Fail(message string) { + self.assertionHelper.fail(message) } diff --git a/pkg/integration/components/assertion_helper.go b/pkg/integration/components/assertion_helper.go new file mode 100644 index 000000000..70f2ff182 --- /dev/null +++ b/pkg/integration/components/assertion_helper.go @@ -0,0 +1,40 @@ +package components + +import ( + "time" + + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +type assertionHelper struct { + gui integrationTypes.GuiDriver +} + +// milliseconds we'll wait when an assertion fails. +var retryWaitTimes = []int{0, 1, 1, 1, 1, 1, 5, 10, 20, 40, 100, 200, 500, 1000, 2000, 4000} + +func (self *assertionHelper) matchString(matcher *matcher, context string, getValue func() string) { + self.assertWithRetries(func() (bool, string) { + value := getValue() + return matcher.context(context).test(value) + }) +} + +func (self *assertionHelper) assertWithRetries(test func() (bool, string)) { + var message string + for _, waitTime := range retryWaitTimes { + time.Sleep(time.Duration(waitTime) * time.Millisecond) + + var ok bool + ok, message = test() + if ok { + return + } + } + + self.fail(message) +} + +func (self *assertionHelper) fail(message string) { + self.gui.Fail(message) +} diff --git a/pkg/integration/components/commit_message_panel_asserter.go b/pkg/integration/components/commit_message_panel_asserter.go index 6ffb6b80c..3a534bd2d 100644 --- a/pkg/integration/components/commit_message_panel_asserter.go +++ b/pkg/integration/components/commit_message_panel_asserter.go @@ -5,7 +5,7 @@ type CommitMessagePanelAsserter struct { input *Input } -func (self *CommitMessagePanelAsserter) getViewAsserter() *ViewAsserter { +func (self *CommitMessagePanelAsserter) getViewAsserter() *Views { return self.assert.Views().ByName("commitMessage") } diff --git a/pkg/integration/components/confirmation_asserter.go b/pkg/integration/components/confirmation_asserter.go index 371e46028..9cf4d653d 100644 --- a/pkg/integration/components/confirmation_asserter.go +++ b/pkg/integration/components/confirmation_asserter.go @@ -7,7 +7,7 @@ type ConfirmationAsserter struct { hasCheckedContent bool } -func (self *ConfirmationAsserter) getViewAsserter() *ViewAsserter { +func (self *ConfirmationAsserter) getViewAsserter() *Views { return self.assert.Views().ByName("confirmation") } diff --git a/pkg/integration/components/file_system.go b/pkg/integration/components/file_system.go new file mode 100644 index 000000000..040234e77 --- /dev/null +++ b/pkg/integration/components/file_system.go @@ -0,0 +1,26 @@ +package components + +import ( + "fmt" + "os" +) + +type FileSystem struct { + *assertionHelper +} + +// This does _not_ check the files panel, it actually checks the filesystem +func (self *FileSystem) PathPresent(path string) { + self.assertWithRetries(func() (bool, string) { + _, err := os.Stat(path) + return err == nil, fmt.Sprintf("Expected path '%s' to exist, but it does not", path) + }) +} + +// This does _not_ check the files panel, it actually checks the filesystem +func (self *FileSystem) PathNotPresent(path string) { + self.assertWithRetries(func() (bool, string) { + _, err := os.Stat(path) + return os.IsNotExist(err), fmt.Sprintf("Expected path '%s' to not exist, but it does", path) + }) +} diff --git a/pkg/integration/components/input.go b/pkg/integration/components/input.go index 014a74d5d..2f1ed18c5 100644 --- a/pkg/integration/components/input.go +++ b/pkg/integration/components/input.go @@ -11,18 +11,20 @@ import ( ) type Input struct { - gui integrationTypes.GuiDriver - keys config.KeybindingConfig - assert *Assert + gui integrationTypes.GuiDriver + keys config.KeybindingConfig + assert *Assert + *assertionHelper pushKeyDelay int } func NewInput(gui integrationTypes.GuiDriver, keys config.KeybindingConfig, assert *Assert, pushKeyDelay int) *Input { return &Input{ - gui: gui, - keys: keys, - assert: assert, - pushKeyDelay: pushKeyDelay, + gui: gui, + keys: keys, + assert: assert, + pushKeyDelay: pushKeyDelay, + assertionHelper: assert.assertionHelper, } } @@ -42,7 +44,7 @@ func (self *Input) press(keyStr string) { func (self *Input) SwitchToStatusWindow() { self.press(self.keys.Universal.JumpToBlock[0]) - self.assert.CurrentWindowName("status") + self.currentWindowName("status") } // switch to status window and assert that the status view is on top @@ -53,7 +55,7 @@ func (self *Input) SwitchToStatusView() { func (self *Input) SwitchToFilesWindow() { self.press(self.keys.Universal.JumpToBlock[1]) - self.assert.CurrentWindowName("files") + self.currentWindowName("files") } // switch to files window and assert that the files view is on top @@ -64,7 +66,7 @@ func (self *Input) SwitchToFilesView() { func (self *Input) SwitchToBranchesWindow() { self.press(self.keys.Universal.JumpToBlock[2]) - self.assert.CurrentWindowName("localBranches") + self.currentWindowName("localBranches") } // switch to branches window and assert that the branches view is on top @@ -75,7 +77,7 @@ func (self *Input) SwitchToBranchesView() { func (self *Input) SwitchToCommitsWindow() { self.press(self.keys.Universal.JumpToBlock[3]) - self.assert.CurrentWindowName("commits") + self.currentWindowName("commits") } // switch to commits window and assert that the commits view is on top @@ -86,7 +88,7 @@ func (self *Input) SwitchToCommitsView() { func (self *Input) SwitchToStashWindow() { self.press(self.keys.Universal.JumpToBlock[4]) - self.assert.CurrentWindowName("stash") + self.currentWindowName("stash") } // switch to stash window and assert that the stash view is on top @@ -166,7 +168,7 @@ func (self *Input) Log(message string) { // 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 *Input) NavigateToListItem(matcher *matcher) { - self.assert.InListContext() + self.inListContext() currentContext := self.gui.CurrentContext().(types.IListContext) @@ -215,32 +217,82 @@ func (self *Input) NavigateToListItem(matcher *matcher) { } } +func (self *Input) 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()) + }) +} + func (self *Input) Confirmation() *ConfirmationAsserter { - self.assert.InConfirm() + self.inConfirm() return &ConfirmationAsserter{assert: self.assert, input: self} } +func (self *Input) inConfirm() { + self.assertWithRetries(func() (bool, string) { + currentView := self.gui.CurrentContext().GetView() + return currentView.Name() == "confirmation" && !currentView.Editable, "Expected confirmation popup to be focused" + }) +} + func (self *Input) Prompt() *PromptAsserter { - self.assert.InPrompt() + self.inPrompt() return &PromptAsserter{assert: self.assert, input: self} } +func (self *Input) inPrompt() { + self.assertWithRetries(func() (bool, string) { + currentView := self.gui.CurrentContext().GetView() + return currentView.Name() == "confirmation" && currentView.Editable, "Expected prompt popup to be focused" + }) +} + func (self *Input) Alert() *AlertAsserter { - self.assert.InAlert() + self.inAlert() return &AlertAsserter{assert: self.assert, input: self} } +func (self *Input) 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, "Expected alert popup to be focused" + }) +} + func (self *Input) Menu() *MenuAsserter { - self.assert.InMenu() + self.inMenu() return &MenuAsserter{assert: self.assert, input: self} } +func (self *Input) inMenu() { + self.assertWithRetries(func() (bool, string) { + return self.gui.CurrentContext().GetView().Name() == "menu", "Expected popup menu to be focused" + }) +} + func (self *Input) CommitMessagePanel() *CommitMessagePanelAsserter { - self.assert.InCommitMessagePanel() + self.inCommitMessagePanel() return &CommitMessagePanelAsserter{assert: self.assert, input: self} } + +func (self *Input) inCommitMessagePanel() { + self.assertWithRetries(func() (bool, string) { + currentView := self.gui.CurrentContext().GetView() + return currentView.Name() == "commitMessage", "Expected commit message panel to be focused" + }) +} + +func (self *Input) 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) + }) +} diff --git a/pkg/integration/components/menu_asserter.go b/pkg/integration/components/menu_asserter.go index ab2256320..e302c235b 100644 --- a/pkg/integration/components/menu_asserter.go +++ b/pkg/integration/components/menu_asserter.go @@ -6,7 +6,7 @@ type MenuAsserter struct { hasCheckedTitle bool } -func (self *MenuAsserter) getViewAsserter() *ViewAsserter { +func (self *MenuAsserter) getViewAsserter() *Views { return self.assert.Views().ByName("menu") } diff --git a/pkg/integration/components/model.go b/pkg/integration/components/model.go new file mode 100644 index 000000000..83d2e97e8 --- /dev/null +++ b/pkg/integration/components/model.go @@ -0,0 +1,72 @@ +package components + +import ( + "fmt" + + integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" +) + +type Model struct { + *assertionHelper + gui integrationTypes.GuiDriver +} + +func (self *Model) WorkingTreeFileCount(expectedCount int) { + self.assertWithRetries(func() (bool, string) { + actualCount := len(self.gui.Model().Files) + + return actualCount == expectedCount, fmt.Sprintf( + "Expected %d changed working tree files, but got %d", + expectedCount, actualCount, + ) + }) +} + +func (self *Model) CommitCount(expectedCount int) { + self.assertWithRetries(func() (bool, string) { + actualCount := len(self.gui.Model().Commits) + + return actualCount == expectedCount, fmt.Sprintf( + "Expected %d commits present, but got %d", + expectedCount, actualCount, + ) + }) +} + +func (self *Model) StashCount(expectedCount int) { + self.assertWithRetries(func() (bool, string) { + actualCount := len(self.gui.Model().StashEntries) + + return actualCount == expectedCount, fmt.Sprintf( + "Expected %d stash entries, but got %d", + expectedCount, actualCount, + ) + }) +} + +func (self *Model) AtLeastOneCommit() { + self.assertWithRetries(func() (bool, string) { + actualCount := len(self.gui.Model().Commits) + + return actualCount > 0, "Expected at least one commit present" + }) +} + +func (self *Model) HeadCommitMessage(matcher *matcher) { + self.assertWithRetries(func() (bool, string) { + return len(self.gui.Model().Commits) > 0, "Expected at least one commit to be present" + }) + + self.matchString(matcher, "Unexpected commit message.", + func() string { + return self.gui.Model().Commits[0].Name + }, + ) +} + +func (self *Model) CurrentBranchName(expectedViewName string) { + self.assertWithRetries(func() (bool, string) { + actual := self.gui.CheckedOutRef().Name + return actual == expectedViewName, fmt.Sprintf("Expected current branch name to be '%s', but got '%s'", expectedViewName, actual) + }) +} diff --git a/pkg/integration/components/prompt_asserter.go b/pkg/integration/components/prompt_asserter.go index 079cc4d3f..b03ee85b7 100644 --- a/pkg/integration/components/prompt_asserter.go +++ b/pkg/integration/components/prompt_asserter.go @@ -6,7 +6,7 @@ type PromptAsserter struct { hasCheckedTitle bool } -func (self *PromptAsserter) getViewAsserter() *ViewAsserter { +func (self *PromptAsserter) getViewAsserter() *Views { return self.assert.Views().ByName("confirmation") } diff --git a/pkg/integration/components/test_test.go b/pkg/integration/components/test_test.go index dbab22883..eb901a402 100644 --- a/pkg/integration/components/test_test.go +++ b/pkg/integration/components/test_test.go @@ -66,7 +66,7 @@ func TestAssertionFailure(t *testing.T) { Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) { input.Press("a |