1package modes
2
3import "testing"
4
5func TestAnchorScrollOffsetKeepsTopVisibleRow(t *testing.T) {
6 // The user is scrolled up. The top visible row of the chat window is
7 // start = chatLen - offset - chatRows. After any redraw that grows
8 // the buffer and/or changes the viewport height, the SAME top row
9 // must remain visible.
10 cases := []struct {
11 name string
12 offset, prevLen, newLen, prevRows, newRows int
13 }{
14 {"agent appends streamed lines", 20, 200, 208, 30, 30},
15 {"bottom band grows (chatRows shrinks)", 20, 200, 200, 30, 26},
16 {"streamed text plus growing status band", 20, 200, 205, 30, 27},
17 {"buffer shrinks when streaming block finalises", 20, 200, 196, 30, 30},
18 {"both grow", 5, 100, 140, 20, 24},
19 }
20 for _, c := range cases {
21 t.Run(c.name, func(t *testing.T) {
22 startBefore := c.prevLen - c.offset - c.prevRows
23 got := anchorScrollOffset(c.offset, c.prevLen, c.newLen, c.prevRows, c.newRows)
24 startAfter := c.newLen - got - c.newRows
25 if startAfter != startBefore {
26 t.Fatalf("top row drifted: before=%d after=%d (offset %d->%d)",
27 startBefore, startAfter, c.offset, got)
28 }
29 })
30 }
31}
32
33func TestAnchorScrollOffsetClampsToZero(t *testing.T) {
34 // A large negative adjustment (buffer shrank a lot) must clamp at 0
35 // rather than going negative.
36 if got := anchorScrollOffset(3, 200, 100, 30, 30); got != 0 {
37 t.Fatalf("offset = %d; want 0", got)
38 }
39}
40
41func TestAnchorScrollOffsetClampsToLen(t *testing.T) {
42 if got := anchorScrollOffset(5, 10, 20, 100, 8); got > 20 {
43 t.Fatalf("offset = %d; want <= newLen 20", got)
44 }
45}