1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2024-12-04 10:34:55 +02:00

Merge pull request #2329 from jesseduffield/yet-more-test-migrations

This commit is contained in:
Jesse Duffield 2022-12-24 19:19:15 +11:00 committed by GitHub
commit 05425cfba0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
229 changed files with 883 additions and 937 deletions

21
.vscode/tasks.json vendored
View File

@ -29,6 +29,9 @@
"group": { "group": {
"kind": "test", "kind": "test",
"isDefault": true "isDefault": true
},
"presentation": {
"focus": true
} }
}, },
{ {
@ -38,6 +41,9 @@
"problemMatcher": [], "problemMatcher": [],
"group": { "group": {
"kind": "test", "kind": "test",
},
"presentation": {
"focus": true
} }
}, },
{ {
@ -47,6 +53,21 @@
"problemMatcher": [], "problemMatcher": [],
"group": { "group": {
"kind": "test", "kind": "test",
},
"presentation": {
"focus": true
}
},
{
"label": "Open deprecated test TUI",
"type": "shell",
"command": "go run pkg/integration/deprecated/cmd/tui/main.go",
"problemMatcher": [],
"group": {
"kind": "test",
},
"presentation": {
"focus": true
} }
} }
], ],

71
pkg/gui/background.go Normal file
View File

@ -0,0 +1,71 @@
package gui
import (
"strings"
"time"
"github.com/jesseduffield/lazygit/pkg/utils"
)
func (gui *Gui) startBackgroundRoutines() {
userConfig := gui.UserConfig
if userConfig.Git.AutoFetch {
fetchInterval := userConfig.Refresher.FetchInterval
if fetchInterval > 0 {
go utils.Safe(gui.startBackgroundFetch)
} else {
gui.c.Log.Errorf(
"Value of config option 'refresher.fetchInterval' (%d) is invalid, disabling auto-fetch",
fetchInterval)
}
}
if userConfig.Git.AutoRefresh {
refreshInterval := userConfig.Refresher.RefreshInterval
if refreshInterval > 0 {
gui.goEvery(time.Second*time.Duration(refreshInterval), gui.stopChan, gui.refreshFilesAndSubmodules)
} else {
gui.c.Log.Errorf(
"Value of config option 'refresher.refreshInterval' (%d) is invalid, disabling auto-refresh",
refreshInterval)
}
}
}
func (gui *Gui) startBackgroundFetch() {
gui.waitForIntro.Wait()
isNew := gui.IsNewRepo
userConfig := gui.UserConfig
if !isNew {
time.After(time.Duration(userConfig.Refresher.FetchInterval) * time.Second)
}
err := gui.backgroundFetch()
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
_ = gui.c.Alert(gui.c.Tr.NoAutomaticGitFetchTitle, gui.c.Tr.NoAutomaticGitFetchBody)
} else {
gui.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), gui.stopChan, func() error {
err := gui.backgroundFetch()
gui.render()
return err
})
}
}
func (gui *Gui) goEvery(interval time.Duration, stop chan struct{}, function func() error) {
go utils.Safe(func() {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if gui.PauseBackgroundThreads {
continue
}
_ = function()
case <-stop:
return
}
}
})
}

View File

