1package modes
2
3import (
4 "strings"
5 "testing"
6
7 "github.com/bnema/zut/packages/tui"
8)
9
10func TestFloatingOverlayTitleFallsBackToOverlayIdentity(t *testing.T) {
11 if got := floatingOverlayTitle("subagent-session"); got != "subagent session" {
12 t.Fatalf("overlay title = %q, want subagent session", got)
13 }
14}
15
16func TestFloatingDialogBodyMovesFrameTitleIntoPaneBorder(t *testing.T) {
17 lines := padDialogFrame([]string{frameHeader(tui.Dark, "settings", 40), "body", frameRule(tui.Dark, 40)})
18 title, body, removed := floatingDialogBody(lines)
19 if title != "settings" || removed != 1 {
20 t.Fatalf("chrome extraction = title:%q removed:%d, want settings/1", title, removed)
21 }
22 if strings.Join(body, "\n") != "\nbody\n" {
23 t.Fatalf("dialog body retained frame chrome: %q", body)
24 }
25}
26
27func TestFloatingBackgroundFramePreservesTopAnchoredViewport(t *testing.T) {
28 tests := []struct {
29 name string
30 chat []string
31 bottom []string
32 rows int
33 want []string
34 }{
35 {
36 name: "session stays at top",
37 chat: []string{"chat"},
38 bottom: []string{"status"},
39 rows: 5,
40 want: []string{"chat", "status", "", "", ""},
41 },
42 {
43 name: "oversized bottom retains newest rows",
44 chat: []string{"chat"},
45 bottom: []string{"old", "newer", "newest"},
46 rows: 3,
47 want: []string{"newer", "newest", ""},
48 },
49 }
50 for _, tt := range tests {
51 t.Run(tt.name, func(t *testing.T) {
52 got := floatingBackgroundFrame(tt.chat, tt.bottom, tt.rows)
53 if strings.Join(got, "\n") != strings.Join(tt.want, "\n") {
54 t.Fatalf("floating background = %q, want %q", got, tt.want)
55 }
56 })
57 }
58}
59
60func TestSettingsDialogDimsBackdropAndMainCursor(t *testing.T) {
61 term := &alertTestTerminal{}
62 i := NewInteractive(InteractiveConfig{Terminal: term, Theme: tui.Dark})
63 i.rend.Resize(80, 24)
64 i.ed.SetValue("draft")
65 if !i.settingsDialog.Open([]settingsItem{{key: "test", label: "test setting"}}) {
66 t.Fatal("settings dialog did not open")
67 }
68
69 i.redraw()
70 got := term.String()
71
72 input := i.cfg.Theme.AccentBar(i.cfg.Theme.Accent) + "draft"
73 if !strings.Contains(got, tui.Dim(input)) {
74 t.Fatalf("settings dialog left the main input undimmed: %q", got)
75 }
76 pane := tui.FloatingPaneMaxRect(80, 24)
77 innerHeader := frameHeader(i.cfg.Theme, "settings", pane.ContentWidth())
78 if strings.Contains(got, innerHeader) {
79 t.Fatalf("settings retained its inner frame header: %q", got)
80 }
81 if !strings.Contains(got, " settings ") {
82 t.Fatalf("settings title was not rendered in the floating border: %q", got)
83 }
84 if strings.Contains(got, tui.Dim(" settings ")) {
85 t.Fatalf("floating pane title was dimmed: %q", got)
86 }
87 // The pane shrinks to its chrome-free content and remains centered.
88 if !strings.Contains(got, tui.MoveTo(10, pane.X+1)) {
89 t.Fatalf("floating pane was not positioned at its computed geometry: %q", got)
90 }
91 cursor := tui.CursorColor(i.cfg.Theme.DimColor(tui.Color256(15), modalBackdropDimPercent))
92 if !strings.Contains(got, cursor) {
93 t.Fatalf("settings dialog left the main cursor bright; missing %q in %q", cursor, got)
94 }
95
96 term.mu.Lock()
97 term.data = nil
98 term.mu.Unlock()
99 i.settingsDialog.Close()
100 i.redraw()
101 if got := term.String(); !strings.Contains(got, tui.CursorColor(tui.Color256(15))) {
102 t.Fatalf("closing settings did not restore the main cursor color: %q", got)
103 }
104}