172 lines
4.7 KiB
Go
172 lines
4.7 KiB
Go
package editor
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"git.gophernest.net/azpect/TextEditor/internal/core"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/charmbracelet/x/exp/teatest"
|
|
)
|
|
|
|
// NOTE: Lots of this actually sucks ass, but it works for now...
|
|
// TODO: Refactor this to implement the builder pattern
|
|
|
|
// sendKeys sends a sequence of keys to the test model
|
|
func sendKeys(tm *teatest.TestModel, keys ...string) {
|
|
for _, key := range keys {
|
|
switch key {
|
|
case "esc":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyEscape})
|
|
case "enter":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyEnter})
|
|
case "backspace":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyBackspace})
|
|
case "ctrl+c":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlC})
|
|
case "ctrl+d":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlD})
|
|
case "ctrl+u":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlU})
|
|
case "ctrl+v":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlV})
|
|
case "ctrl+r":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlR})
|
|
case "ctrl+w":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlW})
|
|
case "tab":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyTab})
|
|
case "left":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyLeft})
|
|
case "right":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyRight})
|
|
case "up":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyUp})
|
|
case "down":
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyDown})
|
|
default:
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune(key)})
|
|
}
|
|
}
|
|
}
|
|
|
|
// sendKeyString is a convenience function for sending many keys.
|
|
func sendKeyString(tm *teatest.TestModel, keyString string) {
|
|
for _, key := range keyString {
|
|
sendKeys(tm, string(key))
|
|
}
|
|
}
|
|
|
|
// TestModelOption is a functional option for configuring test models
|
|
type TestModelOption func(*testModelConfig)
|
|
|
|
type testModelConfig struct {
|
|
lines []string
|
|
pos core.Position
|
|
width int
|
|
height int
|
|
regName rune
|
|
regType core.RegisterType
|
|
regContent []string
|
|
}
|
|
|
|
// WithLines sets the initial buffer lines
|
|
func WithLines(lines []string) TestModelOption {
|
|
return func(c *testModelConfig) {
|
|
c.lines = lines
|
|
}
|
|
}
|
|
|
|
// WithCursorPos sets the initial cursor position
|
|
func WithCursorPos(pos core.Position) TestModelOption {
|
|
return func(c *testModelConfig) {
|
|
c.pos = pos
|
|
}
|
|
}
|
|
|
|
// WithTermSize sets the terminal dimensions
|
|
func WithTermSize(width, height int) TestModelOption {
|
|
return func(c *testModelConfig) {
|
|
c.width = width
|
|
c.height = height
|
|
}
|
|
}
|
|
|
|
// WithRegister sets a register's content
|
|
func WithRegister(name rune, regType core.RegisterType, content []string) TestModelOption {
|
|
return func(c *testModelConfig) {
|
|
c.regName = name
|
|
c.regType = regType
|
|
c.regContent = content
|
|
}
|
|
}
|
|
|
|
// newTestModel creates a test model with optional configuration
|
|
func newTestModel(t *testing.T, opts ...TestModelOption) *teatest.TestModel {
|
|
// Default configuration
|
|
cfg := testModelConfig{
|
|
lines: []string{"line 1", "line 2", "line 3", "line 4", "line 5", "line 6"},
|
|
pos: core.Position{Col: 0, Line: 0},
|
|
width: 80,
|
|
height: 24,
|
|
}
|
|
|
|
// Apply options
|
|
for _, opt := range opts {
|
|
opt(&cfg)
|
|
}
|
|
|
|
buf := core.NewBufferBuilder().
|
|
WithLines(cfg.lines).
|
|
Build()
|
|
|
|
win := core.NewWindowBuilder().
|
|
WithBuffer(&buf).
|
|
WithCursorPos(cfg.pos.Line, cfg.pos.Col).
|
|
WithDimensions(cfg.width, cfg.height).
|
|
Build()
|
|
|
|
// Create model with default registers
|
|
m := NewModelBuilder().
|
|
AddBuffer(&buf).
|
|
AddWindow(&win).
|
|
WithActiveWindowId(win.Id).
|
|
WithTermSize(cfg.width, cfg.height).
|
|
Build()
|
|
|
|
// Set register if provided (must be done AFTER Build because SetRegister
|
|
// requires the register to already exist in the default register map)
|
|
if cfg.regContent != nil {
|
|
err := m.SetRegister(cfg.regName, cfg.regType, cfg.regContent)
|
|
if err != nil {
|
|
t.Fatalf("Failed to set register %c: %v", cfg.regName, err)
|
|
}
|
|
}
|
|
|
|
return teatest.NewTestModel(t, m, teatest.WithInitialTermSize(cfg.width, cfg.height))
|
|
}
|
|
|
|
// Convenience functions for backward compatibility
|
|
func newTestModelWithLines(t *testing.T, lines []string) *teatest.TestModel {
|
|
return newTestModel(t, WithLines(lines))
|
|
}
|
|
|
|
func newTestModelWithCursorPos(t *testing.T, pos core.Position) *teatest.TestModel {
|
|
return newTestModel(t, WithCursorPos(pos))
|
|
}
|
|
|
|
func newTestModelWithLinesAndCursorPos(t *testing.T, lines []string, pos core.Position) *teatest.TestModel {
|
|
return newTestModel(t, WithLines(lines), WithCursorPos(pos))
|
|
}
|
|
|
|
func newTestModelWithTermSize(t *testing.T, lines []string, pos core.Position, width, height int) *teatest.TestModel {
|
|
return newTestModel(t, WithLines(lines), WithCursorPos(pos), WithTermSize(width, height))
|
|
}
|
|
|
|
// getFinalModel extracts the final model state (sends ctrl+c to quit first)
|
|
func getFinalModel(t *testing.T, tm *teatest.TestModel) *Model {
|
|
tm.Send(tea.KeyMsg{Type: tea.KeyCtrlC})
|
|
fm := tm.FinalModel(t, teatest.WithFinalTimeout(time.Second))
|
|
return fm.(*Model)
|
|
}
|