2021-04-02 01:22:00 +02:00
package main
import (
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"github.com/jesseduffield/gocui"
2021-11-01 00:35:54 +02:00
"github.com/jesseduffield/lazygit/pkg/gui"
2021-07-27 15:00:37 +02:00
"github.com/jesseduffield/lazygit/pkg/gui/style"
2021-04-05 14:18:36 +02:00
"github.com/jesseduffield/lazygit/pkg/integration"
2021-04-02 01:22:00 +02:00
"github.com/jesseduffield/lazygit/pkg/secureexec"
)
2022-05-07 07:42:36 +02:00
// this program lets you manage integration tests in a TUI. See docs/Integration_Tests.md for more info.
2021-04-06 01:02:01 +02:00
2021-04-02 01:22:00 +02:00
type App struct {
2021-04-05 14:18:36 +02:00
tests [ ] * integration . Test
2021-04-05 02:20:02 +02:00
itemIdx int
testDir string
editing bool
g * gocui . Gui
2021-04-02 01:22:00 +02:00
}
2021-04-05 14:18:36 +02:00
func ( app * App ) getCurrentTest ( ) * integration . Test {
2021-04-02 01:22:00 +02:00
if len ( app . tests ) > 0 {
return app . tests [ app . itemIdx ]
}
return nil
}
func ( app * App ) refreshTests ( ) {
app . loadTests ( )
app . g . Update ( func ( * gocui . Gui ) error {
listView , err := app . g . View ( "list" )
if err != nil {
return err
}
listView . Clear ( )
for _ , test := range app . tests {
fmt . Fprintln ( listView , test . Name )
}
return nil
} )
}
func ( app * App ) loadTests ( ) {
2021-04-05 14:18:36 +02:00
tests , err := integration . LoadTests ( app . testDir )
2021-04-02 01:22:00 +02:00
if err != nil {
log . Panicln ( err )
}
app . tests = tests
if app . itemIdx > len ( app . tests ) - 1 {
app . itemIdx = len ( app . tests ) - 1
}
}
func main ( ) {
2021-04-05 14:18:36 +02:00
rootDir := integration . GetRootDirectory ( )
2021-04-02 01:22:00 +02:00
testDir := filepath . Join ( rootDir , "test" , "integration" )
app := & App { testDir : testDir }
app . loadTests ( )
2021-11-01 00:35:54 +02:00
g , err := gocui . NewGui ( gocui . OutputTrue , false , gocui . NORMAL , false , gui . RuneReplacements )
2021-04-05 02:20:02 +02:00
if err != nil {
log . Panicln ( err )
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
g . Cursor = false
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
app . g = g
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
g . SetManagerFunc ( app . layout )
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
if err := g . SetKeybinding ( "list" , nil , gocui . KeyArrowUp , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
if app . itemIdx > 0 {
app . itemIdx --
2021-04-02 01:22:00 +02:00
}
2021-10-30 09:17:32 +02:00
listView , err := g . View ( "list" )
if err != nil {
return err
}
listView . FocusPoint ( 0 , app . itemIdx )
2021-04-05 02:20:02 +02:00
return nil
} ) ; err != nil {
log . Panicln ( err )
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
if err := g . SetKeybinding ( "list" , nil , gocui . KeyCtrlC , gocui . ModNone , quit ) ; err != nil {
log . Panicln ( err )
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
if err := g . SetKeybinding ( "list" , nil , 'q' , gocui . ModNone , quit ) ; err != nil {
log . Panicln ( err )
}
if err := g . SetKeybinding ( "list" , nil , 'r' , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
currentTest := app . getCurrentTest ( )
if currentTest == nil {
return nil
2021-04-02 01:22:00 +02:00
}
2022-01-15 11:24:19 +02:00
cmd := secureexec . Command ( "sh" , "-c" , fmt . Sprintf ( "INCLUDE_SKIPPED=true MODE=record go run test/runner/main.go %s" , currentTest . Name ) )
app . runSubprocess ( cmd )
return nil
} ) ; err != nil {
log . Panicln ( err )
}
if err := g . SetKeybinding ( "list" , nil , 's' , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
currentTest := app . getCurrentTest ( )
if currentTest == nil {
return nil
}
cmd := secureexec . Command ( "sh" , "-c" , fmt . Sprintf ( "INCLUDE_SKIPPED=true MODE=sandbox go run test/runner/main.go %s" , currentTest . Name ) )
2021-04-05 02:20:02 +02:00
app . runSubprocess ( cmd )
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
return nil
} ) ; err != nil {
log . Panicln ( err )
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
if err := g . SetKeybinding ( "list" , nil , gocui . KeyEnter , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
currentTest := app . getCurrentTest ( )
if currentTest == nil {
return nil
2021-04-02 01:22:00 +02:00
}
2021-04-06 10:20:34 +02:00
cmd := secureexec . Command ( "sh" , "-c" , fmt . Sprintf ( "INCLUDE_SKIPPED=true go run test/runner/main.go %s" , currentTest . Name ) )
2021-04-05 02:20:02 +02:00
app . runSubprocess ( cmd )
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
return nil
} ) ; err != nil {
log . Panicln ( err )
}
2021-04-02 01:22:00 +02:00
2022-01-15 11:24:19 +02:00
if err := g . SetKeybinding ( "list" , nil , 'u' , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
2021-04-05 12:49:32 +02:00
currentTest := app . getCurrentTest ( )
if currentTest == nil {
return nil
}
2022-01-15 11:24:19 +02:00
cmd := secureexec . Command ( "sh" , "-c" , fmt . Sprintf ( "INCLUDE_SKIPPED=true MODE=updateSnapshot go run test/runner/main.go %s" , currentTest . Name ) )
2021-04-05 12:49:32 +02:00
app . runSubprocess ( cmd )
return nil
} ) ; err != nil {
log . Panicln ( err )
}
2022-01-26 00:07:56 +02:00
if err := g . SetKeybinding ( "list" , nil , 't' , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
currentTest := app . getCurrentTest ( )
if currentTest == nil {
return nil
}
cmd := secureexec . Command ( "sh" , "-c" , fmt . Sprintf ( "INCLUDE_SKIPPED=true SPEED=1 go run test/runner/main.go %s" , currentTest . Name ) )
app . runSubprocess ( cmd )
return nil
} ) ; err != nil {
log . Panicln ( err )
}
2021-04-05 02:20:02 +02:00
if err := g . SetKeybinding ( "list" , nil , 'o' , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
currentTest := app . getCurrentTest ( )
if currentTest == nil {
return nil
2021-04-02 01:22:00 +02:00
}
2021-04-05 02:20:02 +02:00
cmd := secureexec . Command ( "sh" , "-c" , fmt . Sprintf ( "code -r %s/%s/test.json" , app . testDir , currentTest . Name ) )
if err := cmd . Run ( ) ; err != nil {
return err
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
return nil
} ) ; err != nil {
log . Panicln ( err )
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
if err := g . SetKeybinding ( "list" , nil , 'n' , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
currentTest := app . getCurrentTest ( )
if currentTest == nil {
2021-04-02 01:22:00 +02:00
return nil
}
2021-04-05 02:20:02 +02:00
// need to duplicate that folder and then re-fetch our tests.
dir := app . testDir + "/" + app . getCurrentTest ( ) . Name
newDir := dir + "_Copy"
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
cmd := secureexec . Command ( "sh" , "-c" , fmt . Sprintf ( "cp -r %s %s" , dir , newDir ) )
if err := cmd . Run ( ) ; err != nil {
return err
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
app . loadTests ( )
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
app . refreshTests ( )
return nil
} ) ; err != nil {
log . Panicln ( err )
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
if err := g . SetKeybinding ( "list" , nil , 'm' , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
currentTest := app . getCurrentTest ( )
if currentTest == nil {
2021-04-02 01:22:00 +02:00
return nil
}
2021-04-05 02:20:02 +02:00
app . editing = true
if _ , err := g . SetCurrentView ( "editor" ) ; err != nil {
return err
}
editorView , err := g . View ( "editor" )
if err != nil {
return err
}
editorView . Clear ( )
fmt . Fprint ( editorView , currentTest . Name )
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
return nil
} ) ; err != nil {
log . Panicln ( err )
}
if err := g . SetKeybinding ( "list" , nil , 'd' , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
currentTest := app . getCurrentTest ( )
if currentTest == nil {
2021-04-02 01:22:00 +02:00
return nil
}
2021-04-05 02:20:02 +02:00
dir := app . testDir + "/" + app . getCurrentTest ( ) . Name
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
cmd := secureexec . Command ( "sh" , "-c" , fmt . Sprintf ( "rm -rf %s" , dir ) )
if err := cmd . Run ( ) ; err != nil {
return err
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
app . refreshTests ( )
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
return nil
} ) ; err != nil {
log . Panicln ( err )
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
if err := g . SetKeybinding ( "editor" , nil , gocui . KeyEnter , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
currentTest := app . getCurrentTest ( )
if currentTest == nil {
2021-04-02 01:22:00 +02:00
return nil
}
2021-04-05 02:20:02 +02:00
app . editing = false
if _ , err := g . SetCurrentView ( "list" ) ; err != nil {
return err
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
editorView , err := g . View ( "editor" )
if err != nil {
return err
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
dir := app . testDir + "/" + app . getCurrentTest ( ) . Name
newDir := app . testDir + "/" + editorView . Buffer ( )
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
cmd := secureexec . Command ( "sh" , "-c" , fmt . Sprintf ( "mv %s %s" , dir , newDir ) )
if err := cmd . Run ( ) ; err != nil {
return err
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
editorView . Clear ( )
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
app . refreshTests ( )
return nil
} ) ; err != nil {
log . Panicln ( err )
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
if err := g . SetKeybinding ( "editor" , nil , gocui . KeyEsc , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
app . editing = false
if _ , err := g . SetCurrentView ( "list" ) ; err != nil {
return err
2021-04-02 01:22:00 +02:00
}
2021-04-05 02:20:02 +02:00
return nil
} ) ; err != nil {
log . Panicln ( err )
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
err = g . MainLoop ( )
g . Close ( )
2022-01-15 06:18:16 +02:00
switch err {
case gocui . ErrQuit :
return
default :
log . Panicln ( err )
2021-04-05 02:20:02 +02:00
}
}
2021-04-02 01:22:00 +02:00
2021-04-05 02:20:02 +02:00
func ( app * App ) runSubprocess ( cmd * exec . Cmd ) {
2021-04-05 04:19:45 +02:00
if err := gocui . Screen . Suspend ( ) ; err != nil {
panic ( err )
}
2021-04-05 02:20:02 +02:00
cmd . Stdin = os . Stdin
cmd . Stderr = os . Stderr
cmd . Stdout = os . Stdout
if err := cmd . Run ( ) ; err != nil {
log . Println ( err . Error ( ) )
2021-04-02 01:22:00 +02:00
}
2021-04-05 02:20:02 +02:00
cmd . Stdin = nil
cmd . Stderr = nil
cmd . Stdout = nil
2021-07-27 15:00:37 +02:00
fmt . Fprintf ( os . Stdout , "\n%s" , style . FgGreen . Sprint ( "press enter to return" ) )
2021-04-05 02:20:02 +02:00
fmt . Scanln ( ) // wait for enter press
2021-04-05 04:19:45 +02:00
if err := gocui . Screen . Resume ( ) ; err != nil {
panic ( err )
}
2021-04-02 01:22:00 +02:00
}
func ( app * App ) layout ( g * gocui . Gui ) error {
maxX , maxY := g . Size ( )
descriptionViewHeight := 7
keybindingsViewHeight := 3
editorViewHeight := 3
if ! app . editing {
editorViewHeight = 0
} else {
descriptionViewHeight = 0
keybindingsViewHeight = 0
}
g . Cursor = app . editing
g . FgColor = gocui . ColorGreen
listView , err := g . SetView ( "list" , 0 , 0 , maxX - 1 , maxY - descriptionViewHeight - keybindingsViewHeight - editorViewHeight - 1 , 0 )
if err != nil {
if err . Error ( ) != "unknown view" {
return err
}
listView . Highlight = true
listView . Clear ( )
for _ , test := range app . tests {
fmt . Fprintln ( listView , test . Name )
}
listView . Title = "Tests"
listView . FgColor = gocui . ColorDefault
if _ , err := g . SetCurrentView ( "list" ) ; err != nil {
return err
}
}
descriptionView , err := g . SetViewBeneath ( "description" , "list" , descriptionViewHeight )
if err != nil {
if err . Error ( ) != "unknown view" {
return err
}
descriptionView . Title = "Test description"
descriptionView . Wrap = true
descriptionView . FgColor = gocui . ColorDefault
}
keybindingsView , err := g . SetViewBeneath ( "keybindings" , "description" , keybindingsViewHeight )
if err != nil {
if err . Error ( ) != "unknown view" {
return err
}
keybindingsView . Title = "Keybindings"
keybindingsView . Wrap = true
keybindingsView . FgColor = gocui . ColorDefault
2022-01-26 00:07:56 +02:00
fmt . Fprintln ( keybindingsView , "up/down: navigate, enter: run test, u: run test and update snapshots, r: record test, s: sandbox, o: open test config, n: duplicate test, m: rename test, d: delete test, t: run test at original speed" )
2021-04-02 01:22:00 +02:00
}
editorView , err := g . SetViewBeneath ( "editor" , "keybindings" , editorViewHeight )
if err != nil {
if err . Error ( ) != "unknown view" {
return err
}
editorView . Title = "Enter Name"
editorView . FgColor = gocui . ColorDefault
editorView . Editable = true
}
currentTest := app . getCurrentTest ( )
if currentTest == nil {
return nil
}
descriptionView . Clear ( )
2021-04-05 14:18:36 +02:00
fmt . Fprintf ( descriptionView , "Speed: %f. %s" , currentTest . Speed , currentTest . Description )
2021-04-02 01:22:00 +02:00
if err := g . SetKeybinding ( "list" , nil , gocui . KeyArrowDown , gocui . ModNone , func ( * gocui . Gui , * gocui . View ) error {
if app . itemIdx < len ( app . tests ) - 1 {
app . itemIdx ++
}
2021-10-30 09:17:32 +02:00
listView , err := g . View ( "list" )
if err != nil {
return err
}
listView . FocusPoint ( 0 , app . itemIdx )
2021-04-02 01:22:00 +02:00
return nil
} ) ; err != nil {
log . Panicln ( err )
}
return nil
}
func quit ( g * gocui . Gui , v * gocui . View ) error {
return gocui . ErrQuit
}