| 1 | package agent |
| 2 | |
| 3 | import ( |
| 4 | "strings" |
| 5 | "testing" |
| 6 | ) |
| 7 | |
| 8 | // TestStreamedRowsBasic covers the four cursor-position cases the markdown |
| 9 | // redraw uses to decide how far up to move before clearing. |
| 10 | func TestStreamedRowsBasic(t *testing.T) { |
| 11 | cases := []struct { |
| 12 | name string |
| 13 | input string |
| 14 | width int |
| 15 | want int |
| 16 | }{ |
| 17 | {"no newline fits", "hello", 80, 0}, |
| 18 | {"newline only", "hello\n", 80, 1}, |
| 19 | {"two lines no trailing", "hello\nworld", 80, 1}, |
| 20 | {"two lines trailing", "hello\nworld\n", 80, 2}, |
| 21 | {"empty", "", 80, 0}, |
| 22 | {"single wrap", "abcdefghij", 5, 1}, // 10 cols / width 5 → 1 wrap |
| 23 | {"two wraps no nl", "abcdefghijklmno", 5, 2}, |
| 24 | {"line exactly width", strings.Repeat("a", 80), 80, 0}, // lazy wrap, no extra row |
| 25 | } |
| 26 | for _, tc := range cases { |
| 27 | t.Run(tc.name, func(t *testing.T) { |
| 28 | if got := streamedRows(tc.input, tc.width); got != tc.want { |
| 29 | t.Errorf("streamedRows(%q, %d) = %d, want %d", tc.input, tc.width, got, tc.want) |
| 30 | } |
| 31 | }) |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | // TestStreamedRowsCJK proves CJK doubles the column footprint so a Chinese |
| 36 | // line wraps at half the column count. |
| 37 | func TestStreamedRowsCJK(t *testing.T) { |
| 38 | in := strings.Repeat("中", 6) // 12 cols at width 10 → 1 wrap |
| 39 | if got := streamedRows(in, 10); got != 1 { |
| 40 | t.Errorf("streamedRows(6×中, 10) = %d, want 1", got) |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | // TestStreamedRowsIgnoresAnsi: ANSI SGR codes must not inflate the row count. |
| 45 | func TestStreamedRowsIgnoresAnsi(t *testing.T) { |
| 46 | in := "\x1b[1mhello\x1b[0m world" |
| 47 | if got := streamedRows(in, 80); got != 0 { |
| 48 | t.Errorf("ANSI in 11-char line at width 80 should be 0 rows, got %d", got) |
| 49 | } |
| 50 | } |
| 51 |