Files
lazygit/pkg/gui/controllers/local_commits_controller_test.go
Stefan Haller eda4ad1192 Drag selected commits to a new position
Pressing the left button on the current selection now starts a drag
that moves the selected commits, both in the normal commits view and
for todos during an interactive rebase. A press anywhere else falls
through to the usual click handling, so dragging from an unselected
line still creates a range selection, and releasing without having
moved collapses the selection to the pressed commit like a plain click
would.

While dragging, the insertion point follows the pointer: rows below
the dragged block insert after the pointed-at commit, rows above it
insert before it, and during a rebase the destination is limited to
the contiguous block of movable todos around the selection. gocui
moves the view cursor along with the pointer, so each drag event moves
it back to keep the original selection highlighted.

The move happens on release. The model may have been refreshed during
the drag, so the dragged commits are located again by their identity
(hash, subject, todo action); if they no longer form a unique
contiguous block, the drop is ignored rather than guessing.
2026-07-31 08:37:28 +02:00

134 lines
3.1 KiB
Go

package controllers
import (
"testing"
"github.com/jesseduffield/lazygit/pkg/commands/models"
"github.com/jesseduffield/lazygit/pkg/utils"
"github.com/stretchr/testify/assert"
)
func TestFindCommitDragBlock(t *testing.T) {
commit := func(hash string) *models.Commit {
return models.NewCommit(&utils.StringPool{}, models.NewCommitOpts{Hash: hash})
}
identities := []commitDragIdentity{
commitDragIdentityForCommit(commit("b")),
commitDragIdentityForCommit(commit("c")),
}
t.Run("finds the original block after selection changes", func(t *testing.T) {
commits := []*models.Commit{commit("a"), commit("b"), commit("c"), commit("d")}
actual, startIndex, endIndex, found := findCommitDragBlock(commits, identities)
assert.True(t, found)
assert.Equal(t, commits[1:3], actual)
assert.Equal(t, 1, startIndex)
assert.Equal(t, 2, endIndex)
})
t.Run("rejects a block that is no longer contiguous", func(t *testing.T) {
_, _, _, found := findCommitDragBlock(
[]*models.Commit{commit("a"), commit("b"), commit("d"), commit("c")}, identities,
)
assert.False(t, found)
})
t.Run("rejects an ambiguous block", func(t *testing.T) {
_, _, _, found := findCommitDragBlock(
[]*models.Commit{commit("b"), commit("c"), commit("b"), commit("c")}, identities,
)
assert.False(t, found)
})
}
func Test_countSquashableCommitsAbove(t *testing.T) {
scenarios := []struct {
name string
commits []*models.Commit
selectedIdx int
rebaseStartIdx int
expectedResult int
}{
{
name: "no squashable commits",
commits: []*models.Commit{
{Name: "abc"},
{Name: "def"},
{Name: "ghi"},
},
selectedIdx: 2,
rebaseStartIdx: 2,
expectedResult: 0,
},
{
name: "some squashable commits, including for the selected commit",
commits: []*models.Commit{
{Name: "fixup! def"},
{Name: "fixup! ghi"},
{Name: "abc"},
{Name: "def"},
{Name: "ghi"},
},
selectedIdx: 4,
rebaseStartIdx: 4,
expectedResult: 2,
},
{
name: "base commit is below rebase start",
commits: []*models.Commit{
{Name: "fixup! def"},
{Name: "abc"},
{Name: "def"},
},
selectedIdx: 1,
rebaseStartIdx: 1,
expectedResult: 0,
},
{
name: "base commit does not exist at all",
commits: []*models.Commit{
{Name: "fixup! xyz"},
{Name: "abc"},
{Name: "def"},
},
selectedIdx: 2,
rebaseStartIdx: 2,
expectedResult: 0,
},
{
name: "selected commit is in the middle of fixups",
commits: []*models.Commit{
{Name: "fixup! def"},
{Name: "abc"},
{Name: "fixup! ghi"},
{Name: "def"},
{Name: "ghi"},
},
selectedIdx: 1,
rebaseStartIdx: 4,
expectedResult: 1,
},
{
name: "selected commit is after rebase start",
commits: []*models.Commit{
{Name: "fixup! def"},
{Name: "abc"},
{Name: "def"},
{Name: "ghi"},
},
selectedIdx: 3,
rebaseStartIdx: 2,
expectedResult: 1,
},
}
for _, s := range scenarios {
t.Run(s.name, func(t *testing.T) {
assert.Equal(t, s.expectedResult, countSquashableCommitsAbove(s.commits, s.selectedIdx, s.rebaseStartIdx))
})
}
}