@ -7,7 +7,6 @@ import (
"os" "os"
"strings" "strings"
"sync" "sync"
"time"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
appTypes "github.com/jesseduffield/lazygit/pkg/app/types" appTypes "github.com/jesseduffield/lazygit/pkg/app/types"
@ -527,27 +526,7 @@ func (gui *Gui) Run(startArgs appTypes.StartArgs) error {
gui.waitForIntro.Add(1) gui.waitForIntro.Add(1)
if userConfig.Git.AutoFetch { gui.startBackgroundRoutines()
fetchInterval := userConfig.Refresher.FetchInterval
if fetchInterval > 0 {
go utils.Safe(gui.startBackgroundFetch)
} else {
gui.c.Log.Errorf(
"Value of config option 'refresher.fetchInterval' (%d) is invalid, disabling auto-fetch",
fetchInterval)
}
}
if userConfig.Git.AutoRefresh {
refreshInterval := userConfig.Refresher.RefreshInterval
if refreshInterval > 0 {
gui.goEvery(time.Second*time.Duration(refreshInterval), gui.stopChan, gui.refreshFilesAndSubmodules)
} else {
gui.c.Log.Errorf(
"Value of config option 'refresher.refreshInterval' (%d) is invalid, disabling auto-refresh",
refreshInterval)
}
}
gui.c.Log.Info("starting main loop") gui.c.Log.Info("starting main loop")
@ -722,43 +701,6 @@ func (gui *Gui) showIntroPopupMessage(done chan struct{}) error {
}) })
} }
func (gui *Gui) goEvery(interval time.Duration, stop chan struct{}, function func() error) {
go utils.Safe(func() {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if gui.PauseBackgroundThreads {
continue
}
_ = function()
case <-stop:
return
}
}
})
}
func (gui *Gui) startBackgroundFetch() {
gui.waitForIntro.Wait()
isNew := gui.IsNewRepo
userConfig := gui.UserConfig
if !isNew {
time.After(time.Duration(userConfig.Refresher.FetchInterval) * time.Second)
}
err := gui.backgroundFetch()
if err != nil && strings.Contains(err.Error(), "exit status 128") && isNew {
_ = gui.c.Alert(gui.c.Tr.NoAutomaticGitFetchTitle, gui.c.Tr.NoAutomaticGitFetchBody)
} else {
gui.goEvery(time.Second*time.Duration(userConfig.Refresher.FetchInterval), gui.stopChan, func() error {
err := gui.backgroundFetch()
gui.render()
return err
})
}
}
// setColorScheme sets the color scheme for the app based on the user config // setColorScheme sets the color scheme for the app based on the user config
func (gui *Gui) setColorScheme() error { func (gui *Gui) setColorScheme() error {
userConfig := gui.UserConfig userConfig := gui.UserConfig

View File

@ -1,6 +1,8 @@
package gui package gui
import ( import (
"fmt"
"strings"
"time" "time"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
@ -55,7 +57,7 @@ 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)
panic(message) panic(fmt.Sprintf("%s\nLog:\n%s", message, strings.Join(self.gui.CmdLog, "\n")))
} }
// 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`

View File

@ -8,6 +8,7 @@ import (
"time" "time"
"github.com/jesseduffield/gocui" "github.com/jesseduffield/gocui"
"github.com/jesseduffield/lazygit/pkg/integration/components"
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types" integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
"github.com/jesseduffield/lazygit/pkg/utils" "github.com/jesseduffield/lazygit/pkg/utils"
) )
@ -17,6 +18,10 @@ type IntegrationTest interface {
} }
func (gui *Gui) handleTestMode(test integrationTypes.IntegrationTest) { func (gui *Gui) handleTestMode(test integrationTypes.IntegrationTest) {
if os.Getenv(components.SANDBOX_ENV_VAR) == "true" {
return
}
if test != nil { if test != nil {
go func() { go func() {
time.Sleep(time.Millisecond * 100) time.Sleep(time.Millisecond * 100)

View File

@ -41,10 +41,28 @@ The run step has four arguments passed in:
Try to do as much setup work as possible in your setup step. For example, if all you're testing is that the user is able to resolve merge conflicts, create the merge conflicts in the setup step. On the other hand, if you're testing to see that lazygit can warn the user about merge conflicts after an attempted merge, it's fine to wait until the run step to actually create the conflicts. If the run step is focused on the thing you're trying to test, the test will run faster and its intent will be clearer. Try to do as much setup work as possible in your setup step. For example, if all you're testing is that the user is able to resolve merge conflicts, create the merge conflicts in the setup step. On the other hand, if you're testing to see that lazygit can warn the user about merge conflicts after an attempted merge, it's fine to wait until the run step to actually create the conflicts. If the run step is focused on the thing you're trying to test, the test will run faster and its intent will be clearer.
Use assertions to ensure that lazygit has processed all your keybindings so far. For example, if you press 'n' on a branch to create a new branch, assert that the confirmation view is now focused. Use assertions to ensure that lazygit has processed all your keybindings so far. Each time you press a key, something should happen on the screen, so you should assert that that thing has happened. This means we won't get into trouble from keys being entered two quickly because at each stage we ensure the key has been processed. This also makes tests more readable because they help explain what we expect to be happening on-screen. For example:
```go
input.Press(keys.Files.CommitChanges)
assert.InCommitMessagePanel()
```
If you find yourself doing something frequently in a test, consider making it a method in one of the helper arguments. For example, instead of calling `input.PressKey(keys.Universal.Confirm)` in 100 places, it's better to have a method `input.Confirm()`. This is not to say that everything should be made into a method on the input struct: just things that are particularly common in tests. If you find yourself doing something frequently in a test, consider making it a method in one of the helper arguments. For example, instead of calling `input.PressKey(keys.Universal.Confirm)` in 100 places, it's better to have a method `input.Confirm()`. This is not to say that everything should be made into a method on the input struct: just things that are particularly common in tests.
Also, given how often we need to select a menu item or type into a prompt panel, there are some helper functions for that. For example:
```go
// asserts that a prompt opens with the title 'Enter a file name', and then types 'my file' and confirms
input.Prompt(Equals("Enter a file name"), "my file")
// asserts that a menu opens with the title: 'Choose file content', and then selects the option which contains 'bar'
input.Menu(Equals("Choose file content"), Contains("bar"))
// asserts a confirmation appears with the title 'Are you sure?' and the content 'Are you REALLY sure' and then confirms
input.AcceptConfirmation(Equals("Are you sure?"), Equals("Are you REALLY sure?"))
```
## Running tests ## Running tests
There are three ways to invoke a test: There are three ways to invoke a test:

View File

@ -21,57 +21,44 @@ func NewAssert(gui integrationTypes.GuiDriver) *Assert {
return &Assert{gui: gui} return &Assert{gui: gui}
} }
// for making assertions on string values
type matcher struct {
testFn func(string) (bool, string)
prefix string
}
func (self *matcher) test(value string) (bool, string) {
ok, message := self.testFn(value)
if ok {
return true, ""
}
if self.prefix != "" {
return false, self.prefix + " " + message
}
return false, message
}
func (self *matcher) context(prefix string) *matcher {
self.prefix = prefix
return self
}
func Contains(target string) *matcher { func Contains(target string) *matcher {
return &matcher{testFn: func(value string) (bool, string) { return NewMatcher(
return strings.Contains(value, target), fmt.Sprintf("Expected '%s' to be found in '%s'", target, value) fmt.Sprintf("contains '%s'", target),
}} func(value string) (bool, string) {
return strings.Contains(value, target), fmt.Sprintf("Expected '%s' to be found in '%s'", target, value)
},
)
} }
func NotContains(target string) *matcher { func NotContains(target string) *matcher {
return &matcher{testFn: func(value string) (bool, string) { return NewMatcher(
return !strings.Contains(value, target), fmt.Sprintf("Expected '%s' to NOT be found in '%s'", target, value) fmt.Sprintf("does not contain '%s'", target),
}} func(value string) (bool, string) {
return !strings.Contains(value, target), fmt.Sprintf("Expected '%s' to NOT be found in '%s'", target, value)
},
)
} }
func MatchesRegexp(regexStr string) *matcher { func MatchesRegexp(target string) *matcher {
return &matcher{testFn: func(value string) (bool, string) { return NewMatcher(
matched, err := regexp.MatchString(regexStr, value) fmt.Sprintf("matches regular expression '%s'", target),
if err != nil { func(value string) (bool, string) {
return false, fmt.Sprintf("Unexpected error parsing regular expression '%s': %s", regexStr, err.Error()) matched, err := regexp.MatchString(target, value)
} if err != nil {
return matched, fmt.Sprintf("Expected '%s' to match regular expression '%s'", value, regexStr) return false, fmt.Sprintf("Unexpected error parsing regular expression '%s': %s", target, err.Error())
}} }
return matched, fmt.Sprintf("Expected '%s' to match regular expression '%s'", value, target)
},
)
} }
func Equals(target string) *matcher { func Equals(target string) *matcher {
return &matcher{testFn: func(value string) (bool, string) { return NewMatcher(
return target == value, fmt.Sprintf("Expected '%s' to equal '%s'", value, target) fmt.Sprintf("equals '%s'", target),
}} func(value string) (bool, string) {
return target == value, fmt.Sprintf("Expected '%s' to equal '%s'", value, target)
},
)
} }
func (self *Assert) WorkingTreeFileCount(expectedCount int) { func (self *Assert) WorkingTreeFileCount(expectedCount int) {
@ -115,7 +102,7 @@ func (self *Assert) AtLeastOneCommit() {
}) })
} }
func (self *Assert) MatchHeadCommitMessage(matcher *matcher) { func (self *Assert) HeadCommitMessage(matcher *matcher) {
self.assertWithRetries(func() (bool, string) { self.assertWithRetries(func() (bool, string) {
return len(self.gui.Model().Commits) > 0, "Expected at least one commit to be present" return len(self.gui.Model().Commits) > 0, "Expected at least one commit to be present"
}) })
@ -156,7 +143,7 @@ func (self *Assert) InListContext() {
}) })
} }
func (self *Assert) MatchSelectedLine(matcher *matcher) { func (self *Assert) SelectedLine(matcher *matcher) {
self.matchString(matcher, "Unexpected selected line.", self.matchString(matcher, "Unexpected selected line.",
func() string { func() string {
return self.gui.CurrentContext().GetView().SelectedLine() return self.gui.CurrentContext().GetView().SelectedLine()
@ -186,13 +173,27 @@ func (self *Assert) InAlert() {
}) })
} }
func (self *Assert) InCommitMessagePanel() {
self.assertWithRetries(func() (bool, string) {
currentView := self.gui.CurrentContext().GetView()
return currentView.Name() == "commitMessage", "Expected commit message panel to be focused"
})
}
func (self *Assert) InMenu() { func (self *Assert) InMenu() {
self.assertWithRetries(func() (bool, string) { self.assertWithRetries(func() (bool, string) {
return self.gui.CurrentContext().GetView().Name() == "menu", "Expected popup menu to be focused" return self.gui.CurrentContext().GetView().Name() == "menu", "Expected popup menu to be focused"
}) })
} }
func (self *Assert) MatchCurrentViewTitle(matcher *matcher) { func (self *Assert) NotInPopup() {
self.assertWithRetries(func() (bool, string) {
currentViewName := self.gui.CurrentContext().GetView().Name()
return currentViewName != "menu" && currentViewName != "confirmation" && currentViewName != "commitMessage", "Expected popup not to be focused"
})
}
func (self *Assert) CurrentViewTitle(matcher *matcher) {
self.matchString(matcher, "Unexpected current view title.", self.matchString(matcher, "Unexpected current view title.",
func() string { func() string {
return self.gui.CurrentContext().GetView().Title return self.gui.CurrentContext().GetView().Title
@ -200,7 +201,7 @@ func (self *Assert) MatchCurrentViewTitle(matcher *matcher) {
) )
} }
func (self *Assert) MatchViewContent(viewName string, matcher *matcher) { func (self *Assert) ViewContent(viewName string, matcher *matcher) {
self.matchString(matcher, fmt.Sprintf("Unexpected content in view '%s'.", viewName), self.matchString(matcher, fmt.Sprintf("Unexpected content in view '%s'.", viewName),
func() string { func() string {
return self.gui.View(viewName).Buffer() return self.gui.View(viewName).Buffer()
@ -208,7 +209,7 @@ func (self *Assert) MatchViewContent(viewName string, matcher *matcher) {
) )
} }
func (self *Assert) MatchCurrentViewContent(matcher *matcher) { func (self *Assert) CurrentViewContent(matcher *matcher) {
self.matchString(matcher, "Unexpected content in current view.", self.matchString(matcher, "Unexpected content in current view.",
func() string { func() string {
return self.gui.CurrentContext().GetView().Buffer() return self.gui.CurrentContext().GetView().Buffer()
@ -216,7 +217,7 @@ func (self *Assert) MatchCurrentViewContent(matcher *matcher) {
) )
} }
func (self *Assert) MatchMainViewContent(matcher *matcher) { func (self *Assert) MainViewContent(matcher *matcher) {
self.matchString(matcher, "Unexpected main view content.", self.matchString(matcher, "Unexpected main view content.",
func() string { func() string {
return self.gui.MainView().Buffer() return self.gui.MainView().Buffer()
@ -224,7 +225,7 @@ func (self *Assert) MatchMainViewContent(matcher *matcher) {
) )
} }
func (self *Assert) MatchSecondaryViewContent(matcher *matcher) { func (self *Assert) SecondaryViewContent(matcher *matcher) {
self.matchString(matcher, "Unexpected secondary view title.", self.matchString(matcher, "Unexpected secondary view title.",
func() string { func() string {
return self.gui.SecondaryView().Buffer() return self.gui.SecondaryView().Buffer()
@ -240,7 +241,7 @@ func (self *Assert) matchString(matcher *matcher, context string, getValue func(
} }
func (self *Assert) assertWithRetries(test func() (bool, string)) { func (self *Assert) assertWithRetries(test func() (bool, string)) {
waitTimes := []int{0, 1, 5, 10, 200, 500, 1000, 2000, 4000} waitTimes := []int{0, 1, 1, 1, 1, 1, 5, 10, 20, 40, 100, 200, 500, 1000, 2000, 4000}
var message string var message string
for _, waitTime := range waitTimes { for _, waitTime := range waitTimes {

View File

@ -28,88 +28,82 @@ func NewInput(gui integrationTypes.GuiDriver, keys config.KeybindingConfig, asse
// key is something like 'w' or '<space>'. It's best not to pass a direct value, // key is something like 'w' or '<space>'. It's best not to pass a direct value,
// but instead to go through the default user config to get a more meaningful key name // but instead to go through the default user config to get a more meaningful key name
func (self *Input) PressKeys(keyStrs ...string) { func (self *Input) Press(keyStrs ...string) {
for _, keyStr := range keyStrs { for _, keyStr := range keyStrs {
self.pressKey(keyStr) self.press(keyStr)
} }
} }
func (self *Input) pressKey(keyStr string) { func (self *Input) press(keyStr string) {
self.Wait(self.pushKeyDelay) self.Wait(self.pushKeyDelay)
self.gui.PressKey(keyStr) self.gui.PressKey(keyStr)
} }
func (self *Input) SwitchToStatusWindow() { func (self *Input) SwitchToStatusWindow() {
self.pressKey(self.keys.Universal.JumpToBlock[0]) self.press(self.keys.Universal.JumpToBlock[0])
self.assert.CurrentWindowName("status") self.assert.CurrentWindowName("status")
} }
func (self *Input) SwitchToFilesWindow() { func (self *Input) SwitchToFilesWindow() {
self.pressKey(self.keys.Universal.JumpToBlock[1]) self.press(self.keys.Universal.JumpToBlock[1])
self.assert.CurrentWindowName("files") self.assert.CurrentWindowName("files")
} }
func (self *Input) SwitchToBranchesWindow() { func (self *Input) SwitchToBranchesWindow() {
self.pressKey(self.keys.Universal.JumpToBlock[2]) self.press(self.keys.Universal.JumpToBlock[2])
self.assert.CurrentWindowName("localBranches") self.assert.CurrentWindowName("localBranches")
} }
func (self *Input) SwitchToCommitsWindow() { func (self *Input) SwitchToCommitsWindow() {
self.pressKey(self.keys.Universal.JumpToBlock[3]) self.press(self.keys.Universal.JumpToBlock[3])
self.assert.CurrentWindowName("commits") self.assert.CurrentWindowName("commits")
} }
func (self *Input) SwitchToStashWindow() { func (self *Input) SwitchToStashWindow() {
self.pressKey(self.keys.Universal.JumpToBlock[4]) self.press(self.keys.Universal.JumpToBlock[4])
self.assert.CurrentWindowName("stash") self.assert.CurrentWindowName("stash")
} }
func (self *Input) Type(content string) { func (self *Input) Type(content string) {
for _, char := range content { for _, char := range content {
self.pressKey(string(char)) self.press(string(char))
} }
} }
// i.e. pressing enter // i.e. pressing enter
func (self *Input) Confirm() { func (self *Input) Confirm() {
self.pressKey(self.keys.Universal.Confirm) self.press(self.keys.Universal.Confirm)
}
func (self *Input) ProceedWhenAsked(matcher *matcher) {
self.assert.InConfirm()
self.assert.MatchCurrentViewContent(matcher)
self.Confirm()
} }
// i.e. same as Confirm // i.e. same as Confirm
func (self *Input) Enter() { func (self *Input) Enter() {
self.pressKey(self.keys.Universal.Confirm) self.press(self.keys.Universal.Confirm)
} }
// i.e. pressing escape // i.e. pressing escape
func (self *Input) Cancel() { func (self *Input) Cancel() {
self.pressKey(self.keys.Universal.Return) self.press(self.keys.Universal.Return)
} }
// i.e. pressing space // i.e. pressing space
func (self *Input) PrimaryAction() { func (self *Input) PrimaryAction() {
self.pressKey(self.keys.Universal.Select) self.press(self.keys.Universal.Select)
} }
// i.e. pressing down arrow // i.e. pressing down arrow
func (self *Input) NextItem() { func (self *Input) NextItem() {
self.pressKey(self.keys.Universal.NextItem) self.press(self.keys.Universal.NextItem)
} }
// i.e. pressing up arrow // i.e. pressing up arrow
func (self *Input) PreviousItem() { func (self *Input) PreviousItem() {
self.pressKey(self.keys.Universal.PrevItem) self.press(self.keys.Universal.PrevItem)
} }
func (self *Input) ContinueMerge() { func (self *Input) ContinueMerge() {
self.PressKeys(self.keys.Universal.CreateRebaseOptionsMenu) self.Press(self.keys.Universal.CreateRebaseOptionsMenu)
self.assert.MatchSelectedLine(Contains("continue")) self.assert.SelectedLine(Contains("continue"))
self.Confirm() self.Confirm()
} }
@ -141,7 +135,7 @@ func (self *Input) Log(message string) {
// If this changes in future, we'll need to update this code to first attempt to find the item // If this changes in future, we'll need to update this code to first attempt to find the item
// in the current page and failing that, jump to the top of the view and iterate through all of it, // in the current page and failing that, jump to the top of the view and iterate through all of it,
// looking for the item. // looking for the item.
func (self *Input) NavigateToListItemContainingText(text string) { func (self *Input) NavigateToListItem(matcher *matcher) {
self.assert.InListContext() self.assert.InListContext()
currentContext := self.gui.CurrentContext().(types.IListContext) currentContext := self.gui.CurrentContext().(types.IListContext)
@ -151,19 +145,20 @@ func (self *Input) NavigateToListItemContainingText(text string) {
var matchIndex int var matchIndex int
self.assert.assertWithRetries(func() (bool, string) { self.assert.assertWithRetries(func() (bool, string) {
matchCount := 0
matchIndex = -1 matchIndex = -1
var matches []string
// first we look for a duplicate on the current screen. We won't bother looking beyond that though. // first we look for a duplicate on the current screen. We won't bother looking beyond that though.
for i, line := range view.ViewBufferLines() { for i, line := range view.ViewBufferLines() {
if strings.Contains(line, text) { ok, _ := matcher.test(line)
matchCount++ if ok {
matches = append(matches, line)
matchIndex = i matchIndex = i
} }
} }
if matchCount > 1 { if len(matches) > 1 {
return false, fmt.Sprintf("Found %d matches for %s, expected only a single match", matchCount, text) return false, fmt.Sprintf("Found %d matches for `%s`, expected only a single match. Lines:\n%s", len(matches), matcher.name, strings.Join(matches, "\n"))
} else if matchCount == 0 { } else if len(matches) == 0 {
return false, fmt.Sprintf("Could not find item containing text: %s", text) return false, fmt.Sprintf("Could not find item matching: %s", matcher.name)
} else { } else {
return true, "" return true, ""
} }
@ -171,20 +166,67 @@ func (self *Input) NavigateToListItemContainingText(text string) {
selectedLineIdx := view.SelectedLineIdx() selectedLineIdx := view.SelectedLineIdx()
if selectedLineIdx == matchIndex { if selectedLineIdx == matchIndex {
self.assert.MatchSelectedLine(Contains(text)) self.assert.SelectedLine(matcher)
return return
} }
if selectedLineIdx < matchIndex { if selectedLineIdx < matchIndex {
for i := selectedLineIdx; i < matchIndex; i++ { for i := selectedLineIdx; i < matchIndex; i++ {
self.NextItem() self.NextItem()
} }
self.assert.MatchSelectedLine(Contains(text)) self.assert.SelectedLine(matcher)
return return
} else { } else {
for i := selectedLineIdx; i > matchIndex; i-- { for i := selectedLineIdx; i > matchIndex; i-- {
self.PreviousItem() self.PreviousItem()
} }
self.assert.MatchSelectedLine(Contains(text)) self.assert.SelectedLine(matcher)
return return
} }
} }
func (self *Input) AcceptConfirmation(title *matcher, content *matcher) {
self.assert.InConfirm()
self.assert.CurrentViewTitle(title)
self.assert.CurrentViewContent(content)
self.Confirm()
}
func (self *Input) DenyConfirmation(title *matcher, content *matcher) {
self.assert.InConfirm()
self.assert.CurrentViewTitle(title)
self.assert.CurrentViewContent(content)
self.Cancel()
}
func (self *Input) Prompt(title *matcher, textToType string) {
self.assert.InPrompt()
self.assert.CurrentViewTitle(title)
self.Type(textToType)
self.Confirm()
}
// type some text into a prompt, then switch to the suggestions panel and expect the first
// item to match the given matcher, then confirm that item.
func (self *Input) Typeahead(title *matcher, textToType string, expectedFirstOption *matcher) {
self.assert.InPrompt()
self.assert.CurrentViewTitle(title)
self.Type(textToType)
self.Press(self.keys.Universal.TogglePanel)
self.assert.CurrentViewName("suggestions")
self.assert.SelectedLine(expectedFirstOption)
self.Confirm()
}
func (self *Input) Menu(title *matcher, optionToSelect *matcher) {
self.assert.InMenu()
self.assert.CurrentViewTitle(title)
self.NavigateToListItem(optionToSelect)
self.Confirm()
}
func (self *Input) Alert(title *matcher, content *matcher) {
self.assert.InListContext()
self.assert.CurrentViewTitle(title)
self.assert.CurrentViewContent(content)
self.Confirm()
}

View File

@ -0,0 +1,35 @@
package components
// for making assertions on string values
type matcher struct {
// e.g. "contains 'foo'"
name string
// returns a bool that says whether the test passed and if it returns false, it
// also returns a string of the error message
testFn func(string) (bool, string)
// this is printed when there's an error so that it's clear what the context of the assertion is
prefix string
}
func NewMatcher(name string, testFn func(string) (bool, string)) *matcher {
return &matcher{name: name, testFn: testFn}
}
func (self *matcher) test(value string) (bool, string) {
ok, message := self.testFn(value)
if ok {
return true, ""
}
if self.prefix != "" {
return false, self.prefix + " " + message
}
return false, message
}
func (self *matcher) context(prefix string) *matcher {
self.prefix = prefix
return self
}

View File

@ -115,6 +115,10 @@ func prepareTestDir(
} }
func buildLazygit() error { func buildLazygit() error {
// // TODO: remove this line!
// // skipping this because I'm not making changes to the app code atm.
// return nil
osCommand := oscommands.NewDummyOSCommand() osCommand := oscommands.NewDummyOSCommand()
return osCommand.Cmd.New(fmt.Sprintf( return osCommand.Cmd.New(fmt.Sprintf(
"go build -o %s pkg/integration/clients/injector/main.go", tempLazygitPath(), "go build -o %s pkg/integration/clients/injector/main.go", tempLazygitPath(),

View File

@ -35,6 +35,32 @@ func (s *Shell) RunCommand(cmdStr string) *Shell {
return s return s
} }
func (s *Shell) RunShellCommand(cmdStr string) *Shell {
cmd := secureexec.Command("sh", "-c", cmdStr)
cmd.Env = os.Environ()
cmd.Dir = s.dir
output, err := cmd.CombinedOutput()
if err != nil {
panic(fmt.Sprintf("error running shell command: %s\n%s", cmdStr, string(output)))
}
return s
}
func (s *Shell) RunShellCommandExpectError(cmdStr string) *Shell {
cmd := secureexec.Command("sh", "-c", cmdStr)
cmd.Env = os.Environ()
cmd.Dir = s.dir
output, err := cmd.CombinedOutput()
if err == nil {
panic(fmt.Sprintf("Expected error running shell command: %s\n%s", cmdStr, string(output)))
}
return s
}
func (s *Shell) CreateFile(path string, content string) *Shell { func (s *Shell) CreateFile(path string, content string) *Shell {
fullPath := filepath.Join(s.dir, path) fullPath := filepath.Join(s.dir, path)
err := os.WriteFile(fullPath, []byte(content), 0o644) err := os.WriteFile(fullPath, []byte(content), 0o644)

View File

@ -64,8 +64,8 @@ func TestAssertionFailure(t *testing.T) {
test := NewIntegrationTest(NewIntegrationTestArgs{ test := NewIntegrationTest(NewIntegrationTestArgs{
Description: unitTestDescription, Description: unitTestDescription,
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) { Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
input.PressKeys("a") input.Press("a")
input.PressKeys("b") input.Press("b")
assert.CommitCount(2) assert.CommitCount(2)
}, },
}) })
@ -91,8 +91,8 @@ func TestSuccess(t *testing.T) {
test := NewIntegrationTest(NewIntegrationTestArgs{ test := NewIntegrationTest(NewIntegrationTestArgs{
Description: unitTestDescription, Description: unitTestDescription,
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) { Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
input.PressKeys("a") input.Press("a")
input.PressKeys("b") input.Press("b")
assert.CommitCount(0) assert.CommitCount(0)
}, },
}) })

View File

@ -20,67 +20,53 @@ var Basic = NewIntegrationTest(NewIntegrationTestArgs{
assert *Assert, assert *Assert,
keys config.KeybindingConfig, keys config.KeybindingConfig,
) { ) {
viewBisectOptions := func() {
input.PressKeys(keys.Commits.ViewBisectOptions)
assert.InMenu()
}
markCommitAsBad := func() { markCommitAsBad := func() {
viewBisectOptions() input.Press(keys.Commits.ViewBisectOptions)
assert.MatchSelectedLine(Contains("bad")) input.Menu(Equals("Bisect"), MatchesRegexp(`mark .* as bad`))
input.Confirm()
} }
markCommitAsGood := func() { markCommitAsGood := func() {
viewBisectOptions() input.Press(keys.Commits.ViewBisectOptions)
assert.MatchSelectedLine(Contains("bad")) input.Menu(Equals("Bisect"), MatchesRegexp(`mark .* as good`))
input.NextItem()
assert.MatchSelectedLine(Contains("good"))
input.Confirm()
} }
assert.AtLeastOneCommit() assert.AtLeastOneCommit()
input.SwitchToCommitsWindow() input.SwitchToCommitsWindow()
assert.MatchSelectedLine(Contains("commit 10")) assert.SelectedLine(Contains("commit 10"))
input.NavigateToListItemContainingText("commit 09") input.NavigateToListItem(Contains("commit 09"))
markCommitAsBad() markCommitAsBad()
assert.MatchViewContent("information", Contains("bisecting")) assert.ViewContent("information", Contains("bisecting"))
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
assert.MatchSelectedLine(Contains("<-- bad")) assert.SelectedLine(Contains("<-- bad"))
input.NavigateToListItemContainingText("commit 02") input.NavigateToListItem(Contains("commit 02"))
markCommitAsGood() markCommitAsGood()
// lazygit will land us in the comit between our good and bad commits. // lazygit will land us in the commit between our good and bad commits.
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
assert.MatchSelectedLine(Contains("commit 05")) assert.SelectedLine(Contains("commit 05"))
assert.MatchSelectedLine(Contains("<-- current")) assert.SelectedLine(Contains("<-- current"))
markCommitAsBad() markCommitAsBad()
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
assert.MatchSelectedLine(Contains("commit 04")) assert.SelectedLine(Contains("commit 04"))
assert.MatchSelectedLine(Contains("<-- current")) assert.SelectedLine(Contains("<-- current"))
markCommitAsGood() markCommitAsGood()
assert.InAlert()
assert.MatchCurrentViewContent(Contains("Bisect complete!"))
// commit 5 is the culprit because we marked 4 as good and 5 as bad. // commit 5 is the culprit because we marked 4 as good and 5 as bad.
assert.MatchCurrentViewContent(Contains("commit 05")) input.Alert(Equals("Bisect complete"), MatchesRegexp("(?s)commit 05.*Do you want to reset"))
assert.MatchCurrentViewContent(Contains("Do you want to reset"))
input.Confirm()
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
assert.MatchCurrentViewContent(Contains("commit 04")) assert.CurrentViewContent(Contains("commit 04"))
assert.MatchViewContent("information", NotContains("bisecting")) assert.ViewContent("information", NotContains("bisecting"))
}, },
}) })

View File

@ -24,46 +24,29 @@ var FromOtherBranch = NewIntegrationTest(NewIntegrationTestArgs{
assert *Assert, assert *Assert,
keys config.KeybindingConfig, keys config.KeybindingConfig,
) { ) {
viewBisectOptions := func() { assert.ViewContent("information", Contains("bisecting"))
input.PressKeys(keys.Commits.ViewBisectOptions)
assert.InMenu()
}
markCommitAsGood := func() {
viewBisectOptions()
assert.MatchSelectedLine(Contains("bad"))
input.NextItem()
assert.MatchSelectedLine(Contains("good"))
input.Confirm()
}
assert.MatchViewContent("information", Contains("bisecting"))
assert.AtLeastOneCommit() assert.AtLeastOneCommit()
input.SwitchToCommitsWindow() input.SwitchToCommitsWindow()
assert.MatchSelectedLine(Contains("<-- bad")) assert.SelectedLine(Contains("<-- bad"))
assert.MatchSelectedLine(Contains("commit 08")) assert.SelectedLine(Contains("commit 08"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("<-- current")) assert.SelectedLine(Contains("<-- current"))
assert.MatchSelectedLine(Contains("commit 07")) assert.SelectedLine(Contains("commit 07"))
markCommitAsGood() input.Press(keys.Commits.ViewBisectOptions)
input.Menu(Equals("Bisect"), MatchesRegexp(`mark .* as good`))
assert.InAlert() input.Alert(Equals("Bisect complete"), MatchesRegexp(`(?s)commit 08.*Do you want to reset`))
assert.MatchCurrentViewContent(Contains("Bisect complete!"))
assert.MatchCurrentViewContent(Contains("commit 08"))
assert.MatchCurrentViewContent(Contains("Do you want to reset"))
input.Confirm()
assert.MatchViewContent("information", NotContains("bisecting")) assert.ViewContent("information", NotContains("bisecting"))
// back in master branch which just had the one commit // back in master branch which just had the one commit
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
assert.CommitCount(1) assert.CommitCount(1)
assert.MatchSelectedLine(Contains("only commit on master")) assert.SelectedLine(Contains("only commit on master"))
}, },
}) })

View File

@ -21,19 +21,16 @@ var CheckoutByName = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToBranchesWindow() input.SwitchToBranchesWindow()
assert.CurrentViewName("localBranches") assert.CurrentViewName("localBranches")
assert.MatchSelectedLine(Contains("master")) assert.SelectedLine(Contains("master"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("@")) assert.SelectedLine(Contains("@"))
input.PressKeys(keys.Branches.CheckoutBranchByName) input.Press(keys.Branches.CheckoutBranchByName)
assert.InPrompt()
assert.MatchCurrentViewTitle(Equals("Branch name:")) input.Prompt(Equals("Branch name:"), "new-branch")
input.Type("new-branch")
input.Confirm() input.Alert(Equals("Branch not found"), Equals("Branch not found. Create a new branch named new-branch?"))
assert.InAlert()
assert.MatchCurrentViewContent(Equals("Branch not found. Create a new branch named new-branch?"))
input.Confirm()
assert.CurrentViewName("localBranches") assert.CurrentViewName("localBranches")
assert.MatchSelectedLine(Contains("new-branch")) assert.SelectedLine(Contains("new-branch"))
}, },
}) })

View File

@ -20,21 +20,18 @@ var Delete = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToBranchesWindow() input.SwitchToBranchesWindow()
assert.CurrentViewName("localBranches") assert.CurrentViewName("localBranches")
assert.MatchSelectedLine(Contains("branch-two")) assert.SelectedLine(Contains("branch-two"))
input.PressKeys(keys.Universal.Remove) input.Press(keys.Universal.Remove)
assert.InAlert() input.Alert(Equals("Error"), Contains("You cannot delete the checked out branch!"))
assert.MatchCurrentViewContent(Contains("You cannot delete the checked out branch!"))
input.Confirm()
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("branch-one")) assert.SelectedLine(Contains("branch-one"))
input.PressKeys(keys.Universal.Remove)
assert.InConfirm() input.Press(keys.Universal.Remove)
assert.MatchCurrentViewContent(Contains("Are you sure you want to delete the branch 'branch-one'?")) input.AcceptConfirmation(Equals("Delete Branch"), Contains("Are you sure you want to delete the branch 'branch-one'?"))
input.Confirm()
assert.CurrentViewName("localBranches") assert.CurrentViewName("localBranches")
assert.MatchSelectedLine(Contains("master")) assert.SelectedLine(Contains("master"))
assert.MatchCurrentViewContent(NotContains("branch-one")) assert.CurrentViewContent(NotContains("branch-one"))
}, },
}) })

View File

@ -18,36 +18,32 @@ var Rebase = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToBranchesWindow() input.SwitchToBranchesWindow()
assert.CurrentViewName("localBranches") assert.CurrentViewName("localBranches")
assert.MatchSelectedLine(Contains("first-change-branch")) assert.SelectedLine(Contains("first-change-branch"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("second-change-branch")) assert.SelectedLine(Contains("second-change-branch"))
input.PressKeys(keys.Branches.RebaseBranch) input.Press(keys.Branches.RebaseBranch)
assert.InConfirm() input.AcceptConfirmation(Equals("Rebasing"), Contains("Are you sure you want to rebase 'first-change-branch' on top of 'second-change-branch'?"))
assert.MatchCurrentViewContent(Contains("Are you sure you want to rebase 'first-change-branch' on top of 'second-change-branch'?"))
input.Confirm()
assert.InConfirm() input.AcceptConfirmation(Equals("Auto-merge failed"), Contains("Conflicts!"))
assert.MatchCurrentViewContent(Contains("Conflicts!"))
input.Confirm()
assert.CurrentViewName("files") assert.CurrentViewName("files")
assert.MatchSelectedLine(Contains("file")) assert.SelectedLine(Contains("file"))
// not using Confirm() convenience method because I suspect we might change this // not using Confirm() convenience method because I suspect we might change this
// keybinding to something more bespoke // keybinding to something more bespoke
input.PressKeys(keys.Universal.Confirm) input.Press(keys.Universal.Confirm)
assert.CurrentViewName("mergeConflicts") assert.CurrentViewName("mergeConflicts")
input.PrimaryAction() input.PrimaryAction()
assert.MatchViewContent("information", Contains("rebasing")) assert.ViewContent("information", Contains("rebasing"))
assert.InConfirm()
assert.MatchCurrentViewContent(Contains("all merge conflicts resolved. Continue?")) input.AcceptConfirmation(Equals("continue"), Contains("all merge conflicts resolved. Continue?"))
input.Confirm()
assert.MatchViewContent("information", NotContains("rebasing")) assert.ViewContent("information", NotContains("rebasing"))
// this proves we actually have integrated the changes from second-change-branch // this proves we actually have integrated the changes from second-change-branch
assert.MatchViewContent("commits", Contains("second-change-branch unrelated change")) assert.ViewContent("commits", Contains("second-change-branch unrelated change"))
}, },
}) })

View File

@ -21,49 +21,45 @@ var RebaseAndDrop = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToBranchesWindow() input.SwitchToBranchesWindow()
assert.CurrentViewName("localBranches") assert.CurrentViewName("localBranches")
assert.MatchSelectedLine(Contains("first-change-branch")) assert.SelectedLine(Contains("first-change-branch"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("second-change-branch")) assert.SelectedLine(Contains("second-change-branch"))
input.PressKeys(keys.Branches.RebaseBranch) input.Press(keys.Branches.RebaseBranch)
assert.InConfirm() input.AcceptConfirmation(Equals("Rebasing"), Contains("Are you sure you want to rebase 'first-change-branch' on top of 'second-change-branch'?"))
assert.MatchCurrentViewContent(Contains("Are you sure you want to rebase 'first-change-branch' on top of 'second-change-branch'?"))
input.Confirm()
assert.MatchViewContent("information", Contains("rebasing")) assert.ViewContent("information", Contains("rebasing"))
assert.InConfirm() input.AcceptConfirmation(Equals("Auto-merge failed"), Contains("Conflicts!"))
assert.MatchCurrentViewContent(Contains("Conflicts!"))
input.Confirm()
assert.CurrentViewName("files") assert.CurrentViewName("files")
assert.MatchSelectedLine(Contains("file")) assert.SelectedLine(Contains("file"))
input.SwitchToCommitsWindow() input.SwitchToCommitsWindow()
assert.MatchSelectedLine(Contains("pick")) // this means it's a rebasing commit assert.SelectedLine(Contains("pick")) // this means it's a rebasing commit
input.NextItem() input.NextItem()
input.PressKeys(keys.Universal.Remove) input.Press(keys.Universal.Remove)
assert.MatchSelectedLine(Contains("to remove")) // this is the commit name
assert.MatchSelectedLine(Contains("drop")) assert.SelectedLine(Contains("to remove"))
// the commit has been marked to drop once we continue the rebase.
assert.SelectedLine(Contains("drop"))
input.SwitchToFilesWindow() input.SwitchToFilesWindow()
// not using Confirm() convenience method because I suspect we might change this // not using Confirm() convenience method because I suspect we might change this
// keybinding to something more bespoke // keybinding to something more bespoke
input.PressKeys(keys.Universal.Confirm) input.Press(keys.Universal.Confirm)
assert.CurrentViewName("mergeConflicts") assert.CurrentViewName("mergeConflicts")
input.PrimaryAction() input.PrimaryAction()
assert.InConfirm() input.AcceptConfirmation(Equals("continue"), Contains("all merge conflicts resolved. Continue?"))
assert.MatchCurrentViewContent(Contains("all merge conflicts resolved. Continue?"))
input.Confirm()
assert.MatchViewContent("information", NotContains("rebasing")) assert.ViewContent("information", NotContains("rebasing"))
// this proves we actually have integrated the changes from second-change-branch // this proves we actually have integrated the changes from second-change-branch
assert.MatchViewContent("commits", Contains("second-change-branch unrelated change")) assert.ViewContent("commits", Contains("second-change-branch unrelated change"))
assert.MatchViewContent("commits", Contains("to keep")) assert.ViewContent("commits", Contains("to keep"))
assert.MatchViewContent("commits", NotContains("to remove")) assert.ViewContent("commits", NotContains("to remove"))
}, },
}) })

View File

@ -24,21 +24,13 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToBranchesWindow() input.SwitchToBranchesWindow()
assert.CurrentViewName("localBranches") assert.CurrentViewName("localBranches")
assert.MatchSelectedLine(Contains("current-branch")) assert.SelectedLine(Contains("current-branch"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("other-branch")) assert.SelectedLine(Contains("other-branch"))
input.PressKeys(keys.Commits.ViewResetOptions) input.Press(keys.Commits.ViewResetOptions)
assert.InMenu()
assert.MatchCurrentViewTitle(Contains("reset to other-branch"))
assert.MatchSelectedLine(Contains("soft reset")) input.Menu(Contains("reset to other-branch"), Contains("hard reset"))
input.NextItem()
assert.MatchSelectedLine(Contains("mixed reset"))
input.NextItem()
assert.MatchSelectedLine(Contains("hard reset"))
input.Confirm()
// ensure that we've returned from the menu before continuing // ensure that we've returned from the menu before continuing
assert.CurrentViewName("localBranches") assert.CurrentViewName("localBranches")
@ -47,8 +39,8 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToCommitsWindow() input.SwitchToCommitsWindow()
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
assert.CommitCount(2) assert.CommitCount(2)
assert.MatchSelectedLine(Contains("other-branch commit")) assert.SelectedLine(Contains("other-branch commit"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("root commit")) assert.SelectedLine(Contains("root commit"))
}, },
}) })

View File

@ -24,18 +24,11 @@ var Suggestions = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToBranchesWindow() input.SwitchToBranchesWindow()
assert.CurrentViewName("localBranches") assert.CurrentViewName("localBranches")
input.PressKeys(keys.Branches.CheckoutBranchByName) input.Press(keys.Branches.CheckoutBranchByName)
assert.CurrentViewName("confirmation")
input.Type("branch-to")
input.PressKeys(keys.Universal.TogglePanel)
assert.CurrentViewName("suggestions")
// we expect the first suggestion to be the branch we want because it most // we expect the first suggestion to be the branch we want because it most
// closely matches what we typed in // closely matches what we typed in
assert.MatchSelectedLine(Contains("branch-to-checkout")) input.Typeahead(Equals("Branch name:"), "branch-to", Contains("branch-to-checkout"))
input.Confirm()
assert.CurrentBranchName("branch-to-checkout") assert.CurrentBranchName("branch-to-checkout")
}, },

