Skip to content

Commit c5e95ee

Browse files
committed
Polish command help usage and commands docs
1 parent 5a76438 commit c5e95ee

4 files changed

Lines changed: 111 additions & 3 deletions

File tree

internal/commands/commands.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ type flagInfo struct {
2424
Description string `json:"description"`
2525
}
2626

27-
// commandsCmd emits a flat catalog of all commands with their flags.
27+
// commandsCmd emits a catalog of all commands with their flags.
2828
var commandsCmd = &cobra.Command{
2929
Use: "commands",
3030
Short: "List all available commands",
31-
Long: "Lists all available commands with their flags in JSON format.",
31+
Long: "Lists all available commands. Use --json for a structured command catalog.",
3232
RunE: func(cmd *cobra.Command, args []string) error {
3333
catalog := walkCommands(rootCmd, "fizzy")
3434
printSuccess(catalog)

internal/commands/commands_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package commands
2+
3+
import (
4+
"strings"
5+
"testing"
6+
7+
"github.com/basecamp/cli/output"
8+
)
9+
10+
func TestCommandsStyledOutputRendersHumanCatalog(t *testing.T) {
11+
mock := NewMockClient()
12+
SetTestModeWithSDK(mock)
13+
SetTestFormat(output.FormatStyled)
14+
defer resetTest()
15+
16+
if err := commandsCmd.RunE(commandsCmd, []string{}); err != nil {
17+
t.Fatalf("unexpected error: %v", err)
18+
}
19+
20+
raw := TestOutput()
21+
if !strings.Contains(raw, "Name") {
22+
t.Fatalf("expected styled catalog header, got:\n%s", raw)
23+
}
24+
if !strings.Contains(raw, "fizzy auth") {
25+
t.Fatalf("expected styled catalog to include commands, got:\n%s", raw)
26+
}
27+
}
28+
29+
func TestCommandsJSONOutputReturnsStructuredCatalog(t *testing.T) {
30+
mock := NewMockClient()
31+
result := SetTestModeWithSDK(mock)
32+
defer resetTest()
33+
34+
if err := commandsCmd.RunE(commandsCmd, []string{}); err != nil {
35+
t.Fatalf("unexpected error: %v", err)
36+
}
37+
38+
if result.Response == nil || !result.Response.OK {
39+
t.Fatalf("expected OK JSON response, got %#v", result.Response)
40+
}
41+
42+
items, ok := result.Response.Data.([]any)
43+
if !ok {
44+
t.Fatalf("expected command catalog slice, got %#v", result.Response.Data)
45+
}
46+
if len(items) == 0 {
47+
t.Fatal("expected command catalog entries")
48+
}
49+
50+
found := false
51+
for _, item := range items {
52+
entry, ok := item.(map[string]any)
53+
if !ok {
54+
continue
55+
}
56+
if entry["name"] == "fizzy commands" {
57+
found = true
58+
break
59+
}
60+
}
61+
if !found {
62+
t.Fatalf("expected command catalog to include fizzy commands, got %#v", items)
63+
}
64+
}

internal/commands/help.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ func renderCommandHelp(cmd *cobra.Command, w io.Writer) {
133133

134134
subs := visibleSubcommands(cmd)
135135
usageLine := cmd.UseLine()
136-
if len(subs) > 0 {
136+
if len(subs) > 0 && !cmd.Runnable() {
137137
usageLine = cmd.CommandPath() + " <command> [flags]"
138138
}
139139

@@ -376,6 +376,7 @@ var commandExamples = map[string]string{
376376
"fizzy card create": "$ fizzy card create --board <id> --title \"Fix billing bug\"",
377377
"fizzy comment list": "$ fizzy comment list --card <number>",
378378
"fizzy comment create": "$ fizzy comment create --card <number> --body \"Looks good\"",
379+
"fizzy commands": "$ fizzy commands\n$ fizzy commands --json",
379380
"fizzy search": "$ fizzy search \"billing bug\"\n$ fizzy search \"billing bug\" --board <id>",
380381
"fizzy notification": "$ fizzy notification tray\n$ fizzy notification list",
381382
"fizzy notification tray": "$ fizzy notification tray",

internal/commands/help_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import (
44
"bytes"
55
"strings"
66
"testing"
7+
8+
"github.com/spf13/cobra"
79
)
810

911
func TestRenderRootHelp(t *testing.T) {
@@ -46,3 +48,44 @@ func TestRenderRootHelpOmitsCommonWorkflows(t *testing.T) {
4648
t.Fatalf("expected root help to omit common workflows, got:\n%s", out)
4749
}
4850
}
51+
52+
func TestRenderRunnableParentCommandHelpPreservesDirectUsage(t *testing.T) {
53+
configureCLIUX()
54+
55+
tests := []struct {
56+
name string
57+
cmd *cobra.Command
58+
want string
59+
}{
60+
{name: "signup", cmd: signupCmd, want: " fizzy signup [flags]"},
61+
{name: "setup", cmd: setupCmd, want: " fizzy setup [flags]"},
62+
{name: "skill", cmd: skillCmd, want: " fizzy skill [flags]"},
63+
}
64+
65+
for _, tt := range tests {
66+
t.Run(tt.name, func(t *testing.T) {
67+
var buf bytes.Buffer
68+
renderHelp(tt.cmd, &buf)
69+
out := buf.String()
70+
71+
if !strings.Contains(out, tt.want) {
72+
t.Fatalf("expected help to contain %q, got:\n%s", tt.want, out)
73+
}
74+
})
75+
}
76+
}
77+
78+
func TestRenderCommandsHelpMentionsJSONCatalog(t *testing.T) {
79+
configureCLIUX()
80+
81+
var buf bytes.Buffer
82+
renderHelp(commandsCmd, &buf)
83+
out := buf.String()
84+
85+
if !strings.Contains(out, "Use --json for a structured command catalog.") {
86+
t.Fatalf("expected commands help to mention --json catalog, got:\n%s", out)
87+
}
88+
if !strings.Contains(out, "EXAMPLES") || !strings.Contains(out, "$ fizzy commands --json") {
89+
t.Fatalf("expected commands help examples to include --json, got:\n%s", out)
90+
}
91+
}

0 commit comments

Comments
 (0)