mirror of
https://github.com/jesseduffield/lazygit.git
synced 2025-02-03 13:21:56 +02:00
Merge pull request #2477 from jesseduffield/migrate-merge-conflict-tests
This commit is contained in:
commit
75aa339b4e
30
.github/workflows/ci.yml
vendored
30
.github/workflows/ci.yml
vendored
@ -46,36 +46,6 @@ jobs:
|
||||
# we're passing -short so that we skip the integration tests, which will be run in parallel below
|
||||
run: |
|
||||
go test ./... -short
|
||||
integration-tests-old:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
parallelism: [5]
|
||||
index: [0,1,2,3,4]
|
||||
name: "Integration Tests (Old pattern) (${{ matrix.index }}/${{ matrix.parallelism }})"
|
||||
env:
|
||||
GOFLAGS: -mod=vendor
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.18.x
|
||||
- name: Cache build
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: |
|
||||
~/.cache/go-build
|
||||
~/go/pkg/mod
|
||||
key: ${{runner.os}}-go-${{hashFiles('**/go.sum')}}-test
|
||||
restore-keys: |
|
||||
${{runner.os}}-go-
|
||||
- name: Test code
|
||||
# for file.allow thing see https://vielmetti.typepad.com/logbook/2022/10/git-security-fixes-lead-to-fatal-transport-file-not-allowed-error-in-ci-systems-cve-2022-39253.html
|
||||
run: |
|
||||
git config --global protocol.file.allow always && PARALLEL_TOTAL=${{ matrix.parallelism }} PARALLEL_INDEX=${{ matrix.index }} go test pkg/integration/deprecated/*.go
|
||||
integration-tests:
|
||||
runs-on: ubuntu-latest
|
||||
name: "Integration Tests"
|
||||
|
9
.gitignore
vendored
9
.gitignore
vendored
@ -35,14 +35,7 @@ lazygit.exe
|
||||
|
||||
test/git_server/data
|
||||
|
||||
# we'll scrap these lines once we've fully moved over to the new integration test approach
|
||||
test/integration/*/actual/
|
||||
test/integration/*/used_config/
|
||||
# these sample hooks waste too much space
|
||||
test/integration/*/expected/**/hooks/
|
||||
test/integration/*/expected_remote/**/hooks/
|
||||
|
||||
test/integration_new/**
|
||||
test/results/**
|
||||
|
||||
oryxBuildBinary
|
||||
__debug_bin
|
||||
|
@ -17,11 +17,6 @@ Usage:
|
||||
If you pass no test names, it runs all tests
|
||||
Accepted environment variables:
|
||||
KEY_PRESS_DELAY (e.g. 200): the number of milliseconds to wait between keypresses
|
||||
MODE:
|
||||
* ask (default): if a snapshot test fails, asks if you want to update the snapshot
|
||||
* check: if a snapshot test fails, exits with an error
|
||||
* update: if a snapshot test fails, updates the snapshot
|
||||
* sandbox: uses the test's setup step to run the test in a sandbox where you can do whatever you want
|
||||
|
||||
TUI mode:
|
||||
> go run cmd/integration_test/main.go tui
|
||||
|
@ -3,8 +3,8 @@ package hosting_service
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/fakes"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/jesseduffield/lazygit/pkg/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@ -328,7 +328,7 @@ func TestGetPullRequestURL(t *testing.T) {
|
||||
s := s
|
||||
t.Run(s.testName, func(t *testing.T) {
|
||||
tr := i18n.EnglishTranslationSet()
|
||||
log := &test.FakeFieldLogger{}
|
||||
log := &fakes.FakeFieldLogger{}
|
||||
hostingServiceMgr := NewHostingServiceMgr(log, &tr, s.remoteUrl, s.configServiceDomains)
|
||||
s.test(hostingServiceMgr.GetPullRequestURL(s.from, s.to))
|
||||
log.AssertErrors(t, s.expectedLoggedErrors)
|
||||
|
@ -1,4 +1,4 @@
|
||||
package test
|
||||
package fakes
|
||||
|
||||
import (
|
||||
"fmt"
|
@ -16,7 +16,7 @@ go run cmd/integration_test/main.go cli [--slow or --sandbox] [testname or testp
|
||||
|
||||
## Writing tests
|
||||
|
||||
The tests live in pkg/integration/tests. Each test is listed in `pkg/integration/tests/tests_gen.go` which is an auto-generated file. You can re-generate that file by running `go generate ./...` at the root of the Lazygit repo.
|
||||
The tests live in pkg/integration/tests. Each test is registered in `pkg/integration/tests/test_list.go` which is an auto-generated file. You can re-generate that file by running `go generate ./...` at the root of the Lazygit repo.
|
||||
|
||||
Each test has two important steps: the setup step and the run step.
|
||||
|
||||
@ -34,25 +34,13 @@ The run step has two arguments passed in:
|
||||
`t` is for driving the gui by pressing certain keys, selecting list items, etc.
|
||||
`keys` is for use when getting the test to press a particular key e.g. `t.Views().Commits().Focus().PressKey(keys.Universal.Confirm)`
|
||||
|
||||
### Tips
|
||||
|
||||
#### Handle most setup in the `shell` part of the test
|
||||
|
||||
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.
|
||||
|
||||
#### Create helper functions for (very) frequently used test logic
|
||||
|
||||
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 `t.PressKey(keys.Universal.Confirm)` in 100 places, it's better to have a method `t.Confirm()`. This is not to say that everything should be made into a helper method: 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. See `ExpectConfirmation` for an example.
|
||||
|
||||
## Running tests
|
||||
|
||||
There are three ways to invoke a test:
|
||||
|
||||
1. go run cmd/integration_test/main.go cli [--slow or --sandbox] [testname or testpath...]
|
||||
2. go run cmd/integration_test/main.go tui
|
||||
3. go test pkg/integration/clients/go_test.go
|
||||
3. go test pkg/integration/clients/*.go
|
||||
|
||||
The first, the test runner, is for directly running a test from the command line. If you pass no arguments, it runs all tests.
|
||||
The second, the TUI, is for running tests from a terminal UI where it's easier to find a test and run it without having to copy it's name and paste it into the terminal. This is the easiest approach by far.
|
||||
@ -62,7 +50,7 @@ The name of a test is based on its path, so the name of the test at `pkg/integra
|
||||
|
||||
You can pass the KEY_PRESS_DELAY env var to the test runner in order to set a delay in milliseconds between keypresses, which helps for watching a test at a realistic speed to understand what it's doing. Or you can pass the '--slow' flag which sets a pre-set 'slow' key delay. In the tui you can press 't' to run the test in slow mode.
|
||||
|
||||
The resultant repo will be stored in `test/integration_new`, so if you're not sure what went wrong you can go there and inspect the repo.
|
||||
The resultant repo will be stored in `test/results`, so if you're not sure what went wrong you can go there and inspect the repo.
|
||||
|
||||
### Running tests in VSCode
|
||||
|
||||
@ -79,20 +67,18 @@ Say you want to do a manual test of how lazygit handles merge-conflicts, but you
|
||||
|
||||
To run a test in sandbox mode you can press 's' on a test in the test TUI or in the test runner pass the --sandbox argument.
|
||||
|
||||
## Migration process
|
||||
## Tips for writing tests
|
||||
|
||||
You can watch how to migrate tests in this youtube [video](https://youtu.be/cJtOJu6-HcA).
|
||||
### Handle most setup in the `shell` part of the test
|
||||
|
||||
At the time of writing, most tests are created under an old approach, where you would record yourself in a lazygit session and then the test would replay the keybindings with the same timestamps. This old approach is great for writing tests quickly, but is much harder to maintain. It has to rely on snapshots to determining if a test passes or fails, and can't do assertions along the way. It's also harder to grok what's the intention behind certain actions that take place within the test (e.g. was the recorder intentionally switching to another panel or was that just a misclick?).
|
||||
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.
|
||||
|
||||
At the moment, all the deprecated test code lives in pkg/integration/deprecated. Hopefully in the very near future we migrate everything across so that we don't need to maintain two systems.
|
||||
### Create helper functions for (very) frequently used test logic
|
||||
|
||||
We should never write any new tests under the old method, and if a given test breaks because of new functionality, it's best to simply rewrite it under the new approach. If you want to run a test for the sake of watching what it does so that you can transcribe it into the new approach, you can run:
|
||||
If within a test directory you find several tests need to share some logic, you can create a file called `shared.go` in that directory to hold shared helper functions (see `pkg/integration/tests/filter_by_path/shared.go` for an example).
|
||||
|
||||
```
|
||||
go run pkg/integration/deprecated/cmd/tui/main.go
|
||||
```
|
||||
If you need to share test logic across test directories you can put helper functions in the `tests/shared` package. If you find yourself frequently doing the same thing from within a test across test directories, for example, responding a particular popup, consider adding a helper method to `pkg/integration/components/common.go`. If you look around the code in the `components` directory you may find another place that's sensible to put your helper function.
|
||||
|
||||
The tests in the old format live in test/integration. In the old format, test definitions are co-located with snapshots. The setup step is done in a `setup.sh` shell script and the `recording.json` file contains the recorded keypresses to be replayed during the test.
|
||||
### Don't do too much in one test
|
||||
|
||||
If you have rewritten an integration test under the new pattern, be sure to delete the old integration test directory.
|
||||
If you're testing different pieces of functionality, it's better to test them in isolation using multiple short tests, compared to one larger longer test. Sometimes it's appropriate to have a longer test which tests how various different pieces interact, but err on the side of keeping things short.
|
||||
|
@ -11,6 +11,7 @@ import (
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/tests"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
// see pkg/integration/README.md
|
||||
@ -65,6 +66,12 @@ func getTestsToRun(testNames []string) []*components.IntegrationTest {
|
||||
)
|
||||
})
|
||||
|
||||
if lo.SomeBy(testNames, func(name string) bool {
|
||||
return strings.HasSuffix(name, "/shared")
|
||||
}) {
|
||||
log.Fatalf("'shared' is a reserved name for tests that are shared between multiple test files. Please rename your test.")
|
||||
}
|
||||
|
||||
outer:
|
||||
for _, testName := range testNames {
|
||||
// check if our given test name actually exists
|
||||
@ -74,7 +81,7 @@ outer:
|
||||
continue outer
|
||||
}
|
||||
}
|
||||
log.Fatalf("test %s not found. Perhaps you forgot to add it to `pkg/integration/integration_tests/tests_gen.go`? This can be done by running `go generate ./...` from the Lazygit root. You'll need to ensure that your test name and the file name match (where the test name is in PascalCase and the file name is in snake_case).", testName)
|
||||
log.Fatalf("test %s not found. Perhaps you forgot to add it to `pkg/integration/integration_tests/test_list.go`? This can be done by running `go generate ./...` from the Lazygit root. You'll need to ensure that your test name and the file name match (where the test name is in PascalCase and the file name is in snake_case).", testName)
|
||||
}
|
||||
|
||||
return testsToRun
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
package clients
|
||||
|
||||
// this is the new way of running tests. See pkg/integration/integration_tests/commit.go
|
||||
// for an example
|
||||
// This file allows you to use `go test` to run integration tests.
|
||||
// See See pkg/integration/README.md for more info.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -137,7 +137,7 @@ func RunTUI() {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("code test/integration_new/%s", currentTest.Name()))
|
||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("code test/results/%s", currentTest.Name()))
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ func retryWaitTimes() []int {
|
||||
// give it more leeway compared to when we're running things locally.
|
||||
return []int{0, 1, 1, 1, 1, 1, 5, 10, 20, 40, 100, 200, 500, 1000, 2000, 4000}
|
||||
} else {
|
||||
return []int{0, 1, 1, 1, 1, 1, 5, 10, 20, 40, 100}
|
||||
return []int{0, 1, 1, 1, 1, 1, 5, 10, 20, 40, 100, 200}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
package components
|
||||
|
||||
// for running common actions
|
||||
type Actions struct {
|
||||
type Common struct {
|
||||
t *TestDriver
|
||||
}
|
||||
|
||||
func (self *Actions) ContinueMerge() {
|
||||
self.t.Views().current().Press(self.t.keys.Universal.CreateRebaseOptionsMenu)
|
||||
func (self *Common) ContinueMerge() {
|
||||
self.t.GlobalPress(self.t.keys.Universal.CreateRebaseOptionsMenu)
|
||||
|
||||
self.t.ExpectPopup().Menu().
|
||||
Title(Equals("Rebase Options")).
|
||||
@ -14,32 +14,32 @@ func (self *Actions) ContinueMerge() {
|
||||
Confirm()
|
||||
}
|
||||
|
||||
func (self *Actions) ContinueRebase() {
|
||||
func (self *Common) ContinueRebase() {
|
||||
self.ContinueMerge()
|
||||
}
|
||||
|
||||
func (self *Actions) AcknowledgeConflicts() {
|
||||
func (self *Common) AcknowledgeConflicts() {
|
||||
self.t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Auto-merge failed")).
|
||||
Content(Contains("Conflicts!")).
|
||||
Confirm()
|
||||
}
|
||||
|
||||
func (self *Actions) ContinueOnConflictsResolved() {
|
||||
func (self *Common) ContinueOnConflictsResolved() {
|
||||
self.t.ExpectPopup().Confirmation().
|
||||
Title(Equals("continue")).
|
||||
Content(Contains("all merge conflicts resolved. Continue?")).
|
||||
Confirm()
|
||||
}
|
||||
|
||||
func (self *Actions) ConfirmDiscardLines() {
|
||||
func (self *Common) ConfirmDiscardLines() {
|
||||
self.t.ExpectPopup().Confirmation().
|
||||
Title(Equals("Unstage lines")).
|
||||
Content(Contains("Are you sure you want to delete the selected lines")).
|
||||
Confirm()
|
||||
}
|
||||
|
||||
func (self *Actions) SelectPatchOption(matcher *Matcher) {
|
||||
func (self *Common) SelectPatchOption(matcher *Matcher) {
|
||||
self.t.GlobalPress(self.t.keys.Universal.CreatePatchOptionsMenu)
|
||||
|
||||
self.t.ExpectPopup().Menu().Title(Equals("Patch Options")).Select(matcher).Confirm()
|
@ -31,7 +31,7 @@ func (self *MenuDriver) Cancel() {
|
||||
}
|
||||
|
||||
func (self *MenuDriver) Select(option *Matcher) *MenuDriver {
|
||||
self.getViewDriver().NavigateToListItem(option)
|
||||
self.getViewDriver().NavigateToLine(option)
|
||||
|
||||
return self
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ package components
|
||||
import "path/filepath"
|
||||
|
||||
// convenience struct for easily getting directories within our test directory.
|
||||
// We have one test directory for each test, found in test/integration_new.
|
||||
// We have one test directory for each test, found in test/results.
|
||||
type Paths struct {
|
||||
// e.g. test/integration/test_name
|
||||
// e.g. test/results/test_name
|
||||
root string
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,6 @@ func (self *PromptDriver) ConfirmSuggestion(matcher *Matcher) {
|
||||
self.t.press(self.t.keys.Universal.TogglePanel)
|
||||
self.t.Views().Suggestions().
|
||||
IsFocused().
|
||||
NavigateToListItem(matcher).
|
||||
NavigateToLine(matcher).
|
||||
PressEnter()
|
||||
}
|
||||
|
@ -11,14 +11,16 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
)
|
||||
|
||||
// this is the integration runner for the new and improved integration interface
|
||||
|
||||
const (
|
||||
TEST_NAME_ENV_VAR = "TEST_NAME"
|
||||
SANDBOX_ENV_VAR = "SANDBOX"
|
||||
GIT_CONFIG_GLOBAL_ENV_VAR = "GIT_CONFIG_GLOBAL"
|
||||
)
|
||||
|
||||
// This function lets you run tests either from within `go test` or from a regular binary.
|
||||
// The reason for having two separate ways of testing is that `go test` isn't great at
|
||||
// showing what's actually happening during the test, but it's still good at running
|
||||
// tests in telling you about their results.
|
||||
func RunTests(
|
||||
tests []*IntegrationTest,
|
||||
logf func(format string, formatArgs ...interface{}),
|
||||
@ -34,7 +36,7 @@ func RunTests(
|
||||
return err
|
||||
}
|
||||
|
||||
testDir := filepath.Join(projectRootDir, "test", "integration_new")
|
||||
testDir := filepath.Join(projectRootDir, "test", "results")
|
||||
|
||||
if err := buildLazygit(); err != nil {
|
||||
return err
|
||||
|
@ -38,6 +38,15 @@ func (self *Shell) RunCommand(cmdStr string) *Shell {
|
||||
return self
|
||||
}
|
||||
|
||||
// Help files are located at test/files from the root the lazygit repo.
|
||||
// E.g. You may want to create a pre-commit hook file there, then call this
|
||||
// function to copy it into your test repo.
|
||||
func (self *Shell) CopyHelpFile(source string, destination string) *Shell {
|
||||
self.RunCommand(fmt.Sprintf("cp ../../../../../files/%s %s", source, destination))
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *Shell) runCommandWithOutput(cmdStr string) (string, error) {
|
||||
args := str.ToArgv(cmdStr)
|
||||
cmd := secureexec.Command(args[0], args[1:]...)
|
||||
@ -190,13 +199,27 @@ func (self *Shell) SetConfig(key string, value string) *Shell {
|
||||
// creates a clone of the repo in a sibling directory and adds the clone
|
||||
// as a remote, then fetches it.
|
||||
func (self *Shell) CloneIntoRemote(name string) *Shell {
|
||||
self.RunCommand(fmt.Sprintf("git clone --bare . ../%s", name))
|
||||
self.Clone(name)
|
||||
self.RunCommand(fmt.Sprintf("git remote add %s ../%s", name, name))
|
||||
self.RunCommand(fmt.Sprintf("git fetch %s", name))
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *Shell) CloneIntoSubmodule(submoduleName string) *Shell {
|
||||
self.Clone("other_repo")
|
||||
self.RunCommand(fmt.Sprintf("git submodule add ../other_repo %s", submoduleName))
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
// clones repo into a sibling directory
|
||||
func (self *Shell) Clone(repoName string) *Shell {
|
||||
self.RunCommand(fmt.Sprintf("git clone --bare . ../%s", repoName))
|
||||
|
||||
return self
|
||||
}
|
||||
|
||||
// e.g. branch: 'master', upstream: 'origin/master'
|
||||
func (self *Shell) SetBranchUpstream(branch string, upstream string) *Shell {
|
||||
self.RunCommand(fmt.Sprintf("git branch --set-upstream-to=%s %s", upstream, branch))
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/utils"
|
||||
)
|
||||
|
||||
// Test describes an integration tests that will be run against the lazygit gui.
|
||||
// IntegrationTest describes an integration test that will be run against the lazygit gui.
|
||||
|
||||
// our unit tests will use this description to avoid a panic caused by attempting
|
||||
// to get the test's name via it's file's path.
|
||||
|
@ -48,8 +48,8 @@ func (self *TestDriver) typeContent(content string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *TestDriver) Actions() *Actions {
|
||||
return &Actions{t: self}
|
||||
func (self *TestDriver) Common() *Common {
|
||||
return &Common{t: self}
|
||||
}
|
||||
|
||||
// for when you want to allow lazygit to process something before continuing
|
||||
|
@ -11,6 +11,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// this file is for testing our test code (meta, I know)
|
||||
|
||||
type fakeGuiDriver struct {
|
||||
failureMessage string
|
||||
pressedKeys []string
|
||||
|
@ -63,6 +63,7 @@ func (self *ViewDriver) Title(expected *Matcher) *ViewDriver {
|
||||
|
||||
// asserts that the view has lines matching the given matchers. One matcher must be passed for each line.
|
||||
// If you only care about the top n lines, use the TopLines method instead.
|
||||
// If you only care about a subset of lines, use the ContainsLines method instead.
|
||||
func (self *ViewDriver) Lines(matchers ...*Matcher) *ViewDriver {
|
||||
self.validateMatchersPassed(matchers)
|
||||
self.LineCount(len(matchers))
|
||||
@ -81,6 +82,7 @@ func (self *ViewDriver) TopLines(matchers ...*Matcher) *ViewDriver {
|
||||
return self.assertLines(0, matchers...)
|
||||
}
|
||||
|
||||
// asserts that somewhere in the view there are consequetive lines matching the given matchers.
|
||||
func (self *ViewDriver) ContainsLines(matchers ...*Matcher) *ViewDriver {
|
||||
self.validateMatchersPassed(matchers)
|
||||
self.validateEnoughLines(matchers)
|
||||
@ -131,7 +133,7 @@ func (self *ViewDriver) ContainsLines(matchers ...*Matcher) *ViewDriver {
|
||||
return self
|
||||
}
|
||||
|
||||
// asserts on the lines that are selected in the view.
|
||||
// asserts on the lines that are selected in the view. Don't use the `IsSelected` matcher with this because it's redundant.
|
||||
func (self *ViewDriver) SelectedLines(matchers ...*Matcher) *ViewDriver {
|
||||
self.validateMatchersPassed(matchers)
|
||||
self.validateEnoughLines(matchers)
|
||||
@ -373,7 +375,7 @@ func (self *ViewDriver) PressEscape() *ViewDriver {
|
||||
// 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,
|
||||
// looking for the item.
|
||||
func (self *ViewDriver) NavigateToListItem(matcher *Matcher) *ViewDriver {
|
||||
func (self *ViewDriver) NavigateToLine(matcher *Matcher) *ViewDriver {
|
||||
self.IsFocused()
|
||||
|
||||
view := self.getView()
|
||||
|
@ -13,16 +13,6 @@ type Views struct {
|
||||
t *TestDriver
|
||||
}
|
||||
|
||||
// not exporting this because I want the test to always be explicit about what
|
||||
// view it's dealing with.
|
||||
func (self *Views) current() *ViewDriver {
|
||||
return &ViewDriver{
|
||||
context: "current view",
|
||||
getView: func() *gocui.View { return self.t.gui.CurrentContext().GetView() },
|
||||
t: self.t,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Views) Main() *ViewDriver {
|
||||
return &ViewDriver{
|
||||
context: "main view",
|
||||
|
@ -1,65 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/deprecated"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Deprecated: This file is part of the old way of doing things.
|
||||
|
||||
// see https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md
|
||||
// This file can be invoked directly, but you might find it easier to go through
|
||||
// test/lazyintegration/main.go, which provides a convenient gui wrapper to integration tests.
|
||||
//
|
||||
// If invoked directly, you can specify a test by passing it as the first argument.
|
||||
// You can also specify that you want to record a test by passing MODE=record
|
||||
// as an env var.
|
||||
|
||||
func main() {
|
||||
mode := deprecated.GetModeFromEnv()
|
||||
speedEnv := os.Getenv("SPEED")
|
||||
includeSkipped := os.Getenv("INCLUDE_SKIPPED") == "true"
|
||||
selectedTestName := os.Args[1]
|
||||
|
||||
err := deprecated.RunTests(
|
||||
log.Printf,
|
||||
runCmdInTerminal,
|
||||
func(test *deprecated.IntegrationTest, f func(*testing.T) error) {
|
||||
if selectedTestName != "" && test.Name != selectedTestName {
|
||||
return
|
||||
}
|
||||
if err := f(nil); err != nil {
|
||||
log.Print(err.Error())
|
||||
}
|
||||
},
|
||||
mode,
|
||||
speedEnv,
|
||||
func(_t *testing.T, expected string, actual string, prefix string) { //nolint:thelper
|
||||
assert.Equal(MockTestingT{}, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual))
|
||||
},
|
||||
includeSkipped,
|
||||
)
|
||||
if err != nil {
|
||||
log.Print(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type MockTestingT struct{}
|
||||
|
||||
func (t MockTestingT) Errorf(format string, args ...interface{}) {
|
||||
fmt.Printf(format, args...)
|
||||
}
|
||||
|
||||
func runCmdInTerminal(cmd *exec.Cmd) error {
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
return cmd.Run()
|
||||
}
|
@ -1,422 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/jesseduffield/gocui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/deprecated"
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
)
|
||||
|
||||
// Deprecated. See lazy_integration for the new approach.
|
||||
|
||||
// this program lets you manage integration tests in a TUI. See https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md for more info.
|
||||
|
||||
type App struct {
|
||||
tests []*deprecated.IntegrationTest
|
||||
itemIdx int
|
||||
testDir string
|
||||
editing bool
|
||||
g *gocui.Gui
|
||||
}
|
||||
|
||||
func (app *App) getCurrentTest() *deprecated.IntegrationTest {
|
||||
if len(app.tests) > 0 {
|
||||
return app.tests[app.itemIdx]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (app *App) refreshTests() {
|
||||
app.loadTests()
|
||||
app.g.Update(func(*gocui.Gui) error {
|
||||
listView, err := app.g.View("list")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
listView.Clear()
|
||||
for _, test := range app.tests {
|
||||
fmt.Fprintln(listView, test.Name)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (app *App) loadTests() {
|
||||
tests, err := deprecated.LoadTests(app.testDir)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
app.tests = tests
|
||||
if app.itemIdx > len(app.tests)-1 {
|
||||
app.itemIdx = len(app.tests) - 1
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
rootDir := deprecated.GetRootDirectory()
|
||||
testDir := filepath.Join(rootDir, "test", "integration")
|
||||
|
||||
app := &App{testDir: testDir}
|
||||
app.loadTests()
|
||||
|
||||
g, err := gocui.NewGui(gocui.OutputTrue, false, gocui.NORMAL, false, gui.RuneReplacements)
|
||||
if err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
g.Cursor = false
|
||||
|
||||
app.g = g
|
||||
|
||||
g.SetManagerFunc(app.layout)
|
||||
|
||||
if err := g.SetKeybinding("list", gocui.KeyArrowUp, gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
if app.itemIdx > 0 {
|
||||
app.itemIdx--
|
||||
}
|
||||
listView, err := g.View("list")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
listView.FocusPoint(0, app.itemIdx)
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("list", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("list", 'q', gocui.ModNone, quit); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("list", 'r', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
currentTest := app.getCurrentTest()
|
||||
if currentTest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=record go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name))
|
||||
app.runSubprocess(cmd)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("list", 's', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
currentTest := app.getCurrentTest()
|
||||
if currentTest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=sandbox go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name))
|
||||
app.runSubprocess(cmd)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("list", gocui.KeyEnter, gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
currentTest := app.getCurrentTest()
|
||||
if currentTest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name))
|
||||
app.runSubprocess(cmd)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("list", 'u', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
currentTest := app.getCurrentTest()
|
||||
if currentTest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true MODE=updateSnapshot go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name))
|
||||
app.runSubprocess(cmd)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("list", 't', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
currentTest := app.getCurrentTest()
|
||||
if currentTest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("INCLUDE_SKIPPED=true SPEED=1 go run pkg/integration/deprecated/cmd/runner/main.go %s", currentTest.Name))
|
||||
app.runSubprocess(cmd)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("list", 'o', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
currentTest := app.getCurrentTest()
|
||||
if currentTest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("code -r %s/%s/test.json", app.testDir, currentTest.Name))
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("list", 'n', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
currentTest := app.getCurrentTest()
|
||||
if currentTest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// need to duplicate that folder and then re-fetch our tests.
|
||||
dir := app.testDir + "/" + app.getCurrentTest().Name
|
||||
newDir := dir + "_Copy"
|
||||
|
||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("cp -r %s %s", dir, newDir))
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
app.loadTests()
|
||||
|
||||
app.refreshTests()
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("list", 'm', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
currentTest := app.getCurrentTest()
|
||||
if currentTest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
app.editing = true
|
||||
if _, err := g.SetCurrentView("editor"); err != nil {
|
||||
return err
|
||||
}
|
||||
editorView, err := g.View("editor")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
editorView.Clear()
|
||||
fmt.Fprint(editorView, currentTest.Name)
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("list", 'd', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
currentTest := app.getCurrentTest()
|
||||
if currentTest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
dir := app.testDir + "/" + app.getCurrentTest().Name
|
||||
|
||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("rm -rf %s", dir))
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
app.refreshTests()
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("editor", gocui.KeyEnter, gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
currentTest := app.getCurrentTest()
|
||||
if currentTest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
app.editing = false
|
||||
if _, err := g.SetCurrentView("list"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
editorView, err := g.View("editor")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dir := app.testDir + "/" + app.getCurrentTest().Name
|
||||
newDir := app.testDir + "/" + editorView.Buffer()
|
||||
|
||||
cmd := secureexec.Command("sh", "-c", fmt.Sprintf("mv %s %s", dir, newDir))
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
editorView.Clear()
|
||||
|
||||
app.refreshTests()
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
if err := g.SetKeybinding("editor", gocui.KeyEsc, gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
app.editing = false
|
||||
if _, err := g.SetCurrentView("list"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
err = g.MainLoop()
|
||||
g.Close()
|
||||
switch err {
|
||||
case gocui.ErrQuit:
|
||||
return
|
||||
default:
|
||||
log.Panicln(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *App) runSubprocess(cmd *exec.Cmd) {
|
||||
if err := gocui.Screen.Suspend(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Stdout = os.Stdout
|
||||
if err := cmd.Run(); err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
cmd.Stdin = nil
|
||||
cmd.Stderr = nil
|
||||
cmd.Stdout = nil
|
||||
|
||||
fmt.Fprintf(os.Stdout, "\n%s", style.FgGreen.Sprint("press enter to return"))
|
||||
fmt.Scanln() // wait for enter press
|
||||
|
||||
if err := gocui.Screen.Resume(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *App) layout(g *gocui.Gui) error {
|
||||
maxX, maxY := g.Size()
|
||||
descriptionViewHeight := 7
|
||||
keybindingsViewHeight := 3
|
||||
editorViewHeight := 3
|
||||
if !app.editing {
|
||||
editorViewHeight = 0
|
||||
} else {
|
||||
descriptionViewHeight = 0
|
||||
keybindingsViewHeight = 0
|
||||
}
|
||||
g.Cursor = app.editing
|
||||
g.FgColor = gocui.ColorGreen
|
||||
listView, err := g.SetView("list", 0, 0, maxX-1, maxY-descriptionViewHeight-keybindingsViewHeight-editorViewHeight-1, 0)
|
||||
if err != nil {
|
||||
if !gocui.IsUnknownView(err) {
|
||||
return err
|
||||
}
|
||||
listView.Highlight = true
|
||||
listView.SelBgColor = gocui.ColorBlue
|
||||
listView.Clear()
|
||||
for _, test := range app.tests {
|
||||
fmt.Fprintln(listView, test.Name)
|
||||
}
|
||||
listView.Title = "Tests"
|
||||
listView.FgColor = gocui.ColorDefault
|
||||
if _, err := g.SetCurrentView("list"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
descriptionView, err := g.SetViewBeneath("description", "list", descriptionViewHeight)
|
||||
if err != nil {
|
||||
if !gocui.IsUnknownView(err) {
|
||||
return err
|
||||
}
|
||||
descriptionView.Title = "Test description"
|
||||
descriptionView.Wrap = true
|
||||
descriptionView.FgColor = gocui.ColorDefault
|
||||
}
|
||||
|
||||
keybindingsView, err := g.SetViewBeneath("keybindings", "description", keybindingsViewHeight)
|
||||
if err != nil {
|
||||
if !gocui.IsUnknownView(err) {
|
||||
return err
|
||||
}
|
||||
keybindingsView.Title = "Keybindings"
|
||||
keybindingsView.Wrap = true
|
||||
keybindingsView.FgColor = gocui.ColorDefault
|
||||
fmt.Fprintln(keybindingsView, "up/down: navigate, enter: run test, u: run test and update snapshots, r: record test, s: sandbox, o: open test config, n: duplicate test, m: rename test, d: delete test, t: run test at original speed")
|
||||
}
|
||||
|
||||
editorView, err := g.SetViewBeneath("editor", "keybindings", editorViewHeight)
|
||||
if err != nil {
|
||||
if !gocui.IsUnknownView(err) {
|
||||
return err
|
||||
}
|
||||
editorView.Title = "Enter Name"
|
||||
editorView.FgColor = gocui.ColorDefault
|
||||
editorView.Editable = true
|
||||
}
|
||||
|
||||
currentTest := app.getCurrentTest()
|
||||
if currentTest == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
descriptionView.Clear()
|
||||
fmt.Fprintf(descriptionView, "Speed: %f. %s", currentTest.Speed, currentTest.Description)
|
||||
|
||||
if err := g.SetKeybinding("list", gocui.KeyArrowDown, gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
|
||||
if app.itemIdx < len(app.tests)-1 {
|
||||
app.itemIdx++
|
||||
}
|
||||
|
||||
listView, err := g.View("list")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
listView.FocusPoint(0, app.itemIdx)
|
||||
return nil
|
||||
}); err != nil {
|
||||
log.Panicln(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func quit(g *gocui.Gui, v *gocui.View) error {
|
||||
return gocui.ErrQuit
|
||||
}
|
@ -1,122 +0,0 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package deprecated
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/creack/pty"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Deprecated.
|
||||
|
||||
// This file is quite similar to integration/main.go. The main difference is that this file is
|
||||
// run via `go test` whereas the other is run via `test/lazyintegration/main.go` which provides
|
||||
// a convenient gui wrapper around our integration tests. The `go test` approach is better
|
||||
// for CI and for running locally in the background to ensure you haven't broken
|
||||
// anything while making changes. If you want to visually see what's happening when a test is run,
|
||||
// you'll need to take the other approach
|
||||
//
|
||||
// As for this file, to run an integration test, e.g. for test 'commit', go:
|
||||
// go test pkg/gui/old_gui_test.go -run /commit
|
||||
//
|
||||
// To update a snapshot for an integration test, pass UPDATE_SNAPSHOTS=true
|
||||
// UPDATE_SNAPSHOTS=true go test pkg/gui/old_gui_test.go -run /commit
|
||||
//
|
||||
// integration tests are run in test/integration/<test_name>/actual and the final test does
|
||||
// not clean up that directory so you can cd into it to see for yourself what
|
||||
// happened when a test fails.
|
||||
//
|
||||
// To override speed, pass e.g. `SPEED=1` as an env var. Otherwise we start each test
|
||||
// at a high speed and then drop down to lower speeds upon each failure until finally
|
||||
// trying at the original playback speed (speed 1). A speed of 2 represents twice the
|
||||
// original playback speed. Speed may be a decimal.
|
||||
|
||||
func Test(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration tests in short mode")
|
||||
}
|
||||
|
||||
mode := GetModeFromEnv()
|
||||
speedEnv := os.Getenv("SPEED")
|
||||
includeSkipped := os.Getenv("INCLUDE_SKIPPED") != ""
|
||||
|
||||
parallelTotal := tryConvert(os.Getenv("PARALLEL_TOTAL"), 1)
|
||||
parallelIndex := tryConvert(os.Getenv("PARALLEL_INDEX"), 0)
|
||||
testNumber := 0
|
||||
|
||||
err := RunTests(
|
||||
t.Logf,
|
||||
runCmdHeadless,
|
||||
func(test *IntegrationTest, f func(*testing.T) error) {
|
||||
defer func() { testNumber += 1 }()
|
||||
if testNumber%parallelTotal != parallelIndex {
|
||||
return
|
||||
}
|
||||
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
err := f(t)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
},
|
||||
mode,
|
||||
speedEnv,
|
||||
func(t *testing.T, expected string, actual string, prefix string) {
|
||||
t.Helper()
|
||||
assert.Equal(t, expected, actual, fmt.Sprintf("Unexpected %s. Expected:\n%s\nActual:\n%s\n", prefix, expected, actual))
|
||||
},
|
||||
includeSkipped,
|
||||
)
|
||||
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func tryConvert(numStr string, defaultVal int) int {
|
||||
num, err := strconv.Atoi(numStr)
|
||||
if err != nil {
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
return num
|
||||
}
|
||||
|
||||
func runCmdHeadless(cmd *exec.Cmd) error {
|
||||
cmd.Env = append(
|
||||
cmd.Env,
|
||||
"HEADLESS=true",
|
||||
"TERM=xterm",
|
||||
)
|
||||
|
||||
// not writing stderr to the pty because we want to capture a panic if
|
||||
// there is one. But some commands will not be in tty mode if stderr is
|
||||
// not a terminal. We'll need to keep an eye out for that.
|
||||
stderr := new(bytes.Buffer)
|
||||
cmd.Stderr = stderr
|
||||
|
||||
// these rows and columns are ignored because internally we use tcell's
|
||||
// simulation screen. However we still need the pty for the sake of
|
||||
// running other commands in a pty.
|
||||
f, err := pty.StartWithSize(cmd, &pty.Winsize{Rows: 100, Cols: 100})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _ = io.Copy(ioutil.Discard, f)
|
||||
|
||||
if cmd.Wait() != nil {
|
||||
// return an error with the stderr output
|
||||
return errors.New(stderr.String())
|
||||
}
|
||||
|
||||
return f.Close()
|
||||
}
|
@ -1,564 +0,0 @@
|
||||
package deprecated
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/jesseduffield/generics/slices"
|
||||
"github.com/jesseduffield/lazygit/pkg/commands/oscommands"
|
||||
"github.com/jesseduffield/lazygit/pkg/secureexec"
|
||||
)
|
||||
|
||||
// Deprecated: This file is part of the old way of doing things. See pkg/integration/integration.go for the new way
|
||||
|
||||
// This package is for running our integration test suite. See https://github.com/jesseduffield/lazygit/blob/master/pkg/integration/README.md for more info.
|
||||
|
||||
type IntegrationTest struct {
|
||||
Name string `json:"name"`
|
||||
Speed float64 `json:"speed"`
|
||||
Description string `json:"description"`
|
||||
ExtraCmdArgs string `json:"extraCmdArgs"`
|
||||
Skip bool `json:"skip"`
|
||||
}
|
||||
|
||||
type Mode int
|
||||
|
||||
const (
|
||||
// default: for when we're just running a test and comparing to the snapshot
|
||||
TEST = iota
|
||||
// for when we want to record a test and set the snapshot based on the result
|
||||
RECORD
|
||||
// when we just want to use the setup of the test for our own sandboxing purposes.
|
||||
// This does not record the session and does not create/update snapshots
|
||||
SANDBOX
|
||||
// running a test but updating the snapshot
|
||||
UPDATE_SNAPSHOT
|
||||
)
|
||||
|
||||
func GetModeFromEnv() Mode {
|
||||
switch os.Getenv("MODE") {
|
||||
case "record":
|
||||
return RECORD
|
||||
case "", "test":
|
||||
return TEST
|
||||
case "updateSnapshot":
|
||||
return UPDATE_SNAPSHOT
|
||||
case "sandbox":
|
||||
return SANDBOX
|
||||
default:
|
||||
log.Fatalf("unknown test mode: %s, must be one of [test, record, updateSnapshot, sandbox]", os.Getenv("MODE"))
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
// this function is used by both `go test` and from our lazyintegration gui, but
|
||||
// errors need to be handled differently in each (for example go test is always
|
||||
// working with *testing.T) so we pass in any differences as args here.
|
||||
func RunTests(
|
||||
logf func(format string, formatArgs ...interface{}),
|
||||
runCmd func(cmd *exec.Cmd) error,
|
||||
fnWrapper func(test *IntegrationTest, f func(*testing.T) error),
|
||||
mode Mode,
|
||||
speedEnv string,
|
||||
onFail func(t *testing.T, expected string, actual string, prefix string),
|
||||
includeSkipped bool,
|
||||
) error {
|
||||
rootDir := GetRootDirectory()
|
||||
err := os.Chdir(rootDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
testDir := filepath.Join(rootDir, "test", "integration")
|
||||
|
||||
osCommand := oscommands.NewDummyOSCommand()
|
||||
err = osCommand.Cmd.New("go build -o " + tempLazygitPath()).Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tests, err := LoadTests(testDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
test := test
|
||||
|
||||
fnWrapper(test, func(t *testing.T) error { //nolint: thelper
|
||||
if test.Skip && !includeSkipped {
|
||||
logf("skipping test: %s", test.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
speeds := getTestSpeeds(test.Speed, mode, speedEnv)
|
||||
testPath := filepath.Join(testDir, test.Name)
|
||||
actualDir := filepath.Join(testPath, "actual")
|
||||
expectedDir := filepath.Join(testPath, "expected")
|
||||
actualRepoDir := filepath.Join(actualDir, "repo")
|
||||
logf("path: %s", testPath)
|
||||
|
||||
for i, speed := range speeds {
|
||||
if mode != SANDBOX && mode != RECORD {
|
||||
logf("%s: attempting test at speed %f\n", test.Name, speed)
|
||||
}
|
||||
|
||||
findOrCreateDir(testPath)
|
||||
prepareIntegrationTestDir(actualDir)
|
||||
findOrCreateDir(actualRepoDir)
|
||||
err := createFixture(testPath, actualRepoDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configDir := filepath.Join(testPath, "used_config")
|
||||
|
||||
cmd, err := getLazygitCommand(testPath, rootDir, mode, speed, test.ExtraCmdArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = runCmd(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if mode == UPDATE_SNAPSHOT || mode == RECORD {
|
||||
// create/update snapshot
|
||||
err = oscommands.CopyDir(actualDir, expectedDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := renameSpecialPaths(expectedDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logf("%s", "updated snapshot")
|
||||
} else {
|
||||
if err := validateSameRepos(expectedDir, actualDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// iterate through each repo in the expected dir and comparet to the corresponding repo in the actual dir
|
||||
expectedFiles, err := ioutil.ReadDir(expectedDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
success := true
|
||||
for _, f := range expectedFiles {
|
||||
if !f.IsDir() {
|
||||
return errors.New("unexpected file (as opposed to directory) in integration test 'expected' directory")
|
||||
}
|
||||
|
||||
// get corresponding file name from actual dir
|
||||
actualRepoPath := filepath.Join(actualDir, f.Name())
|
||||
expectedRepoPath := filepath.Join(expectedDir, f.Name())
|
||||
|
||||
actualRepo, expectedRepo, err := generateSnapshots(actualRepoPath, expectedRepoPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if expectedRepo != actualRepo {
|
||||
success = false
|
||||
// if the snapshot doesn't match and we haven't tried all playback speeds different we'll retry at a slower speed
|
||||
if i < len(speeds)-1 {
|
||||
break
|
||||
}
|
||||
|
||||
// get the log file and print it
|
||||
bytes, err := os.ReadFile(filepath.Join(configDir, "development.log"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logf("%s", string(bytes))
|
||||
|
||||
onFail(t, expectedRepo, actualRepo, f.Name())
|
||||
}
|
||||
}
|
||||
|
||||
if success {
|
||||
logf("%s: success at speed %f\n", test.Name, speed)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validates that the actual and expected dirs have the same repo names (doesn't actually check the contents of the repos)
|
||||
func validateSameRepos(expectedDir string, actualDir string) error {
|
||||
// iterate through each repo in the expected dir and compare to the corresponding repo in the actual dir
|
||||
expectedFiles, err := ioutil.ReadDir(expectedDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var actualFiles []os.FileInfo
|
||||
actualFiles, err = ioutil.ReadDir(actualDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
expectedFileNames := slices.Map(expectedFiles, getFileName)
|
||||
actualFileNames := slices.Map(actualFiles, getFileName)
|
||||
if !slices.Equal(expectedFileNames, actualFileNames) {
|
||||
return fmt.Errorf("expected and actual repo dirs do not match: expected: %s, actual: %s", expectedFileNames, actualFileNames)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFileName(f os.FileInfo) string {
|
||||
return f.Name()
|
||||
}
|
||||
|
||||
func prepareIntegrationTestDir(actualDir string) {
|
||||
// remove contents of integration test directory
|
||||
dir, err := ioutil.ReadDir(actualDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.Mkdir(actualDir, 0o777)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
for _, d := range dir {
|
||||
os.RemoveAll(filepath.Join(actualDir, d.Name()))
|
||||
}
|
||||
}
|
||||
|
||||
func GetRootDirectory() string {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for {
|
||||
_, err := os.Stat(filepath.Join(path, ".git"))
|
||||
|
||||
if err == nil {
|
||||
return path
|
||||
}
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
path = filepath.Dir(path)
|
||||
|
||||
if path == "/" {
|
||||
log.Fatal("must run in lazygit folder or child folder")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createFixture(testPath, actualDir string) error {
|
||||
bashScriptPath := filepath.Join(testPath, "setup.sh")
|
||||
cmd := secureexec.Command("bash", bashScriptPath, actualDir)
|
||||
|
||||
if output, err := cmd.CombinedOutput(); err != nil {
|
||||
return errors.New(string(output))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func tempLazygitPath() string {
|
||||
return filepath.Join("/tmp", "lazygit", "test_lazygit")
|
||||
}
|
||||
|
||||
func getTestSpeeds(testStartSpeed float64, mode Mode, speedStr string) []float64 {
|
||||
if mode != TEST {
|
||||
// have to go at original speed if updating snapshots in case we go to fast and create a junk snapshot
|
||||
return []float64{1.0}
|
||||
}
|
||||
|
||||
if speedStr != "" {
|
||||
speed, err := strconv.ParseFloat(speedStr, 64)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return []float64{speed}
|
||||
}
|
||||
|
||||
// default is 10, 5, 1
|
||||
startSpeed := 10.0
|
||||
if testStartSpeed != 0 {
|
||||
startSpeed = testStartSpeed
|
||||
}
|
||||
speeds := []float64{startSpeed}
|
||||
if startSpeed > 5 {
|
||||
speeds = append(speeds, 5)
|
||||
}
|
||||
speeds = append(speeds, 1, 0.5, 0.5)
|
||||
|
||||
return speeds
|
||||
}
|
||||
|
||||
func LoadTests(testDir string) ([]*IntegrationTest, error) {
|
||||
paths, err := filepath.Glob(filepath.Join(testDir, "/*/test.json"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tests := make([]*IntegrationTest, len(paths))
|
||||
|
||||
for i, path := range paths {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
test := &IntegrationTest{}
|
||||
|
||||
err = json.Unmarshal(data, test)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
test.Name = strings.TrimPrefix(filepath.Dir(path), testDir+"/")
|
||||
|
||||
tests[i] = test
|
||||
}
|
||||
|
||||
return tests, nil
|
||||
}
|
||||
|
||||
func findOrCreateDir(path string) {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
err = os.MkdirAll(path, 0o777)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// note that we don't actually store this snapshot in the lazygit repo.
|
||||
// Instead we store the whole expected git repo of our test, so that
|
||||
// we can easily change what we want to compare without needing to regenerate
|
||||
// snapshots for each test.
|
||||
func generateSnapshot(dir string) (string, error) {
|
||||
osCommand := oscommands.NewDummyOSCommand()
|
||||
|
||||
_, err := os.Stat(filepath.Join(dir, ".git"))
|
||||
if err != nil {
|
||||
return "git directory not found", nil
|
||||
}
|
||||
|
||||
snapshot := ""
|
||||
|
||||
cmdStrs := []string{
|
||||
`remote show -n origin`, // remote branches
|
||||
// TODO: find a way to bring this back without breaking tests
|
||||
// `ls-remote origin`,
|
||||
`status`, // file tree
|
||||
`log --pretty=%B|%an|%ae -p -1`, // log
|
||||
`tag -n`, // tags
|
||||
`stash list`, // stash
|
||||
`submodule foreach 'git status'`, // submodule status
|
||||
`submodule foreach 'git log --pretty=%B -p -1'`, // submodule log
|
||||
`submodule foreach 'git tag -n'`, // submodule tags
|
||||
`submodule foreach 'git stash list'`, // submodule stash
|
||||
}
|
||||
|
||||
for _, cmdStr := range cmdStrs {
|
||||
// ignoring error for now. If there's an error it could be that there are no results
|
||||
output, _ := osCommand.Cmd.New(fmt.Sprintf("git -C %s %s", dir, cmdStr)).RunWithOutput()
|
||||
|
||||
snapshot += fmt.Sprintf("git %s:\n%s\n", cmdStr, output)
|
||||
}
|
||||
|
||||
snapshot += "files in repo:\n"
|
||||
err = filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.IsDir() {
|
||||
if f.Name() == ".git" {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
bytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
relativePath, err := filepath.Rel(dir, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
snapshot += fmt.Sprintf("path: %s\ncontent:\n%s\n", relativePath, string(bytes))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return snapshot, nil
|
||||
}
|
||||
|
||||
func generateSnapshots(actualDir string, expectedDir string) (string, string, error) {
|
||||
actual, err := generateSnapshot(actualDir)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// there are a couple of reasons we're not generating the snapshot in expectedDir directly:
|
||||
// Firstly we don't want to have to revert our .git file back to .git_keep.
|
||||
// Secondly, the act of calling git commands like 'git status' actually changes the index
|
||||
// for some reason, and we don't want to leave your lazygit working tree dirty as a result.
|
||||
expectedDirCopyDir := filepath.Join(filepath.Dir(expectedDir), "expected_dir_test")
|
||||
err = oscommands.CopyDir(expectedDir, expectedDirCopyDir)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err := os.RemoveAll(expectedDirCopyDir)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := restoreSpecialPaths(expectedDirCopyDir); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
expected, err := generateSnapshot(expectedDirCopyDir)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return actual, expected, nil
|
||||
}
|
||||
|
||||
func getPathsToRename(dir string, needle string, contains string) []string {
|
||||
pathsToRename := []string{}
|
||||
|
||||
err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if f.Name() == needle && (contains == "" || strings.Contains(path, contains)) {
|
||||
pathsToRename = append(pathsToRename, path)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return pathsToRename
|
||||
}
|
||||
|
||||
var specialPathMappings = []struct{ original, new, contains string }{
|
||||
// git refuses to track .git or .gitmodules in subdirectories so we need to rename them
|
||||
{".git", ".git_keep", ""},
|
||||
{".gitmodules", ".gitmodules_keep", ""},
|
||||
// we also need git to ignore the contents of our test gitignore files so that
|
||||
// we actually commit files that are ignored within the test.
|
||||
{".gitignore", "lg_ignore_file", ""},
|
||||
// this is the .git/info/exclude file. We're being a little more specific here
|
||||
// so that we don't accidentally mess with some other file named 'exclude' in the test.
|
||||
{"exclude", "lg_exclude_file", ".git/info/exclude"},
|
||||
}
|
||||
|
||||
func renameSpecialPaths(dir string) error {
|
||||
for _, specialPath := range specialPathMappings {
|
||||
for _, path := range getPathsToRename(dir, specialPath.original, specialPath.contains) {
|
||||
err := os.Rename(path, filepath.Join(filepath.Dir(path), specialPath.new))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func restoreSpecialPaths(dir string) error {
|
||||
for _, specialPath := range specialPathMappings {
|
||||
for _, path := range getPathsToRename(dir, specialPath.new, specialPath.contains) {
|
||||
err := os.Rename(path, filepath.Join(filepath.Dir(path), specialPath.original))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLazygitCommand(testPath string, rootDir string, mode Mode, speed float64, extraCmdArgs string) (*exec.Cmd, error) {
|
||||
osCommand := oscommands.NewDummyOSCommand()
|
||||
|
||||
replayPath := filepath.Join(testPath, "recording.json")
|
||||
templateConfigDir := filepath.Join(rootDir, "test", "default_test_config")
|
||||
actualRepoDir := filepath.Join(testPath, "actual", "repo")
|
||||
|
||||
exists, err := osCommand.FileExists(filepath.Join(testPath, "config"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if exists {
|
||||
templateConfigDir = filepath.Join(testPath, "config")
|
||||
}
|
||||
|
||||
configDir := filepath.Join(testPath, "used_config")
|
||||
|
||||
err = os.RemoveAll(configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = oscommands.CopyDir(templateConfigDir, configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmdStr := fmt.Sprintf("%s -debug --use-config-dir=%s --path=%s %s", tempLazygitPath(), configDir, actualRepoDir, extraCmdArgs)
|
||||
|
||||
cmdObj := osCommand.Cmd.New(cmdStr)
|
||||
cmdObj.AddEnvVars(fmt.Sprintf("SPEED=%f", speed))
|
||||
|
||||
switch mode {
|
||||
case RECORD:
|
||||
cmdObj.AddEnvVars(fmt.Sprintf("RECORD_EVENTS_TO=%s", replayPath))
|
||||
case TEST, UPDATE_SNAPSHOT:
|
||||
cmdObj.AddEnvVars(fmt.Sprintf("REPLAY_EVENTS_FROM=%s", replayPath))
|
||||
}
|
||||
|
||||
return cmdObj.GetCmd(), nil
|
||||
}
|
@ -32,14 +32,14 @@ var Basic = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
SelectedLine(Contains("commit 10")).
|
||||
NavigateToListItem(Contains("commit 09")).
|
||||
NavigateToLine(Contains("commit 09")).
|
||||
Tap(func() {
|
||||
markCommitAsBad()
|
||||
|
||||
t.Views().Information().Content(Contains("bisecting"))
|
||||
}).
|
||||
SelectedLine(Contains("<-- bad")).
|
||||
NavigateToListItem(Contains("commit 02")).
|
||||
NavigateToLine(Contains("commit 02")).
|
||||
Tap(markCommitAsGood).
|
||||
// lazygit will land us in the commit between our good and bad commits.
|
||||
SelectedLine(Contains("commit 05").Contains("<-- current")).
|
||||
|
@ -35,7 +35,7 @@ var Rebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Content(Contains("Are you sure you want to rebase 'first-change-branch' on top of 'second-change-branch'?")).
|
||||
Confirm()
|
||||
|
||||
t.Actions().AcknowledgeConflicts()
|
||||
t.Common().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
@ -48,7 +48,7 @@ var Rebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().Information().Content(Contains("rebasing"))
|
||||
|
||||
t.Actions().ContinueOnConflictsResolved()
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Information().Content(DoesNotContain("rebasing"))
|
||||
|
||||
|
@ -43,7 +43,7 @@ var RebaseAndDrop = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().Information().Content(Contains("rebasing"))
|
||||
|
||||
t.Actions().AcknowledgeConflicts()
|
||||
t.Common().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Files().IsFocused().
|
||||
SelectedLine(MatchesRegexp("UU.*file"))
|
||||
@ -75,7 +75,7 @@ var RebaseAndDrop = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
IsFocused().
|
||||
PressPrimaryAction()
|
||||
|
||||
t.Actions().ContinueOnConflictsResolved()
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Information().Content(DoesNotContain("rebasing"))
|
||||
|
||||
|
@ -52,7 +52,7 @@ var CherryPickConflicts = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Content(Contains("Are you sure you want to cherry-pick the copied commits onto this branch?")).
|
||||
Confirm()
|
||||
|
||||
t.Actions().AcknowledgeConflicts()
|
||||
t.Common().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
@ -65,7 +65,7 @@ var CherryPickConflicts = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
SelectNextItem().
|
||||
PressPrimaryAction()
|
||||
|
||||
t.Actions().ContinueOnConflictsResolved()
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Files().IsEmpty()
|
||||
|
||||
|
38
pkg/integration/tests/conflicts/filter.go
Normal file
38
pkg/integration/tests/conflicts/filter.go
Normal file
@ -0,0 +1,38 @@
|
||||
package conflicts
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/shared"
|
||||
)
|
||||
|
||||
var Filter = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Ensures that when there are merge conflicts, the files panel only shows conflicted files",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shared.CreateMergeConflictFiles(shell)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("UU").Contains("file1").IsSelected(),
|
||||
Contains("UU").Contains("file2"),
|
||||
).
|
||||
Press(keys.Files.OpenStatusFilter).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Menu().
|
||||
Title(Equals("Filtering")).
|
||||
Select(Contains("Reset filter")).
|
||||
Confirm()
|
||||
}).
|
||||
Lines(
|
||||
Contains("UU").Contains("file1").IsSelected(),
|
||||
Contains("UU").Contains("file2"),
|
||||
// now we see the non-merge conflict file
|
||||
Contains("A ").Contains("file3"),
|
||||
)
|
||||
},
|
||||
})
|
33
pkg/integration/tests/conflicts/resolve_externally.go
Normal file
33
pkg/integration/tests/conflicts/resolve_externally.go
Normal file
@ -0,0 +1,33 @@
|
||||
package conflicts
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/shared"
|
||||
)
|
||||
|
||||
var ResolveExternally = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Ensures that when merge conflicts are resolved outside of lazygit, lazygit prompts you to continue",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shared.CreateMergeConflictFile(shell)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("UU file").IsSelected(),
|
||||
).
|
||||
Tap(func() {
|
||||
t.Shell().UpdateFile("file", "resolved content")
|
||||
}).
|
||||
Press(keys.Universal.Refresh)
|
||||
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Files().
|
||||
IsEmpty()
|
||||
},
|
||||
})
|
54
pkg/integration/tests/conflicts/resolve_multiple_files.go
Normal file
54
pkg/integration/tests/conflicts/resolve_multiple_files.go
Normal file
@ -0,0 +1,54 @@
|
||||
package conflicts
|
||||
|
||||
import (
|
||||
"github.com/jesseduffield/lazygit/pkg/config"
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
"github.com/jesseduffield/lazygit/pkg/integration/tests/shared"
|
||||
)
|
||||
|
||||
var ResolveMultipleFiles = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Ensures that upon resolving conflicts for one file, the next file is selected",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shared.CreateMergeConflictFiles(shell)
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("UU").Contains("file1").IsSelected(),
|
||||
Contains("UU").Contains("file2"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().MergeConflicts().
|
||||
IsFocused().
|
||||
SelectedLines(
|
||||
Contains("<<<<<<< HEAD"),
|
||||
Contains("First Change"),
|
||||
Contains("======="),
|
||||
).
|
||||
PressPrimaryAction()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("UU").Contains("file2").IsSelected(),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
// coincidentally these files have the same conflict
|
||||
t.Views().MergeConflicts().
|
||||
IsFocused().
|
||||
SelectedLines(
|
||||
Contains("<<<<<<< HEAD"),
|
||||
Contains("First Change"),
|
||||
Contains("======="),
|
||||
).
|
||||
PressPrimaryAction()
|
||||
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
},
|
||||
})
|
@ -10,9 +10,7 @@ var DirWithUntrackedFile = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "When selecting a directory that contains an untracked file, we should not get an error",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.UserConfig.Gui.ShowFileTree = true
|
||||
},
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.CreateDir("dir")
|
||||
shell.CreateFile("dir/file", "foo")
|
||||
|
@ -99,7 +99,7 @@ var DiscardChanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
{status: "DU", label: "deleted-us.txt", menuTitle: "deleted-us.txt"},
|
||||
})
|
||||
|
||||
t.Actions().ContinueOnConflictsResolved()
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
discardOneByOne([]statusFile{
|
||||
{status: "MD", label: "change-delete.txt", menuTitle: "change-delete.txt"},
|
||||
|
@ -25,15 +25,15 @@ var DiscardStagedChanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains(` M file2`).IsSelected(),
|
||||
Contains(` M fileToRemove`),
|
||||
Contains(`?? file3`),
|
||||
Contains(` M fileToRemove`),
|
||||
).
|
||||
SelectNextItem().
|
||||
NavigateToLine(Contains(`fileToRemove`)).
|
||||
PressPrimaryAction().
|
||||
Lines(
|
||||
Contains(` M file2`),
|
||||
Contains(`M fileToRemove`).IsSelected(),
|
||||
Contains(`?? file3`),
|
||||
Contains(`M fileToRemove`).IsSelected(),
|
||||
).
|
||||
Press(keys.Files.ViewResetOptions)
|
||||
|
||||
|
@ -37,40 +37,3 @@ var SelectFile = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
postFilterTest(t)
|
||||
},
|
||||
})
|
||||
|
||||
func commonSetup(shell *Shell) {
|
||||
shell.CreateFileAndAdd("filterFile", "original filterFile content")
|
||||
shell.CreateFileAndAdd("otherFile", "original otherFile content")
|
||||
shell.Commit("both files")
|
||||
|
||||
shell.UpdateFileAndAdd("otherFile", "new otherFile content")
|
||||
shell.Commit("only otherFile")
|
||||
|
||||
shell.UpdateFileAndAdd("filterFile", "new filterFile content")
|
||||
shell.Commit("only filterFile")
|
||||
}
|
||||
|
||||
func postFilterTest(t *TestDriver) {
|
||||
t.Views().Information().Content(Contains("filtering by 'filterFile'"))
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains(`only filterFile`).IsSelected(),
|
||||
Contains(`both files`),
|
||||
).
|
||||
SelectNextItem().
|
||||
PressEnter()
|
||||
|
||||
// we only show the filtered file's changes in the main view
|
||||
t.Views().Main().
|
||||
Content(Contains("filterFile").DoesNotContain("otherFile"))
|
||||
|
||||
// when you click into the commit itself, you see all files from that commit
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains(`filterFile`),
|
||||
Contains(`otherFile`),
|
||||
)
|
||||
}
|
||||
|
42
pkg/integration/tests/filter_by_path/shared.go
Normal file
42
pkg/integration/tests/filter_by_path/shared.go
Normal file
@ -0,0 +1,42 @@
|
||||
package filter_by_path
|
||||
|
||||
import (
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
func commonSetup(shell *Shell) {
|
||||
shell.CreateFileAndAdd("filterFile", "original filterFile content")
|
||||
shell.CreateFileAndAdd("otherFile", "original otherFile content")
|
||||
shell.Commit("both files")
|
||||
|
||||
shell.UpdateFileAndAdd("otherFile", "new otherFile content")
|
||||
shell.Commit("only otherFile")
|
||||
|
||||
shell.UpdateFileAndAdd("filterFile", "new filterFile content")
|
||||
shell.Commit("only filterFile")
|
||||
}
|
||||
|
||||
func postFilterTest(t *TestDriver) {
|
||||
t.Views().Information().Content(Contains("filtering by 'filterFile'"))
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains(`only filterFile`).IsSelected(),
|
||||
Contains(`both files`),
|
||||
).
|
||||
SelectNextItem().
|
||||
PressEnter()
|
||||
|
||||
// we only show the filtered file's changes in the main view
|
||||
t.Views().Main().
|
||||
Content(Contains("filterFile").DoesNotContain("otherFile"))
|
||||
|
||||
// when you click into the commit itself, you see all files from that commit
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains(`filterFile`),
|
||||
Contains(`otherFile`),
|
||||
)
|
||||
}
|
@ -22,7 +22,7 @@ var AmendFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
NavigateToListItem(Contains("commit 01")).
|
||||
NavigateToLine(Contains("commit 01")).
|
||||
Press(keys.Commits.AmendToCommit).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
|
@ -21,14 +21,14 @@ var EditFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
NavigateToListItem(Contains("commit 01")).
|
||||
NavigateToLine(Contains("commit 01")).
|
||||
Press(keys.Universal.Edit).
|
||||
Lines(
|
||||
Contains("commit 02"),
|
||||
MatchesRegexp("YOU ARE HERE.*commit 01").IsSelected(),
|
||||
).
|
||||
Tap(func() {
|
||||
t.Actions().ContinueRebase()
|
||||
t.Common().ContinueRebase()
|
||||
}).
|
||||
Lines(
|
||||
Contains("commit 02"),
|
||||
|
@ -21,7 +21,7 @@ var FixupFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
NavigateToListItem(Contains("commit 01")).
|
||||
NavigateToLine(Contains("commit 01")).
|
||||
Press(keys.Commits.MarkCommitAsFixup).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Alert().
|
||||
|
@ -22,7 +22,7 @@ var FixupSecondCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
NavigateToListItem(Contains("commit 02")).
|
||||
NavigateToLine(Contains("commit 02")).
|
||||
Press(keys.Commits.MarkCommitAsFixup).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
|
@ -22,7 +22,7 @@ var MoveInRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
NavigateToListItem(Contains("commit 01")).
|
||||
NavigateToLine(Contains("commit 01")).
|
||||
Press(keys.Universal.Edit).
|
||||
Lines(
|
||||
Contains("commit 04"),
|
||||
@ -84,7 +84,7 @@ var MoveInRebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("YOU ARE HERE").Contains("commit 01"),
|
||||
).
|
||||
Tap(func() {
|
||||
t.Actions().ContinueRebase()
|
||||
t.Common().ContinueRebase()
|
||||
}).
|
||||
Lines(
|
||||
Contains("commit 04"),
|
||||
|
@ -31,7 +31,7 @@ var Rebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("first commit to edit"),
|
||||
Contains("initial commit"),
|
||||
).
|
||||
NavigateToListItem(Contains("first commit to edit")).
|
||||
NavigateToLine(Contains("first commit to edit")).
|
||||
Press(keys.Universal.Edit).
|
||||
Lines(
|
||||
MatchesRegexp("pick.*commit to fixup"),
|
||||
@ -82,7 +82,7 @@ var Rebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("initial commit"),
|
||||
).
|
||||
Tap(func() {
|
||||
t.Actions().ContinueRebase()
|
||||
t.Common().ContinueRebase()
|
||||
}).
|
||||
Lines(
|
||||
MatchesRegexp("fixup.*commit to fixup").IsSelected(),
|
||||
@ -92,7 +92,7 @@ var Rebase = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("initial commit"),
|
||||
).
|
||||
Tap(func() {
|
||||
t.Actions().ContinueRebase()
|
||||
t.Common().ContinueRebase()
|
||||
}).
|
||||
Lines(
|
||||
Contains("second commit to edit").IsSelected(),
|
||||
|
@ -24,7 +24,7 @@ var RewordFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
NavigateToListItem(Contains("commit 01")).
|
||||
NavigateToLine(Contains("commit 01")).
|
||||
Press(keys.Commits.RenameCommit).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Prompt().
|
||||
|
68
pkg/integration/tests/interactive_rebase/shared.go
Normal file
68
pkg/integration/tests/interactive_rebase/shared.go
Normal file
@ -0,0 +1,68 @@
|
||||
package interactive_rebase
|
||||
|
||||
import (
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
func handleConflictsFromSwap(t *TestDriver) {
|
||||
t.Common().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("UU myfile"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().MergeConflicts().
|
||||
IsFocused().
|
||||
TopLines(
|
||||
Contains("<<<<<<< HEAD"),
|
||||
Contains("one"),
|
||||
Contains("======="),
|
||||
Contains("three"),
|
||||
Contains(">>>>>>>"),
|
||||
).
|
||||
SelectNextItem().
|
||||
PressPrimaryAction() // pick "three"
|
||||
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Common().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("UU myfile"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().MergeConflicts().
|
||||
IsFocused().
|
||||
TopLines(
|
||||
Contains("<<<<<<< HEAD"),
|
||||
Contains("three"),
|
||||
Contains("======="),
|
||||
Contains("two"),
|
||||
Contains(">>>>>>>"),
|
||||
).
|
||||
SelectNextItem().
|
||||
PressPrimaryAction() // pick "two"
|
||||
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("commit two").IsSelected(),
|
||||
Contains("commit three"),
|
||||
Contains("commit one"),
|
||||
).
|
||||
Tap(func() {
|
||||
t.Views().Main().Content(Contains("-three").Contains("+two"))
|
||||
}).
|
||||
SelectNextItem().
|
||||
Tap(func() {
|
||||
t.Views().Main().Content(Contains("-one").Contains("+three"))
|
||||
})
|
||||
}
|
@ -21,7 +21,7 @@ var SquashDownFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
NavigateToListItem(Contains("commit 01")).
|
||||
NavigateToLine(Contains("commit 01")).
|
||||
Press(keys.Commits.SquashDown).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Alert().
|
||||
|
@ -22,7 +22,7 @@ var SquashDownSecondCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
NavigateToListItem(Contains("commit 02")).
|
||||
NavigateToLine(Contains("commit 02")).
|
||||
Press(keys.Commits.SquashDown).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
|
@ -22,7 +22,7 @@ var SquashFixupsAboveFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("commit 02"),
|
||||
Contains("commit 01"),
|
||||
).
|
||||
NavigateToListItem(Contains("commit 01")).
|
||||
NavigateToLine(Contains("commit 01")).
|
||||
Press(keys.Commits.CreateFixupCommit).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
@ -30,7 +30,7 @@ var SquashFixupsAboveFirstCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Content(Contains("Are you sure you want to create a fixup! commit for commit")).
|
||||
Confirm()
|
||||
}).
|
||||
NavigateToListItem(Contains("commit 01")).
|
||||
NavigateToLine(Contains("commit 01")).
|
||||
Press(keys.Commits.SquashAboveCommits).
|
||||
Tap(func() {
|
||||
t.ExpectPopup().Confirmation().
|
||||
|
@ -26,7 +26,7 @@ var SwapInRebaseWithConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("commit two"),
|
||||
Contains("commit one"),
|
||||
).
|
||||
NavigateToListItem(Contains("commit one")).
|
||||
NavigateToLine(Contains("commit one")).
|
||||
Press(keys.Universal.Edit).
|
||||
Lines(
|
||||
Contains("commit three"),
|
||||
@ -41,72 +41,9 @@ var SwapInRebaseWithConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("YOU ARE HERE").Contains("commit one"),
|
||||
).
|
||||
Tap(func() {
|
||||
t.Actions().ContinueRebase()
|
||||
t.Common().ContinueRebase()
|
||||
})
|
||||
|
||||
handleConflictsFromSwap(t)
|
||||
},
|
||||
})
|
||||
|
||||
func handleConflictsFromSwap(t *TestDriver) {
|
||||
t.Actions().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("UU myfile"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().MergeConflicts().
|
||||
IsFocused().
|
||||
TopLines(
|
||||
Contains("<<<<<<< HEAD"),
|
||||
Contains("one"),
|
||||
Contains("======="),
|
||||
Contains("three"),
|
||||
Contains(">>>>>>>"),
|
||||
).
|
||||
SelectNextItem().
|
||||
PressPrimaryAction() // pick "three"
|
||||
|
||||
t.Actions().ContinueOnConflictsResolved()
|
||||
|
||||
t.Actions().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("UU myfile"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().MergeConflicts().
|
||||
IsFocused().
|
||||
TopLines(
|
||||
Contains("<<<<<<< HEAD"),
|
||||
Contains("three"),
|
||||
Contains("======="),
|
||||
Contains("two"),
|
||||
Contains(">>>>>>>"),
|
||||
).
|
||||
SelectNextItem().
|
||||
PressPrimaryAction() // pick "two"
|
||||
|
||||
t.Actions().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("commit two").IsSelected(),
|
||||
Contains("commit three"),
|
||||
Contains("commit one"),
|
||||
).
|
||||
Tap(func() {
|
||||
t.Views().Main().Content(Contains("-three").Contains("+two"))
|
||||
}).
|
||||
SelectNextItem().
|
||||
Tap(func() {
|
||||
t.Views().Main().Content(Contains("-one").Contains("+three"))
|
||||
})
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ var Apply = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().PatchBuildingSecondary().Content(Contains("second line"))
|
||||
|
||||
t.Actions().SelectPatchOption(MatchesRegexp(`apply patch$`))
|
||||
t.Common().SelectPatchOption(MatchesRegexp(`apply patch$`))
|
||||
|
||||
t.Views().Files().
|
||||
Focus().
|
||||
|
@ -35,7 +35,7 @@ var ApplyInReverse = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().PatchBuildingSecondary().Content(Contains("+file1 content"))
|
||||
|
||||
t.Actions().SelectPatchOption(Contains("apply patch in reverse"))
|
||||
t.Common().SelectPatchOption(Contains("apply patch in reverse"))
|
||||
|
||||
t.Views().Files().
|
||||
Focus().
|
||||
|
@ -40,7 +40,7 @@ var CopyPatchToClipboard = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().Information().Content(Contains("building patch"))
|
||||
|
||||
t.Actions().SelectPatchOption(Contains("copy patch to clipboard"))
|
||||
t.Common().SelectPatchOption(Contains("copy patch to clipboard"))
|
||||
|
||||
t.ExpectToast(Contains("Patch copied to clipboard"))
|
||||
|
||||
|
@ -35,7 +35,7 @@ var MoveToIndex = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().PatchBuildingSecondary().Content(Contains("+file1 content"))
|
||||
|
||||
t.Actions().SelectPatchOption(Contains("move patch out into index"))
|
||||
t.Common().SelectPatchOption(Contains("move patch out into index"))
|
||||
|
||||
t.Views().Files().
|
||||
Lines(
|
||||
|
@ -28,7 +28,7 @@ var MoveToIndexPartial = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("second commit"),
|
||||
Contains("first commit"),
|
||||
).
|
||||
NavigateToListItem(Contains("second commit")).
|
||||
NavigateToLine(Contains("second commit")).
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
@ -61,7 +61,7 @@ var MoveToIndexPartial = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains(` third line`),
|
||||
)
|
||||
|
||||
t.Actions().SelectPatchOption(Contains("move patch out into index"))
|
||||
t.Common().SelectPatchOption(Contains("move patch out into index"))
|
||||
|
||||
t.Views().Files().
|
||||
Lines(
|
||||
|
@ -40,9 +40,9 @@ var MoveToIndexWithConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().Information().Content(Contains("building patch"))
|
||||
|
||||
t.Actions().SelectPatchOption(Contains("move patch out into index"))
|
||||
t.Common().SelectPatchOption(Contains("move patch out into index"))
|
||||
|
||||
t.Actions().AcknowledgeConflicts()
|
||||
t.Common().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
@ -62,7 +62,7 @@ var MoveToIndexWithConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
PressPrimaryAction()
|
||||
|
||||
t.Actions().ContinueOnConflictsResolved()
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.ExpectPopup().Alert().
|
||||
Title(Equals("Error")).
|
||||
|
@ -40,7 +40,7 @@ var MoveToNewCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().Information().Content(Contains("building patch"))
|
||||
|
||||
t.Actions().SelectPatchOption(Contains("move patch into new commit"))
|
||||
t.Common().SelectPatchOption(Contains("move patch into new commit"))
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
|
@ -35,7 +35,7 @@ var RemoveFromCommit = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().PatchBuildingSecondary().Content(Contains("+file1 content"))
|
||||
|
||||
t.Actions().SelectPatchOption(Contains("remove patch from original commit"))
|
||||
t.Common().SelectPatchOption(Contains("remove patch from original commit"))
|
||||
|
||||
t.Views().Files().IsEmpty()
|
||||
|
||||
|
@ -43,7 +43,7 @@ var SpecificSelection = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().Secondary().Content(Contains("direct file content"))
|
||||
}).
|
||||
NavigateToListItem(Contains("hunk-file")).
|
||||
NavigateToLine(Contains("hunk-file")).
|
||||
PressEnter()
|
||||
|
||||
t.Views().PatchBuilding().
|
||||
@ -92,7 +92,7 @@ var SpecificSelection = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().CommitFiles().
|
||||
IsFocused().
|
||||
NavigateToListItem(Contains("line-file")).
|
||||
NavigateToLine(Contains("line-file")).
|
||||
PressEnter()
|
||||
|
||||
t.Views().PatchBuilding().
|
||||
@ -106,11 +106,11 @@ var SpecificSelection = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("+2a"),
|
||||
).
|
||||
PressPrimaryAction().
|
||||
NavigateToListItem(Contains("+2c")).
|
||||
NavigateToLine(Contains("+2c")).
|
||||
Press(keys.Main.ToggleDragSelect).
|
||||
NavigateToListItem(Contains("+2e")).
|
||||
NavigateToLine(Contains("+2e")).
|
||||
PressPrimaryAction().
|
||||
NavigateToListItem(Contains("+2g")).
|
||||
NavigateToLine(Contains("+2g")).
|
||||
PressPrimaryAction().
|
||||
Tap(func() {
|
||||
t.Views().Information().Content(Contains("building patch"))
|
||||
|
@ -41,7 +41,7 @@ var StartNewPatch = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
|
||||
t.Views().Commits().
|
||||
IsFocused().
|
||||
NavigateToListItem(Contains("first commit")).
|
||||
NavigateToLine(Contains("first commit")).
|
||||
PressEnter()
|
||||
|
||||
t.Views().CommitFiles().
|
||||
|
@ -60,6 +60,33 @@ var CreateMergeCommit = func(shell *Shell) {
|
||||
shell.ContinueMerge()
|
||||
}
|
||||
|
||||
// creates a merge conflict where there are two files with conflicts and a separate file without conflicts
|
||||
var CreateMergeConflictFiles = func(shell *Shell) {
|
||||
shell.
|
||||
NewBranch("original-branch").
|
||||
EmptyCommit("one").
|
||||
EmptyCommit("two").
|
||||
EmptyCommit("three").
|
||||
CreateFileAndAdd("file1", OriginalFileContent).
|
||||
CreateFileAndAdd("file2", OriginalFileContent).
|
||||
Commit("original").
|
||||
NewBranch("first-change-branch").
|
||||
UpdateFileAndAdd("file1", FirstChangeFileContent).
|
||||
UpdateFileAndAdd("file2", FirstChangeFileContent).
|
||||
Commit("first change").
|
||||
Checkout("original-branch").
|
||||
NewBranch("second-change-branch").
|
||||
UpdateFileAndAdd("file1", SecondChangeFileContent).
|
||||
UpdateFileAndAdd("file2", SecondChangeFileContent).
|
||||
// this file is not changed in the second branch
|
||||
CreateFileAndAdd("file3", "content").
|
||||
Commit("second change").
|
||||
EmptyCommit("second-change-branch unrelated change").
|
||||
Checkout("first-change-branch")
|
||||
|
||||
shell.RunShellCommandExpectError("git merge --no-edit second-change-branch")
|
||||
}
|
||||
|
||||
// These 'multiple' variants are just like the short ones but with longer file contents and with multiple conflicts within the file.
|
||||
|
||||
var OriginalFileContentMultiple = `
|
||||
@ -110,8 +137,7 @@ Other
|
||||
Other Second Change
|
||||
`
|
||||
|
||||
// prepares us for a rebase/merge that has conflicts
|
||||
var MergeConflictsSetupMultiple = func(shell *Shell) {
|
||||
var CreateMergeConflictFileMultiple = func(shell *Shell) {
|
||||
shell.
|
||||
NewBranch("original-branch").
|
||||
EmptyCommit("one").
|
||||
@ -128,16 +154,6 @@ var MergeConflictsSetupMultiple = func(shell *Shell) {
|
||||
Commit("second change").
|
||||
EmptyCommit("second-change-branch unrelated change").
|
||||
Checkout("first-change-branch")
|
||||
}
|
||||
|
||||
var CreateMergeConflictFileMultiple = func(shell *Shell) {
|
||||
MergeConflictsSetupMultiple(shell)
|
||||
|
||||
shell.RunShellCommandExpectError("git merge --no-edit second-change-branch")
|
||||
}
|
||||
|
||||
var CreateMergeCommitMultiple = func(shell *Shell) {
|
||||
CreateMergeConflictFileMultiple(shell)
|
||||
shell.UpdateFileAndAdd("file", SecondChangeFileContentMultiple)
|
||||
shell.ContinueMerge()
|
||||
}
|
||||
|
@ -33,13 +33,13 @@ var DiscardAllChanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
// discard the line
|
||||
Press(keys.Universal.Remove).
|
||||
Tap(func() {
|
||||
t.Actions().ConfirmDiscardLines()
|
||||
t.Common().ConfirmDiscardLines()
|
||||
}).
|
||||
SelectedLines(Contains("+four")).
|
||||
// discard the other line
|
||||
Press(keys.Universal.Remove).
|
||||
Tap(func() {
|
||||
t.Actions().ConfirmDiscardLines()
|
||||
t.Common().ConfirmDiscardLines()
|
||||
|
||||
// because there are no more changes in file1 we switch to file2
|
||||
t.Views().Files().
|
||||
|
@ -139,7 +139,7 @@ var StageHunks = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
Press(keys.Universal.Remove).
|
||||
Tap(func() {
|
||||
t.Actions().ConfirmDiscardLines()
|
||||
t.Common().ConfirmDiscardLines()
|
||||
}).
|
||||
Content(DoesNotContain("-3a").DoesNotContain("+3b")).
|
||||
SelectedLines(
|
||||
|
@ -30,7 +30,7 @@ var StageRanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("+three"),
|
||||
).
|
||||
Press(keys.Main.ToggleDragSelect).
|
||||
NavigateToListItem(Contains("+five")).
|
||||
NavigateToLine(Contains("+five")).
|
||||
SelectedLines(
|
||||
Contains("+three"),
|
||||
Contains("+four"),
|
||||
@ -61,7 +61,7 @@ var StageRanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Contains("+three"),
|
||||
).
|
||||
Press(keys.Main.ToggleDragSelect).
|
||||
NavigateToListItem(Contains("+five")).
|
||||
NavigateToLine(Contains("+five")).
|
||||
SelectedLines(
|
||||
Contains("+three"),
|
||||
Contains("+four"),
|
||||
@ -96,7 +96,7 @@ var StageRanges = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
Press(keys.Universal.Remove).
|
||||
Tap(func() {
|
||||
t.Actions().ConfirmDiscardLines()
|
||||
t.Common().ConfirmDiscardLines()
|
||||
}).
|
||||
ContainsLines(
|
||||
Contains("+three"),
|
||||
|
@ -12,7 +12,7 @@ var Add = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("first commit")
|
||||
shell.RunCommand("git clone --bare . ../other_repo")
|
||||
shell.Clone("other_repo")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Submodules().Focus().
|
||||
|
@ -20,8 +20,7 @@ var Enter = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("first commit")
|
||||
shell.RunCommand("git clone --bare . ../other_repo")
|
||||
shell.RunCommand("git submodule add ../other_repo my_submodule")
|
||||
shell.CloneIntoSubmodule("my_submodule")
|
||||
shell.GitAddAll()
|
||||
shell.Commit("add submodule")
|
||||
},
|
||||
|
@ -12,8 +12,7 @@ var Remove = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("first commit")
|
||||
shell.RunCommand("git clone --bare . ../other_repo")
|
||||
shell.RunCommand("git submodule add ../other_repo my_submodule")
|
||||
shell.CloneIntoSubmodule("my_submodule")
|
||||
shell.GitAddAll()
|
||||
shell.Commit("add submodule")
|
||||
},
|
||||
|
@ -20,8 +20,7 @@ var Reset = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
shell.EmptyCommit("first commit")
|
||||
shell.RunCommand("git clone --bare . ../other_repo")
|
||||
shell.RunCommand("git submodule add ../other_repo my_submodule")
|
||||
shell.CloneIntoSubmodule("my_submodule")
|
||||
shell.GitAddAll()
|
||||
shell.Commit("add submodule")
|
||||
},
|
||||
|
@ -9,9 +9,7 @@ var FetchPrune = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Fetch from the remote with the 'prune' option set in the git config",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.UserConfig.Git.AutoFetch = false
|
||||
},
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
// This option makes it so that git checks for deleted branches in the remote
|
||||
// upon fetching.
|
||||
|
@ -5,25 +5,6 @@ import (
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
func createTwoBranchesReadyToForcePush(shell *Shell) {
|
||||
shell.EmptyCommit("one")
|
||||
shell.EmptyCommit("two")
|
||||
|
||||
shell.NewBranch("other_branch")
|
||||
|
||||
shell.CloneIntoRemote("origin")
|
||||
|
||||
shell.SetBranchUpstream("master", "origin/master")
|
||||
shell.SetBranchUpstream("other_branch", "origin/other_branch")
|
||||
|
||||
// remove the 'two' commit so that we have something to pull from the remote
|
||||
shell.HardReset("HEAD^")
|
||||
|
||||
shell.Checkout("master")
|
||||
// doing the same for master
|
||||
shell.HardReset("HEAD^")
|
||||
}
|
||||
|
||||
var ForcePushMultipleUpstream = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Force push to only the upstream branch of the current branch because the user has push.default upstream",
|
||||
ExtraCmdArgs: "",
|
||||
|
@ -40,7 +40,7 @@ var PullMergeConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
IsFocused().
|
||||
Press(keys.Universal.Pull)
|
||||
|
||||
t.Actions().AcknowledgeConflicts()
|
||||
t.Common().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
@ -60,7 +60,7 @@ var PullMergeConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
).
|
||||
PressPrimaryAction() // choose 'content4'
|
||||
|
||||
t.Actions().ContinueOnConflictsResolved()
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Status().Content(Contains("↑2 repo → master"))
|
||||
|
||||
|
@ -40,7 +40,7 @@ var PullRebaseConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
IsFocused().
|
||||
Press(keys.Universal.Pull)
|
||||
|
||||
t.Actions().AcknowledgeConflicts()
|
||||
t.Common().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Files().
|
||||
IsFocused().
|
||||
@ -61,7 +61,7 @@ var PullRebaseConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
SelectNextItem().
|
||||
PressPrimaryAction() // choose 'content4'
|
||||
|
||||
t.Actions().ContinueOnConflictsResolved()
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Status().Content(Contains("↑1 repo → master"))
|
||||
|
||||
|
@ -42,7 +42,7 @@ var PullRebaseInteractiveConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
IsFocused().
|
||||
Press(keys.Universal.Pull)
|
||||
|
||||
t.Actions().AcknowledgeConflicts()
|
||||
t.Common().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Commits().
|
||||
Lines(
|
||||
@ -71,7 +71,7 @@ var PullRebaseInteractiveConflict = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
SelectNextItem().
|
||||
PressPrimaryAction() // choose 'content4'
|
||||
|
||||
t.Actions().ContinueOnConflictsResolved()
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Status().Content(Contains("↑2 repo → master"))
|
||||
|
||||
|
@ -42,7 +42,7 @@ var PullRebaseInteractiveConflictDrop = NewIntegrationTest(NewIntegrationTestArg
|
||||
IsFocused().
|
||||
Press(keys.Universal.Pull)
|
||||
|
||||
t.Actions().AcknowledgeConflicts()
|
||||
t.Common().AcknowledgeConflicts()
|
||||
|
||||
t.Views().Commits().
|
||||
Focus().
|
||||
@ -79,7 +79,7 @@ var PullRebaseInteractiveConflictDrop = NewIntegrationTest(NewIntegrationTestArg
|
||||
SelectNextItem().
|
||||
PressPrimaryAction() // choose 'content4'
|
||||
|
||||
t.Actions().ContinueOnConflictsResolved()
|
||||
t.Common().ContinueOnConflictsResolved()
|
||||
|
||||
t.Views().Status().Content(Contains("↑1 repo → master"))
|
||||
|
||||
|
@ -30,28 +30,3 @@ var Push = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
assertSuccessfullyPushed(t)
|
||||
},
|
||||
})
|
||||
|
||||
func assertSuccessfullyPushed(t *TestDriver) {
|
||||
t.Views().Status().Content(Contains("✓ repo → master"))
|
||||
|
||||
t.Views().Remotes().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("origin"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().RemoteBranches().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("master"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().SubCommits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("two"),
|
||||
Contains("one"),
|
||||
)
|
||||
}
|
||||
|
@ -23,8 +23,7 @@ var PushWithCredentialPrompt = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
// actually getting a password prompt is tricky: it requires SSH'ing into localhost under a newly created, restricted, user.
|
||||
// This is not easy to do in a cross-platform way, nor is it easy to do in a docker container.
|
||||
// If you can think of a way to do it, please let me know!
|
||||
shell.RunCommand("cp ../../../../../hooks/pre-push .git/hooks/pre-push")
|
||||
shell.RunCommand("chmod +x .git/hooks/pre-push")
|
||||
shell.CopyHelpFile("pre-push", ".git/hooks/pre-push")
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
t.Views().Status().Content(Contains("↑1 repo → master"))
|
||||
|
49
pkg/integration/tests/sync/shared.go
Normal file
49
pkg/integration/tests/sync/shared.go
Normal file
@ -0,0 +1,49 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
. "github.com/jesseduffield/lazygit/pkg/integration/components"
|
||||
)
|
||||
|
||||
func createTwoBranchesReadyToForcePush(shell *Shell) {
|
||||
shell.EmptyCommit("one")
|
||||
shell.EmptyCommit("two")
|
||||
|
||||
shell.NewBranch("other_branch")
|
||||
|
||||
shell.CloneIntoRemote("origin")
|
||||
|
||||
shell.SetBranchUpstream("master", "origin/master")
|
||||
shell.SetBranchUpstream("other_branch", "origin/other_branch")
|
||||
|
||||
// remove the 'two' commit so that we have something to pull from the remote
|
||||
shell.HardReset("HEAD^")
|
||||
|
||||
shell.Checkout("master")
|
||||
// doing the same for master
|
||||
shell.HardReset("HEAD^")
|
||||
}
|
||||
|
||||
func assertSuccessfullyPushed(t *TestDriver) {
|
||||
t.Views().Status().Content(Contains("✓ repo → master"))
|
||||
|
||||
t.Views().Remotes().
|
||||
Focus().
|
||||
Lines(
|
||||
Contains("origin"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().RemoteBranches().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("master"),
|
||||
).
|
||||
PressEnter()
|
||||
|
||||
t.Views().SubCommits().
|
||||
IsFocused().
|
||||
Lines(
|
||||
Contains("two"),
|
||||
Contains("one"),
|
||||
)
|
||||
}
|
@ -59,6 +59,9 @@ var tests = []*components.IntegrationTest{
|
||||
commit.StagedWithoutHooks,
|
||||
commit.Unstaged,
|
||||
config.RemoteNamedStar,
|
||||
conflicts.Filter,
|
||||
conflicts.ResolveExternally,
|
||||
conflicts.ResolveMultipleFiles,
|
||||
conflicts.UndoChooseHunk,
|
||||
custom_commands.Basic,
|
||||
custom_commands.FormPrompts,
|
@ -1,7 +1,7 @@
|
||||
//go:build ignore
|
||||
|
||||
// This file is invoked with `go generate ./...` and it generates the tests_gen.go file
|
||||
// The tests_gen.go file is a list of all the integration tests.
|
||||
// This file is invoked with `go generate ./...` and it generates the test_list.go file
|
||||
// The test_list.go file is a list of all the integration tests.
|
||||
// It's annoying to have to manually add an entry in that file for each test you
|
||||
// create, so this generator is here to make the process easier.
|
||||
|
||||
@ -26,7 +26,7 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := ioutil.WriteFile("tests_gen.go", formattedCode, 0o644); err != nil {
|
||||
if err := ioutil.WriteFile("test_list.go", formattedCode, 0o644); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
//go:generate go run tests_generator.go
|
||||
//go:generate go run test_list_generator.go
|
||||
|
||||
package tests
|
||||
|
||||
@ -30,7 +30,7 @@ func GetTests() []*components.IntegrationTest {
|
||||
if err := filepath.Walk(filepath.Join(utils.GetLazyRootDirectory(), "pkg/integration/tests"), func(path string, info os.FileInfo, err error) error {
|
||||
if !info.IsDir() && strings.HasSuffix(path, ".go") {
|
||||
// ignoring non-test files
|
||||
if filepath.Base(path) == "tests.go" || filepath.Base(path) == "tests_gen.go" || filepath.Base(path) == "tests_generator.go" {
|
||||
if filepath.Base(path) == "tests.go" || filepath.Base(path) == "test_list.go" || filepath.Base(path) == "test_list_generator.go" {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -39,6 +39,11 @@ func GetTests() []*components.IntegrationTest {
|
||||
return nil
|
||||
}
|
||||
|
||||
// any file named shared.go will also be ignored, because those files are only used for shared helper functions
|
||||
if filepath.Base(path) == "shared.go" {
|
||||
return nil
|
||||
}
|
||||
|
||||
nameFromPath := components.TestNameFromFilePath(path)
|
||||
if !testNamesSet.Includes(nameFromPath) {
|
||||
missingTestNames = append(missingTestNames, nameFromPath)
|
||||
@ -51,13 +56,13 @@ func GetTests() []*components.IntegrationTest {
|
||||
}
|
||||
|
||||
if len(missingTestNames) > 0 {
|
||||
panic(fmt.Sprintf("The following tests are missing from the list of tests: %s. You need to add them to `pkg/integration/tests/tests_gen.go`. Use `go generate ./...` to regenerate the tests list.", strings.Join(missingTestNames, ", ")))
|
||||
panic(fmt.Sprintf("The following tests are missing from the list of tests: %s. You need to add them to `pkg/integration/tests/test_list.go`. Use `go generate ./...` to regenerate the tests list.", strings.Join(missingTestNames, ", ")))
|
||||
}
|
||||
|
||||
if testCount > len(tests) {
|
||||
panic("you have not added all of the tests to the tests list in `pkg/integration/tests/tests_gen.go`. Use `go generate ./...` to regenerate the tests list.")
|
||||
panic("you have not added all of the tests to the tests list in `pkg/integration/tests/test_list.go`. Use `go generate ./...` to regenerate the tests list.")
|
||||
} else if testCount < len(tests) {
|
||||
panic("There are more tests in `pkg/integration/tests/tests_gen.go` than there are test files in the tests directory. Ensure that you only have one test per file and you haven't included the same test twice in the tests list. Use `go generate ./...` to regenerate the tests list.")
|
||||
panic("There are more tests in `pkg/integration/tests/test_list.go` than there are test files in the tests directory. Ensure that you only have one test per file and you haven't included the same test twice in the tests list. Use `go generate ./...` to regenerate the tests list.")
|
||||
}
|
||||
|
||||
return tests
|
||||
|
@ -9,9 +9,7 @@ var SwitchTabFromMenu = NewIntegrationTest(NewIntegrationTestArgs{
|
||||
Description: "Switch tab via the options menu",
|
||||
ExtraCmdArgs: "",
|
||||
Skip: false,
|
||||
SetupConfig: func(config *config.AppConfig) {
|
||||
config.UserConfig.Git.AutoFetch = false
|
||||
},
|
||||
SetupConfig: func(config *config.AppConfig) {},
|
||||
SetupRepo: func(shell *Shell) {
|
||||
},
|
||||
Run: func(t *TestDriver, keys config.KeybindingConfig) {
|
||||
|
2
test/README.md
Normal file
2
test/README.md
Normal file
@ -0,0 +1,2 @@
|
||||
This directory contains some files used by out integration tests. The tests themselves live in [/pkg/integration/](/pkg/integration/). See [here](/pkg/integration/README.md) for more info
|
||||
|
@ -1,3 +1,5 @@
|
||||
# This config is used in our integration tests. If we want to modify this for a specific test, you can do so in the SetupConfig function
|
||||
|
||||
disableStartupPopups: true
|
||||
gui:
|
||||
theme:
|
||||
@ -6,5 +8,10 @@ gui:
|
||||
- bold
|
||||
SelectedRangeBgcolor:
|
||||
- reverse
|
||||
# TODO: we should update most tests to use a file tree now that it's the default
|
||||
showFileTree: false
|
||||
git:
|
||||
# We don't want to run any periodic background git commands because it'll introduce race conditions and flakiness.
|
||||
# If we need to refresh something from within the test (which should only really happen if we've invoked a
|
||||
# shell command in the background) we should have the user press shift+R to refresh.
|
||||
# TODO: add tests which explicitly test auto-refresh functionality
|
||||
autoRefresh: false
|
||||
autoFetch: false
|
||||
|
0
test/hooks/pre-push → test/files/pre-push
Normal file → Executable file
0
test/hooks/pre-push → test/files/pre-push
Normal file → Executable file
@ -1,3 +1,5 @@
|
||||
# This is the global git config we use for all our integration tests
|
||||
|
||||
[user]
|
||||
name = CI
|
||||
email = CI@example.com
|
||||
|
@ -1,5 +0,0 @@
|
||||
disableStartupPopups: true
|
||||
gui:
|
||||
showFileTree: false
|
||||
refresher:
|
||||
refreshInterval: 1
|
@ -1,36 +0,0 @@
|
||||
Merge branch 'develop' into other_branch
|
||||
|
||||
# Conflicts:
|
||||
# directory/file
|
||||
# directory/file2
|
||||
# file1
|
||||
# file3
|
||||
# file4
|
||||
# file5
|
||||
#
|
||||
# It looks like you may be committing a merge.
|
||||
# If this is not correct, please remove the file
|
||||
# /Users/jesseduffieldduffield/go/src/github.com/jesseduffield/lazygit/test/integration/mergeConflicts/actual/.git/MERGE_HEAD
|
||||
# and try again.
|
||||
|
||||
|
||||
# Please enter the commit message for your changes. Lines starting
|
||||
# with '#' will be ignored, and an empty message aborts the commit.
|
||||
#
|
||||
# On branch other_branch
|
||||
# All conflicts fixed but you are still merging.
|
||||
#
|
||||
# Changes to be committed:
|
||||
# new file: cherrypicking1
|
||||
# new file: cherrypicking2
|
||||
# new file: cherrypicking3
|
||||
# new file: cherrypicking4
|
||||
# new file: cherrypicking5
|
||||
# new file: cherrypicking6
|
||||
# new file: cherrypicking7
|
||||
# new file: cherrypicking8
|
||||
# new file: cherrypicking9
|
||||
# modified: file1
|
||||
# modified: file4
|
||||
# modified: file5
|
||||
#
|
@ -1 +0,0 @@
|
||||
ref: refs/heads/other_branch
|
@ -1 +0,0 @@
|
||||
562af0640203fb5a6e92c090d8d1ded26806d2c4
|
@ -1,10 +0,0 @@
|
||||
[core]
|
||||
repositoryformatversion = 0
|
||||
filemode = true
|
||||
bare = false
|
||||
logallrefupdates = true
|
||||
ignorecase = true
|
||||
precomposeunicode = true
|
||||
[user]
|
||||
email = CI@example.com
|
||||
name = CI
|
@ -1 +0,0 @@
|
||||
Unnamed repository; edit this file 'description' to name the repository.
|
Binary file not shown.
@ -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
|
@ -1,34 +0,0 @@
|
||||
0000000000000000000000000000000000000000 1618ce1085acb41fd710e279ac38911aadfb0a09 CI <CI@example.com> 1617671441 +1000 commit (initial): first commit
|
||||
1618ce1085acb41fd710e279ac38911aadfb0a09 1618ce1085acb41fd710e279ac38911aadfb0a09 CI <CI@example.com> 1617671441 +1000 checkout: moving from master to feature/cherry-picking
|
||||
1618ce1085acb41fd710e279ac38911aadfb0a09 b2d5312a06a9c56e9ada21c48a12f57ce8dd4c4a CI <CI@example.com> 1617671441 +1000 commit: first commit freshman year
|
||||
b2d5312a06a9c56e9ada21c48a12f57ce8dd4c4a f7f30ea7f84d4521d3ce9cc08b780c7a1bf7cc5e CI <CI@example.com> 1617671441 +1000 commit: second commit subway eat fresh
|
||||
f7f30ea7f84d4521d3ce9cc08b780c7a1bf7cc5e edd4e2e50eb82125428b045c540a9194d934e180 CI <CI@example.com> 1617671441 +1000 commit: third commit fresh
|
||||
edd4e2e50eb82125428b045c540a9194d934e180 cc19bee93215b6c20ab129fb2c006762d4ae1497 CI <CI@example.com> 1617671441 +1000 commit: fourth commit cool
|
||||
cc19bee93215b6c20ab129fb2c006762d4ae1497 b0753bdba91b84e3f406e21dbc7deba8e98f1fc8 CI <CI@example.com> 1617671441 +1000 commit: fifth commit nice
|
||||
b0753bdba91b84e3f406e21dbc7deba8e98f1fc8 df2c0daa40dcba0dded361a25ff7806b13db59a6 CI <CI@example.com> 1617671441 +1000 commit: sixth commit haha
|
||||
df2c0daa40dcba0dded361a25ff7806b13db59a6 1fbc3eb4b11cb89b204a593572c2e01462ca5a89 CI <CI@example.com> 1617671441 +1000 commit: seventh commit yeah
|
||||
1fbc3eb4b11cb89b204a593572c2e01462ca5a89 d3e2708327280097b5e1f8ab69309934b24f8b64 CI <CI@example.com> 1617671441 +1000 commit: eighth commit woo
|
||||
d3e2708327280097b5e1f8ab69309934b24f8b64 d3e2708327280097b5e1f8ab69309934b24f8b64 CI <CI@example.com> 1617671441 +1000 checkout: moving from feature/cherry-picking to develop
|
||||
d3e2708327280097b5e1f8ab69309934b24f8b64 a1e00cd67130c6f7e2b9bb7f23f0cda2b37eb30b CI <CI@example.com> 1617671441 +1000 commit: first commit on develop
|
||||
a1e00cd67130c6f7e2b9bb7f23f0cda2b37eb30b 1618ce1085acb41fd710e279ac38911aadfb0a09 CI <CI@example.com> 1617671441 +1000 checkout: moving from develop to master
|
||||
1618ce1085acb41fd710e279ac38911aadfb0a09 f89097f0bd23eda6d8977c0edfae7f913ffc5db3 CI <CI@example.com> 1617671441 +1000 commit: first commit on master
|
||||
f89097f0bd23eda6d8977c0edfae7f913ffc5db3 a1e00cd67130c6f7e2b9bb7f23f0cda2b37eb30b CI <CI@example.com> 1617671441 +1000 checkout: moving from master to develop
|
||||
a1e00cd67130c6f7e2b9bb7f23f0cda2b37eb30b 55f688e6b47b7a5ca8ffc4e25b77c1af6222b503 CI <CI@example.com> 1617671441 +1000 commit: second commit on develop
|
||||
55f688e6b47b7a5ca8ffc4e25b77c1af6222b503 f89097f0bd23eda6d8977c0edfae7f913ffc5db3 CI <CI@example.com> 1617671441 +1000 checkout: moving from develop to master
|
||||
f89097f0bd23eda6d8977c0edfae7f913ffc5db3 a19ec0b99e516795f349033f09383f87be0b74e9 CI <CI@example.com> 1617671441 +1000 commit: second commit on master
|
||||
a19ec0b99e516795f349033f09383f87be0b74e9 55f688e6b47b7a5ca8ffc4e25b77c1af6222b503 CI <CI@example.com> 1617671441 +1000 checkout: moving from master to develop
|
||||
55f688e6b47b7a5ca8ffc4e25b77c1af6222b503 dd259e90c3748e269bdf1ee3ce537a006d2394aa CI <CI@example.com> 1617671441 +1000 commit: third commit on develop
|
||||
dd259e90c3748e269bdf1ee3ce537a006d2394aa a19ec0b99e516795f349033f09383f87be0b74e9 CI <CI@example.com> 1617671441 +1000 checkout: moving from develop to master
|
||||
a19ec0b99e516795f349033f09383f87be0b74e9 61db24350a92fa37b2fe35f13eb3dd3f7655f6cf CI <CI@example.com> 1617671441 +1000 commit: third commit on master
|
||||
61db24350a92fa37b2fe35f13eb3dd3f7655f6cf dd259e90c3748e269bdf1ee3ce537a006d2394aa CI <CI@example.com> 1617671441 +1000 checkout: moving from master to develop
|
||||
dd259e90c3748e269bdf1ee3ce537a006d2394aa e563585cb87cc39b553ca421902d631ea8890118 CI <CI@example.com> 1617671441 +1000 commit: fourth commit on develop
|
||||
e563585cb87cc39b553ca421902d631ea8890118 61db24350a92fa37b2fe35f13eb3dd3f7655f6cf CI <CI@example.com> 1617671441 +1000 checkout: moving from develop to master
|
||||
61db24350a92fa37b2fe35f13eb3dd3f7655f6cf 559043765dc6c32c943b6278b4abbff1e6f52839 CI <CI@example.com> 1617671441 +1000 commit: fourth commit on master
|
||||
559043765dc6c32c943b6278b4abbff1e6f52839 559043765dc6c32c943b6278b4abbff1e6f52839 CI <CI@example.com> 1617671441 +1000 checkout: moving from master to base_branch
|
||||
559043765dc6c32c943b6278b4abbff1e6f52839 0b2387a3f67ec050f6d4e08f379e3cbb0a9913f1 CI <CI@example.com> 1617671441 +1000 commit: file
|
||||
0b2387a3f67ec050f6d4e08f379e3cbb0a9913f1 0b2387a3f67ec050f6d4e08f379e3cbb0a9913f1 CI <CI@example.com> 1617671441 +1000 checkout: moving from base_branch to other_branch
|
||||
0b2387a3f67ec050f6d4e08f379e3cbb0a9913f1 0b2387a3f67ec050f6d4e08f379e3cbb0a9913f1 CI <CI@example.com> 1617671441 +1000 checkout: moving from other_branch to base_branch
|
||||
0b2387a3f67ec050f6d4e08f379e3cbb0a9913f1 341cf8213827614a274c750cd7dec4307eb41de7 CI <CI@example.com> 1617671441 +1000 commit: file changed
|
||||
341cf8213827614a274c750cd7dec4307eb41de7 0b2387a3f67ec050f6d4e08f379e3cbb0a9913f1 CI <CI@example.com> 1617671441 +1000 checkout: moving from base_branch to other_branch
|
||||
0b2387a3f67ec050f6d4e08f379e3cbb0a9913f1 562af0640203fb5a6e92c090d8d1ded26806d2c4 CI <CI@example.com> 1617671444 +1000 commit: asd
|
||||
562af0640203fb5a6e92c090d8d1ded26806d2c4 f8dd12b796f400be7f59d9471670c3080f9c90a1 CI <CI@example.com> 1617671461 +1000 commit (merge): Merge branch 'develop' into other_branch
|
@ -1,3 +0,0 @@
|
||||
0000000000000000000000000000000000000000 559043765dc6c32c943b6278b4abbff1e6f52839 CI <CI@example.com> 1617671441 +1000 branch: Created from HEAD
|
||||
559043765dc6c32c943b6278b4abbff1e6f52839 0b2387a3f67ec050f6d4e08f379e3cbb0a9913f1 CI <CI@example.com> 1617671441 +1000 commit: file
|
||||
0b2387a3f67ec050f6d4e08f379e3cbb0a9913f1 341cf8213827614a274c750cd7dec4307eb41de7 CI <CI@example.com> 1617671441 +1000 commit: file changed
|
@ -1,5 +0,0 @@
|
||||
0000000000000000000000000000000000000000 d3e2708327280097b5e1f8ab69309934b24f8b64 CI <CI@example.com> 1617671441 +1000 branch: Created from HEAD
|
||||
d3e2708327280097b5e1f8ab69309934b24f8b64 a1e00cd67130c6f7e2b9bb7f23f0cda2b37eb30b CI <CI@example.com> 1617671441 +1000 commit: first commit on develop
|
||||
a1e00cd67130c6f7e2b9bb7f23f0cda2b37eb30b 55f688e6b47b7a5ca8ffc4e25b77c1af6222b503 CI <CI@example.com> 1617671441 +1000 commit: second commit on develop
|
||||
55f688e6b47b7a5ca8ffc4e25b77c1af6222b503 dd259e90c3748e269bdf1ee3ce537a006d2394aa CI <CI@example.com> 1617671441 +1000 commit: third commit on develop
|
||||
dd259e90c3748e269bdf1ee3ce537a006d2394aa e563585cb87cc39b553ca421902d631ea8890118 CI <CI@example.com> 1617671441 +1000 commit: fourth commit on develop
|
@ -1,9 +0,0 @@
|
||||
0000000000000000000000000000000000000000 1618ce1085acb41fd710e279ac38911aadfb0a09 CI <CI@example.com> 1617671441 +1000 branch: Created from HEAD
|
||||
1618ce1085acb41fd710e279ac38911aadfb0a09 b2d5312a06a9c56e9ada21c48a12f57ce8dd4c4a CI <CI@example.com> 1617671441 +1000 commit: first commit freshman year
|
||||
b2d5312a06a9c56e9ada21c48a12f57ce8dd4c4a f7f30ea7f84d4521d3ce9cc08b780c7a1bf7cc5e CI <CI@example.com> 1617671441 +1000 commit: second commit subway eat fresh
|
||||
f7f30ea7f84d4521d3ce9cc08b780c7a1bf7cc5e edd4e2e50eb82125428b045c540a9194d934e180 CI <CI@example.com> 1617671441 +1000 commit: third commit fresh
|
||||
edd4e2e50eb82125428b045c540a9194d934e180 cc19bee93215b6c20ab129fb2c006762d4ae1497 CI <CI@example.com> 1617671441 +1000 commit: fourth commit cool
|
||||
cc19bee93215b6c20ab129fb2c006762d4ae1497 b0753bdba91b84e3f406e21dbc7deba8e98f1fc8 CI <CI@example.com> 1617671441 +1000 commit: fifth commit nice
|
||||
b0753bdba91b84e3f406e21dbc7deba8e98f1fc8 df2c0daa40dcba0dded361a25ff7806b13db59a6 CI <CI@example.com> 1617671441 +1000 commit: sixth commit haha
|
||||
df2c0daa40dcba0dded361a25ff7806b13db59a6 1fbc3eb4b11cb89b204a593572c2e01462ca5a89 CI <CI@example.com> 1617671441 +1000 commit: seventh commit yeah
|
||||
1fbc3eb4b11cb89b204a593572c2e01462ca5a89 d3e2708327280097b5e1f8ab69309934b24f8b64 CI <CI@example.com> 1617671441 +1000 commit: eighth commit woo
|
@ -1,5 +0,0 @@
|
||||
0000000000000000000000000000000000000000 1618ce1085acb41fd710e279ac38911aadfb0a09 CI <CI@example.com> 1617671441 +1000 commit (initial): first commit
|
||||
1618ce1085acb41fd710e279ac38911aadfb0a09 f89097f0bd23eda6d8977c0edfae7f913ffc5db3 CI <CI@example.com> 1617671441 +1000 commit: first commit on master
|
||||
f89097f0bd23eda6d8977c0edfae7f913ffc5db3 a19ec0b99e516795f349033f09383f87be0b74e9 CI <CI@example.com> 1617671441 +1000 commit: second commit on master
|
||||
a19ec0b99e516795f349033f09383f87be0b74e9 61db24350a92fa37b2fe35f13eb3dd3f7655f6cf CI <CI@example.com> 1617671441 +1000 commit: third commit on master
|
||||
61db24350a92fa37b2fe35f13eb3dd3f7655f6cf 559043765dc6c32c943b6278b4abbff1e6f52839 CI <CI@example.com> 1617671441 +1000 commit: fourth commit on master
|
@ -1,3 +0,0 @@
|
||||
0000000000000000000000000000000000000000 0b2387a3f67ec050f6d4e08f379e3cbb0a9913f1 CI <CI@example.com> 1617671441 +1000 branch: Created from HEAD
|
||||
0b2387a3f67ec050f6d4e08f379e3cbb0a9913f1 562af0640203fb5a6e92c090d8d1ded26806d2c4 CI <CI@example.com> 1617671444 +1000 commit: asd
|
||||
562af0640203fb5a6e92c090d8d1ded26806d2c4 f8dd12b796f400be7f59d9471670c3080f9c90a1 CI <CI@example.com> 1617671461 +1000 commit (merge): Merge branch 'develop' into other_branch
|
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user