View File

@ -27,40 +27,38 @@ var CherryPick = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToBranchesWindow() input.SwitchToBranchesWindow()
assert.CurrentViewName("localBranches") assert.CurrentViewName("localBranches")
assert.MatchSelectedLine(Contains("first-branch")) assert.SelectedLine(Contains("first-branch"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("second-branch")) assert.SelectedLine(Contains("second-branch"))
input.Enter() input.Enter()
assert.CurrentViewName("subCommits") assert.CurrentViewName("subCommits")
assert.MatchSelectedLine(Contains("four")) assert.SelectedLine(Contains("four"))
input.PressKeys(keys.Commits.CherryPickCopy) input.Press(keys.Commits.CherryPickCopy)
assert.MatchViewContent("information", Contains("1 commit copied")) assert.ViewContent("information", Contains("1 commit copied"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("three")) assert.SelectedLine(Contains("three"))
input.PressKeys(keys.Commits.CherryPickCopy) input.Press(keys.Commits.CherryPickCopy)
assert.MatchViewContent("information", Contains("2 commits copied")) assert.ViewContent("information", Contains("2 commits copied"))
input.SwitchToCommitsWindow() input.SwitchToCommitsWindow()
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
assert.MatchSelectedLine(Contains("two")) assert.SelectedLine(Contains("two"))
input.PressKeys(keys.Commits.PasteCommits) input.Press(keys.Commits.PasteCommits)
assert.InAlert() input.Alert(Equals("Cherry-Pick"), Contains("Are you sure you want to cherry-pick the copied commits onto this branch?"))
assert.MatchCurrentViewContent(Contains("Are you sure you want to cherry-pick the copied commits onto this branch?"))
input.Confirm()
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
assert.MatchSelectedLine(Contains("four")) assert.SelectedLine(Contains("four"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("three")) assert.SelectedLine(Contains("three"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("two")) assert.SelectedLine(Contains("two"))
assert.MatchViewContent("information", Contains("2 commits copied")) assert.ViewContent("information", Contains("2 commits copied"))
input.PressKeys(keys.Universal.Return) input.Press(keys.Universal.Return)
assert.MatchViewContent("information", NotContains("commits copied")) assert.ViewContent("information", NotContains("commits copied"))
}, },
}) })

