mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-05-10 20:05:50 +02:00
refactor to ensure code doesn't depend on integration code
This commit is contained in:
parent
2bdefe2049
commit
304d74370e
16 changed files with 203 additions and 169 deletions
135
main.go
135
main.go
|
@ -1,151 +1,24 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"runtime/debug"
|
|
||||||
|
|
||||||
"github.com/integrii/flaggy"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/app"
|
"github.com/jesseduffield/lazygit/pkg/app"
|
||||||
"github.com/jesseduffield/lazygit/pkg/integration"
|
|
||||||
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
|
|
||||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
|
||||||
"github.com/samber/lo"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const DEFAULT_VERSION = "unversioned"
|
// These values may be set by the build script via the LDFLAGS argument
|
||||||
|
|
||||||
// These values may be set by the build script.
|
|
||||||
// we'll overwrite them if they haven't been set by the build script and if Go itself has set corresponding values in the binary
|
|
||||||
var (
|
var (
|
||||||
commit string
|
commit string
|
||||||
version = DEFAULT_VERSION
|
|
||||||
date string
|
date string
|
||||||
|
version string
|
||||||
buildSource = "unknown"
|
buildSource = "unknown"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cliArgs := parseCliArgsAndEnvVars()
|
ldFlagsBuildInfo := &app.BuildInfo{
|
||||||
buildInfo := getBuildInfo()
|
|
||||||
integrationTest := getIntegrationTest()
|
|
||||||
|
|
||||||
app.Start(cliArgs, buildInfo, integrationTest)
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseCliArgsAndEnvVars() *app.CliArgs {
|
|
||||||
flaggy.DefaultParser.ShowVersionWithVersionFlag = false
|
|
||||||
|
|
||||||
repoPath := ""
|
|
||||||
flaggy.String(&repoPath, "p", "path", "Path of git repo. (equivalent to --work-tree=<path> --git-dir=<path>/.git/)")
|
|
||||||
|
|
||||||
filterPath := ""
|
|
||||||
flaggy.String(&filterPath, "f", "filter", "Path to filter on in `git log -- <path>`. When in filter mode, the commits, reflog, and stash are filtered based on the given path, and some operations are restricted")
|
|
||||||
|
|
||||||
gitArg := ""
|
|
||||||
flaggy.AddPositionalValue(&gitArg, "git-arg", 1, false, "Panel to focus upon opening lazygit. Accepted values (based on git terminology): status, branch, log, stash. Ignored if --filter arg is passed.")
|
|
||||||
|
|
||||||
printVersionInfo := false
|
|
||||||
flaggy.Bool(&printVersionInfo, "v", "version", "Print the current version")
|
|
||||||
|
|
||||||
debug := false
|
|
||||||
flaggy.Bool(&debug, "d", "debug", "Run in debug mode with logging (see --logs flag below). Use the LOG_LEVEL env var to set the log level (debug/info/warn/error)")
|
|
||||||
|
|
||||||
tailLogs := false
|
|
||||||
flaggy.Bool(&tailLogs, "l", "logs", "Tail lazygit logs (intended to be used when `lazygit --debug` is called in a separate terminal tab)")
|
|
||||||
|
|
||||||
printDefaultConfig := false
|
|
||||||
flaggy.Bool(&printDefaultConfig, "c", "config", "Print the default config")
|
|
||||||
|
|
||||||
printConfigDir := false
|
|
||||||
flaggy.Bool(&printConfigDir, "cd", "print-config-dir", "Print the config directory")
|
|
||||||
|
|
||||||
useConfigDir := ""
|
|
||||||
flaggy.String(&useConfigDir, "ucd", "use-config-dir", "override default config directory with provided directory")
|
|
||||||
|
|
||||||
workTree := ""
|
|
||||||
flaggy.String(&workTree, "w", "work-tree", "equivalent of the --work-tree git argument")
|
|
||||||
|
|
||||||
gitDir := ""
|
|
||||||
flaggy.String(&gitDir, "g", "git-dir", "equivalent of the --git-dir git argument")
|
|
||||||
|
|
||||||
customConfigFile := ""
|
|
||||||
flaggy.String(&customConfigFile, "ucf", "use-config-file", "Comma separated list to custom config file(s)")
|
|
||||||
|
|
||||||
flaggy.Parse()
|
|
||||||
|
|
||||||
if os.Getenv("DEBUG") == "TRUE" {
|
|
||||||
debug = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return &app.CliArgs{
|
|
||||||
RepoPath: repoPath,
|
|
||||||
FilterPath: filterPath,
|
|
||||||
GitArg: gitArg,
|
|
||||||
PrintVersionInfo: printVersionInfo,
|
|
||||||
Debug: debug,
|
|
||||||
TailLogs: tailLogs,
|
|
||||||
PrintDefaultConfig: printDefaultConfig,
|
|
||||||
PrintConfigDir: printConfigDir,
|
|
||||||
UseConfigDir: useConfigDir,
|
|
||||||
WorkTree: workTree,
|
|
||||||
GitDir: gitDir,
|
|
||||||
CustomConfigFile: customConfigFile,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getBuildInfo() *app.BuildInfo {
|
|
||||||
buildInfo := &app.BuildInfo{
|
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
Date: date,
|
Date: date,
|
||||||
Version: version,
|
Version: version,
|
||||||
BuildSource: buildSource,
|
BuildSource: buildSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the version has already been set by build flags then we'll honour that.
|
app.Start(ldFlagsBuildInfo, nil)
|
||||||
// chances are it's something like v0.31.0 which is more informative than a
|
|
||||||
// commit hash.
|
|
||||||
if buildInfo.Version != DEFAULT_VERSION {
|
|
||||||
return buildInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
goBuildInfo, ok := debug.ReadBuildInfo()
|
|
||||||
if !ok {
|
|
||||||
return buildInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
revision, ok := lo.Find(goBuildInfo.Settings, func(setting debug.BuildSetting) bool {
|
|
||||||
return setting.Key == "vcs.revision"
|
|
||||||
})
|
|
||||||
if ok {
|
|
||||||
buildInfo.Commit = revision.Value
|
|
||||||
// if lazygit was built from source we'll show the version as the
|
|
||||||
// abbreviated commit hash
|
|
||||||
buildInfo.Version = utils.ShortSha(revision.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if version hasn't been set we assume that neither has the date
|
|
||||||
time, ok := lo.Find(goBuildInfo.Settings, func(setting debug.BuildSetting) bool {
|
|
||||||
return setting.Key == "vcs.time"
|
|
||||||
})
|
|
||||||
if ok {
|
|
||||||
buildInfo.Date = time.Value
|
|
||||||
}
|
|
||||||
|
|
||||||
return buildInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func getIntegrationTest() integrationTypes.IntegrationTest {
|
|
||||||
integrationTestName := os.Getenv("LAZYGIT_TEST_NAME")
|
|
||||||
if integrationTestName == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsetting so that if we run lazygit in as a 'daemon' we don't think we're trying to run a test again
|
|
||||||
os.Unsetenv("LAZYGIT_TEST_NAME")
|
|
||||||
for _, candidateTest := range integration.Tests {
|
|
||||||
if candidateTest.Name() == integrationTestName {
|
|
||||||
return candidateTest
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
panic("Could not find integration test with name: " + integrationTestName)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,18 +7,22 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"runtime/debug"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/integrii/flaggy"
|
||||||
"github.com/jesseduffield/lazygit/pkg/app/daemon"
|
"github.com/jesseduffield/lazygit/pkg/app/daemon"
|
||||||
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/config"
|
"github.com/jesseduffield/lazygit/pkg/config"
|
||||||
"github.com/jesseduffield/lazygit/pkg/env"
|
"github.com/jesseduffield/lazygit/pkg/env"
|
||||||
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
|
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
|
||||||
"github.com/jesseduffield/lazygit/pkg/logs"
|
"github.com/jesseduffield/lazygit/pkg/logs"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||||
|
"github.com/samber/lo"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CliArgs struct {
|
type cliArgs struct {
|
||||||
RepoPath string
|
RepoPath string
|
||||||
FilterPath string
|
FilterPath string
|
||||||
GitArg string
|
GitArg string
|
||||||
|
@ -40,7 +44,10 @@ type BuildInfo struct {
|
||||||
BuildSource string
|
BuildSource string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Start(cliArgs *CliArgs, buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTest) {
|
func Start(buildInfo *BuildInfo, integrationTest integrationTypes.IntegrationTest) {
|
||||||
|
cliArgs := parseCliArgsAndEnvVars()
|
||||||
|
mergeBuildInfo(buildInfo)
|
||||||
|
|
||||||
if cliArgs.RepoPath != "" {
|
if cliArgs.RepoPath != "" {
|
||||||
if cliArgs.WorkTree != "" || cliArgs.GitDir != "" {
|
if cliArgs.WorkTree != "" || cliArgs.GitDir != "" {
|
||||||
log.Fatal("--path option is incompatible with the --work-tree and --git-dir options")
|
log.Fatal("--path option is incompatible with the --work-tree and --git-dir options")
|
||||||
|
@ -132,6 +139,67 @@ func Start(cliArgs *CliArgs, buildInfo *BuildInfo, integrationTest integrationTy
|
||||||
Run(appConfig, common, appTypes.NewStartArgs(cliArgs.FilterPath, parsedGitArg, integrationTest))
|
Run(appConfig, common, appTypes.NewStartArgs(cliArgs.FilterPath, parsedGitArg, integrationTest))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseCliArgsAndEnvVars() *cliArgs {
|
||||||
|
flaggy.DefaultParser.ShowVersionWithVersionFlag = false
|
||||||
|
|
||||||
|
repoPath := ""
|
||||||
|
flaggy.String(&repoPath, "p", "path", "Path of git repo. (equivalent to --work-tree=<path> --git-dir=<path>/.git/)")
|
||||||
|
|
||||||
|
filterPath := ""
|
||||||
|
flaggy.String(&filterPath, "f", "filter", "Path to filter on in `git log -- <path>`. When in filter mode, the commits, reflog, and stash are filtered based on the given path, and some operations are restricted")
|
||||||
|
|
||||||
|
gitArg := ""
|
||||||
|
flaggy.AddPositionalValue(&gitArg, "git-arg", 1, false, "Panel to focus upon opening lazygit. Accepted values (based on git terminology): status, branch, log, stash. Ignored if --filter arg is passed.")
|
||||||
|
|
||||||
|
printVersionInfo := false
|
||||||
|
flaggy.Bool(&printVersionInfo, "v", "version", "Print the current version")
|
||||||
|
|
||||||
|
debug := false
|
||||||
|
flaggy.Bool(&debug, "d", "debug", "Run in debug mode with logging (see --logs flag below). Use the LOG_LEVEL env var to set the log level (debug/info/warn/error)")
|
||||||
|
|
||||||
|
tailLogs := false
|
||||||
|
flaggy.Bool(&tailLogs, "l", "logs", "Tail lazygit logs (intended to be used when `lazygit --debug` is called in a separate terminal tab)")
|
||||||
|
|
||||||
|
printDefaultConfig := false
|
||||||
|
flaggy.Bool(&printDefaultConfig, "c", "config", "Print the default config")
|
||||||
|
|
||||||
|
printConfigDir := false
|
||||||
|
flaggy.Bool(&printConfigDir, "cd", "print-config-dir", "Print the config directory")
|
||||||
|
|
||||||
|
useConfigDir := ""
|
||||||
|
flaggy.String(&useConfigDir, "ucd", "use-config-dir", "override default config directory with provided directory")
|
||||||
|
|
||||||
|
workTree := ""
|
||||||
|
flaggy.String(&workTree, "w", "work-tree", "equivalent of the --work-tree git argument")
|
||||||
|
|
||||||
|
gitDir := ""
|
||||||
|
flaggy.String(&gitDir, "g", "git-dir", "equivalent of the --git-dir git argument")
|
||||||
|
|
||||||
|
customConfigFile := ""
|
||||||
|
flaggy.String(&customConfigFile, "ucf", "use-config-file", "Comma separated list to custom config file(s)")
|
||||||
|
|
||||||
|
flaggy.Parse()
|
||||||
|
|
||||||
|
if os.Getenv("DEBUG") == "TRUE" {
|
||||||
|
debug = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cliArgs{
|
||||||
|
RepoPath: repoPath,
|
||||||
|
FilterPath: filterPath,
|
||||||
|
GitArg: gitArg,
|
||||||
|
PrintVersionInfo: printVersionInfo,
|
||||||
|
Debug: debug,
|
||||||
|
TailLogs: tailLogs,
|
||||||
|
PrintDefaultConfig: printDefaultConfig,
|
||||||
|
PrintConfigDir: printConfigDir,
|
||||||
|
UseConfigDir: useConfigDir,
|
||||||
|
WorkTree: workTree,
|
||||||
|
GitDir: gitDir,
|
||||||
|
CustomConfigFile: customConfigFile,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func parseGitArg(gitArg string) appTypes.GitArg {
|
func parseGitArg(gitArg string) appTypes.GitArg {
|
||||||
typedArg := appTypes.GitArg(gitArg)
|
typedArg := appTypes.GitArg(gitArg)
|
||||||
|
|
||||||
|
@ -155,3 +223,43 @@ func parseGitArg(gitArg string) appTypes.GitArg {
|
||||||
|
|
||||||
panic("unreachable")
|
panic("unreachable")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the buildInfo struct we get passed in is based on what's baked into the lazygit
|
||||||
|
// binary via the LDFLAGS argument. Some lazygit distributions will make use of these
|
||||||
|
// arguments and some will not. Go recently started baking in build info
|
||||||
|
// into the binary by default e.g. the git commit hash. So in this function
|
||||||
|
// we merge the two together, giving priority to the stuff set by LDFLAGS.
|
||||||
|
// Note: this mutates the argument passed in
|
||||||
|
func mergeBuildInfo(buildInfo *BuildInfo) {
|
||||||
|
// if the version has already been set by build flags then we'll honour that.
|
||||||
|
// chances are it's something like v0.31.0 which is more informative than a
|
||||||
|
// commit hash.
|
||||||
|
if buildInfo.Version != "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
buildInfo.Version = "unversioned"
|
||||||
|
|
||||||
|
goBuildInfo, ok := debug.ReadBuildInfo()
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
revision, ok := lo.Find(goBuildInfo.Settings, func(setting debug.BuildSetting) bool {
|
||||||
|
return setting.Key == "vcs.revision"
|
||||||
|
})
|
||||||
|
if ok {
|
||||||
|
buildInfo.Commit = revision.Value
|
||||||
|
// if lazygit was built from source we'll show the version as the
|
||||||
|
// abbreviated commit hash
|
||||||
|
buildInfo.Version = utils.ShortSha(revision.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if version hasn't been set we assume that neither has the date
|
||||||
|
time, ok := lo.Find(goBuildInfo.Settings, func(setting debug.BuildSetting) bool {
|
||||||
|
return setting.Key == "vcs.time"
|
||||||
|
})
|
||||||
|
if ok {
|
||||||
|
buildInfo.Date = time.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,13 +14,13 @@ import (
|
||||||
|
|
||||||
// this gives our integration test a way of interacting with the gui for sending keypresses
|
// this gives our integration test a way of interacting with the gui for sending keypresses
|
||||||
// and reading state.
|
// and reading state.
|
||||||
type GuiAdapter struct {
|
type GuiDriver struct {
|
||||||
gui *Gui
|
gui *Gui
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ integrationTypes.GuiAdapter = &GuiAdapter{}
|
var _ integrationTypes.GuiDriver = &GuiDriver{}
|
||||||
|
|
||||||
func (self *GuiAdapter) PressKey(keyStr string) {
|
func (self *GuiDriver) PressKey(keyStr string) {
|
||||||
key := keybindings.GetKey(keyStr)
|
key := keybindings.GetKey(keyStr)
|
||||||
|
|
||||||
var r rune
|
var r rune
|
||||||
|
@ -39,19 +39,19 @@ func (self *GuiAdapter) PressKey(keyStr string) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *GuiAdapter) Keys() config.KeybindingConfig {
|
func (self *GuiDriver) Keys() config.KeybindingConfig {
|
||||||
return self.gui.Config.GetUserConfig().Keybinding
|
return self.gui.Config.GetUserConfig().Keybinding
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *GuiAdapter) CurrentContext() types.Context {
|
func (self *GuiDriver) CurrentContext() types.Context {
|
||||||
return self.gui.c.CurrentContext()
|
return self.gui.c.CurrentContext()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *GuiAdapter) Model() *types.Model {
|
func (self *GuiDriver) Model() *types.Model {
|
||||||
return self.gui.State.Model
|
return self.gui.State.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *GuiAdapter) Fail(message string) {
|
func (self *GuiDriver) Fail(message string) {
|
||||||
self.gui.g.Close()
|
self.gui.g.Close()
|
||||||
// need to give the gui time to close
|
// need to give the gui time to close
|
||||||
time.Sleep(time.Millisecond * 100)
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
@ -59,15 +59,15 @@ func (self *GuiAdapter) Fail(message string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// logs to the normal place that you log to i.e. viewable with `lazygit --logs`
|
// logs to the normal place that you log to i.e. viewable with `lazygit --logs`
|
||||||
func (self *GuiAdapter) Log(message string) {
|
func (self *GuiDriver) Log(message string) {
|
||||||
self.gui.c.Log.Warn(message)
|
self.gui.c.Log.Warn(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// logs in the actual UI (in the commands panel)
|
// logs in the actual UI (in the commands panel)
|
||||||
func (self *GuiAdapter) LogUI(message string) {
|
func (self *GuiDriver) LogUI(message string) {
|
||||||
self.gui.c.LogAction(message)
|
self.gui.c.LogAction(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *GuiAdapter) CheckedOutRef() *models.Branch {
|
func (self *GuiDriver) CheckedOutRef() *models.Branch {
|
||||||
return self.gui.helpers.Refs.GetCheckedOutRef()
|
return self.gui.helpers.Refs.GetCheckedOutRef()
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type IntegrationTest interface {
|
type IntegrationTest interface {
|
||||||
Run(guiAdapter *GuiAdapter)
|
Run(guiAdapter *GuiDriver)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gui *Gui) handleTestMode(test integrationTypes.IntegrationTest) {
|
func (gui *Gui) handleTestMode(test integrationTypes.IntegrationTest) {
|
||||||
|
@ -22,7 +22,7 @@ func (gui *Gui) handleTestMode(test integrationTypes.IntegrationTest) {
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(time.Millisecond * 100)
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
|
||||||
test.Run(&GuiAdapter{gui: gui})
|
test.Run(&GuiDriver{gui: gui})
|
||||||
|
|
||||||
gui.g.Update(func(*gocui.Gui) error {
|
gui.g.Update(func(*gocui.Gui) error {
|
||||||
return gocui.ErrQuit
|
return gocui.ErrQuit
|
||||||
|
|
|
@ -37,15 +37,15 @@ 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/runner/main.go [<testname>...]
|
1. go run pkg/integration/cmd/runner/main.go [<testname>...]
|
||||||
2. go run pkg/integration/tui/main.go
|
2. go run pkg/integration/cmd/tui/main.go
|
||||||
3. go test pkg/integration/integration_test.go
|
3. go test pkg/integration/integration_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/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 pkg/integration/cmd/runner/main.go 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.
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ At the moment, all the deprecated test code lives in pkg/integration/deprecated.
|
||||||
We should never write any new tests under the old method, and if a given test breaks because of new functionality, it's best to simply rewrite it under the new approach. If you want to run a test for the sake of watching what it does so that you can transcribe it into the new approach, you can run:
|
We should never write any new tests under the old method, and if a given test breaks because of new functionality, it's best to simply rewrite it under the new approach. If you want to run a test for the sake of watching what it does so that you can transcribe it into the new approach, you can run:
|
||||||
|
|
||||||
```
|
```
|
||||||
go run pkg/integration/deprecated/tui/main.go
|
go run pkg/integration/deprecated/cmd/tui/main.go
|
||||||
```
|
```
|
||||||
|
|
||||||
The tests in the old format live in test/integration. In the old format, test definitions are co-located with the snapshots. The setup step is done in a `setup.sh` shell script and the `recording.json` file contains the recorded keypresses to be replayed during the test.
|
The tests in the old format live in test/integration. In the old format, test definitions are co-located with the snapshots. The setup step is done in a `setup.sh` shell script and the `recording.json` file contains the recorded keypresses to be replayed during the test.
|
||||||
|
|
47
pkg/integration/cmd/injector/main.go
Normal file
47
pkg/integration/cmd/injector/main.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/app"
|
||||||
|
"github.com/jesseduffield/lazygit/pkg/integration"
|
||||||
|
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// that would mean lazygit would be depending on integration test code which
|
||||||
|
// would bloat the binary.
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
dummyBuildInfo := &app.BuildInfo{
|
||||||
|
Commit: "",
|
||||||
|
Date: "",
|
||||||
|
Version: "",
|
||||||
|
BuildSource: "integration test",
|
||||||
|
}
|
||||||
|
|
||||||
|
integrationTest := getIntegrationTest()
|
||||||
|
|
||||||
|
app.Start(dummyBuildInfo, integrationTest)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIntegrationTest() integrationTypes.IntegrationTest {
|
||||||
|
integrationTestName := os.Getenv("LAZYGIT_TEST_NAME")
|
||||||
|
if integrationTestName == "" {
|
||||||
|
panic("expected LAZYGIT_TEST_NAME environment variable to be set, given that we're running an integration test")
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsetting so that if we run lazygit in as a 'daemon' we don't think we're trying to run a test again
|
||||||
|
os.Unsetenv("LAZYGIT_TEST_NAME")
|
||||||
|
for _, candidateTest := range integration.Tests {
|
||||||
|
if candidateTest.Name() == integrationTestName {
|
||||||
|
return candidateTest
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic("Could not find integration test with name: " + integrationTestName)
|
||||||
|
}
|
|
@ -12,7 +12,13 @@ import (
|
||||||
|
|
||||||
// see pkg/integration/README.md
|
// see pkg/integration/README.md
|
||||||
|
|
||||||
// If invoked directly, you can specify tests to run by passing them as positional arguments.
|
// The purpose of this program is to run integration tests. It does this by
|
||||||
|
// building our injector program (in the sibling injector directory) and then for
|
||||||
|
// each test we're running, invoke the injector program with the test's name as
|
||||||
|
// an environment variable. Then the injector finds the test and passes it to
|
||||||
|
// the lazygit startup code.
|
||||||
|
|
||||||
|
// If invoked directly, you can specify tests to run by passing their names as positional arguments
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
mode := integration.GetModeFromEnv()
|
mode := integration.GetModeFromEnv()
|
|
@ -15,7 +15,7 @@ import (
|
||||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||||
)
|
)
|
||||||
|
|
||||||
// this program lets you manage integration tests in 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 {
|
type App struct {
|
||||||
tests []*components.IntegrationTest
|
tests []*components.IntegrationTest
|
||||||
|
@ -85,7 +85,7 @@ func main() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run pkg/integration/runner/main.go %s", currentTest.Name()))
|
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run pkg/integration/cmd/runner/main.go %s", currentTest.Name()))
|
||||||
app.runSubprocess(cmd)
|
app.runSubprocess(cmd)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -99,7 +99,7 @@ func main() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true go run pkg/integration/runner/main.go %s", currentTest.Name()))
|
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true go run pkg/integration/cmd/runner/main.go %s", currentTest.Name()))
|
||||||
app.runSubprocess(cmd)
|
app.runSubprocess(cmd)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -113,7 +113,7 @@ func main() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true KEY_PRESS_DELAY=200 go run pkg/integration/runner/main.go %s", currentTest.Name()))
|
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true KEY_PRESS_DELAY=200 go run pkg/integration/cmd/runner/main.go %s", currentTest.Name()))
|
||||||
app.runSubprocess(cmd)
|
app.runSubprocess(cmd)
|
||||||
|
|
||||||
return nil
|
return nil
|
|
@ -12,10 +12,10 @@ import (
|
||||||
// through this struct we assert on the state of the lazygit gui
|
// through this struct we assert on the state of the lazygit gui
|
||||||
|
|
||||||
type Assert struct {
|
type Assert struct {
|
||||||
gui integrationTypes.GuiAdapter
|
gui integrationTypes.GuiDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAssert(gui integrationTypes.GuiAdapter) *Assert {
|
func NewAssert(gui integrationTypes.GuiDriver) *Assert {
|
||||||
return &Assert{gui: gui}
|
return &Assert{gui: gui}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Input struct {
|
type Input struct {
|
||||||
gui integrationTypes.GuiAdapter
|
gui integrationTypes.GuiDriver
|
||||||
keys config.KeybindingConfig
|
keys config.KeybindingConfig
|
||||||
assert *Assert
|
assert *Assert
|
||||||
pushKeyDelay int
|
pushKeyDelay int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewInput(gui integrationTypes.GuiAdapter, keys config.KeybindingConfig, assert *Assert, pushKeyDelay int) *Input {
|
func NewInput(gui integrationTypes.GuiDriver, keys config.KeybindingConfig, assert *Assert, pushKeyDelay int) *Input {
|
||||||
return &Input{
|
return &Input{
|
||||||
gui: gui,
|
gui: gui,
|
||||||
keys: keys,
|
keys: keys,
|
||||||
|
|
|
@ -81,7 +81,7 @@ func (self *IntegrationTest) SetupRepo(shell *Shell) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// I want access to all contexts, the model, the ability to press a key, the ability to log,
|
// I want access to all contexts, the model, the ability to press a key, the ability to log,
|
||||||
func (self *IntegrationTest) Run(gui integrationTypes.GuiAdapter) {
|
func (self *IntegrationTest) Run(gui integrationTypes.GuiDriver) {
|
||||||
shell := NewShell()
|
shell := NewShell()
|
||||||
assert := NewAssert(gui)
|
assert := NewAssert(gui)
|
||||||
keys := gui.Keys()
|
keys := gui.Keys()
|
||||||
|
|
|
@ -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/runner/main.go for the new way
|
// Deprecated: This file is part of the old way of doing things. See pkg/integration/cmd/runner/main.go for the new way
|
||||||
|
|
||||||
// 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
|
|
@ -108,7 +108,7 @@ func main() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=record go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name))
|
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=record go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name))
|
||||||
app.runSubprocess(cmd)
|
app.runSubprocess(cmd)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -122,7 +122,7 @@ func main() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name))
|
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name))
|
||||||
app.runSubprocess(cmd)
|
app.runSubprocess(cmd)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -136,7 +136,7 @@ func main() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name))
|
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name))
|
||||||
app.runSubprocess(cmd)
|
app.runSubprocess(cmd)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -150,7 +150,7 @@ func main() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=updateSnapshot go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name))
|
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=updateSnapshot go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name))
|
||||||
app.runSubprocess(cmd)
|
app.runSubprocess(cmd)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -164,7 +164,7 @@ func main() {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true SPEED=1 go run pkg/integration/deprecated/runner/main.go %s", currentTest.Name))
|
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true SPEED=1 go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name))
|
||||||
app.runSubprocess(cmd)
|
app.runSubprocess(cmd)
|
||||||
|
|
||||||
return nil
|
return nil
|
|
@ -20,7 +20,7 @@ import (
|
||||||
|
|
||||||
// Deprecated: This file is part of the old way of doing things. See pkg/integration/integration.go for the new way
|
// Deprecated: This file is part of the old way of doing things. See pkg/integration/integration.go for the new way
|
||||||
|
|
||||||
// This package is for running our integration test suite. See https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.mdfor more info.
|
// This package is for running our integration test suite. See https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md for more info.
|
||||||
|
|
||||||
type IntegrationTest struct {
|
type IntegrationTest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
|
@ -58,7 +58,7 @@ func RunTests(
|
||||||
testDir := filepath.Join(rootDir, "test", "integration_new")
|
testDir := filepath.Join(rootDir, "test", "integration_new")
|
||||||
|
|
||||||
osCommand := oscommands.NewDummyOSCommand()
|
osCommand := oscommands.NewDummyOSCommand()
|
||||||
err = osCommand.Cmd.New("go build -o " + tempLazygitPath()).Run()
|
err = osCommand.Cmd.New("go build pkg/integration/cmd/intector.go -o " + tempLazygitPath()).Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,12 @@ import (
|
||||||
// to provide to a test in order for the test to run.
|
// to provide to a test in order for the test to run.
|
||||||
|
|
||||||
type IntegrationTest interface {
|
type IntegrationTest interface {
|
||||||
Run(GuiAdapter)
|
Run(GuiDriver)
|
||||||
SetupConfig(config *config.AppConfig)
|
SetupConfig(config *config.AppConfig)
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is the interface through which our integration tests interact with the lazygit gui
|
// this is the interface through which our integration tests interact with the lazygit gui
|
||||||
type GuiAdapter interface {
|
type GuiDriver interface {
|
||||||
PressKey(string)
|
PressKey(string)
|
||||||
Keys() config.KeybindingConfig
|
Keys() config.KeybindingConfig
|
||||||
CurrentContext() types.Context
|
CurrentContext() types.Context
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue