diff --git a/pkg/gui/gui_driver.go b/pkg/gui/gui_driver.go index 632e271c3..57425231a 100644 --- a/pkg/gui/gui_driver.go +++ b/pkg/gui/gui_driver.go @@ -153,6 +153,12 @@ func (self *GuiDriver) View(viewName string) *gocui.View { return view } +// TopViewInWindow returns the frontmost visible view in the given window, i.e. +// the tab that is currently shown when a window holds several tabbed views. +func (self *GuiDriver) TopViewInWindow(windowName string) *gocui.View { + return self.gui.helpers.Window.TopViewInWindow(windowName, false) +} + func (self *GuiDriver) SetCaption(caption string) { self.gui.setCaption(caption) self.waitTillIdle() diff --git a/pkg/integration/components/test_test.go b/pkg/integration/components/test_test.go index b00a2a672..e7d03ada9 100644 --- a/pkg/integration/components/test_test.go +++ b/pkg/integration/components/test_test.go @@ -75,6 +75,10 @@ func (self *fakeGuiDriver) View(viewName string) *gocui.View { return nil } +func (self *fakeGuiDriver) TopViewInWindow(windowName string) *gocui.View { + return nil +} + func (self *fakeGuiDriver) SetCaption(string) { } diff --git a/pkg/integration/components/view_driver.go b/pkg/integration/components/view_driver.go index e9e5fbbc7..df4b9d7d8 100644 --- a/pkg/integration/components/view_driver.go +++ b/pkg/integration/components/view_driver.go @@ -408,6 +408,28 @@ func (self *ViewDriver) IsFocused() *ViewDriver { return self } +// asserts that the view is the one currently shown in its window, i.e. it's the +// active tab of its panel (drawn in front of the window's other tabs). Unlike +// IsFocused, this is about what's displayed rather than which view has keyboard +// focus; the two can disagree, e.g. if a config reload reshuffles the tabs. +func (self *ViewDriver) IsActiveTab() *ViewDriver { + self.t.assertWithRetries(func() (bool, string) { + expected := self.getView().Name() + context := self.t.gui.ContextForView(expected) + if context == nil { + return false, fmt.Sprintf("%s: Could not find context for view, so can't determine its window", expected) + } + topView := self.t.gui.TopViewInWindow(context.GetWindowName()) + actual := "" + if topView != nil { + actual = topView.Name() + } + return actual == expected, fmt.Sprintf("%s: Expected view to be the active tab of its window, but it was %s", expected, actual) + }) + + return self +} + func (self *ViewDriver) Press(key config.Keybinding) *ViewDriver { self.IsFocused() diff --git a/pkg/integration/types/types.go b/pkg/integration/types/types.go index 3d87e7d6e..cd6102cdf 100644 --- a/pkg/integration/types/types.go +++ b/pkg/integration/types/types.go @@ -44,6 +44,8 @@ type GuiDriver interface { // e.g. when we're showing both staged and unstaged changes SecondaryView() *gocui.View View(viewName string) *gocui.View + // the frontmost visible view in the given window, i.e. the currently shown tab + TopViewInWindow(windowName string) *gocui.View SetCaption(caption string) SetCaptionPrefix(prefix string) // Pop the next toast that was displayed; returns nil if there was none