View File

@ -18,50 +18,44 @@ var CherryPickConflicts = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToBranchesWindow() input.SwitchToBranchesWindow()
assert.CurrentViewName("localBranches") assert.CurrentViewName("localBranches")
assert.MatchSelectedLine(Contains("first-change-branch")) assert.SelectedLine(Contains("first-change-branch"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("second-change-branch")) assert.SelectedLine(Contains("second-change-branch"))
input.Enter() input.Enter()
assert.CurrentViewName("subCommits") assert.CurrentViewName("subCommits")
assert.MatchSelectedLine(Contains("second-change-branch unrelated change")) assert.SelectedLine(Contains("second-change-branch unrelated change"))
input.PressKeys(keys.Commits.CherryPickCopy) input.Press(keys.Commits.CherryPickCopy)
assert.MatchViewContent("information", Contains("1 commit copied")) assert.ViewContent("information", Contains("1 commit copied"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("second change")) assert.SelectedLine(Contains("second change"))
input.PressKeys(keys.Commits.CherryPickCopy) input.Press(keys.Commits.CherryPickCopy)
assert.MatchViewContent("information", Contains("2 commits copied")) assert.ViewContent("information", Contains("2 commits copied"))
input.SwitchToCommitsWindow() input.SwitchToCommitsWindow()
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
assert.MatchSelectedLine(Contains("first change")) assert.SelectedLine(Contains("first change"))
input.PressKeys(keys.Commits.PasteCommits) input.Press(keys.Commits.PasteCommits)
assert.InAlert() input.Alert(Equals("Cherry-Pick"), Contains("Are you sure you want to cherry-pick the copied commits onto this branch?"))
assert.MatchCurrentViewContent(Contains("Are you sure you want to cherry-pick the copied commits onto this branch?"))
input.Confirm() input.AcceptConfirmation(Equals("Auto-merge failed"), Contains("Conflicts!"))
assert.MatchCurrentViewContent(Contains("Conflicts!"))
input.Confirm()
assert.CurrentViewName("files") assert.CurrentViewName("files")
assert.MatchSelectedLine(Contains("file")) assert.SelectedLine(Contains("file"))
// not using Confirm() convenience method because I suspect we might change this // not using Confirm() convenience method because I suspect we might change this
// keybinding to something more bespoke // keybinding to something more bespoke
input.PressKeys(keys.Universal.Confirm) input.Press(keys.Universal.Confirm)
assert.CurrentViewName("mergeConflicts") assert.CurrentViewName("mergeConflicts")
// picking 'Second change' // picking 'Second change'
input.NextItem() input.NextItem()
input.PrimaryAction() input.PrimaryAction()
assert.InConfirm() input.AcceptConfirmation(Equals("continue"), Contains("all merge conflicts resolved. Continue?"))
assert.MatchCurrentViewContent(Contains("all merge conflicts resolved. Continue?"))
input.Confirm()
assert.CurrentViewName("files") assert.CurrentViewName("files")
assert.WorkingTreeFileCount(0) assert.WorkingTreeFileCount(0)
@ -69,19 +63,19 @@ var CherryPickConflicts = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToCommitsWindow() input.SwitchToCommitsWindow()
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
assert.MatchSelectedLine(Contains("second-change-branch unrelated change")) assert.SelectedLine(Contains("second-change-branch unrelated change"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("second change")) assert.SelectedLine(Contains("second change"))
// because we picked 'Second change' when resolving the conflict, // because we picked 'Second change' when resolving the conflict,
// we now see this commit as having replaced First Change with Second Change, // we now see this commit as having replaced First Change with Second Change,
// as opposed to replacing 'Original' with 'Second change' // as opposed to replacing 'Original' with 'Second change'
assert.MatchMainViewContent(Contains("-First Change")) assert.MainViewContent(Contains("-First Change"))
assert.MatchMainViewContent(Contains("+Second Change")) assert.MainViewContent(Contains("+Second Change"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Contains("first change")) assert.SelectedLine(Contains("first change"))
assert.MatchViewContent("information", Contains("2 commits copied")) assert.ViewContent("information", Contains("2 commits copied"))
input.PressKeys(keys.Universal.Return) input.Press(keys.Universal.Return)
assert.MatchViewContent("information", NotContains("commits copied")) assert.ViewContent("information", NotContains("commits copied"))
}, },
}) })

View File

@ -20,13 +20,14 @@ var Commit = NewIntegrationTest(NewIntegrationTestArgs{
input.PrimaryAction() input.PrimaryAction()
input.NextItem() input.NextItem()
input.PrimaryAction() input.PrimaryAction()
input.PressKeys(keys.Files.CommitChanges) input.Press(keys.Files.CommitChanges)
assert.InCommitMessagePanel()
commitMessage := "my commit message" commitMessage := "my commit message"
input.Type(commitMessage) input.Type(commitMessage)
input.Confirm() input.Confirm()
assert.CommitCount(1) assert.CommitCount(1)
assert.MatchHeadCommitMessage(Equals(commitMessage)) assert.HeadCommitMessage(Equals(commitMessage))
}, },
}) })

View File

@ -17,18 +17,19 @@ var CommitMultiline = NewIntegrationTest(NewIntegrationTestArgs{
assert.CommitCount(0) assert.CommitCount(0)
input.PrimaryAction() input.PrimaryAction()
input.PressKeys(keys.Files.CommitChanges) input.Press(keys.Files.CommitChanges)
assert.InCommitMessagePanel()
input.Type("first line") input.Type("first line")
input.PressKeys(keys.Universal.AppendNewline) input.Press(keys.Universal.AppendNewline)
input.PressKeys(keys.Universal.AppendNewline) input.Press(keys.Universal.AppendNewline)
input.Type("third line") input.Type("third line")
input.Confirm() input.Confirm()
assert.CommitCount(1) assert.CommitCount(1)
assert.MatchHeadCommitMessage(Equals("first line")) assert.HeadCommitMessage(Equals("first line"))
input.SwitchToCommitsWindow() input.SwitchToCommitsWindow()
assert.MatchMainViewContent(MatchesRegexp("first line\n\\s*\n\\s*third line")) assert.MainViewContent(MatchesRegexp("first line\n\\s*\n\\s*third line"))
}, },
}) })

View File

@ -23,16 +23,13 @@ var NewBranch = NewIntegrationTest(NewIntegrationTestArgs{
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
input.NextItem() input.NextItem()
input.PressKeys(keys.Universal.New) input.Press(keys.Universal.New)
assert.CurrentViewName("confirmation")
branchName := "my-branch-name" branchName := "my-branch-name"
input.Type(branchName) input.Prompt(Contains("New Branch Name"), branchName)
input.Confirm()
assert.CommitCount(2) assert.CommitCount(2)
assert.MatchHeadCommitMessage(Contains("commit 2")) assert.HeadCommitMessage(Contains("commit 2"))
assert.CurrentBranchName(branchName) assert.CurrentBranchName(branchName)
}, },
}) })

View File

@ -20,18 +20,13 @@ var Revert = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToCommitsWindow() input.SwitchToCommitsWindow()
input.PressKeys(keys.Commits.RevertCommit) input.Press(keys.Commits.RevertCommit)
assert.InConfirm() input.AcceptConfirmation(Equals("Revert commit"), MatchesRegexp(`Are you sure you want to revert \w+?`))
assert.MatchCurrentViewTitle(Equals("Revert commit"))
assert.MatchCurrentViewContent(MatchesRegexp("Are you sure you want to revert \\w+?"))
input.Confirm()
assert.CommitCount(2) assert.CommitCount(2)
assert.MatchHeadCommitMessage(Contains("Revert \"first commit\"")) assert.HeadCommitMessage(Contains("Revert \"first commit\""))
input.PreviousItem() input.PreviousItem()
assert.MatchMainViewContent(Contains("-myfile content")) assert.MainViewContent(Contains("-myfile content"))
assert.FileSystemPathNotPresent("myfile") assert.FileSystemPathNotPresent("myfile")
input.Wait(10)
}, },
}) })

View File

@ -18,17 +18,36 @@ var Staged = NewIntegrationTest(NewIntegrationTestArgs{
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) { Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
assert.CommitCount(0) assert.CommitCount(0)
assert.CurrentViewName("files")
assert.SelectedLine(Contains("myfile"))
// stage the file
input.PrimaryAction() input.PrimaryAction()
input.Confirm() input.Enter()
input.PrimaryAction() assert.CurrentViewName("stagingSecondary")
input.PressKeys(keys.Files.CommitChanges) // we start with both lines having been staged
assert.ViewContent("stagingSecondary", Contains("+myfile content"))
assert.ViewContent("stagingSecondary", Contains("+with a second line"))
assert.ViewContent("staging", NotContains("+myfile content"))
assert.ViewContent("staging", NotContains("+with a second line"))
// unstage the selected line
input.PrimaryAction()
// the line should have been moved to the main view
assert.ViewContent("stagingSecondary", NotContains("+myfile content"))
assert.ViewContent("stagingSecondary", Contains("+with a second line"))
assert.ViewContent("staging", Contains("+myfile content"))
assert.ViewContent("staging", NotContains("+with a second line"))
input.Press(keys.Files.CommitChanges)
commitMessage := "my commit message" commitMessage := "my commit message"
input.Type(commitMessage) input.Type(commitMessage)
input.Confirm() input.Confirm()
assert.CommitCount(1) assert.CommitCount(1)
assert.MatchHeadCommitMessage(Equals(commitMessage)) assert.HeadCommitMessage(Equals(commitMessage))
assert.CurrentWindowName("stagingSecondary") assert.CurrentWindowName("stagingSecondary")
// TODO: assert that the staging panel has been refreshed (it currently does not get correctly refreshed)
}, },
}) })

View File

@ -18,17 +18,38 @@ var StagedWithoutHooks = NewIntegrationTest(NewIntegrationTestArgs{
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) { Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
assert.CommitCount(0) assert.CommitCount(0)
// stage the file
assert.CurrentViewName("files")
assert.SelectedLine(Contains("myfile"))
input.PrimaryAction() input.PrimaryAction()
input.Confirm() input.Enter()
input.PrimaryAction() assert.CurrentViewName("stagingSecondary")
input.PressKeys(keys.Files.CommitChangesWithoutHook) // we start with both lines having been staged
assert.ViewContent("stagingSecondary", Contains("+myfile content"))
assert.ViewContent("stagingSecondary", Contains("+with a second line"))
assert.ViewContent("staging", NotContains("+myfile content"))
assert.ViewContent("staging", NotContains("+with a second line"))
commitMessage := "my commit message" // unstage the selected line
input.PrimaryAction()
// the line should have been moved to the main view
assert.ViewContent("stagingSecondary", NotContains("+myfile content"))
assert.ViewContent("stagingSecondary", Contains("+with a second line"))
assert.ViewContent("staging", Contains("+myfile content"))
assert.ViewContent("staging", NotContains("+with a second line"))
input.Press(keys.Files.CommitChangesWithoutHook)
assert.InCommitMessagePanel()
assert.CurrentViewContent(Contains("WIP"))
commitMessage := ": my commit message"
input.Type(commitMessage) input.Type(commitMessage)
input.Confirm() input.Confirm()
assert.CommitCount(1) assert.CommitCount(1)
assert.MatchHeadCommitMessage(Equals("WIP" + commitMessage)) assert.HeadCommitMessage(Equals("WIP" + commitMessage))
assert.CurrentWindowName("stagingSecondary") assert.CurrentViewName("stagingSecondary")
// TODO: assert that the staging panel has been refreshed (it currently does not get correctly refreshed)
}, },
}) })

View File

@ -5,6 +5,8 @@ import (
. "github.com/jesseduffield/lazygit/pkg/integration/components" . "github.com/jesseduffield/lazygit/pkg/integration/components"
) )
// TODO: find out why we can't use assert.SelectedLine() on the staging/stagingSecondary views.
var Unstaged = NewIntegrationTest(NewIntegrationTestArgs{ var Unstaged = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Staging a couple files, going in the unstaged files menu, staging a line and committing", Description: "Staging a couple files, going in the unstaged files menu, staging a line and committing",
ExtraCmdArgs: "", ExtraCmdArgs: "",
@ -18,16 +20,26 @@ var Unstaged = NewIntegrationTest(NewIntegrationTestArgs{
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) { Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
assert.CommitCount(0) assert.CommitCount(0)
input.Confirm() assert.CurrentViewName("files")
assert.SelectedLine(Contains("myfile"))
input.Enter()
assert.CurrentViewName("staging")
assert.ViewContent("stagingSecondary", NotContains("+myfile content"))
// stage the first line
input.PrimaryAction() input.PrimaryAction()
input.PressKeys(keys.Files.CommitChanges) assert.ViewContent("staging", NotContains("+myfile content"))
assert.ViewContent("stagingSecondary", Contains("+myfile content"))
input.Press(keys.Files.CommitChanges)
assert.InCommitMessagePanel()
commitMessage := "my commit message" commitMessage := "my commit message"
input.Type(commitMessage) input.Type(commitMessage)
input.Confirm() input.Confirm()
assert.CommitCount(1) assert.CommitCount(1)
assert.MatchHeadCommitMessage(Equals(commitMessage)) assert.HeadCommitMessage(Equals(commitMessage))
assert.CurrentWindowName("staging") assert.CurrentWindowName("staging")
// TODO: assert that the staging panel has been refreshed (it currently does not get correctly refreshed)
}, },
}) })

View File

@ -1,33 +0,0 @@
package commit
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var UnstagedWithoutHooks = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Staging a couple files, going in the unstaged files menu, staging a line and committing without pre-commit hooks",
ExtraCmdArgs: "",
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.
CreateFile("myfile", "myfile content\nwith a second line").
CreateFile("myfile2", "myfile2 content")
},
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
assert.CommitCount(0)
input.Confirm()
input.PrimaryAction()
input.PressKeys(keys.Files.CommitChangesWithoutHook)
commitMessage := "my commit message"
input.Type(commitMessage)
input.Confirm()
assert.CommitCount(1)
assert.MatchHeadCommitMessage(Equals("WIP" + commitMessage))
assert.CurrentWindowName("staging")
},
})

View File

@ -29,8 +29,8 @@ var Basic = NewIntegrationTest(NewIntegrationTestArgs{
) { ) {
assert.WorkingTreeFileCount(0) assert.WorkingTreeFileCount(0)
input.PressKeys("a") input.Press("a")
assert.WorkingTreeFileCount(1) assert.WorkingTreeFileCount(1)
assert.MatchSelectedLine(Contains("myfile")) assert.SelectedLine(Contains("myfile"))
}, },
}) })

View File

@ -63,26 +63,16 @@ var FormPrompts = NewIntegrationTest(NewIntegrationTestArgs{
) { ) {
assert.WorkingTreeFileCount(0) assert.WorkingTreeFileCount(0)
input.PressKeys("a") input.Press("a")
assert.InPrompt() input.Prompt(Equals("Enter a file name"), "my file")
assert.MatchCurrentViewTitle(Equals("Enter a file name"))
input.Type("my file")
input.Confirm()
assert.InMenu() input.Menu(Equals("Choose file content"), Contains("bar"))
assert.MatchCurrentViewTitle(Equals("Choose file content"))
assert.MatchSelectedLine(Contains("foo"))
input.NextItem()
assert.MatchSelectedLine(Contains("bar"))
input.Confirm()
assert.InConfirm() input.AcceptConfirmation(Equals("Are you sure?"), Equals("Are you REALLY sure you want to make this file? Up to you buddy."))
assert.MatchCurrentViewTitle(Equals("Are you sure?"))
input.Confirm()
assert.WorkingTreeFileCount(1) assert.WorkingTreeFileCount(1)
assert.MatchSelectedLine(Contains("my file")) assert.SelectedLine(Contains("my file"))
assert.MatchMainViewContent(Contains(`"BAR"`)) assert.MainViewContent(Contains(`"BAR"`))
}, },
}) })

