mirror of
https://github.com/zoriya/bubbles.git
synced 2026-08-05 04:36:07 +00:00
Improve textarea tests (#490)
This commit is contained in:
+268
-187
@@ -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 != expected {
|
||||
t.Fatalf("Expected:\n%v\nGot:\n%v\n", expected, view)
|
||||
if view != wantView {
|
||||
t.Fatalf("Want:\n%v\nGot:\n%v\n", wantView, 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")
|
||||
|
||||
Reference in New Issue
Block a user