From 277142fc4b9db9d722b648efb29b6fa905b5fb36 Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Mon, 17 Jul 2023 22:03:51 +1000 Subject: Add worktree integration tests --- pkg/integration/components/shell.go | 7 ++ pkg/integration/components/views.go | 4 + pkg/integration/tests/test_list.go | 5 + pkg/integration/tests/worktree/add_from_branch.go | 60 +++++++++++ pkg/integration/tests/worktree/crud.go | 120 +++++++++++++++++++++ pkg/integration/tests/worktree/rebase.go | 70 ++++++++++++ pkg/integration/tests/worktree/worktree_in_repo.go | 85 +++++++++++++++ 7 files changed, 351 insertions(+) create mode 100644 pkg/integration/tests/worktree/add_from_branch.go create mode 100644 pkg/integration/tests/worktree/crud.go create mode 100644 pkg/integration/tests/worktree/rebase.go create mode 100644 pkg/integration/tests/worktree/worktree_in_repo.go (limited to 'pkg/integration') diff --git a/pkg/integration/components/shell.go b/pkg/integration/components/shell.go index decb748da..809cb1d5b 100644 --- a/pkg/integration/components/shell.go +++ b/pkg/integration/components/shell.go @@ -254,6 +254,13 @@ func (self *Shell) Init() *Shell { return self } +func (self *Shell) AddWorktree(base string, path string, newBranchName string) *Shell { + return self.RunCommand([]string{ + "git", "worktree", "add", "-b", + newBranchName, path, base, + }) +} + func (self *Shell) MakeExecutable(path string) *Shell { // 0755 sets the executable permission for owner, and read/execute permissions for group and others err := os.Chmod(filepath.Join(self.dir, path), 0o755) diff --git a/pkg/integration/components/views.go b/pkg/integration/components/views.go index 1a6e54b7e..eb4f585dc 100644 --- a/pkg/integration/components/views.go +++ b/pkg/integration/components/views.go @@ -129,6 +129,10 @@ func (self *Views) Files() *ViewDriver { return self.regularView("files") } +func (self *Views) Worktrees() *ViewDriver { + return self.regularView("worktrees") +} + func (self *Views) Status() *ViewDriver { return self.regularView("status") } diff --git a/pkg/integration/tests/test_list.go b/pkg/integration/tests/test_list.go index 88d1883ac..d2e2848e7 100644 --- a/pkg/integration/tests/test_list.go +++ b/pkg/integration/tests/test_list.go @@ -26,6 +26,7 @@ import ( "github.com/jesseduffield/lazygit/pkg/integration/tests/tag" "github.com/jesseduffield/lazygit/pkg/integration/tests/ui" "github.com/jesseduffield/lazygit/pkg/integration/tests/undo" + "github.com/jesseduffield/lazygit/pkg/integration/tests/worktree" ) var tests = []*components.IntegrationTest{ @@ -219,4 +220,8 @@ var tests = []*components.IntegrationTest{ ui.SwitchTabFromMenu, undo.UndoCheckoutAndDrop, undo.UndoDrop, + worktree.AddFromBranch, + worktree.Crud, + worktree.Rebase, + worktree.WorktreeInRepo, } diff --git a/pkg/integration/tests/worktree/add_from_branch.go b/pkg/integration/tests/worktree/add_from_branch.go new file mode 100644 index 000000000..53636536d --- /dev/null +++ b/pkg/integration/tests/worktree/add_from_branch.go @@ -0,0 +1,60 @@ +package worktree + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var AddFromBranch = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Add a worktree via the branches view, then switch back to the main worktree via the branches view", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.NewBranch("mybranch") + shell.CreateFileAndAdd("README.md", "hello world") + shell.Commit("initial commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Branches(). + Focus(). + Lines( + Contains("mybranch"), + ). + Press(keys.Worktrees.ViewWorktreeOptions). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("Worktree")). + Select(Contains(`Create worktree from mybranch`).DoesNotContain("detached")). + Confirm() + + t.ExpectPopup().Prompt(). + Title(Equals("New worktree path")). + Type("../linked-worktree"). + Confirm() + + t.ExpectPopup().Prompt(). + Title(Equals("New branch name")). + Type("newbranch"). + Confirm() + }). + // confirm we're still focused on the branches view + IsFocused(). + Lines( + Contains("newbranch").IsSelected(), + Contains("mybranch (worktree)"), + ). + NavigateToLine(Contains("mybranch")). + Press(keys.Universal.Select). + Tap(func() { + t.ExpectPopup().Confirmation(). + Title(Equals("Switch to worktree")). + Content(Equals("This branch is checked out by worktree repo. Do you want to switch to that worktree?")). + Confirm() + }). + Lines( + Contains("mybranch").IsSelected(), + Contains("newbranch (worktree)"), + ) + }, +}) diff --git a/pkg/integration/tests/worktree/crud.go b/pkg/integration/tests/worktree/crud.go new file mode 100644 index 000000000..504d47b72 --- /dev/null +++ b/pkg/integration/tests/worktree/crud.go @@ -0,0 +1,120 @@ +package worktree + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var Crud = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "From the worktrees view, add a work tree, switch to it, switch back, and remove it", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.NewBranch("mybranch") + shell.CreateFileAndAdd("README.md", "hello world") + shell.Commit("initial commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Branches(). + Lines( + Contains("mybranch"), + ) + + t.Views().Status(). + Lines( + Contains("repo → mybranch"), + ) + + t.Views().Worktrees(). + Focus(). + Lines( + Contains("repo (main)"), + ). + Press(keys.Universal.New). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("Worktree")). + Select(Contains(`Create worktree from ref`).DoesNotContain(("detached"))). + Confirm() + + t.ExpectPopup().Prompt(). + Title(Equals("New worktree base ref")). + InitialText(Equals("mybranch")). + Confirm() + + t.ExpectPopup().Prompt(). + Title(Equals("New worktree path")). + Type("../linked-worktree"). + Confirm() + + t.ExpectPopup().Prompt(). + Title(Equals("New branch name (leave blank to checkout mybranch)")). + Type("newbranch"). + Confirm() + }). + Lines( + Contains("linked-worktree").IsSelected(), + Contains("repo (main)"), + ). + // confirm we're still in the same view + IsFocused() + + // status panel includes the worktree if it's a linked worktree + t.Views().Status(). + Lines( + Contains("repo(linked-worktree) → newbranch"), + ) + + t.Views().Branches(). + Lines( + Contains("newbranch"), + Contains("mybranch"), + ) + + t.Views().Worktrees(). + // confirm we can't remove the current worktree + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Alert(). + Title(Equals("Error")). + Content(Equals("You cannot remove the current worktree!")). + Confirm() + }). + // confirm we cannot remove the main worktree + NavigateToLine(Contains("repo (main)")). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Alert(). + Title(Equals("Error")). + Content(Equals("You cannot remove the main worktree!")). + Confirm() + }). + // switch back to main worktree + Press(keys.Universal.Select). + Lines( + Contains("repo (main)").IsSelected(), + Contains("linked-worktree"), + ) + + t.Views().Branches(). + Lines( + Contains("mybranch"), + Contains("newbranch"), + ) + + t.Views().Worktrees(). + // remove linked worktree + NavigateToLine(Contains("linked-worktree")). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Confirmation(). + Title(Equals("Remove worktree")). + Content(Contains("Are you sure you want to remove worktree 'linked-worktree'?")). + Confirm() + }). + Lines( + Contains("repo (main)").IsSelected(), + ) + }, +}) diff --git a/pkg/integration/tests/worktree/rebase.go b/pkg/integration/tests/worktree/rebase.go new file mode 100644 index 000000000..e8ae59556 --- /dev/null +++ b/pkg/integration/tests/worktree/rebase.go @@ -0,0 +1,70 @@ +package worktree + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +// This is important because `git worktree list` will show a worktree being in a detached head state (which is true) +// when it's in the middle of a rebase, but it won't tell you about the branch it's on. +// Even so, if you attempt to check out that branch from another worktree git won't let you, so we need to +// keep track of the association ourselves. + +var Rebase = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Verify that when you start a rebase in a worktree, Lazygit still associates the worktree with the branch", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.NewBranch("mybranch") + shell.CreateFileAndAdd("README.md", "hello world") + shell.Commit("initial commit") + shell.EmptyCommit("commit 2") + shell.EmptyCommit("commit 3") + shell.AddWorktree("mybranch", "../linked-worktree", "newbranch") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Branches(). + Focus(). + Lines( + Contains("mybranch"), + Contains("newbranch (worktree)"), + ) + + t.Views().Commits(). + Focus(). + NavigateToLine(Contains("commit 2")). + Press(keys.Universal.Edit) + + t.Views().Information().Content(Contains("Rebasing")) + + t.Views().Branches(). + Focus(). + // switch to linked worktree + NavigateToLine(Contains("newbranch")). + Press(keys.Universal.Select). + Tap(func() { + t.ExpectPopup().Confirmation(). + Title(Equals("Switch to worktree")). + Content(Equals("This branch is checked out by worktree linked-worktree. Do you want to switch to that worktree?")). + Confirm() + + t.Views().Information().Content(DoesNotContain("Rebasing")) + }). + Lines( + Contains("newbranch").IsSelected(), + Contains("mybranch (worktree)"), + ). + // switch back to main worktree + NavigateToLine(Contains("mybranch")). + Press(keys.Universal.Select). + Tap(func() { + t.ExpectPopup().Confirmation(). + Title(Equals("Switch to worktree")). + Content(Equals("This branch is checked out by worktree repo. Do you want to switch to that worktree?")). + Confirm() + + t.Views().Information().Content(Contains("Rebasing")) + }) + }, +}) diff --git a/pkg/integration/tests/worktree/worktree_in_repo.go b/pkg/integration/tests/worktree/worktree_in_repo.go new file mode 100644 index 000000000..743abddf5 --- /dev/null +++ b/pkg/integration/tests/worktree/worktree_in_repo.go @@ -0,0 +1,85 @@ +package worktree + +import ( + "github.com/jesseduffield/lazygit/pkg/config" + . "github.com/jesseduffield/lazygit/pkg/integration/components" +) + +var WorktreeInRepo = NewIntegrationTest(NewIntegrationTestArgs{ + Description: "Add a worktree inside the repo, then remove the directory and confirm the worktree is removed", + ExtraCmdArgs: []string{}, + Skip: false, + SetupConfig: func(config *config.AppConfig) {}, + SetupRepo: func(shell *Shell) { + shell.NewBranch("mybranch") + shell.CreateFileAndAdd("README.md", "hello world") + shell.Commit("initial commit") + }, + Run: func(t *TestDriver, keys config.KeybindingConfig) { + t.Views().Branches(). + Lines( + Contains("mybranch"), + ) + + t.Views().Worktrees(). + Focus(). + Lines( + Contains("repo (main)"), + ). + Press(keys.Universal.New). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("Worktree")). + Select(Contains(`Create worktree from ref`).DoesNotContain(("detached"))). + Confirm() + + t.ExpectPopup().Prompt(). + Title(Equals("New worktree base ref")). + InitialText(Equals("mybranch")). + Confirm() + + t.ExpectPopup().Prompt(). + Title(Equals("New worktree path")). + Type("linked-worktree"). + Confirm() + + t.ExpectPopup().Prompt(). + Title(Equals("New branch name (leave blank to checkout mybranch)")). + Type("newbranch"). + Confirm() + }). + Lines( + Contains("linked-worktree").IsSelected(), + Contains("repo (main)"), + ). + // switch back to main worktree + NavigateToLine(Contains("repo (main)")). + Press(keys.Universal.Select). + Lines( + Contains("repo (main)").IsSelected(), + Contains("linked-worktree"), + ) + + t.Views().Files(). + Focus(). + Lines( + Contains("linked-worktree"), + ). + Press(keys.Universal.Remove). + Tap(func() { + t.ExpectPopup().Menu(). + Title(Equals("linked-worktree")). + Select(Contains("Discard all changes")). + Confirm() + }). + IsEmpty() + + // confirm worktree appears as missing + t.Views().Worktrees(). + Focus(). + Lines( + Contains("repo (main)").IsSelected(), + Contains("linked-worktree (missing)"), + ) + }, +}) -- cgit v1.2.3