View File

@ -51,24 +51,16 @@ var MenuFromCommand = NewIntegrationTest(NewIntegrationTestArgs{
assert.WorkingTreeFileCount(0) assert.WorkingTreeFileCount(0)
input.SwitchToBranchesWindow() input.SwitchToBranchesWindow()
input.PressKeys("a") input.Press("a")
assert.InMenu() input.Menu(Equals("Choose commit message"), Contains("bar"))
assert.MatchCurrentViewTitle(Equals("Choose commit message"))
assert.MatchSelectedLine(Equals("baz"))
input.NextItem()
assert.MatchSelectedLine(Equals("bar"))
input.Confirm()
assert.InPrompt() input.Prompt(Equals("Description"), " my branch")
assert.MatchCurrentViewTitle(Equals("Description"))
input.Type(" my branch")
input.Confirm()
input.SwitchToFilesWindow() input.SwitchToFilesWindow()
assert.WorkingTreeFileCount(1) assert.WorkingTreeFileCount(1)
assert.MatchSelectedLine(Contains("output.txt")) assert.SelectedLine(Contains("output.txt"))
assert.MatchMainViewContent(Contains("bar Branch: #feature/foo my branch feature/foo")) assert.MainViewContent(Contains("bar Branch: #feature/foo my branch feature/foo"))
}, },
}) })

View File

@ -47,22 +47,19 @@ var MenuFromCommandsOutput = NewIntegrationTest(NewIntegrationTestArgs{
assert *Assert, assert *Assert,
keys config.KeybindingConfig, keys config.KeybindingConfig,
) { ) {
assert.CurrentBranchName("feature/bar")
assert.WorkingTreeFileCount(0) assert.WorkingTreeFileCount(0)
input.SwitchToBranchesWindow() input.SwitchToBranchesWindow()
input.PressKeys("a") input.Press("a")
assert.InPrompt() assert.InPrompt()
assert.MatchCurrentViewTitle(Equals("Which git command do you want to run?")) assert.CurrentViewTitle(Equals("Which git command do you want to run?"))
assert.MatchSelectedLine(Equals("branch")) assert.SelectedLine(Equals("branch"))
input.Confirm() input.Confirm()
assert.InMenu() input.Menu(Equals("Branch:"), Equals("master"))
assert.MatchCurrentViewTitle(Equals("Branch:"))
input.NextItem()
input.NextItem()
assert.MatchSelectedLine(Equals("master"))
input.Confirm()
assert.CurrentBranchName("master") assert.CurrentBranchName("master")
}, },

View File

@ -61,26 +61,16 @@ var MultiplePrompts = NewIntegrationTest(NewIntegrationTestArgs{
) { ) {
assert.WorkingTreeFileCount(0) assert.WorkingTreeFileCount(0)
input.PressKeys("a") input.Press("a")
assert.InPrompt() input.Prompt(Equals("Enter a file name"), "myfile")
assert.MatchCurrentViewTitle(Equals("Enter a file name"))
input.Type("myfile")
input.Confirm()
assert.InMenu() input.Menu(Equals("Choose file content"), Contains("bar"))
assert.MatchCurrentViewTitle(Equals("Choose file content"))
assert.MatchSelectedLine(Contains("foo"))
input.NextItem()
assert.MatchSelectedLine(Contains("bar"))
input.Confirm()
assert.InConfirm() input.AcceptConfirmation(Equals("Are you sure?"), Equals("Are you REALLY sure you want to make this file? Up to you buddy."))
assert.MatchCurrentViewTitle(Equals("Are you sure?"))
input.Confirm()
assert.WorkingTreeFileCount(1) assert.WorkingTreeFileCount(1)
assert.MatchSelectedLine(Contains("myfile")) assert.SelectedLine(Contains("myfile"))
assert.MatchMainViewContent(Contains("BAR")) assert.MainViewContent(Contains("BAR"))
}, },
}) })

View File

@ -0,0 +1,57 @@
package diff
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var Diff = NewIntegrationTest(NewIntegrationTestArgs{
Description: "View the diff of two branches, then view the reverse diff",
ExtraCmdArgs: "",
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.NewBranch("branch-a")
shell.CreateFileAndAdd("file1", "first line")
shell.Commit("first commit")
shell.NewBranch("branch-b")
shell.UpdateFileAndAdd("file1", "first line\nsecond line")
shell.Commit("update")
shell.Checkout("branch-a")
},
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
input.SwitchToBranchesWindow()
assert.CurrentViewName("localBranches")
assert.SelectedLine(Contains("branch-a"))
input.Press(keys.Universal.DiffingMenu)
input.Menu(Equals("Diffing"), Contains(`diff branch-a`))
assert.CurrentViewName("localBranches")
assert.ViewContent("information", Contains("showing output for: git diff branch-a branch-a"))
input.NextItem()
assert.ViewContent("information", Contains("showing output for: git diff branch-a branch-b"))
assert.MainViewContent(Contains("+second line"))
input.Enter()
assert.CurrentViewName("subCommits")
assert.MainViewContent(Contains("+second line"))
assert.SelectedLine(Contains("update"))
input.Enter()
assert.CurrentViewName("commitFiles")
assert.SelectedLine(Contains("file1"))
assert.MainViewContent(Contains("+second line"))
input.Press(keys.Universal.Return)
input.Press(keys.Universal.Return)
assert.CurrentViewName("localBranches")
input.Press(keys.Universal.DiffingMenu)
input.Menu(Equals("Diffing"), Contains("reverse diff direction"))
assert.ViewContent("information", Contains("showing output for: git diff branch-a branch-b -R"))
assert.MainViewContent(Contains("-second line"))
},
})

View File

@ -0,0 +1,66 @@
package diff
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var DiffAndApplyPatch = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Create a patch from the diff between two branches and apply the patch.",
ExtraCmdArgs: "",
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.NewBranch("branch-a")
shell.CreateFileAndAdd("file1", "first line\n")
shell.Commit("first commit")
shell.NewBranch("branch-b")
shell.UpdateFileAndAdd("file1", "first line\nsecond line\n")
shell.Commit("update")
shell.Checkout("branch-a")
},
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
input.SwitchToBranchesWindow()
assert.CurrentViewName("localBranches")
assert.SelectedLine(Contains("branch-a"))
input.Press(keys.Universal.DiffingMenu)
input.Menu(Equals("Diffing"), Equals("diff branch-a"))
assert.CurrentViewName("localBranches")
assert.ViewContent("information", Contains("showing output for: git diff branch-a branch-a"))
input.NextItem()
assert.ViewContent("information", Contains("showing output for: git diff branch-a branch-b"))
assert.MainViewContent(Contains("+second line"))
input.Enter()
assert.CurrentViewName("subCommits")
assert.MainViewContent(Contains("+second line"))
assert.SelectedLine(Contains("update"))
input.Enter()
assert.CurrentViewName("commitFiles")
assert.SelectedLine(Contains("file1"))
assert.MainViewContent(Contains("+second line"))
// add the file to the patch
input.PrimaryAction()
input.Press(keys.Universal.DiffingMenu)
input.Menu(Equals("Diffing"), Contains("exit diff mode"))
assert.ViewContent("information", NotContains("building patch"))
input.Press(keys.Universal.CreatePatchOptionsMenu)
// adding the regex '$' here to distinguish the menu item from the 'apply patch in reverse' item
input.Menu(Equals("Patch Options"), MatchesRegexp("apply patch$"))
input.SwitchToFilesWindow()
assert.SelectedLine(Contains("file1"))
assert.MainViewContent(Contains("+second line"))
},
})

View File

@ -0,0 +1,53 @@
package diff
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var DiffCommits = NewIntegrationTest(NewIntegrationTestArgs{
Description: "View the diff between two commits",
ExtraCmdArgs: "",
Skip: false,
SetupConfig: func(config *config.AppConfig) {},
SetupRepo: func(shell *Shell) {
shell.CreateFileAndAdd("file1", "first line\n")
shell.Commit("first commit")
shell.UpdateFileAndAdd("file1", "first line\nsecond line\n")
shell.Commit("second commit")
shell.UpdateFileAndAdd("file1", "first line\nsecond line\nthird line\n")
shell.Commit("third commit")
},
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
input.SwitchToCommitsWindow()
assert.CurrentViewName("commits")
assert.SelectedLine(Contains("third commit"))
input.Press(keys.Universal.DiffingMenu)
input.Menu(Equals("Diffing"), MatchesRegexp(`diff \w+`))
assert.NotInPopup()
assert.ViewContent("information", Contains("showing output for: git diff"))
input.NextItem()
input.NextItem()
assert.SelectedLine(Contains("first commit"))
assert.MainViewContent(Contains("-second line\n-third line"))
input.Press(keys.Universal.DiffingMenu)
input.Menu(Equals("Diffing"), Contains("reverse diff direction"))
assert.NotInPopup()
assert.MainViewContent(Contains("+second line\n+third line"))
input.Enter()
assert.CurrentViewName("commitFiles")
assert.SelectedLine(Contains("file1"))
assert.MainViewContent(Contains("+second line\n+third line"))
},
})

View File

@ -24,9 +24,9 @@ var DirWithUntrackedFile = NewIntegrationTest(NewIntegrationTestArgs{
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) { Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
assert.CommitCount(1) assert.CommitCount(1)
assert.MatchMainViewContent(NotContains("error: Could not access")) assert.MainViewContent(NotContains("error: Could not access"))
// we show baz because it's a modified file but we don't show bar because it's untracked // we show baz because it's a modified file but we don't show bar because it's untracked
// (though it would be cool if we could show that too) // (though it would be cool if we could show that too)
assert.MatchMainViewContent(Contains("baz")) assert.MainViewContent(Contains("baz"))
}, },
}) })

View File

