package lipgloss import ( "testing" "time" "github.com/charmbracelet/x/ansi" ) func TestWhitespaceRenderWithTab(t *testing.T) { // This test verifies that rendering whitespace with tab characters // doesn't cause an infinite loop (issue #108) done := make(chan bool, 1) go func() { ws := newWhitespace(WithWhitespaceChars("\t")) _ = ws.render(10) done <- true }() select { case <-done: // Success - render completed case <-time.After(2 * time.Second): t.Fatal("whitespace.render() with tab character caused infinite loop") } } func TestWhitespaceRenderWithZeroWidthChar(t *testing.T) { // Test with zero-width joiner (another zero-width character) done := make(chan bool, 1) go func() { ws := newWhitespace(WithWhitespaceChars("\u200d")) // zero-width joiner _ = ws.render(5) done <- true }() select { case <-done: // Success case <-time.After(2 * time.Second): t.Fatal("whitespace.render() with zero-width character caused infinite loop") } } func TestWhitespaceRenderNormal(t *testing.T) { // Verify normal behavior still works ws := newWhitespace(WithWhitespaceChars("*")) result := ws.render(5) if len(result) != 5 { t.Errorf("expected 5 characters, got %d", len(result)) } } func TestWhitespaceRenderWideRune(t *testing.T) { // A whitespace char that is more than one cell wide must never render // wider than the requested width; the trailing cells are padded with // spaces instead of overflowing. ws := newWhitespace(WithWhitespaceChars("東")) // width 2 for _, width := range []int{0, 1, 2, 3, 4, 5} { got := ansi.StringWidth(ws.render(width)) if got != width { t.Errorf("render(%d): expected rendered width %d, got %d", width, width, got) } } } func TestPlaceHorizontalWideWhitespace(t *testing.T) { // Placing content in a box with a wide whitespace char must keep the // box exactly the requested width. got := PlaceHorizontal(5, Left, "ab", WithWhitespaceChars("東")) if w := ansi.StringWidth(got); w != 5 { t.Errorf("PlaceHorizontal width: expected 5, got %d (%q)", w, got) } }