Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions internal/commands/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type Board struct {
var setupCmd = &cobra.Command{
Use: "setup",
Short: "Interactive setup wizard",
Long: "Configure Fizzy CLI with your API token, account, and default board.",
Long: "Configure Fizzy CLI with your API token, account, and default board.\nNew users without an account will be guided through signup.",
RunE: runSetup,
}

Expand All @@ -45,6 +45,26 @@ func runSetup(cmd *cobra.Command, args []string) error {
fmt.Println("Welcome to Fizzy CLI setup!")
fmt.Println()

// Ask if user has an account before checking existing config
var hasAccount string
err := huh.NewSelect[string]().
Title("Do you have a Fizzy account?").
Options(
huh.NewOption("Yes, I have an account", "yes"),
huh.NewOption("No, I'd like to sign up", "no"),
).
Value(&hasAccount).
Run()

if err != nil {
fmt.Println("Setup cancelled.")
return nil //nolint:nilerr // user cancelled prompt
}

if hasAccount == "no" {
return signupWizard()
}
Comment thread
robzolkos marked this conversation as resolved.

// Check for existing config
globalExists := config.Exists()
localPath := config.LocalConfigPath()
Expand All @@ -56,7 +76,7 @@ func runSetup(cmd *cobra.Command, args []string) error {
configLocation = "local config (" + localPath + ")"
}

err := huh.NewConfirm().
err = huh.NewConfirm().
Title(fmt.Sprintf("Existing %s found. Reconfigure?", configLocation)).
Value(&reconfigure).
Run()
Expand All @@ -74,7 +94,7 @@ func runSetup(cmd *cobra.Command, args []string) error {

// Ask hosted vs self-hosted
var hostingType string
err := huh.NewSelect[string]().
err = huh.NewSelect[string]().
Title("Are you using the hosted or self-hosted version?").
Options(
huh.NewOption("Hosted (app.fizzy.do)", "hosted"),
Expand Down
50 changes: 50 additions & 0 deletions internal/commands/setup_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,62 @@ package commands
import (
"os"
"path/filepath"
"strings"
"testing"

"github.com/basecamp/fizzy-cli/internal/config"
"gopkg.in/yaml.v3"
)

func TestSetupCommandDescription(t *testing.T) {
t.Run("long description mentions signup for new users", func(t *testing.T) {
if !strings.Contains(setupCmd.Long, "signup") {
t.Error("expected setup command long description to mention signup")
}
})
}

func TestSetupRejectsMachineOutput(t *testing.T) {
t.Run("rejects agent mode", func(t *testing.T) {
defer ResetTestMode()
ResetTestMode()
cfgAgent = true
err := runSetup(setupCmd, nil)
if err == nil {
t.Fatal("expected error when running setup with --agent")
}
if !strings.Contains(err.Error(), "interactive terminal") {
t.Errorf("unexpected error: %v", err)
}
})

t.Run("rejects JSON mode", func(t *testing.T) {
defer ResetTestMode()
ResetTestMode()
cfgJSON = true
err := runSetup(setupCmd, nil)
if err == nil {
t.Fatal("expected error when running setup with --json")
}
if !strings.Contains(err.Error(), "interactive terminal") {
t.Errorf("unexpected error: %v", err)
}
})

t.Run("rejects quiet mode", func(t *testing.T) {
defer ResetTestMode()
ResetTestMode()
cfgQuiet = true
err := runSetup(setupCmd, nil)
if err == nil {
t.Fatal("expected error when running setup with --quiet")
}
if !strings.Contains(err.Error(), "interactive terminal") {
t.Errorf("unexpected error: %v", err)
}
})
}
Comment thread
robzolkos marked this conversation as resolved.
Outdated

func TestParseAccounts(t *testing.T) {
t.Run("parses accounts from identity response", func(t *testing.T) {
data := map[string]any{
Expand Down
6 changes: 6 additions & 0 deletions internal/commands/signup.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ func runSignup(cmd *cobra.Command, args []string) error {
fmt.Println("Welcome to Fizzy CLI signup!")
fmt.Println()

return signupWizard()
}

// signupWizard contains the core interactive signup flow (steps 1-9).
// Called by both runSignup and runSetup to avoid duplicate preamble.
func signupWizard() error {
// Step 1: Hosting type
var hostingType string
err := huh.NewSelect[string]().
Expand Down