@ -0,0 +1,123 @@
package file
import (
"github.com/jesseduffield/lazygit/pkg/config"
. "github.com/jesseduffield/lazygit/pkg/integration/components"
)
var DiscardChanges = NewIntegrationTest(NewIntegrationTestArgs{
Description: "Discarding all possible permutations of changed files",
ExtraCmdArgs: "",
Skip: true, // failing due to index.lock file being created
SetupConfig: func(config *config.AppConfig) {
},
SetupRepo: func(shell *Shell) {
// typically we would use more bespoke shell methods here, but I struggled to find a way to do that,
// and this is copied over from a legacy integration test which did everything in a big shell script
// so I'm just copying it across.
// common stuff
shell.RunShellCommand(`echo test > both-deleted.txt`)
shell.RunShellCommand(`git checkout -b conflict && git add both-deleted.txt`)
shell.RunShellCommand(`echo bothmodded > both-modded.txt && git add both-modded.txt`)
shell.RunShellCommand(`echo haha > deleted-them.txt && git add deleted-them.txt`)
shell.RunShellCommand(`echo haha2 > deleted-us.txt && git add deleted-us.txt`)
shell.RunShellCommand(`echo mod > modded.txt & git add modded.txt`)
shell.RunShellCommand(`echo mod > modded-staged.txt & git add modded-staged.txt`)
shell.RunShellCommand(`echo del > deleted.txt && git add deleted.txt`)
shell.RunShellCommand(`echo del > deleted-staged.txt && git add deleted-staged.txt`)
shell.RunShellCommand(`echo change-delete > change-delete.txt && git add change-delete.txt`)
shell.RunShellCommand(`echo delete-change > delete-change.txt && git add delete-change.txt`)
shell.RunShellCommand(`echo double-modded > double-modded.txt && git add double-modded.txt`)
shell.RunShellCommand(`echo "renamed\nhaha" > renamed.txt && git add renamed.txt`)
shell.RunShellCommand(`git commit -m one`)
// stuff on other branch
shell.RunShellCommand(`git branch conflict_second && git mv both-deleted.txt added-them-changed-us.txt`)
shell.RunShellCommand(`git commit -m "both-deleted.txt renamed in added-them-changed-us.txt"`)
shell.RunShellCommand(`echo blah > both-added.txt && git add both-added.txt`)
shell.RunShellCommand(`echo mod1 > both-modded.txt && git add both-modded.txt`)
shell.RunShellCommand(`rm deleted-them.txt && git add deleted-them.txt`)
shell.RunShellCommand(`echo modded > deleted-us.txt && git add deleted-us.txt`)
shell.RunShellCommand(`git commit -m "two"`)
// stuff on our branch
shell.RunShellCommand(`git checkout conflict_second`)
shell.RunShellCommand(`git mv both-deleted.txt changed-them-added-us.txt`)
shell.RunShellCommand(`git commit -m "both-deleted.txt renamed in changed-them-added-us.txt"`)
shell.RunShellCommand(`echo mod2 > both-modded.txt && git add both-modded.txt`)
shell.RunShellCommand(`echo blah2 > both-added.txt && git add both-added.txt`)
shell.RunShellCommand(`echo modded > deleted-them.txt && git add deleted-them.txt`)
shell.RunShellCommand(`rm deleted-us.txt && git add deleted-us.txt`)
shell.RunShellCommand(`git commit -m "three"`)
shell.RunShellCommand(`git reset --hard conflict_second`)
shell.RunShellCommandExpectError(`git merge conflict`)
shell.RunShellCommand(`echo "new" > new.txt`)
shell.RunShellCommand(`echo "new staged" > new-staged.txt && git add new-staged.txt`)
shell.RunShellCommand(`echo mod2 > modded.txt`)
shell.RunShellCommand(`echo mod2 > modded-staged.txt && git add modded-staged.txt`)
shell.RunShellCommand(`rm deleted.txt`)
shell.RunShellCommand(`rm deleted-staged.txt && git add deleted-staged.txt`)
shell.RunShellCommand(`echo change-delete2 > change-delete.txt && git add change-delete.txt`)
shell.RunShellCommand(`rm change-delete.txt`)
shell.RunShellCommand(`rm delete-change.txt && git add delete-change.txt`)
shell.RunShellCommand(`echo "changed" > delete-change.txt`)
shell.RunShellCommand(`echo "change1" > double-modded.txt && git add double-modded.txt`)
shell.RunShellCommand(`echo "change2" > double-modded.txt`)
shell.RunShellCommand(`echo before > added-changed.txt && git add added-changed.txt`)
shell.RunShellCommand(`echo after > added-changed.txt`)
shell.RunShellCommand(`rm renamed.txt && git add renamed.txt`)
shell.RunShellCommand(`echo "renamed\nhaha" > renamed2.txt && git add renamed2.txt`)
},
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
assert.CommitCount(3)
type statusFile struct {
status string
label string
menuTitle string
}
discardOneByOne := func(files []statusFile) {
for _, file := range files {
assert.SelectedLine(Contains(file.status + " " + file.label))
input.Press(keys.Universal.Remove)
input.Menu(Equals(file.menuTitle), Contains("discard all changes"))
}
}
discardOneByOne([]statusFile{
{status: "UA", label: "added-them-changed-us.txt", menuTitle: "added-them-changed-us.txt"},
{status: "AA", label: "both-added.txt", menuTitle: "both-added.txt"},
{status: "DD", label: "both-deleted.txt", menuTitle: "both-deleted.txt"},
{status: "UU", label: "both-modded.txt", menuTitle: "both-modded.txt"},
{status: "AU", label: "changed-them-added-us.txt", menuTitle: "changed-them-added-us.txt"},
{status: "UD", label: "deleted-them.txt", menuTitle: "deleted-them.txt"},
{status: "DU", label: "deleted-us.txt", menuTitle: "deleted-us.txt"},
})
assert.InConfirm()
assert.CurrentViewTitle(Contains("continue"))
assert.CurrentViewContent(Contains("all merge conflicts resolved. Continue?"))
input.Press(keys.Universal.Return)
discardOneByOne([]statusFile{
{status: "MD", label: "change-delete.txt", menuTitle: "change-delete.txt"},
{status: "D ", label: "delete-change.txt", menuTitle: "delete-change.txt"},
{status: "D ", label: "deleted-staged.txt", menuTitle: "deleted-staged.txt"},
{status: " D", label: "deleted.txt", menuTitle: "deleted.txt"},
{status: "MM", label: "double-modded.txt", menuTitle: "double-modded.txt"},
{status: "M ", label: "modded-staged.txt", menuTitle: "modded-staged.txt"},
{status: " M", label: "modded.txt", menuTitle: "modded.txt"},
// the menu title only includes the new file
{status: "R ", label: "renamed.txt → renamed2.txt", menuTitle: "renamed2.txt"},
{status: "AM", label: "added-changed.txt", menuTitle: "added-changed.txt"},
{status: "A ", label: "new-staged.txt", menuTitle: "new-staged.txt"},
{status: "??", label: "new.txt", menuTitle: "new.txt"},
})
assert.WorkingTreeFileCount(0)
},
})

View File

@ -34,17 +34,17 @@ var AmendMerge = NewIntegrationTest(NewIntegrationTestArgs{
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
mergeCommitMessage := "Merge branch 'feature-branch' into development-branch" mergeCommitMessage := "Merge branch 'feature-branch' into development-branch"
assert.MatchHeadCommitMessage(Contains(mergeCommitMessage)) assert.HeadCommitMessage(Contains(mergeCommitMessage))
input.PressKeys(keys.Commits.AmendToCommit) input.Press(keys.Commits.AmendToCommit)
input.ProceedWhenAsked(Contains("Are you sure you want to amend this commit with your staged files?")) input.AcceptConfirmation(Equals("Amend Commit"), Contains("Are you sure you want to amend this commit with your staged files?"))
// assuring we haven't added a brand new commit // assuring we haven't added a brand new commit
assert.CommitCount(3) assert.CommitCount(3)
assert.MatchHeadCommitMessage(Contains(mergeCommitMessage)) assert.HeadCommitMessage(Contains(mergeCommitMessage))
// assuring the post-merge file shows up in the merge commit. // assuring the post-merge file shows up in the merge commit.
assert.MatchMainViewContent(Contains(postMergeFilename)) assert.MainViewContent(Contains(postMergeFilename))
assert.MatchMainViewContent(Contains("++" + postMergeFileContent)) assert.MainViewContent(Contains("++" + postMergeFileContent))
}, },
}) })

View File

@ -18,21 +18,21 @@ var One = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToCommitsWindow() input.SwitchToCommitsWindow()
assert.CurrentViewName("commits") assert.CurrentViewName("commits")
input.NavigateToListItemContainingText("commit 02") input.NavigateToListItem(Contains("commit 02"))
input.PressKeys(keys.Universal.Edit) input.Press(keys.Universal.Edit)
assert.MatchSelectedLine(Contains("YOU ARE HERE")) assert.SelectedLine(Contains("YOU ARE HERE"))
input.PreviousItem() input.PreviousItem()
input.PressKeys(keys.Commits.MarkCommitAsFixup) input.Press(keys.Commits.MarkCommitAsFixup)
assert.MatchSelectedLine(Contains("fixup")) assert.SelectedLine(Contains("fixup"))
input.PreviousItem() input.PreviousItem()
input.PressKeys(keys.Universal.Remove) input.Press(keys.Universal.Remove)
assert.MatchSelectedLine(Contains("drop")) assert.SelectedLine(Contains("drop"))
input.PreviousItem() input.PreviousItem()
input.PressKeys(keys.Commits.SquashDown) input.Press(keys.Commits.SquashDown)
assert.MatchSelectedLine(Contains("squash")) assert.SelectedLine(Contains("squash"))
input.ContinueRebase() input.ContinueRebase()

View File

@ -11,15 +11,12 @@ var ConfirmOnQuit = NewIntegrationTest(NewIntegrationTestArgs{
Skip: false, Skip: false,
SetupConfig: func(config *config.AppConfig) { SetupConfig: func(config *config.AppConfig) {
config.UserConfig.ConfirmOnQuit = true config.UserConfig.ConfirmOnQuit = true
config.UserConfig.Gui.Theme.ActiveBorderColor = []string{"red"}
}, },
SetupRepo: func(shell *Shell) {}, SetupRepo: func(shell *Shell) {},
Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) { Run: func(shell *Shell, input *Input, assert *Assert, keys config.KeybindingConfig) {
assert.CommitCount(0) assert.CommitCount(0)
input.PressKeys(keys.Universal.Quit) input.Press(keys.Universal.Quit)
assert.InConfirm() input.AcceptConfirmation(Equals(""), Contains("Are you sure you want to quit?"))
assert.MatchCurrentViewContent(Contains("Are you sure you want to quit?"))
input.Confirm()
}, },
}) })

View File

@ -22,16 +22,13 @@ var Rename = NewIntegrationTest(NewIntegrationTestArgs{
input.SwitchToStashWindow() input.SwitchToStashWindow()
assert.CurrentViewName("stash") assert.CurrentViewName("stash")
assert.MatchSelectedLine(Equals("On master: bar")) assert.SelectedLine(Equals("On master: bar"))
input.NextItem() input.NextItem()
assert.MatchSelectedLine(Equals("On master: foo")) assert.SelectedLine(Equals("On master: foo"))
input.PressKeys(keys.Stash.RenameStash) input.Press(keys.Stash.RenameStash)
assert.InPrompt()
assert.MatchCurrentViewTitle(Equals("Rename stash: stash@{1}"))
input.Type(" baz") input.Prompt(Equals("Rename stash: stash@{1}"), " baz")
input.Confirm()
assert.MatchSelectedLine(Equals("On master: foo baz")) assert.SelectedLine(Equals("On master: foo baz"))
}, },
}) })

View File

@ -19,15 +19,12 @@ var Stash = NewIntegrationTest(NewIntegrationTestArgs{
assert.StashCount(0) assert.StashCount(0)
assert.WorkingTreeFileCount(1) assert.WorkingTreeFileCount(1)
input.PressKeys(keys.Files.ViewStashOptions) input.Press(keys.Files.ViewStashOptions)
assert.InMenu()
input.PressKeys("a") input.Menu(Equals("Stash options"), MatchesRegexp("stash all changes$"))
assert.InPrompt()
assert.MatchCurrentViewTitle(Equals("Stash changes")) input.Prompt(Equals("Stash changes"), "my stashed file")
input.Type("my stashed file")
input.Confirm()
assert.StashCount(1) assert.StashCount(1)
assert.WorkingTreeFileCount(0) assert.WorkingTreeFileCount(0)
}, },

View File

@ -20,15 +20,12 @@ var StashIncludingUntrackedFiles = NewIntegrationTest(NewIntegrationTestArgs{
assert.StashCount(0) assert.StashCount(0)
assert.WorkingTreeFileCount(2) assert.WorkingTreeFileCount(2)
input.PressKeys(keys.Files.ViewStashOptions) input.Press(keys.Files.ViewStashOptions)
assert.InMenu()
input.PressKeys("U") input.Menu(Equals("Stash options"), Contains("stash all changes including untracked files"))
assert.InPrompt()
assert.MatchCurrentViewTitle(Equals("Stash changes")) input.Prompt(Equals("Stash changes"), "my stashed file")
input.Type("my stashed file")
input.Confirm()
assert.StashCount(1) assert.StashCount(1)
assert.WorkingTreeFileCount(0) assert.WorkingTreeFileCount(0)
}, },

View File

