1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-01-20 05:19:24 +02:00

better CLI interface

This commit is contained in:
Jesse Duffield 2022-08-14 14:33:44 +10:00
parent 349a7d2453
commit 5173d7f5e1
13 changed files with 287 additions and 140 deletions

View File

@ -98,7 +98,7 @@ jobs:
${{runner.os}}-go- ${{runner.os}}-go-
- name: Test code - name: Test code
run: | run: |
go test pkg/integration/*.go go test pkg/integration/clients/*.go
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:

View File

@ -0,0 +1,49 @@
package main
import (
"fmt"
"log"
"os"
"github.com/jesseduffield/lazygit/pkg/integration/clients"
)
var usage = `
Usage:
See https://github.com/jesseduffield/lazygit/tree/master/pkg/integration/README.md
CLI mode:
> go run cmd/integration_test/main.go cli <test1> <test2> ...
If you pass no test names, it runs all tests
Accepted environment variables:
KEY_PRESS_DELAY (e.g. 200): the number of milliseconds to wait between keypresses
MODE:
* ask (default): if a snapshot test fails, asks if you want to update the snapshot
* check: if a snapshot test fails, exits with an error
* update: if a snapshot test fails, updates the snapshot
* sandbox: uses the test's setup step to run the test in a sandbox where you can do whatever you want
TUI mode:
> go run cmd/integration_test/main.go tui
This will open up a terminal UI where you can run tests
Help:
> go run cmd/integration_test/main.go help
`
func main() {
if len(os.Args) < 2 {
log.Fatal(usage)
}
switch os.Args[1] {
case "help":
fmt.Println(usage)
case "cli":
clients.RunCLI(os.Args[2:])
case "tui":
clients.RunTUI()
default:
log.Fatal(usage)
}
}

View File

@ -20,7 +20,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/gui/keybindings" "github.com/jesseduffield/lazygit/pkg/gui/keybindings"
"github.com/jesseduffield/lazygit/pkg/gui/types" "github.com/jesseduffield/lazygit/pkg/gui/types"
"github.com/jesseduffield/lazygit/pkg/i18n" "github.com/jesseduffield/lazygit/pkg/i18n"
"github.com/jesseduffield/lazygit/pkg/integration" "github.com/jesseduffield/lazygit/pkg/utils"
"github.com/samber/lo" "github.com/samber/lo"
) )
@ -45,7 +45,7 @@ func CommandToRun() string {
} }
func GetDir() string { func GetDir() string {
return integration.GetProjectRootDirectory() + "/docs/keybindings" return utils.GetLazygitRootDirectory() + "/docs/keybindings"
} }
func generateAtDir(cheatsheetDir string) { func generateAtDir(cheatsheetDir string) {

View File

@ -37,21 +37,21 @@ If you find yourself doing something frequently in a test, consider making it a
There are three ways to invoke a test: There are three ways to invoke a test:
1. go run pkg/integration/cmd/runner/main.go [<testname>...] 1. go run cmd/integration_test/main.go cli [<testname>...]
2. go run pkg/integration/cmd/tui/main.go 2. go run cmd/integration_test/main.go tui
3. go test pkg/integration/go_test.go 3. go test pkg/integration/clients/go_test.go
The first, the test runner, is for directly running a test from the command line. If you pass no arguments, it runs all tests. The first, the test runner, is for directly running a test from the command line. If you pass no arguments, it runs all tests.
The second, the TUI, is for running tests from a terminal UI where it's easier to find a test and run it without having to copy it's name and paste it into the terminal. This is the easiest approach by far. The second, the TUI, is for running tests from a terminal UI where it's easier to find a test and run it without having to copy it's name and paste it into the terminal. This is the easiest approach by far.
The third, the go-test command, intended only for use in CI, to be run along with the other `go test` tests. This runs the tests in headless mode so there's no visual output. The third, the go-test command, intended only for use in CI, to be run along with the other `go test` tests. This runs the tests in headless mode so there's no visual output.
The name of a test is based on its path, so the name of the test at `pkg/integration/tests/commit/new_branch.go` is commit/new_branch. So to run it with our test runner you would run `go run pkg/integration/cmd/runner/main.go commit/new_branch`. The name of a test is based on its path, so the name of the test at `pkg/integration/tests/commit/new_branch.go` is commit/new_branch. So to run it with our test runner you would run `go run cmd/integration_test/main.go cli commit/new_branch`.
You can pass the KEY_PRESS_DELAY env var to the test runner in order to set a delay in milliseconds between keypresses, which helps for watching a test at a realistic speed to understand what it's doing. Or in the tui you can press 't' to run the test with a pre-set delay. You can pass the KEY_PRESS_DELAY env var to the test runner in order to set a delay in milliseconds between keypresses, which helps for watching a test at a realistic speed to understand what it's doing. Or in the tui you can press 't' to run the test with a pre-set delay.
### Snapshots ### Snapshots
At the moment (this is subject to change) each test has a snapshot repo created after running for the first time. These snapshots live in `test/integration_new`, in folders named 'expected' (alongside the 'actual' folders which contain the resulting repo from the last test run). Whenever you run a test, the resultant repo will be compared against the snapshot repo and if they're different, you'll be asked whether you want to update the snapshot. If you want to update a snapshot without being prompted you can pass MODE=updateSnapshot to the test runner. At the moment (this is subject to change) each test has a snapshot repo created after running for the first time. These snapshots live in `test/integration_new`, in folders named 'expected' (alongside the 'actual' folders which contain the resulting repo from the last test run). Whenever you run a test, the resultant repo will be compared against the snapshot repo and if they're different, you'll be asked whether you want to update the snapshot. If you want to update a snapshot without being prompted you can pass MODE=update to the test runner.
### Sandbox mode ### Sandbox mode

View File

@ -1,9 +1,10 @@
package main package clients
import ( import (
"log" "log"
"os" "os"
"os/exec" "os/exec"
"strconv"
"github.com/jesseduffield/lazygit/pkg/integration/components" "github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/jesseduffield/lazygit/pkg/integration/tests" "github.com/jesseduffield/lazygit/pkg/integration/tests"
@ -19,32 +20,35 @@ import (
// If invoked directly, you can specify tests to run by passing their names as positional arguments // If invoked directly, you can specify tests to run by passing their names as positional arguments
func main() { func RunCLI(testNames []string) {
err := components.RunTests( err := components.RunTests(
getTestsToRun(), getTestsToRun(testNames),
log.Printf, log.Printf,
runCmdInTerminal, runCmdInTerminal,
func(test *components.IntegrationTest, f func() error) { runAndPrintError,
if err := f(); err != nil {
log.Print(err.Error())
}
},
getModeFromEnv(), getModeFromEnv(),
tryConvert(os.Getenv("KEY_PRESS_DELAY"), 0),
) )
if err != nil { if err != nil {
log.Print(err.Error()) log.Print(err.Error())
} }
} }
func getTestsToRun() []*components.IntegrationTest { func runAndPrintError(test *components.IntegrationTest, f func() error) {
if err := f(); err != nil {
log.Print(err.Error())
}
}
func getTestsToRun(testNames []string) []*components.IntegrationTest {
var testsToRun []*components.IntegrationTest var testsToRun []*components.IntegrationTest
if len(os.Args) < 2 { if len(testNames) == 0 {
return tests.Tests return tests.Tests
} }
outer: outer:
for _, testName := range os.Args[1:] { for _, testName := range testNames {
// check if our given test name actually exists // check if our given test name actually exists
for _, test := range tests.Tests { for _, test := range tests.Tests {
if test.Name() == testName { if test.Name() == testName {
@ -72,12 +76,21 @@ func getModeFromEnv() components.Mode {
return components.ASK_TO_UPDATE_SNAPSHOT return components.ASK_TO_UPDATE_SNAPSHOT
case "check": case "check":
return components.CHECK_SNAPSHOT return components.CHECK_SNAPSHOT
case "updateSnapshot": case "update":
return components.UPDATE_SNAPSHOT return components.UPDATE_SNAPSHOT
case "sandbox": case "sandbox":
return components.SANDBOX return components.SANDBOX
default: default:
log.Fatalf("unknown test mode: %s, must be one of [test, record, updateSnapshot, sandbox]", os.Getenv("MODE")) log.Fatalf("unknown test mode: %s, must be one of [ask, check, update, sandbox]", os.Getenv("MODE"))
panic("unreachable") panic("unreachable")
} }
} }
func tryConvert(numStr string, defaultVal int) int {
num, err := strconv.Atoi(numStr)
if err != nil {
return defaultVal
}
return num
}

View File

@ -1,7 +1,7 @@
//go:build !windows //go:build !windows
// +build !windows // +build !windows
package integration package clients
// this is the new way of running tests. See pkg/integration/integration_tests/commit.go // this is the new way of running tests. See pkg/integration/integration_tests/commit.go
// for an example // for an example
@ -11,7 +11,6 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"strconv"
"testing" "testing"
"github.com/creack/pty" "github.com/creack/pty"
@ -45,6 +44,7 @@ func TestIntegration(t *testing.T) {
}) })
}, },
components.CHECK_SNAPSHOT, components.CHECK_SNAPSHOT,
0,
) )
assert.NoError(t, err) assert.NoError(t, err)
@ -66,12 +66,3 @@ func runCmdHeadless(cmd *exec.Cmd) error {
return f.Close() return f.Close()
} }
func tryConvert(numStr string, defaultVal int) int {
num, err := strconv.Atoi(numStr)
if err != nil {
return defaultVal
}
return num
}

View File

@ -12,12 +12,12 @@ import (
) )
// The purpose of this program is to run lazygit with an integration test passed in. // The purpose of this program is to run lazygit with an integration test passed in.
// We could have done the check on LAZYGIT_TEST_NAME in the root main.go but // We could have done the check on TEST_NAME in the root main.go but
// that would mean lazygit would be depending on integration test code which // that would mean lazygit would be depending on integration test code which
// would bloat the binary. // would bloat the binary.
// You should not invoke this program directly. Instead you should go through // You should not invoke this program directly. Instead you should go through
// pkg/integration/cmd/runner/main.go or pkg/integration/cmd/tui/main.go // go run cmd/integration_test/main.go
func main() { func main() {
dummyBuildInfo := &app.BuildInfo{ dummyBuildInfo := &app.BuildInfo{
@ -39,11 +39,16 @@ func getIntegrationTest() integrationTypes.IntegrationTest {
return nil return nil
} }
integrationTestName := os.Getenv(components.LAZYGIT_TEST_NAME_ENV_VAR) if os.Getenv(components.SANDBOX_ENV_VAR) == "true" {
// when in sandbox mode we don't want the test controlling the gui
return nil
}
integrationTestName := os.Getenv(components.TEST_NAME_ENV_VAR)
if integrationTestName == "" { if integrationTestName == "" {
panic(fmt.Sprintf( panic(fmt.Sprintf(
"expected %s environment variable to be set, given that we're running an integration test", "expected %s environment variable to be set, given that we're running an integration test",
components.LAZYGIT_TEST_NAME_ENV_VAR, components.TEST_NAME_ENV_VAR,
)) ))
} }

View File

@ -1,49 +1,29 @@
package main package clients
import ( import (
"fmt" "fmt"
"log" "log"
"os" "os"
"os/exec"
"path/filepath" "path/filepath"
"strings"
"github.com/jesseduffield/generics/slices"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/gui" "github.com/jesseduffield/lazygit/pkg/gui"
"github.com/jesseduffield/lazygit/pkg/gui/style" "github.com/jesseduffield/lazygit/pkg/gui/style"
"github.com/jesseduffield/lazygit/pkg/integration/components" "github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/jesseduffield/lazygit/pkg/integration/tests" "github.com/jesseduffield/lazygit/pkg/integration/tests"
"github.com/jesseduffield/lazygit/pkg/secureexec" "github.com/jesseduffield/lazygit/pkg/secureexec"
"github.com/jesseduffield/lazygit/pkg/utils"
) )
// This program lets you run integration tests from a TUI. See pkg/integration/README.md for more info. // This program lets you run integration tests from a TUI. See pkg/integration/README.md for more info.
type App struct { func RunTUI() {
tests []*components.IntegrationTest rootDir := utils.GetLazygitRootDirectory()
itemIdx int
testDir string
filtering bool
g *gocui.Gui
}
func (app *App) getCurrentTest() *components.IntegrationTest {
if len(app.tests) > 0 {
return app.tests[app.itemIdx]
}
return nil
}
func (app *App) loadTests() {
app.tests = tests.Tests
if app.itemIdx > len(app.tests)-1 {
app.itemIdx = len(app.tests) - 1
}
}
func main() {
rootDir := components.GetProjectRootDirectory()
testDir := filepath.Join(rootDir, "test", "integration") testDir := filepath.Join(rootDir, "test", "integration")
app := &App{testDir: testDir} app := newApp(testDir)
app.loadTests() app.loadTests()
g, err := gocui.NewGui(gocui.OutputTrue, false, gocui.NORMAL, false, gui.RuneReplacements) g, err := gocui.NewGui(gocui.OutputTrue, false, gocui.NORMAL, false, gui.RuneReplacements)
@ -71,6 +51,21 @@ func main() {
log.Panicln(err) log.Panicln(err)
} }
if err := g.SetKeybinding("list", gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
if app.itemIdx < len(app.filteredTests)-1 {
app.itemIdx++
}
listView, err := g.View("list")
if err != nil {
return err
}
listView.FocusPoint(0, app.itemIdx)
return nil
}); err != nil {
log.Panicln(err)
}
if err := g.SetKeybinding("list", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { if err := g.SetKeybinding("list", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
log.Panicln(err) log.Panicln(err)
} }
@ -85,8 +80,7 @@ func main() {
return nil return nil
} }
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("MODE=sandbox go run pkg/integration/cmd/runner/main.go %s", currentTest.Name())) suspendAndRunTest(currentTest, components.SANDBOX, 0)
app.runSubprocess(cmd)
return nil return nil
}); err != nil { }); err != nil {
@ -99,8 +93,7 @@ func main() {
return nil return nil
} }
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("go run pkg/integration/cmd/runner/main.go %s", currentTest.Name())) suspendAndRunTest(currentTest, components.ASK_TO_UPDATE_SNAPSHOT, 0)
app.runSubprocess(cmd)
return nil return nil
}); err != nil { }); err != nil {
@ -113,8 +106,7 @@ func main() {
return nil return nil
} }
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("KEY_PRESS_DELAY=200 go run pkg/integration/cmd/runner/main.go %s", currentTest.Name())) suspendAndRunTest(currentTest, components.ASK_TO_UPDATE_SNAPSHOT, 200)
app.runSubprocess(cmd)
return nil return nil
}); err != nil { }); err != nil {
@ -176,6 +168,26 @@ func main() {
return err return err
} }
app.filteredTests = tests.Tests
app.renderTests()
app.editorView.TextArea.Clear()
app.editorView.Clear()
app.editorView.Reset()
return nil
}); err != nil {
log.Panicln(err)
}
if err := g.SetKeybinding("editor", gocui.KeyEnter, gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
app.filtering = false
if _, err := g.SetCurrentView("list"); err != nil {
return err
}
app.renderTests()
return nil return nil
}); err != nil { }); err != nil {
log.Panicln(err) log.Panicln(err)
@ -191,20 +203,74 @@ func main() {
} }
} }
func (app *App) runSubprocess(cmd *exec.Cmd) { type app struct {
filteredTests []*components.IntegrationTest
itemIdx int
testDir string
filtering bool
g *gocui.Gui
listView *gocui.View
editorView *gocui.View
}
func newApp(testDir string) *app {
return &app{testDir: testDir}
}
func (self *app) getCurrentTest() *components.IntegrationTest {
self.adjustCursor()
if len(self.filteredTests) > 0 {
return self.filteredTests[self.itemIdx]
}
return nil
}
func (self *app) loadTests() {
self.filteredTests = tests.Tests
self.adjustCursor()
}
func (self *app) adjustCursor() {
self.itemIdx = utils.Clamp(self.itemIdx, 0, len(self.filteredTests)-1)
}
func (self *app) filterWithString(needle string) {
if needle == "" {
self.filteredTests = tests.Tests
} else {
self.filteredTests = slices.Filter(tests.Tests, func(test *components.IntegrationTest) bool {
return strings.Contains(test.Name(), needle)
})
}
self.renderTests()
self.g.Update(func(g *gocui.Gui) error { return nil })
}
func (self *app) renderTests() {
self.listView.Clear()
for _, test := range self.filteredTests {
fmt.Fprintln(self.listView, test.Name())
}
}
func (self *app) wrapEditor(f func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) bool) func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) bool {
return func(v *gocui.View, key gocui.Key, ch rune, mod gocui.Modifier) bool {
matched := f(v, key, ch, mod)
if matched {
self.filterWithString(v.TextArea.GetContent())
}
return matched
}
}
func suspendAndRunTest(test *components.IntegrationTest, mode components.Mode, keyPressDelay int) {
if err := gocui.Screen.Suspend(); err != nil { if err := gocui.Screen.Suspend(); err != nil {
panic(err) panic(err)
} }
cmd.Stdin = os.Stdin runTuiTest(test, mode, keyPressDelay)
cmd.Stderr = os.Stderr
cmd.Stdout = os.Stdout
if err := cmd.Run(); err != nil {
log.Println(err.Error())
}
cmd.Stdin = nil
cmd.Stderr = nil
cmd.Stdout = nil
fmt.Fprintf(os.Stdout, "\n%s", style.FgGreen.Sprint("press enter to return")) fmt.Fprintf(os.Stdout, "\n%s", style.FgGreen.Sprint("press enter to return"))
fmt.Scanln() // wait for enter press fmt.Scanln() // wait for enter press
@ -214,29 +280,31 @@ func (app *App) runSubprocess(cmd *exec.Cmd) {
} }
} }
func (app *App) layout(g *gocui.Gui) error { func (self *app) layout(g *gocui.Gui) error {
maxX, maxY := g.Size() maxX, maxY := g.Size()
descriptionViewHeight := 7 descriptionViewHeight := 7
keybindingsViewHeight := 3 keybindingsViewHeight := 3
editorViewHeight := 3 editorViewHeight := 3
if !app.filtering { if !self.filtering {
editorViewHeight = 0 editorViewHeight = 0
} else { } else {
descriptionViewHeight = 0 descriptionViewHeight = 0
keybindingsViewHeight = 0 keybindingsViewHeight = 0
} }
g.Cursor = app.filtering g.Cursor = self.filtering
g.FgColor = gocui.ColorGreen g.FgColor = gocui.ColorGreen
listView, err := g.SetView("list", 0, 0, maxX-1, maxY-descriptionViewHeight-keybindingsViewHeight-editorViewHeight-1, 0) listView, err := g.SetView("list", 0, 0, maxX-1, maxY-descriptionViewHeight-keybindingsViewHeight-editorViewHeight-1, 0)
if err != nil { if err != nil {
if err.Error() != "unknown view" { if err.Error() != "unknown view" {
return err return err
} }
listView.Highlight = true
listView.Clear() if self.listView == nil {
for _, test := range app.tests { self.listView = listView
fmt.Fprintln(listView, test.Name())
} }
listView.Highlight = true
self.renderTests()
listView.Title = "Tests" listView.Title = "Tests"
listView.FgColor = gocui.ColorDefault listView.FgColor = gocui.ColorDefault
if _, err := g.SetCurrentView("list"); err != nil { if _, err := g.SetCurrentView("list"); err != nil {
@ -270,12 +338,18 @@ func (app *App) layout(g *gocui.Gui) error {
if err.Error() != "unknown view" { if err.Error() != "unknown view" {
return err return err
} }
if self.editorView == nil {
self.editorView = editorView
}
editorView.Title = "Filter" editorView.Title = "Filter"
editorView.FgColor = gocui.ColorDefault editorView.FgColor = gocui.ColorDefault
editorView.Editable = true editorView.Editable = true
editorView.Editor = gocui.EditorFunc(self.wrapEditor(gocui.SimpleEditor))
} }
currentTest := app.getCurrentTest() currentTest := self.getCurrentTest()
if currentTest == nil { if currentTest == nil {
return nil return nil
} }
@ -283,24 +357,23 @@ func (app *App) layout(g *gocui.Gui) error {
descriptionView.Clear() descriptionView.Clear()
fmt.Fprint(descriptionView, currentTest.Description()) fmt.Fprint(descriptionView, currentTest.Description())
if err := g.SetKeybinding("list", gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
if app.itemIdx < len(app.tests)-1 {
app.itemIdx++
}
listView, err := g.View("list")
if err != nil {
return err
}
listView.FocusPoint(0, app.itemIdx)
return nil
}); err != nil {
log.Panicln(err)
}
return nil return nil
} }
func quit(g *gocui.Gui, v *gocui.View) error { func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit return gocui.ErrQuit
} }
func runTuiTest(test *components.IntegrationTest, mode components.Mode, keyPressDelay int) {
err := components.RunTests(
[]*components.IntegrationTest{test},
log.Printf,
runCmdInTerminal,
runAndPrintError,
mode,
keyPressDelay,
)
if err != nil {
log.Println(err.Error())
}
}

View File

@ -3,17 +3,20 @@ package components
import ( import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log"
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"github.com/jesseduffield/lazygit/pkg/commands/oscommands" "github.com/jesseduffield/lazygit/pkg/commands/oscommands"
"github.com/jesseduffield/lazygit/pkg/utils"
) )
// this is the integration runner for the new and improved integration interface // this is the integration runner for the new and improved integration interface
const LAZYGIT_TEST_NAME_ENV_VAR = "LAZYGIT_TEST_NAME" const (
TEST_NAME_ENV_VAR = "TEST_NAME"
SANDBOX_ENV_VAR = "SANDBOX"
)
type Mode int type Mode int
@ -38,8 +41,9 @@ func RunTests(
runCmd func(cmd *exec.Cmd) error, runCmd func(cmd *exec.Cmd) error,
testWrapper func(test *IntegrationTest, f func() error), testWrapper func(test *IntegrationTest, f func() error),
mode Mode, mode Mode,
keyPressDelay int,
) error { ) error {
projectRootDir := GetProjectRootDirectory() projectRootDir := utils.GetLazygitRootDirectory()
err := os.Chdir(projectRootDir) err := os.Chdir(projectRootDir)
if err != nil { if err != nil {
return err return err
@ -59,7 +63,7 @@ func RunTests(
) )
testWrapper(test, func() error { //nolint: thelper testWrapper(test, func() error { //nolint: thelper
return runTest(test, paths, projectRootDir, logf, runCmd, mode) return runTest(test, paths, projectRootDir, logf, runCmd, mode, keyPressDelay)
}) })
} }
@ -73,6 +77,7 @@ func runTest(
logf func(format string, formatArgs ...interface{}), logf func(format string, formatArgs ...interface{}),
runCmd func(cmd *exec.Cmd) error, runCmd func(cmd *exec.Cmd) error,
mode Mode, mode Mode,
keyPressDelay int,
) error { ) error {
if test.Skip() { if test.Skip() {
logf("Skipping test %s", test.Name()) logf("Skipping test %s", test.Name())
@ -85,7 +90,7 @@ func runTest(
return err return err
} }
cmd, err := getLazygitCommand(test, paths, projectRootDir) cmd, err := getLazygitCommand(test, paths, projectRootDir, mode, keyPressDelay)
if err != nil { if err != nil {
return err return err
} }
@ -116,7 +121,7 @@ func prepareTestDir(
func buildLazygit() error { func buildLazygit() error {
osCommand := oscommands.NewDummyOSCommand() osCommand := oscommands.NewDummyOSCommand()
return osCommand.Cmd.New(fmt.Sprintf( return osCommand.Cmd.New(fmt.Sprintf(
"go build -o %s pkg/integration/cmd/injector/main.go", tempLazygitPath(), "go build -o %s pkg/integration/clients/injector/main.go", tempLazygitPath(),
)).Run() )).Run()
} }
@ -144,7 +149,7 @@ func createFixture(test *IntegrationTest, paths Paths) error {
return nil return nil
} }
func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string) (*exec.Cmd, error) { func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string, mode Mode, keyPressDelay int) (*exec.Cmd, error) {
osCommand := oscommands.NewDummyOSCommand() osCommand := oscommands.NewDummyOSCommand()
templateConfigDir := filepath.Join(rootDir, "test", "default_test_config") templateConfigDir := filepath.Join(rootDir, "test", "default_test_config")
@ -162,36 +167,18 @@ func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string) (*exe
cmdObj := osCommand.Cmd.New(cmdStr) cmdObj := osCommand.Cmd.New(cmdStr)
cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", LAZYGIT_TEST_NAME_ENV_VAR, test.Name())) cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", TEST_NAME_ENV_VAR, test.Name()))
if mode == SANDBOX {
cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", "SANDBOX", "true"))
}
if keyPressDelay > 0 {
cmdObj.AddEnvVars(fmt.Sprintf("KEY_PRESS_DELAY=%d", keyPressDelay))
}
return cmdObj.GetCmd(), nil return cmdObj.GetCmd(), nil
} }
func GetProjectRootDirectory() string {
path, err := os.Getwd()
if err != nil {
panic(err)
}
for {
_, err := os.Stat(filepath.Join(path, ".git"))
if err == nil {
return path
}
if !os.IsNotExist(err) {
panic(err)
}
path = filepath.Dir(path)
if path == "/" {
log.Fatal("must run in lazygit folder or child folder")
}
}
}
func tempLazygitPath() string { func tempLazygitPath() string {
return filepath.Join("/tmp", "lazygit", "test_lazygit") return filepath.Join("/tmp", "lazygit", "test_lazygit")
} }

View File

@ -54,7 +54,7 @@ func (self *Snapshotter) handleSnapshots() error {
case ASK_TO_UPDATE_SNAPSHOT: case ASK_TO_UPDATE_SNAPSHOT:
return self.handleAskToUpdate() return self.handleAskToUpdate()
case SANDBOX: case SANDBOX:
self.logf("Session exited") self.logf("Sandbox session exited")
} }
return nil return nil
} }
@ -68,6 +68,7 @@ func (self *Snapshotter) handleUpdate() error {
} }
func (self *Snapshotter) handleCheck() error { func (self *Snapshotter) handleCheck() error {
self.logf("Comparing snapshots")
if err := self.compareSnapshots(); err != nil { if err := self.compareSnapshots(); err != nil {
return err return err
} }
@ -85,6 +86,7 @@ func (self *Snapshotter) handleAskToUpdate() error {
return nil return nil
} }
self.logf("Comparing snapshots...")
if err := self.compareSnapshots(); err != nil { if err := self.compareSnapshots(); err != nil {
self.logf("%s", err) self.logf("%s", err)

View File

@ -11,7 +11,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// Deprecated: This file is part of the old way of doing things. See pkg/integration/cmd/runner/main.go for the new way // Deprecated: This file is part of the old way of doing things.
// see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md // see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md
// This file can be invoked directly, but you might find it easier to go through // This file can be invoked directly, but you might find it easier to go through

View File

@ -135,3 +135,30 @@ func FilePath(skip int) string {
_, path, _, _ := runtime.Caller(skip) _, path, _, _ := runtime.Caller(skip)
return path return path
} }
// for our cheatsheet script and integration tests. Not to be confused with finding the
// root directory of _any_ random repo.
func GetLazygitRootDirectory() string {
path, err := os.Getwd()
if err != nil {
panic(err)
}
for {
_, err := os.Stat(filepath.Join(path, ".git"))
if err == nil {
return path
}
if !os.IsNotExist(err) {
panic(err)
}
path = filepath.Dir(path)
if path == "/" {
log.Fatal("must run in lazygit folder or child folder")
}
}
}

View File

@ -24,10 +24,10 @@ func (f EditorFunc) Edit(v *View, key Key, ch rune, mod Modifier) bool {
} }
// DefaultEditor is the default editor. // DefaultEditor is the default editor.
var DefaultEditor Editor = EditorFunc(simpleEditor) var DefaultEditor Editor = EditorFunc(SimpleEditor)
// simpleEditor is used as the default gocui editor. // SimpleEditor is used as the default gocui editor.
func simpleEditor(v *View, key Key, ch rune, mod Modifier) bool { func SimpleEditor(v *View, key Key, ch rune, mod Modifier) bool {
switch { switch {
case key == KeyBackspace || key == KeyBackspace2: case key == KeyBackspace || key == KeyBackspace2:
v.TextArea.BackSpaceChar() v.TextArea.BackSpaceChar()