1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
|
package export
import (
"errors"
"strings"
"testing"
tea "charm.land/bubbletea/v2"
)
// TestViewInvisibleReturnsEmpty verifies that View returns an empty string when
// the modal is not visible.
func TestViewInvisibleReturnsEmpty(t *testing.T) {
m := NewModel()
if out := m.View(80, 24); out != "" {
t.Fatalf("expected empty view when invisible, got %q", out)
}
}
// TestViewVisibleContainsOptionLabels verifies that View renders option labels
// while the modal is open.
func TestViewVisibleContainsOptionLabels(t *testing.T) {
m := NewModel().Open()
out := m.View(80, 24)
for _, label := range optionLabels {
if !strings.Contains(out, label) {
t.Fatalf("expected option label %q in view, got:\n%s", label, out)
}
}
if !strings.Contains(out, "Enter confirm") {
t.Fatalf("expected help text in view, got:\n%s", out)
}
}
// TestViewZeroDimensionsUsesDefaults verifies that zero width/height fall back
// to defaults without panicking.
func TestViewZeroDimensionsUsesDefaults(t *testing.T) {
m := NewModel().Open()
out := m.View(0, 0)
if out == "" {
t.Fatal("expected non-empty view with zero dimensions")
}
}
// TestViewNarrowWidthClamped verifies that very narrow widths are clamped to a
// minimum modal width.
func TestViewNarrowWidthClamped(t *testing.T) {
m := NewModel().Open()
// Should not panic on very narrow terminals.
out := m.View(10, 24)
if out == "" {
t.Fatal("expected non-empty view on narrow terminal")
}
}
// TestViewExportingHidesHelp verifies that the help text "Enter confirm" is
// hidden while an export is in progress.
func TestViewExportingHidesHelp(t *testing.T) {
m := NewModel().Open()
m.exporting = true
out := m.View(80, 24)
if strings.Contains(out, "Enter confirm") {
t.Fatalf("expected help hidden while exporting, got:\n%s", out)
}
}
// TestViewShowsStatus verifies that status messages appear in the view.
func TestViewShowsStatus(t *testing.T) {
m := NewModel().Open()
m.status = "Export done: out.csv"
out := m.View(80, 24)
if !strings.Contains(out, "Export done: out.csv") {
t.Fatalf("expected status in view, got:\n%s", out)
}
}
// TestUpDownKeysChangeSelection verifies that up/down and j/k move the
// selected option index.
func TestUpDownKeysChangeSelection(t *testing.T) {
m := NewModel().Open()
// Move down to last option.
m, _ = m.Update(tea.KeyPressMsg{Code: []rune{'j'}[0], Text: "j"})
if m.selected != 1 {
t.Fatalf("expected selected=1 after j, got %d", m.selected)
}
// Move up back to first.
m, _ = m.Update(tea.KeyPressMsg{Code: []rune{'k'}[0], Text: "k"})
if m.selected != 0 {
t.Fatalf("expected selected=0 after k, got %d", m.selected)
}
// down key alias.
m, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyDown})
if m.selected != 1 {
t.Fatalf("expected selected=1 after down, got %d", m.selected)
}
// up key alias.
m, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyUp})
if m.selected != 0 {
t.Fatalf("expected selected=0 after up, got %d", m.selected)
}
}
// TestKeyIgnoredWhenNotVisible verifies that key events are no-ops when the
// modal is closed.
func TestKeyIgnoredWhenNotVisible(t *testing.T) {
m := NewModel() // not opened
next, cmd := m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
if cmd != nil {
t.Fatal("expected no command when modal is invisible")
}
if next.selected != 0 {
t.Fatalf("expected no selection change when invisible, got %d", next.selected)
}
}
// TestEscWhileExportingCloses verifies that esc can close the modal even while
// an export is in progress.
func TestEscWhileExportingCloses(t *testing.T) {
m := NewModel().Open()
m.exporting = true
m, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyEsc})
if m.Visible() {
t.Fatal("expected modal closed by esc while exporting")
}
}
// TestCompletedMsgWithEmptyPathUsesDone verifies that an empty path in
// CompletedMsg defaults to "done".
func TestCompletedMsgWithEmptyPathUsesDone(t *testing.T) {
m := NewModel().Open()
m.exporting = true
m, _ = m.Update(CompletedMsg{Path: ""})
if !strings.Contains(m.status, "done") {
t.Fatalf("expected 'done' in status for empty path, got %q", m.status)
}
}
// TestFailedMsgWithNilErrUsesDefault verifies that a nil error in FailedMsg
// produces a non-empty error message.
func TestFailedMsgWithNilErrUsesDefault(t *testing.T) {
m := NewModel().Open()
m.exporting = true
m, _ = m.Update(FailedMsg{Err: nil})
if m.status == "" {
t.Fatal("expected non-empty status after FailedMsg with nil error")
}
}
// TestUnknownMsgIsNoOp verifies that unsupported message types leave the model
// unchanged.
func TestUnknownMsgIsNoOp(t *testing.T) {
m := NewModel().Open()
next, cmd := m.Update("unknown message type")
if cmd != nil {
t.Fatal("expected no command for unknown message")
}
if next.selected != m.selected {
t.Fatalf("unexpected state change on unknown msg")
}
}
// TestKeyIgnoredWhenExportingAndNotEsc verifies that non-esc keys are ignored
// during an in-progress export.
func TestKeyIgnoredWhenExportingAndNotEsc(t *testing.T) {
m := NewModel().Open()
m.exporting = true
origSelected := m.selected
next, cmd := m.Update(tea.KeyPressMsg{Code: []rune{'j'}[0], Text: "j"})
if cmd != nil {
t.Fatal("expected no command for j during export")
}
if next.selected != origSelected {
t.Fatalf("expected selection unchanged during export, got %d", next.selected)
}
if !next.Visible() {
t.Fatal("expected modal still visible during export")
}
}
// TestUpdateWithErrors verifies the error recovery path for unknown errors.
func TestUpdateWithErrors(t *testing.T) {
m := NewModel().Open()
m.exporting = true
m, _ = m.Update(FailedMsg{Err: errors.New("disk full")})
if !strings.Contains(m.status, "disk full") {
t.Fatalf("expected disk full in status, got %q", m.status)
}
if m.exporting {
t.Fatal("expected exporting=false after failure")
}
}
|