26f897c1ec
release / release-please (push) Failing after 1m49s
docs / build (push) Failing after 6m34s
release / build-and-upload (arm64, linux) (push) Has been cancelled
release / build-and-upload (arm64, windows) (push) Has been cancelled
release / build-darwin (amd64, darwin) (push) Has been cancelled
release / checksums (push) Has been cancelled
release / finalize (push) Has been cancelled
release / build-darwin (arm64, darwin) (push) Has been cancelled
release / build-and-upload (amd64, linux) (push) Has been cancelled
release / build-and-upload (amd64, windows) (push) Has been cancelled
docs / deploy (push) Has been cancelled
181 lines
6.8 KiB
Go
181 lines
6.8 KiB
Go
package tui
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
"github.com/kunchenguid/no-mistakes/internal/ipc"
|
|
"github.com/kunchenguid/no-mistakes/internal/types"
|
|
"github.com/muesli/termenv"
|
|
)
|
|
|
|
func TestPipelineView_CompactNoConnectors(t *testing.T) {
|
|
// When terminal height is small (< 30), connector lines between steps should be suppressed.
|
|
run := testRun()
|
|
steps := []ipc.StepResultInfo{
|
|
{StepName: types.StepReview, Status: types.StepStatusCompleted},
|
|
{StepName: types.StepTest, Status: types.StepStatusRunning},
|
|
{StepName: types.StepLint, Status: types.StepStatusPending},
|
|
}
|
|
compact := stripANSI(renderPipelineView(run, steps, 80, 0, 25))
|
|
normal := stripANSI(renderPipelineView(run, steps, 80, 0, 40))
|
|
// Compact should have fewer lines than normal (no connector lines).
|
|
compactLines := len(strings.Split(compact, "\n"))
|
|
normalLines := len(strings.Split(normal, "\n"))
|
|
if compactLines >= normalLines {
|
|
t.Errorf("compact pipeline (height=25) should have fewer lines than normal (height=40): compact=%d, normal=%d", compactLines, normalLines)
|
|
}
|
|
}
|
|
|
|
func TestPipelineView_NormalHasConnectors(t *testing.T) {
|
|
// When terminal height is >= 30, connector lines should still be present.
|
|
run := testRun()
|
|
steps := []ipc.StepResultInfo{
|
|
{StepName: types.StepReview, Status: types.StepStatusCompleted},
|
|
{StepName: types.StepTest, Status: types.StepStatusRunning},
|
|
{StepName: types.StepLint, Status: types.StepStatusPending},
|
|
}
|
|
result := stripANSI(renderPipelineView(run, steps, 80, 0, 40))
|
|
lines := strings.Split(result, "\n")
|
|
connectorCount := 0
|
|
for _, line := range lines {
|
|
inner := boxContentLine(line)
|
|
if inner == "│" {
|
|
connectorCount++
|
|
}
|
|
}
|
|
if connectorCount < 2 {
|
|
t.Errorf("expected at least 2 connector lines in normal mode (height=40), found %d", connectorCount)
|
|
}
|
|
}
|
|
|
|
func TestModel_View_CompactPipeline(t *testing.T) {
|
|
// Integration test: model with small height should produce compact pipeline.
|
|
run := testRun()
|
|
run.Steps[0].Status = types.StepStatusCompleted
|
|
run.Steps[1].Status = types.StepStatusRunning
|
|
m := NewModel("/tmp/test.sock", nil, run)
|
|
m.width = 80
|
|
m.height = 20
|
|
view := stripANSI(m.View())
|
|
lines := strings.Split(view, "\n")
|
|
for _, line := range lines {
|
|
inner := boxContentLine(line)
|
|
if inner == "│" {
|
|
t.Errorf("found connector line in compact view (height=20), should be suppressed")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOutcomeBanner_NoDurationWhenNoStepTimes(t *testing.T) {
|
|
run := testRun()
|
|
run.Status = types.RunCompleted
|
|
steps := []ipc.StepResultInfo{
|
|
{StepName: types.StepReview, Status: types.StepStatusCompleted},
|
|
{StepName: types.StepTest, Status: types.StepStatusCompleted},
|
|
}
|
|
banner := stripANSI(renderOutcomeBanner(run, steps))
|
|
// When no steps have DurationMS, no time should be shown.
|
|
if !strings.Contains(banner, "Pipeline passed") {
|
|
t.Errorf("expected 'Pipeline passed' in banner, got: %s", banner)
|
|
}
|
|
if strings.Contains(banner, "s") && strings.Contains(banner, ".") {
|
|
// Rough check: shouldn't have a duration string like "0.0s"
|
|
t.Errorf("expected no elapsed time when no step durations available, got: %s", banner)
|
|
}
|
|
}
|
|
|
|
func TestModel_View_LogTailCompact(t *testing.T) {
|
|
// In stacked layout, compact terminals should still use the remaining height budget.
|
|
prev := lipgloss.ColorProfile()
|
|
defer lipgloss.SetColorProfile(prev)
|
|
|
|
lipgloss.SetColorProfile(termenv.Ascii)
|
|
run := testRun()
|
|
m := NewModel("/tmp/sock", nil, run)
|
|
m.width = 80
|
|
m.height = 25
|
|
for i := 1; i <= 20; i++ {
|
|
m.logs = append(m.logs, fmt.Sprintf("log line %d", i))
|
|
}
|
|
|
|
view := stripANSI(m.View())
|
|
count := strings.Count(view, "log line")
|
|
if count <= 3 {
|
|
t.Fatalf("expected compact stacked layout to expand beyond the old 3-line cap, got %d\n%s", count, view)
|
|
}
|
|
if got := lipgloss.Height(view); got != m.height {
|
|
t.Fatalf("expected compact stacked layout to use full terminal height %d, got %d\n%s", m.height, got, view)
|
|
}
|
|
}
|
|
|
|
func TestModel_View_LogTailHiddenTiny(t *testing.T) {
|
|
// In very small terminals (height < 20), log box should be hidden entirely.
|
|
run := testRun()
|
|
m := NewModel("/tmp/sock", nil, run)
|
|
m.width = 80
|
|
m.height = 15 // tiny terminal
|
|
m.logs = []string{"log line 1", "log line 2", "log line 3"}
|
|
view := m.View()
|
|
if strings.Contains(view, "log line") {
|
|
t.Error("expected log box hidden in tiny terminal (height=15)")
|
|
}
|
|
if strings.Contains(view, "Log") {
|
|
t.Error("expected no Log box title in tiny terminal")
|
|
}
|
|
}
|
|
|
|
func TestModel_View_ShortTerminalDoesNotOverflowHeight(t *testing.T) {
|
|
lipgloss.SetColorProfile(termenv.Ascii)
|
|
|
|
run := testRun()
|
|
run.Steps[0].Status = types.StepStatusAwaitingApproval
|
|
run.Steps[0].FindingsJSON = ptr(`{"summary":"The review surfaced several issues that need attention before continuing.","items":[{"id":"f1","severity":"warning","file":"internal/pipeline/steps/review.go","line":101,"description":"This finding has a long description that should wrap across multiple lines in a narrow viewport and still keep the pipeline header visible."},{"id":"f2","severity":"info","file":"internal/pipeline/steps/test.go","line":202,"description":"Another wrapped finding to force the findings panel to compete for height with the pipeline and footer sections."},{"id":"f3","severity":"warning","file":"internal/pipeline/steps/lint.go","line":303,"description":"A third wrapped finding makes the old item-count heuristic overflow the terminal height."}]}`)
|
|
|
|
m := NewModel("/tmp/sock", nil, run)
|
|
m.width = 80
|
|
m.height = 18
|
|
view := stripANSI(m.View())
|
|
|
|
if got := lipgloss.Height(view); got > m.height {
|
|
t.Fatalf("expected rendered view height <= terminal height (%d), got %d\n%s", m.height, got, view)
|
|
}
|
|
if !strings.Contains(view, "feature/foo") {
|
|
t.Fatalf("expected pipeline header to remain visible in short terminal\n%s", view)
|
|
}
|
|
}
|
|
|
|
func TestModel_View_StackedLogBoxFillsRemainingHeight(t *testing.T) {
|
|
prev := lipgloss.ColorProfile()
|
|
defer lipgloss.SetColorProfile(prev)
|
|
|
|
lipgloss.SetColorProfile(termenv.Ascii)
|
|
run := testRun()
|
|
m := NewModel("/tmp/sock", nil, run)
|
|
m.width = 80
|
|
m.height = 40
|
|
for i := 1; i <= 40; i++ {
|
|
m.logs = append(m.logs, fmt.Sprintf("log line %d", i))
|
|
}
|
|
|
|
pipelineView := renderPipelineView(run, m.stepsWithRunningElapsed(), m.width, 0, m.height)
|
|
footer := renderFooter(false, false, false, false, run, "", m.width)
|
|
expectedLogLines := m.height - sectionsHeight([]string{pipelineView}, 2) - 2 - lipgloss.Height(footer) - 2
|
|
if expectedLogLines <= 5 {
|
|
t.Fatalf("expected stacked layout to leave room for more than 5 log lines, got %d", expectedLogLines)
|
|
}
|
|
|
|
view := stripANSI(m.View())
|
|
count := strings.Count(view, "log line")
|
|
if count != expectedLogLines {
|
|
t.Fatalf("expected stacked log box to fill remaining height with %d log lines, got %d\n%s", expectedLogLines, count, view)
|
|
}
|
|
if got := lipgloss.Height(view); got > m.height {
|
|
t.Fatalf("expected rendered view height <= terminal height (%d), got %d\n%s", m.height, got, view)
|
|
}
|
|
}
|
|
|
|
// --- Abort Confirmation Tests ---
|