@ -16,6 +16,7 @@ import (
"github.com/jesseduffield/lazygit/pkg/integration/tests/commit" "github.com/jesseduffield/lazygit/pkg/integration/tests/commit"
"github.com/jesseduffield/lazygit/pkg/integration/tests/config" "github.com/jesseduffield/lazygit/pkg/integration/tests/config"
"github.com/jesseduffield/lazygit/pkg/integration/tests/custom_commands" "github.com/jesseduffield/lazygit/pkg/integration/tests/custom_commands"
"github.com/jesseduffield/lazygit/pkg/integration/tests/diff"
"github.com/jesseduffield/lazygit/pkg/integration/tests/file" "github.com/jesseduffield/lazygit/pkg/integration/tests/file"
"github.com/jesseduffield/lazygit/pkg/integration/tests/interactive_rebase" "github.com/jesseduffield/lazygit/pkg/integration/tests/interactive_rebase"
"github.com/jesseduffield/lazygit/pkg/integration/tests/misc" "github.com/jesseduffield/lazygit/pkg/integration/tests/misc"
@ -44,19 +45,22 @@ var tests = []*components.IntegrationTest{
commit.Staged, commit.Staged,
commit.Unstaged, commit.Unstaged,
commit.StagedWithoutHooks, commit.StagedWithoutHooks,
commit.UnstagedWithoutHooks,
custom_commands.Basic, custom_commands.Basic,
custom_commands.FormPrompts, custom_commands.FormPrompts,
custom_commands.MenuFromCommand, custom_commands.MenuFromCommand,
custom_commands.MenuFromCommandsOutput, custom_commands.MenuFromCommandsOutput,
custom_commands.MultiplePrompts, custom_commands.MultiplePrompts,
file.DirWithUntrackedFile, file.DirWithUntrackedFile,
file.DiscardChanges,
interactive_rebase.AmendMerge, interactive_rebase.AmendMerge,
interactive_rebase.One, interactive_rebase.One,
stash.Rename, stash.Rename,
stash.Stash, stash.Stash,
stash.StashIncludingUntrackedFiles, stash.StashIncludingUntrackedFiles,
config.RemoteNamedStar, config.RemoteNamedStar,
diff.Diff,
diff.DiffAndApplyPatch,
diff.DiffCommits,
} }
func GetTests() []*components.IntegrationTest { func GetTests() []*components.IntegrationTest {

View File

@ -1 +0,0 @@
ref: refs/heads/branch2

View File

@ -1,10 +0,0 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[user]
email = CI@example.com
name = CI

View File

@ -1 +0,0 @@
Unnamed repository; edit this file 'description' to name the repository.

View File

@ -1,7 +0,0 @@
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
.DS_Store

View File

@ -1,9 +0,0 @@
0000000000000000000000000000000000000000 5751731b38a36f8eb54a4bb304522ca539e04522 CI <CI@example.com> 1617680560 +1000 commit (initial): file0
5751731b38a36f8eb54a4bb304522ca539e04522 d15e253139400c94b42fc266641d1698720d4ecf CI <CI@example.com> 1617680560 +1000 commit: file1
d15e253139400c94b42fc266641d1698720d4ecf 0519814b4923f4639f1a47348b1539e3c5c54904 CI <CI@example.com> 1617680560 +1000 commit: file2
0519814b4923f4639f1a47348b1539e3c5c54904 144da8a531224129210249f43dded86056891506 CI <CI@example.com> 1617680561 +1000 commit: file4
144da8a531224129210249f43dded86056891506 144da8a531224129210249f43dded86056891506 CI <CI@example.com> 1617680561 +1000 checkout: moving from master to branch2
144da8a531224129210249f43dded86056891506 96a6d041bbb131df0e74d179c3adcd2ace0e7f9c CI <CI@example.com> 1617680561 +1000 commit: file4
96a6d041bbb131df0e74d179c3adcd2ace0e7f9c 75b31f81dd4387724638dbd3aff7380155c672cd CI <CI@example.com> 1617680561 +1000 commit: file4
75b31f81dd4387724638dbd3aff7380155c672cd f677ef8a14ca2770e48129cc13acfa1c369908cc CI <CI@example.com> 1617680561 +1000 commit: file2
f677ef8a14ca2770e48129cc13acfa1c369908cc a100b407f33fd2e97a3cb6f62b68ed6b7cc6c676 CI <CI@example.com> 1617680570 +1000 commit: test

View File

@ -1,5 +0,0 @@
0000000000000000000000000000000000000000 144da8a531224129210249f43dded86056891506 CI <CI@example.com> 1617680561 +1000 branch: Created from HEAD
144da8a531224129210249f43dded86056891506 96a6d041bbb131df0e74d179c3adcd2ace0e7f9c CI <CI@example.com> 1617680561 +1000 commit: file4
96a6d041bbb131df0e74d179c3adcd2ace0e7f9c 75b31f81dd4387724638dbd3aff7380155c672cd CI <CI@example.com> 1617680561 +1000 commit: file4
75b31f81dd4387724638dbd3aff7380155c672cd f677ef8a14ca2770e48129cc13acfa1c369908cc CI <CI@example.com> 1617680561 +1000 commit: file2
f677ef8a14ca2770e48129cc13acfa1c369908cc a100b407f33fd2e97a3cb6f62b68ed6b7cc6c676 CI <CI@example.com> 1617680570 +1000 commit: test

View File

@ -1,4 +0,0 @@
0000000000000000000000000000000000000000 5751731b38a36f8eb54a4bb304522ca539e04522 CI <CI@example.com> 1617680560 +1000 commit (initial): file0
5751731b38a36f8eb54a4bb304522ca539e04522 d15e253139400c94b42fc266641d1698720d4ecf CI <CI@example.com> 1617680560 +1000 commit: file1
d15e253139400c94b42fc266641d1698720d4ecf 0519814b4923f4639f1a47348b1539e3c5c54904 CI <CI@example.com> 1617680560 +1000 commit: file2
0519814b4923f4639f1a47348b1539e3c5c54904 144da8a531224129210249f43dded86056891506 CI <CI@example.com> 1617680561 +1000 commit: file4

View File

@ -1,2 +0,0 @@
x�ÎA
Â0@Q×9Eö‚d’I2"BW=Æ$N°`l)<¾=‚ÛÏ[üºö¾ ìOcWµ‘ÅieR ÌU²ÔK–B…°EŸK’æÉ™Mv}ë"0d¦À s@*k¨±Fd‡F>ã¹îvšíušïú•¾½ôR×~³� 'r1�=ƒsÎõ˜ú'7my)šéÍ8N

View File

@ -1,2 +0,0 @@
x+)JMU03c040031QHヒフI5`ーアコイ燹ヨカwチ�w.ス��モ[H
矢y�5�来ミ(桍ァ ^-ンW(x9

View File

@ -1,2 +0,0 @@
xŤŽK
�0@»Î)f_(3ů�Rpĺ1&ăHSERčń+=AwŹÇ[<Ůj]P¶—v¨Â„)ŠdĚ,Ěvrq.’8‡yrjFź}ôdv>ôŐ ¤@ÉQqźe§%xöĄ8ôÁZáŕ˛ţĐđ»=·†úa|č‡ëľęM¶zŠ”b‡!"\ ÍiĎ©¦ćf^V%óHr8â

View File

@ -1,3 +0,0 @@
x�ÎM
Â0@a×9Eö‚dò33¡«cšL°ÐØR"x|{·�oñòÖÚÒ- þÒU«D˜¼—ì™Ñ¯³`Õ�«Ä*Iiàƒ˜]}wKiPJ‰�‰|ÄÀe.Aj¥ÀRÊH>#ŸþÚ;Nö>NOýJÛW½å­=, ²Kö
Î9sÖsªëŸÜÔeUo~b,9O

View File

@ -1 +0,0 @@
a100b407f33fd2e97a3cb6f62b68ed6b7cc6c676

View File

@ -1 +0,0 @@
144da8a531224129210249f43dded86056891506

View File

@ -1 +0,0 @@
test0

View File

@ -1 +0,0 @@
test1

View File

@ -1 +0,0 @@
test2

View File

@ -1 +0,0 @@
line one

View File

@ -1 +0,0 @@
{"KeyEvents":[{"Timestamp":497,"Mod":0,"Key":259,"Ch":0},{"Timestamp":1570,"Mod":0,"Key":256,"Ch":87},{"Timestamp":1882,"Mod":0,"Key":13,"Ch":13},{"Timestamp":2258,"Mod":0,"Key":258,"Ch":0},{"Timestamp":2514,"Mod":0,"Key":13,"Ch":13},{"Timestamp":3602,"Mod":0,"Key":13,"Ch":13},{"Timestamp":5057,"Mod":0,"Key":256,"Ch":32},{"Timestamp":5250,"Mod":0,"Key":258,"Ch":0},{"Timestamp":5410,"Mod":0,"Key":256,"Ch":32},{"Timestamp":6010,"Mod":2,"Key":16,"Ch":16},{"Timestamp":6730,"Mod":0,"Key":258,"Ch":0},{"Timestamp":7106,"Mod":0,"Key":13,"Ch":13},{"Timestamp":8090,"Mod":0,"Key":260,"Ch":0},{"Timestamp":8330,"Mod":0,"Key":256,"Ch":99},{"Timestamp":8545,"Mod":0,"Key":256,"Ch":116},{"Timestamp":8601,"Mod":0,"Key":256,"Ch":101},{"Timestamp":8778,"Mod":0,"Key":256,"Ch":115},{"Timestamp":8809,"Mod":0,"Key":256,"Ch":116},{"Timestamp":9074,"Mod":0,"Key":13,"Ch":13},{"Timestamp":9722,"Mod":0,"Key":256,"Ch":113}],"ResizeEvents":[{"Timestamp":0,"Width":272,"Height":74}]}

View File

@ -1,40 +0,0 @@
#!/bin/sh
set -e
cd $1
git init
git config user.email "CI@example.com"
git config user.name "CI"
echo test0 > file0
git add .
git commit -am file0
echo test1 > file1
git add .
git commit -am file1
echo test2 > file2
git add .
git commit -am file2
echo "line one" > file4
git add .
git commit -am file4
git checkout -b branch2
echo "line two" >> file4
git add .
git commit -am file4
echo "line three" >> file4
git add .
git commit -am file4
echo "line two" >> file2
git add .
git commit -am file2

View File

@ -1 +0,0 @@
{ "description": "diffing two branches and making a patch from their diff files", "speed": 10 }

View File

@ -1 +0,0 @@
ref: refs/heads/branch2

View File

@ -1,10 +0,0 @@
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[user]
email = CI@example.com
name = CI

View File

@ -1 +0,0 @@
Unnamed repository; edit this file 'description' to name the repository.

View File

@ -1,7 +0,0 @@
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
.DS_Store

View File

@ -1,9 +0,0 @@
0000000000000000000000000000000000000000 7bf3d13079ced18f5b00e29c48c777e23f687d0a CI <CI@example.com> 1617680651 +1000 commit (initial): file0
7bf3d13079ced18f5b00e29c48c777e23f687d0a e876c3dfe2826621bea1bd3c87c2b9e2be88e69e CI <CI@example.com> 1617680651 +1000 commit: file1
e876c3dfe2826621bea1bd3c87c2b9e2be88e69e c6756882cc166f52b096a5e4fb9e4f5d507870c8 CI <CI@example.com> 1617680651 +1000 commit: file2
c6756882cc166f52b096a5e4fb9e4f5d507870c8 06da465196938ea235323950ee451ffb36a431cf CI <CI@example.com> 1617680651 +1000 commit: file4
06da465196938ea235323950ee451ffb36a431cf 06da465196938ea235323950ee451ffb36a431cf CI <CI@example.com> 1617680651 +1000 checkout: moving from master to branch2
06da465196938ea235323950ee451ffb36a431cf 6d04f5ed53b383c0a4c63cac168df557b6df1e44 CI <CI@example.com> 1617680651 +1000 commit: file4
6d04f5ed53b383c0a4c63cac168df557b6df1e44 a11d868e88adb55a48fc55ee1377b3255c0cd329 CI <CI@example.com> 1617680651 +1000 commit: file4
a11d868e88adb55a48fc55ee1377b3255c0cd329 1b74d64fe4055d4502ac600072586068b27d4aa7 CI <CI@example.com> 1617680651 +1000 commit: file2
1b74d64fe4055d4502ac600072586068b27d4aa7 0804f2069f5af172770da3d231be982ca320bf8b CI <CI@example.com> 1617680662 +1000 commit: asd

View File

@ -1,5 +0,0 @@
0000000000000000000000000000000000000000 06da465196938ea235323950ee451ffb36a431cf CI <CI@example.com> 1617680651 +1000 branch: Created from HEAD
06da465196938ea235323950ee451ffb36a431cf 6d04f5ed53b383c0a4c63cac168df557b6df1e44 CI <CI@example.com> 1617680651 +1000 commit: file4
6d04f5ed53b383c0a4c63cac168df557b6df1e44 a11d868e88adb55a48fc55ee1377b3255c0cd329 CI <CI@example.com> 1617680651 +1000 commit: file4
a11d868e88adb55a48fc55ee1377b3255c0cd329 1b74d64fe4055d4502ac600072586068b27d4aa7 CI <CI@example.com> 1617680651 +1000 commit: file2
1b74d64fe4055d4502ac600072586068b27d4aa7 0804f2069f5af172770da3d231be982ca320bf8b CI <CI@example.com> 1617680662 +1000 commit: asd

View File

@ -1,4 +0,0 @@
0000000000000000000000000000000000000000 7bf3d13079ced18f5b00e29c48c777e23f687d0a CI <CI@example.com> 1617680651 +1000 commit (initial): file0
7bf3d13079ced18f5b00e29c48c777e23f687d0a e876c3dfe2826621bea1bd3c87c2b9e2be88e69e CI <CI@example.com> 1617680651 +1000 commit: file1
e876c3dfe2826621bea1bd3c87c2b9e2be88e69e c6756882cc166f52b096a5e4fb9e4f5d507870c8 CI <CI@example.com> 1617680651 +1000 commit: file2
c6756882cc166f52b096a5e4fb9e4f5d507870c8 06da465196938ea235323950ee451ffb36a431cf CI <CI@example.com> 1617680651 +1000 commit: file4

Some files were not shown because too many files have changed in this diff Show More