Add a way for integration tests to press keys in rapid succession

The test driver waits for lazygit to become idle after every keypress, so
tests could never exercise what happens when a key arrives while the
previous key's processing is still in flight — for example while the
refresh triggered by the previous key hasn't updated the model yet. Real
users type faster than that all the time.

PressRapidly injects all its keys back to back and waits for idle only
once at the end, so the second and later keys are queued before the first
one's processing has finished. The next commit uses this to demonstrate a
bug in exactly that scenario.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Stefan Haller
2026-07-21 22:23:58 +02:00
co-authored by Claude Fable 5
parent 2e653ceeba
commit 7a902b56cc
5 changed files with 49 additions and 8 deletions
+18 -8
View File
@@ -25,17 +25,27 @@ type GuiDriver struct {
var _ integrationTypes.GuiDriver = &GuiDriver{}
func (self *GuiDriver) PressKey(keyStr string) {
self.PressKeysRapidly(keyStr)
}
// PressKeysRapidly presses the given keys in immediate succession, waiting for
// lazygit to become idle only after the last one. Keys pressed this way can
// arrive while the previous key's processing is still in flight, like a user
// typing faster than lazygit handles the input.
func (self *GuiDriver) PressKeysRapidly(keyStrs ...string) {
self.CheckAllToastsAcknowledged()
key, ok := config.KeyFromLabel(keyStr)
if !ok {
self.Fail("Unrecognized key: " + keyStr)
}
for _, keyStr := range keyStrs {
key, ok := config.KeyFromLabel(keyStr)
if !ok {
self.Fail("Unrecognized key: " + keyStr)
}
self.gui.g.ReplayKeyEvent(gocui.NewTcellKeyEventWrapper(
tcell.NewEventKey(tcell.Key(key.KeyName()), key.Str(), tcell.ModMask(key.Mod())),
0,
))
self.gui.g.ReplayKeyEvent(gocui.NewTcellKeyEventWrapper(
tcell.NewEventKey(tcell.Key(key.KeyName()), key.Str(), tcell.ModMask(key.Mod())),
0,
))
}
self.waitTillIdle()
}
+10
View File
@@ -2,6 +2,7 @@ package components
import (
"fmt"
"strings"
"time"
"github.com/jesseduffield/lazygit/pkg/config"
@@ -42,6 +43,15 @@ func (self *TestDriver) pressFast(keyStr string) {
self.Wait(self.inputDelay / 5)
}
// presses the keys in immediate succession, without waiting for lazygit to
// become idle in between, to simulate a user typing faster than lazygit
// processes the input
func (self *TestDriver) pressRapidly(keyStrs []string) {
self.SetCaption(fmt.Sprintf("Pressing %s", strings.Join(keyStrs, ", ")))
self.gui.PressKeysRapidly(keyStrs...)
self.Wait(self.inputDelay)
}
func (self *TestDriver) click(x, y int) {
self.SetCaption(fmt.Sprintf("Clicking %d, %d", x, y))
self.gui.Click(x, y)
+4
View File
@@ -30,6 +30,10 @@ func (self *fakeGuiDriver) PressKey(key string) {
self.pressedKeys = append(self.pressedKeys, key)
}
func (self *fakeGuiDriver) PressKeysRapidly(keys ...string) {
self.pressedKeys = append(self.pressedKeys, keys...)
}
func (self *fakeGuiDriver) Click(x, y int) {
self.clickedCoordinates = append(self.clickedCoordinates, coordinate{x: x, y: y})
}
+13
View File
@@ -454,6 +454,19 @@ func (self *ViewDriver) PressFast(key config.Keybinding) *ViewDriver {
return self
}
// Presses the given keys in immediate succession, without waiting for lazygit
// to become idle in between (Press waits after every key). Use this to
// simulate a user typing faster than lazygit processes the input.
func (self *ViewDriver) PressRapidly(keys ...config.Keybinding) *ViewDriver {
self.IsFocused()
self.t.pressRapidly(lo.Map(keys, func(key config.Keybinding, _ int) string {
return key[0]
}))
return self
}
func (self *ViewDriver) Click(x, y int) *ViewDriver {
offsetX, offsetY, _, _ := self.getView().Dimensions()
+4
View File
@@ -23,6 +23,10 @@ type IntegrationTest interface {
// this is the interface through which our integration tests interact with the lazygit gui
type GuiDriver interface {
PressKey(string)
// Like PressKey, but presses several keys in immediate succession, waiting
// for lazygit to become idle only after the last one. Use it to simulate a
// user typing faster than lazygit processes the input.
PressKeysRapidly(...string)
Click(int, int)
// Simulate the terminal window regaining focus (which triggers a reload of
// changed config files)