Files
tuio/internal/tui/nav_test.go
T
jbandClaude Opus 4.8 255b4cc299 feat: add back navigation, review step, and error retry to TUI
Implements three UX improvements (TODO #1–#3):

- Back navigation: Esc returns to the previous input step. The Model keeps
  a history stack of input steps (advance/goBack helpers); action/wait
  steps are excluded via isInputStep so back never re-enters a
  side-effecting step. Form values are preserved.
- Review step (StepReview): shows all collected config for confirmation
  before any file is written; Enter installs, Esc edits.
- Retry vs. fix: transient failures (image pull, container run) offer
  "r: tentar novamente" instead of quitting; validation failures route
  back to the relevant form to correct the value.

Adds nav_test.go for the history/navigation logic and updates
README/CLAUDE/docs (incl. docs/TODO.md tracking the remaining ideas).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:19:49 -03:00

106 lines
2.9 KiB
Go

package tui
import "testing"
func TestIsInputStep(t *testing.T) {
input := []step{
StepDockerLogin, StepIPQuestion, StepWireguardConfig,
StepAppConfig, StepServerConfig, StepDatabaseConfig,
StepCertConfig, StepReview,
}
for _, s := range input {
if !isInputStep(s) {
t.Errorf("isInputStep(%d) = false, want true", s)
}
}
action := []step{
StepCheckDocker, StepDockerInstall, StepDownloadImage,
StepGenerateWireguardFile, StepDownloadWireguard, StepRunWireguard,
StepGenerateFile, StepRunDocker, StepDone,
}
for _, s := range action {
if isInputStep(s) {
t.Errorf("isInputStep(%d) = true, want false", s)
}
}
}
func TestAdvancePushesInputSteps(t *testing.T) {
m := Model{currentStep: StepAppConfig}
m.advance(StepServerConfig)
if m.currentStep != StepServerConfig {
t.Fatalf("currentStep = %d, want StepServerConfig", m.currentStep)
}
if len(m.history) != 1 || m.history[0] != StepAppConfig {
t.Fatalf("history = %v, want [StepAppConfig]", m.history)
}
}
func TestAdvanceSkipsActionSteps(t *testing.T) {
// Advancing away from an action step must not record it on the stack.
m := Model{currentStep: StepDownloadImage}
m.advance(StepIPQuestion)
if len(m.history) != 0 {
t.Fatalf("history = %v, want empty (action step not recorded)", m.history)
}
}
func TestAdvanceDedupesConsecutive(t *testing.T) {
m := Model{currentStep: StepIPQuestion}
m.advance(StepIPQuestion) // self-advance (e.g. re-entering) must not duplicate
m.currentStep = StepIPQuestion
m.advance(StepIPQuestion)
if len(m.history) > 1 {
t.Fatalf("history = %v, want at most one entry", m.history)
}
}
func TestGoBack(t *testing.T) {
m := Model{currentStep: StepCertConfig, history: []step{StepAppConfig, StepServerConfig, StepDatabaseConfig}}
if !m.goBack() {
t.Fatal("goBack() = false, want true")
}
if m.currentStep != StepDatabaseConfig {
t.Errorf("currentStep = %d, want StepDatabaseConfig", m.currentStep)
}
if len(m.history) != 2 {
t.Errorf("history len = %d, want 2", len(m.history))
}
}
func TestGoBackEmptyHistory(t *testing.T) {
m := Model{currentStep: StepDockerLogin}
if m.goBack() {
t.Error("goBack() = true on empty history, want false")
}
if m.currentStep != StepDockerLogin {
t.Error("currentStep changed on empty goBack")
}
}
func TestCanGoBack(t *testing.T) {
// Input step with history -> can go back.
m := Model{currentStep: StepServerConfig, history: []step{StepAppConfig}}
if !m.canGoBack() {
t.Error("canGoBack() = false, want true for input step with history")
}
// Input step, empty history -> cannot.
m = Model{currentStep: StepDockerLogin}
if m.canGoBack() {
t.Error("canGoBack() = true, want false with empty history")
}
// Action step with history -> cannot (esc handled by the step, not global back).
m = Model{currentStep: StepDownloadImage, history: []step{StepDockerLogin}}
if m.canGoBack() {
t.Error("canGoBack() = true on action step, want false")
}
}