1
0
mirror of https://github.com/jesseduffield/lazygit.git synced 2025-04-04 22:34:39 +02:00
lazygit/pkg/utils/lines_test.go

464 lines
9.5 KiB
Go

package utils
import (
"bufio"
"strings"
"testing"
"github.com/jesseduffield/gocui"
"github.com/stretchr/testify/assert"
)
// TestSplitLines is a function.
func TestSplitLines(t *testing.T) {
type scenario struct {
multilineString string
expected []string
}
scenarios := []scenario{
{
"",
[]string{},
},
{
"\n",
[]string{},
},
{
"hello world !\nhello universe !\n",
[]string{
"hello world !",
"hello universe !",
},
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, SplitLines(s.multilineString))
}
}
func TestSplitNul(t *testing.T) {
type scenario struct {
multilineString string
expected []string
}
scenarios := []scenario{
{
"",
[]string{},
},
{
"\x00",
[]string{
"",
},
},
{
"hello world !\x00hello universe !\x00",
[]string{
"hello world !",
"hello universe !",
},
},
}
for _, s := range scenarios {
assert.EqualValues(t, s.expected, SplitNul(s.multilineString))
}
}
// TestNormalizeLinefeeds is a function.
func TestNormalizeLinefeeds(t *testing.T) {
type scenario struct {
byteArray []byte
expected []byte
}
scenarios := []scenario{
{
// \r\n
[]byte{97, 115, 100, 102, 13, 10},
[]byte{97, 115, 100, 102, 10},
},
{
// bash\r\nblah
[]byte{97, 115, 100, 102, 13, 10, 97, 115, 100, 102},
[]byte{97, 115, 100, 102, 10, 97, 115, 100, 102},
},
{
// \r
[]byte{97, 115, 100, 102, 13},
[]byte{97, 115, 100, 102},
},
{
// \n
[]byte{97, 115, 100, 102, 10},
[]byte{97, 115, 100, 102, 10},
},
}
for _, s := range scenarios {
assert.EqualValues(t, string(s.expected), NormalizeLinefeeds(string(s.byteArray)))
}
}
func TestScanLinesAndTruncateWhenLongerThanBuffer(t *testing.T) {
type scenario struct {
input string
expectedLines []string
}
scenarios := []scenario{
{
"",
[]string{},
},
{
"\n",
[]string{""},
},
{
"abc",
[]string{"abc"},
},
{
"abc\ndef",
[]string{"abc", "def"},
},
{
"abc\n\ndef",
[]string{"abc", "", "def"},
},
{
"abc\r\ndef\r",
[]string{"abc", "def"},
},
{
"abcdef",
[]string{"abcde"},
},
{
"abcdef\n",
[]string{"abcde"},
},
{
"abcdef\nghijkl\nx",
[]string{"abcde", "ghijk", "x"},
},
{
"abc\ndefghijklmnopqrstuvw\nx",
[]string{"abc", "defgh", "x"},
},
}
for _, s := range scenarios {
scanner := bufio.NewScanner(strings.NewReader(s.input))
scanner.Buffer(make([]byte, 5), 5)
scanner.Split(ScanLinesAndTruncateWhenLongerThanBuffer(5))
result := []string{}
for scanner.Scan() {
result = append(result, scanner.Text())
}
assert.NoError(t, scanner.Err())
assert.EqualValues(t, s.expectedLines, result)
}
}
func TestWrapViewLinesToWidth(t *testing.T) {
tests := []struct {
name string
wrap bool
editable bool
text string
width int
tabWidth int
expectedWrappedLines []string
expectedWrappedLinesIndices []int
expectedOriginalLinesIndices []int
}{
{
name: "Wrap off",
wrap: false,
text: "1st line\n2nd line\n3rd line",
width: 5,
expectedWrappedLines: []string{
"1st line",
"2nd line",
"3rd line",
},
expectedWrappedLinesIndices: []int{0, 1, 2},
expectedOriginalLinesIndices: []int{0, 1, 2},
},
{
name: "Wrap on space",
wrap: true,
text: "Hello World",
width: 5,
expectedWrappedLines: []string{
"Hello",
"World",
},
expectedWrappedLinesIndices: []int{0},
expectedOriginalLinesIndices: []int{0, 0},
},
{
name: "Wrap on hyphen",
wrap: true,
text: "Hello-World",
width: 6,
expectedWrappedLines: []string{
"Hello-",
"World",
},
},
{
name: "Wrap on hyphen 2",
wrap: true,
text: "Blah Hello-World",
width: 12,
expectedWrappedLines: []string{
"Blah Hello-",
"World",
},
},
{
name: "Wrap on hyphen 3",
wrap: true,
text: "Blah Hello-World",
width: 11,
expectedWrappedLines: []string{
"Blah Hello-",
"World",
},
},
{
name: "Wrap on hyphen 4",
wrap: true,
text: "Blah Hello-World",
width: 10,
expectedWrappedLines: []string{
"Blah Hello",
"-World",
},
},
{
name: "Wrap on space 2",
wrap: true,
text: "Blah Hello World",
width: 10,
expectedWrappedLines: []string{
"Blah Hello",
"World",
},
},
{
name: "Wrap on space with more words",
wrap: true,
text: "Longer word here",
width: 10,
expectedWrappedLines: []string{
"Longer",
"word here",
},
},
{
name: "Split word that's too long",
wrap: true,
text: "ThisWordIsWayTooLong",
width: 10,
expectedWrappedLines: []string{
"ThisWordIs",
"WayTooLong",
},
},
{
name: "Split word that's too long over multiple lines",
wrap: true,
text: "ThisWordIsWayTooLong",
width: 5,
expectedWrappedLines: []string{
"ThisW",
"ordIs",
"WayTo",
"oLong",
},
},
{
name: "Lots of hyphens",
wrap: true,
text: "one-two-three-four-five",
width: 8,
expectedWrappedLines: []string{
"one-two-",
"three-",
"four-",
"five",
},
},
{
name: "Several lines using all the available width",
wrap: true,
text: "aaa bb cc ddd-ee ff",
width: 5,
expectedWrappedLines: []string{
"aaa",
"bb cc",
"ddd-",
"ee ff",
},
},
{
name: "Several lines using all the available width, with multi-cell runes",
wrap: true,
text: "🐤🐤🐤 🐝🐝 🙉🙉 🦊🦊🦊-🐬🐬 🦢🦢",
width: 9,
expectedWrappedLines: []string{
"🐤🐤🐤",
"🐝🐝 🙉🙉",
"🦊🦊🦊-",
"🐬🐬 🦢🦢",
},
},
{
name: "Space in last column",
wrap: true,
text: "hello world",
width: 6,
expectedWrappedLines: []string{
"hello",
"world",
},
},
{
name: "Hyphen in last column",
wrap: true,
text: "hello-world",
width: 6,
expectedWrappedLines: []string{
"hello-",
"world",
},
},
{
name: "English text",
wrap: true,
text: "+The sea reach of the Thames stretched before us like the bedinnind of an interminable waterway. In the offind the sea and the sky were welded todether without a joint, and in the luminous space the tanned sails of the bardes drifting blah blah",
width: 81,
expectedWrappedLines: []string{
"+The sea reach of the Thames stretched before us like the bedinnind of an",
"interminable waterway. In the offind the sea and the sky were welded todether",
"without a joint, and in the luminous space the tanned sails of the bardes",
"drifting blah blah",
},
},
{
name: "Tabs, width 4",
wrap: true,
text: "\ta\tbb\tccc\tdddd\teeeee",
width: 50,
tabWidth: 4,
expectedWrappedLines: []string{
" a bb ccc dddd eeeee",
},
},
{
name: "Tabs, width 8",
wrap: true,
text: "\ta\tbb\tccc\tdddddddd\teeeee",
width: 100,
tabWidth: 8,
expectedWrappedLines: []string{
" a bb ccc dddddddd eeeee",
},
},
{
name: "Multiple lines",
wrap: true,
text: "First paragraph\nThe second paragraph is a bit longer.\nThird paragraph\n",
width: 10,
expectedWrappedLines: []string{
"First",
"paragraph",
"The second",
"paragraph",
"is a bit",
"longer.",
"Third",
"paragraph",
},
expectedWrappedLinesIndices: []int{0, 2, 6},
expectedOriginalLinesIndices: []int{0, 0, 1, 1, 1, 1, 2, 2},
},
{
name: "Avoid blank line at end if not editable",
wrap: true,
editable: false,
text: "First\nSecond\nThird\n",
width: 10,
expectedWrappedLines: []string{
"First",
"Second",
"Third",
},
expectedWrappedLinesIndices: []int{0, 1, 2},
expectedOriginalLinesIndices: []int{0, 1, 2},
},
{
name: "Avoid blank line at end if not editable",
wrap: true,
editable: false,
text: "First\nSecond\nThird\n",
width: 10,
expectedWrappedLines: []string{
"First",
"Second",
"Third",
},
expectedWrappedLinesIndices: []int{0, 1, 2},
expectedOriginalLinesIndices: []int{0, 1, 2},
},
{
name: "Keep blank line at end if editable",
wrap: true,
editable: true,
text: "First\nSecond\nThird\n",
width: 10,
expectedWrappedLines: []string{
"First",
"Second",
"Third",
"",
},
expectedWrappedLinesIndices: []int{0, 1, 2, 3},
expectedOriginalLinesIndices: []int{0, 1, 2, 3},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tabWidth := tt.tabWidth
if tabWidth == 0 {
tabWidth = 4
}
wrappedLines, wrappedLinesIndices, originalLinesIndices := WrapViewLinesToWidth(tt.wrap, tt.editable, tt.text, tt.width, tabWidth)
assert.Equal(t, tt.expectedWrappedLines, wrappedLines)
if tt.expectedWrappedLinesIndices != nil {
assert.Equal(t, tt.expectedWrappedLinesIndices, wrappedLinesIndices)
}
if tt.expectedOriginalLinesIndices != nil {
assert.Equal(t, tt.expectedOriginalLinesIndices, originalLinesIndices)
}
// As a sanity check, also test that gocui's line wrapping behaves the same way
view := gocui.NewView("", 0, 0, tt.width+1, 1000, gocui.OutputNormal)
view.TabWidth = tabWidth
assert.Equal(t, tt.width, view.InnerWidth())
view.Wrap = tt.wrap
view.Editable = tt.editable
view.SetContent(tt.text)
assert.Equal(t, wrappedLines, view.ViewBufferLines())
})
}
}