377 lines
9.8 KiB
Go
377 lines
9.8 KiB
Go
package motion
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"git.gophernest.net/azpect/TextEditor/internal/action"
|
|
"git.gophernest.net/azpect/TextEditor/internal/core"
|
|
)
|
|
|
|
// ==================================================
|
|
// MoveCommandHistoryUp Tests
|
|
// ==================================================
|
|
|
|
func TestMoveCommandHistoryUp(t *testing.T) {
|
|
t.Run("navigate up from start", func(t *testing.T) {
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "",
|
|
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
|
|
CommandHistoryCur: 0,
|
|
}
|
|
|
|
action := MoveCommandHistoryUp{}
|
|
action.Execute(m)
|
|
|
|
// Should load first command in history
|
|
if m.Command() != "cmd3" {
|
|
t.Errorf("Command = %q, want %q", m.Command(), "cmd3")
|
|
}
|
|
|
|
// Cursor should move to end of command
|
|
if m.CommandCursor() != len("cmd3") {
|
|
t.Errorf("CommandCursor = %d, want %d", m.CommandCursor(), len("cmd3"))
|
|
}
|
|
|
|
// History cursor should advance
|
|
if m.CommandHistoryCursor() != 1 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 1", m.CommandHistoryCursor())
|
|
}
|
|
})
|
|
|
|
t.Run("navigate up multiple times", func(t *testing.T) {
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "",
|
|
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
|
|
CommandHistoryCur: 0,
|
|
}
|
|
|
|
action := MoveCommandHistoryUp{}
|
|
|
|
// First up
|
|
action.Execute(m)
|
|
if m.Command() != "cmd3" {
|
|
t.Errorf("After 1st up: Command = %q, want 'cmd3'", m.Command())
|
|
}
|
|
|
|
// Second up
|
|
action.Execute(m)
|
|
if m.Command() != "cmd2" {
|
|
t.Errorf("After 2nd up: Command = %q, want 'cmd2'", m.Command())
|
|
}
|
|
|
|
// Third up
|
|
action.Execute(m)
|
|
if m.Command() != "cmd1" {
|
|
t.Errorf("After 3rd up: Command = %q, want 'cmd1'", m.Command())
|
|
}
|
|
|
|
if m.CommandHistoryCursor() != 3 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 3", m.CommandHistoryCursor())
|
|
}
|
|
})
|
|
|
|
t.Run("cannot navigate past end of history", func(t *testing.T) {
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "",
|
|
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
|
|
CommandHistoryCur: 3, // Already at end
|
|
}
|
|
|
|
action := MoveCommandHistoryUp{}
|
|
action.Execute(m)
|
|
|
|
// Should not change command (still showing cmd1)
|
|
if m.Command() != "" {
|
|
t.Errorf("Command = %q, want empty (should not go past end)", m.Command())
|
|
}
|
|
|
|
// Cursor should not advance
|
|
if m.CommandHistoryCursor() != 3 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 3 (should not advance)", m.CommandHistoryCursor())
|
|
}
|
|
})
|
|
|
|
t.Run("empty history does nothing", func(t *testing.T) {
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "current",
|
|
CommandHistoryList: []string{},
|
|
CommandHistoryCur: 0,
|
|
}
|
|
|
|
action := MoveCommandHistoryUp{}
|
|
action.Execute(m)
|
|
|
|
// Command should not change
|
|
if m.Command() != "current" {
|
|
t.Errorf("Command = %q, want 'current' (no history to navigate)", m.Command())
|
|
}
|
|
|
|
if m.CommandHistoryCursor() != 0 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 0", m.CommandHistoryCursor())
|
|
}
|
|
})
|
|
|
|
t.Run("command cursor moves to end", func(t *testing.T) {
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "",
|
|
CommandCursorVal: 0,
|
|
CommandHistoryList: []string{"long command here"},
|
|
CommandHistoryCur: 0,
|
|
}
|
|
|
|
action := MoveCommandHistoryUp{}
|
|
action.Execute(m)
|
|
|
|
expectedLen := len("long command here")
|
|
if m.CommandCursor() != expectedLen {
|
|
t.Errorf("CommandCursor = %d, want %d (end of command)", m.CommandCursor(), expectedLen)
|
|
}
|
|
})
|
|
}
|
|
|
|
// ==================================================
|
|
// MoveCommandHistoryDown Tests
|
|
// ==================================================
|
|
|
|
func TestMoveCommandHistoryDown(t *testing.T) {
|
|
t.Run("navigate down from middle of history", func(t *testing.T) {
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "cmd2",
|
|
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
|
|
CommandHistoryCur: 2, // At cmd2
|
|
}
|
|
|
|
action := MoveCommandHistoryDown{}
|
|
action.Execute(m)
|
|
|
|
// Should load cmd3 (more recent)
|
|
if m.Command() != "cmd3" {
|
|
t.Errorf("Command = %q, want 'cmd3'", m.Command())
|
|
}
|
|
|
|
// Cursor should be at 1
|
|
if m.CommandHistoryCursor() != 1 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 1", m.CommandHistoryCursor())
|
|
}
|
|
})
|
|
|
|
t.Run("navigate down to empty command", func(t *testing.T) {
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "cmd3",
|
|
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
|
|
CommandHistoryCur: 1, // At first command
|
|
}
|
|
|
|
action := MoveCommandHistoryDown{}
|
|
action.Execute(m)
|
|
|
|
// Should clear command (back to current input)
|
|
if m.Command() != "" {
|
|
t.Errorf("Command = %q, want empty (back to current)", m.Command())
|
|
}
|
|
|
|
if m.CommandCursor() != 0 {
|
|
t.Errorf("CommandCursor = %d, want 0", m.CommandCursor())
|
|
}
|
|
|
|
if m.CommandHistoryCursor() != 0 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 0", m.CommandHistoryCursor())
|
|
}
|
|
})
|
|
|
|
t.Run("navigate down from cursor 0 does nothing", func(t *testing.T) {
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "",
|
|
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
|
|
CommandHistoryCur: 0, // Already at bottom
|
|
}
|
|
|
|
action := MoveCommandHistoryDown{}
|
|
action.Execute(m)
|
|
|
|
// Should clear command
|
|
if m.Command() != "" {
|
|
t.Errorf("Command = %q, want empty", m.Command())
|
|
}
|
|
|
|
if m.CommandHistoryCursor() != 0 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 0", m.CommandHistoryCursor())
|
|
}
|
|
})
|
|
|
|
t.Run("command cursor moves to end", func(t *testing.T) {
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "",
|
|
CommandCursorVal: 0,
|
|
CommandHistoryList: []string{"cmd3", "long command here"},
|
|
CommandHistoryCur: 2,
|
|
}
|
|
|
|
action := MoveCommandHistoryDown{}
|
|
action.Execute(m)
|
|
|
|
expectedLen := len("cmd3")
|
|
if m.CommandCursor() != expectedLen {
|
|
t.Errorf("CommandCursor = %d, want %d (end of command)", m.CommandCursor(), expectedLen)
|
|
}
|
|
})
|
|
}
|
|
|
|
// ==================================================
|
|
// Integration Tests
|
|
// ==================================================
|
|
|
|
func TestCommandHistoryIntegration(t *testing.T) {
|
|
t.Run("up down up sequence", func(t *testing.T) {
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "",
|
|
CommandHistoryList: []string{"cmd3", "cmd2", "cmd1"},
|
|
CommandHistoryCur: 0,
|
|
}
|
|
|
|
upAction := MoveCommandHistoryUp{}
|
|
downAction := MoveCommandHistoryDown{}
|
|
|
|
// Up twice
|
|
upAction.Execute(m) // cursor=1, cmd=cmd3
|
|
upAction.Execute(m) // cursor=2, cmd=cmd2
|
|
|
|
// Down once
|
|
downAction.Execute(m) // cursor=1, cmd=cmd3
|
|
|
|
// Up again
|
|
upAction.Execute(m) // cursor=2, cmd=cmd2
|
|
|
|
if m.Command() != "cmd2" {
|
|
t.Errorf("Command = %q, want 'cmd2'", m.Command())
|
|
}
|
|
if m.CommandHistoryCursor() != 2 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 2", m.CommandHistoryCursor())
|
|
}
|
|
})
|
|
|
|
t.Run("navigate up past end does not crash", func(t *testing.T) {
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "",
|
|
CommandHistoryList: []string{"cmd1", "cmd2"},
|
|
CommandHistoryCur: 0,
|
|
}
|
|
|
|
upAction := MoveCommandHistoryUp{}
|
|
|
|
// Navigate to end
|
|
upAction.Execute(m) // cursor = 1, cmd = cmd1
|
|
upAction.Execute(m) // cursor = 2, cmd = cmd2
|
|
|
|
// Try to go past end
|
|
upAction.Execute(m) // Should do nothing
|
|
|
|
if m.CommandHistoryCursor() != 2 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 2 (should stay at end)", m.CommandHistoryCursor())
|
|
}
|
|
})
|
|
|
|
t.Run("navigate down past bottom does not crash", func(t *testing.T) {
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "",
|
|
CommandHistoryList: []string{"cmd1"},
|
|
CommandHistoryCur: 0,
|
|
}
|
|
|
|
downAction := MoveCommandHistoryDown{}
|
|
|
|
// Try to go down from bottom
|
|
downAction.Execute(m)
|
|
|
|
if m.CommandHistoryCursor() != 0 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 0 (should stay at bottom)", m.CommandHistoryCursor())
|
|
}
|
|
|
|
// Try again
|
|
downAction.Execute(m)
|
|
|
|
if m.CommandHistoryCursor() != 0 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 0 (should stay at bottom)", m.CommandHistoryCursor())
|
|
}
|
|
})
|
|
}
|
|
|
|
// ==================================================
|
|
// Long History Tests
|
|
// ==================================================
|
|
|
|
func TestCommandHistoryWithLongHistory(t *testing.T) {
|
|
t.Run("navigate through 20 commands", func(t *testing.T) {
|
|
history := make([]string, 20)
|
|
for i := range 20 {
|
|
history[i] = string(rune('A' + i))
|
|
}
|
|
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "",
|
|
CommandHistoryList: history,
|
|
CommandHistoryCur: 0,
|
|
}
|
|
|
|
upAction := MoveCommandHistoryUp{}
|
|
|
|
// Navigate to 10th command
|
|
for range 10 {
|
|
upAction.Execute(m)
|
|
}
|
|
|
|
want := string(rune('A' + 9))
|
|
if m.Command() != want {
|
|
t.Errorf("Command after 10 ups = %q, want %q", m.Command(), want)
|
|
}
|
|
if m.CommandHistoryCursor() != 10 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 10", m.CommandHistoryCursor())
|
|
}
|
|
})
|
|
|
|
t.Run("navigate to very end of long history", func(t *testing.T) {
|
|
history := make([]string, 50)
|
|
for i := range 50 {
|
|
history[i] = string(rune('0' + (i % 10)))
|
|
}
|
|
|
|
m := &action.MockModel{
|
|
ModeVal: core.CommandMode,
|
|
CommandVal: "",
|
|
CommandHistoryList: history,
|
|
CommandHistoryCur: 0,
|
|
}
|
|
|
|
upAction := MoveCommandHistoryUp{}
|
|
|
|
// Navigate all the way to the end
|
|
for range 100 { // Try to go past end
|
|
upAction.Execute(m)
|
|
}
|
|
|
|
// Should be at position 50 (end of 50-item array)
|
|
if m.CommandHistoryCursor() != 50 {
|
|
t.Errorf("CommandHistoryCursor = %d, want 50 (at end)", m.CommandHistoryCursor())
|
|
}
|
|
|
|
// Should be showing last command
|
|
want := string(rune('0' + 49%10))
|
|
if m.Command() != want {
|
|
t.Errorf("Command = %q, want %q (last in history)", m.Command(), want)
|
|
}
|
|
})
|
|
}
|