diff options
| author | Paul Buetow <paul@buetow.org> | 2026-05-13 14:28:37 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-05-13 14:28:37 +0300 |
| commit | 27b94f917064948fa33141309a3f08deb40ffde2 (patch) | |
| tree | 0f1c63eba01da1cc89fbbedcfe71cdcb55b06cb0 /internal/tui/export | |
| parent | 140d6c0fe472f112170022b9831dfe700698f382 (diff) | |
improve unit test coverage to >=60% in probes, common, export, streamrow, pidpicker, tui/export
Before: probes=30%, tui/common=41%, export=0%, streamrow=25%, pidpicker=59%, tui/export=45%
After: probes=89%, tui/common=97%, export=77%, streamrow=100%, pidpicker=73%, tui/export=99%
New test files cover RingBuffer push/wrap/reset, Row accessor methods, nil
Sequencer safety, SnapshotCSV nil and data paths, helper functions snapValue /
snapValueF / trendSummary, all table navigation keys, VisibleTableWindow/
ClampTableCol edge cases, RenderTableHeader/Row, PickerShortHelp, probe modal
navigation/search/toggle/view/error paths, truncateText/sanitizeOneLine,
export modal View rendering, key navigation, status messages, scanAllThreadsFrom,
readThreadInfo guards, formatProcess variants, and clamp helper.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/tui/export')
| -rw-r--r-- | internal/tui/export/export_extra_test.go | 195 |
1 files changed, 195 insertions, 0 deletions
diff --git a/internal/tui/export/export_extra_test.go b/internal/tui/export/export_extra_test.go new file mode 100644 index 0000000..edc0632 --- /dev/null +++ b/internal/tui/export/export_extra_test.go @@ -0,0 +1,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") + } +} |
