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>
106 lines
2.9 KiB
Go
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")
|
|
}
|
|
}
|