diff --git a/textarea/textarea_test.go b/textarea/textarea_test.go index d37a74db..e02ecf8f 100644 --- a/textarea/textarea_test.go +++ b/textarea/textarea_test.go @@ -10,101 +10,6 @@ import ( tea "github.com/charmbracelet/bubbletea" ) -func TestNew(t *testing.T) { - textarea := newTextArea() - view := textarea.View() - - if !strings.Contains(view, ">") { - t.Log(view) - t.Error("Text area did not render the prompt") - } - - if !strings.Contains(view, "World!") { - t.Log(view) - t.Error("Text area did not render the placeholder") - } -} - -func TestInput(t *testing.T) { - textarea := newTextArea() - - input := "foo" - - for _, k := range []rune(input) { - textarea, _ = textarea.Update(keyPress(k)) - } - - view := textarea.View() - - if !strings.Contains(view, input) { - t.Log(view) - t.Error("Text area did not render the input") - } - - if textarea.col != len(input) { - t.Log(view) - t.Error("Text area did not move the cursor to the correct position") - } -} - -func TestSoftWrap(t *testing.T) { - textarea := newTextArea() - textarea.Prompt = "" - textarea.ShowLineNumbers = false - textarea.SetWidth(5) - textarea.SetHeight(5) - textarea.CharLimit = 60 - - textarea, _ = textarea.Update(nil) - - input := "foo bar baz" - - for _, k := range []rune(input) { - textarea, _ = textarea.Update(keyPress(k)) - } - - view := textarea.View() - - for _, word := range strings.Split(input, " ") { - if !strings.Contains(view, word) { - t.Log(view) - t.Error("Text area did not render the input") - } - } - - // Due to the word wrapping, each word will be on a new line and the - // text area will look like this: - // - // > foo - // > bar - // > baz█ - // - // However, due to soft-wrapping the column will still be at the end of the line. - if textarea.row != 0 || textarea.col != len(input) { - t.Log(view) - t.Error("Text area did not move the cursor to the correct position") - } -} - -func TestCharLimit(t *testing.T) { - textarea := newTextArea() - - // First input (foo bar) should be accepted as it will fall within the - // CharLimit. Second input (baz) should not appear in the input. - input := []string{"foo bar", "baz"} - textarea.CharLimit = len(input[0]) - - for _, k := range []rune(strings.Join(input, " ")) { - textarea, _ = textarea.Update(keyPress(k)) - } - - view := textarea.View() - if strings.Contains(view, input[1]) { - t.Log(view) - t.Error("Text area should not include input past the character limit") - } -} - func TestVerticalScrolling(t *testing.T) { textarea := newTextArea() textarea.Prompt = "" @@ -420,21 +325,29 @@ func TestVerticalNavigationShouldRememberPositionWhileTraversing(t *testing.T) { } func TestView(t *testing.T) { + type want struct { + view string + cursorRow int + cursorCol int + } + tests := []struct { name string modelFunc func(Model) Model - expected string + want want }{ { name: "placeholder", - expected: heredoc.Doc(` - > 1 Hello, World! - > ~ - > ~ - > ~ - > ~ - > ~ - `), + want: want{ + view: heredoc.Doc(` + > 1 Hello, World! + > ~ + > ~ + > ~ + > ~ + > ~ + `), + }, }, { name: "single line", @@ -443,14 +356,18 @@ func TestView(t *testing.T) { return m }, - expected: heredoc.Doc(` - > 1 the first line - > ~ - > ~ - > ~ - > ~ - > ~ - `), + want: want{ + view: heredoc.Doc(` + > 1 the first line + > ~ + > ~ + > ~ + > ~ + > ~ + `), + cursorRow: 0, + cursorCol: 14, + }, }, { name: "multiple lines", @@ -459,14 +376,18 @@ func TestView(t *testing.T) { return m }, - expected: heredoc.Doc(` - > 1 the first line - > 2 the second line - > 3 the third line - > ~ - > ~ - > ~ - `), + want: want{ + view: heredoc.Doc(` + > 1 the first line + > 2 the second line + > 3 the third line + > ~ + > ~ + > ~ + `), + cursorRow: 2, + cursorCol: 14, + }, }, { name: "single line without line numbers", @@ -476,14 +397,18 @@ func TestView(t *testing.T) { return m }, - expected: heredoc.Doc(` - > the first line - > ~ - > ~ - > ~ - > ~ - > ~ - `), + want: want{ + view: heredoc.Doc(` + > the first line + > ~ + > ~ + > ~ + > ~ + > ~ + `), + cursorRow: 0, + cursorCol: 14, + }, }, { name: "multipline lines without line numbers", @@ -493,14 +418,18 @@ func TestView(t *testing.T) { return m }, - expected: heredoc.Doc(` - > the first line - > the second line - > the third line - > ~ - > ~ - > ~ - `), + want: want{ + view: heredoc.Doc(` + > the first line + > the second line + > the third line + > ~ + > ~ + > ~ + `), + cursorRow: 2, + cursorCol: 14, + }, }, { name: "single line and custom end of buffer character", @@ -510,14 +439,18 @@ func TestView(t *testing.T) { return m }, - expected: heredoc.Doc(` - > 1 the first line - > * - > * - > * - > * - > * - `), + want: want{ + view: heredoc.Doc(` + > 1 the first line + > * + > * + > * + > * + > * + `), + cursorRow: 0, + cursorCol: 14, + }, }, { name: "multiple lines and custom end of buffer character", @@ -527,14 +460,18 @@ func TestView(t *testing.T) { return m }, - expected: heredoc.Doc(` - > 1 the first line - > 2 the second line - > 3 the third line - > * - > * - > * - `), + want: want{ + view: heredoc.Doc(` + > 1 the first line + > 2 the second line + > 3 the third line + > * + > * + > * + `), + cursorRow: 2, + cursorCol: 14, + }, }, { name: "single line without line numbers and custom end of buffer character", @@ -545,14 +482,18 @@ func TestView(t *testing.T) { return m }, - expected: heredoc.Doc(` - > the first line - > * - > * - > * - > * - > * - `), + want: want{ + view: heredoc.Doc(` + > the first line + > * + > * + > * + > * + > * + `), + cursorRow: 0, + cursorCol: 14, + }, }, { name: "multiple lines without line numbers and custom end of buffer character", @@ -563,14 +504,18 @@ func TestView(t *testing.T) { return m }, - expected: heredoc.Doc(` - > the first line - > the second line - > the third line - > * - > * - > * - `), + want: want{ + view: heredoc.Doc(` + > the first line + > the second line + > the third line + > * + > * + > * + `), + cursorRow: 2, + cursorCol: 14, + }, }, { name: "single line and custom prompt", @@ -580,14 +525,18 @@ func TestView(t *testing.T) { return m }, - expected: heredoc.Doc(` - * 1 the first line - * ~ - * ~ - * ~ - * ~ - * ~ - `), + want: want{ + view: heredoc.Doc(` + * 1 the first line + * ~ + * ~ + * ~ + * ~ + * ~ + `), + cursorRow: 0, + cursorCol: 14, + }, }, { name: "multiple lines and custom prompt", @@ -597,14 +546,131 @@ func TestView(t *testing.T) { return m }, - expected: heredoc.Doc(` - * 1 the first line - * 2 the second line - * 3 the third line - * ~ - * ~ - * ~ - `), + want: want{ + view: heredoc.Doc(` + * 1 the first line + * 2 the second line + * 3 the third line + * ~ + * ~ + * ~ + `), + cursorRow: 2, + cursorCol: 14, + }, + }, + { + name: "type single line", + modelFunc: func(m Model) Model { + input := "foo" + m = sendString(m, input) + + return m + }, + want: want{ + view: heredoc.Doc(` + > 1 foo + > ~ + > ~ + > ~ + > ~ + > ~ + `), + cursorRow: 0, + cursorCol: 3, + }, + }, + { + name: "type multiple lines", + modelFunc: func(m Model) Model { + input := "foo\nbar\nbaz" + m = sendString(m, input) + + return m + }, + want: want{ + view: heredoc.Doc(` + > 1 foo + > 2 bar + > 3 baz + > ~ + > ~ + > ~ + `), + cursorRow: 2, + cursorCol: 3, + }, + }, + { + name: "softwrap", + modelFunc: func(m Model) Model { + m.ShowLineNumbers = false + m.Prompt = "" + m.SetWidth(5) + + input := "foo bar baz" + m = sendString(m, input) + + return m + }, + want: want{ + view: heredoc.Doc(` + foo + bar + baz + ~ + ~ + ~ + `), + cursorRow: 2, + cursorCol: 3, + }, + }, + { + name: "single line character limit", + modelFunc: func(m Model) Model { + m.CharLimit = 7 + + input := "foo bar baz" + m = sendString(m, input) + + return m + }, + want: want{ + view: heredoc.Doc(` + > 1 foo bar + > ~ + > ~ + > ~ + > ~ + > ~ + `), + cursorRow: 0, + cursorCol: 7, + }, + }, + { + name: "multiple lines character limit", + modelFunc: func(m Model) Model { + m.CharLimit = 19 + + input := "foo bar baz\nfoo bar baz" + m = sendString(m, input) + + return m + }, + want: want{ + view: heredoc.Doc(` + > 1 foo bar baz + > 2 foo bar + > ~ + > ~ + > ~ + > ~ + `), + cursorRow: 1, + cursorCol: 7, + }, }, } @@ -617,10 +683,17 @@ func TestView(t *testing.T) { } view := stripString(textarea.View()) - expected := stripString(tt.expected) + wantView := stripString(tt.want.view) + + if view != wantView { + t.Fatalf("Want:\n%v\nGot:\n%v\n", wantView, view) + } - if view != expected { - t.Fatalf("Expected:\n%v\nGot:\n%v\n", expected, view) + cursorRow := textarea.cursorLineNumber() + cursorCol := textarea.LineInfo().ColumnOffset + if tt.want.cursorRow != cursorRow || tt.want.cursorCol != cursorCol { + format := "Want cursor at row: %v, col: %v Got: row: %v col: %v\n" + t.Fatalf(format, tt.want.cursorRow, tt.want.cursorCol, cursorRow, cursorCol) } }) } @@ -643,6 +716,14 @@ func keyPress(key rune) tea.Msg { return tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{key}, Alt: false} } +func sendString(m Model, str string) Model { + for _, k := range []rune(str) { + m, _ = m.Update(keyPress(k)) + } + + return m +} + func stripString(str string) string { s := stripansi.Strip(str) ss := strings.Split(s, "\n")