Skip to content
This repository was archived by the owner on Oct 17, 2021. It is now read-only.

Commit 42c337d

Browse files
author
Faris Huskovic
authored
Merge pull request #17 from cdr/update-help-output-format
Update help output
2 parents 42acd40 + 6615750 commit 42c337d

File tree

3 files changed

+77
-13
lines changed

3 files changed

+77
-13
lines changed

command_test.go

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ import (
1212
var subCmd = new(mockSubCmd)
1313

1414
type (
15+
// Mock for root/parent command.
1516
mockParentCmd struct{}
16-
mockSubCmd struct {
17+
// Mock subcommand with aliases and a nested sucommand of its own.
18+
mockSubCmd struct {
1719
buf *bytes.Buffer
1820
}
21+
// Mock subcommand with aliases and no nested subcommands.
22+
mockSubCmdNoNested struct{}
1923
)
2024

2125
func (c *mockParentCmd) Run(fl *pflag.FlagSet) {}
@@ -40,6 +44,10 @@ func (c *mockSubCmd) Run(fl *pflag.FlagSet) {
4044
}
4145
}
4246

47+
func (c *mockSubCmd) Subcommands() []Command {
48+
return []Command{new(mockSubCmd)}
49+
}
50+
4351
func (c *mockSubCmd) WriteString(s string) (int, error) {
4452
return c.buf.WriteString(s)
4553
}
@@ -49,7 +57,18 @@ func (c *mockSubCmd) Spec() CommandSpec {
4957
Name: "mockSubCmd",
5058
Usage: "Test a subcommand.",
5159
Aliases: []string{"s", "sc", "sub"},
52-
Desc: "A simple mock subcommand with aliases.",
60+
Desc: "A simple mock subcommand with aliases and its own subcommand.",
61+
}
62+
}
63+
64+
func (c *mockSubCmdNoNested) Run(fl *pflag.FlagSet) {}
65+
66+
func (c *mockSubCmdNoNested) Spec() CommandSpec {
67+
return CommandSpec{
68+
Name: "mockSubCmdNoNested",
69+
Usage: "Used for help output tests.",
70+
Aliases: []string{"s", "sc", "sub"},
71+
Desc: "A simple mock subcommand with aliases and no nested subcommands.",
5372
}
5473
}
5574

@@ -73,19 +92,65 @@ func TestCmdHelpOutput(t *testing.T) {
7392
t.Run(t.Name(), func(t *testing.T) {
7493
expected := `Usage: mockParentCmd Mock parent command usage.
7594
76-
Mock parent command description.
95+
Description: Mock parent command description.
7796
7897
Commands:
79-
s,sc,sub,mockSubCmd - A simple mock subcommand with aliases.
98+
s, sc, sub, mockSubCmd - A simple mock subcommand with aliases and its own subcommand.
8099
`
81100
buf := new(bytes.Buffer)
82101
cmd := new(mockParentCmd)
83102
name := cmd.Spec().Name
84103
fl := pflag.NewFlagSet(name, pflag.ExitOnError)
85104
// If the help output doesn't contain the subcommand and
86105
// isn't formatted the way we expect the test will fail.
87-
renderHelp(name, cmd, fl, buf)
88-
got := string(buf.Bytes())
106+
renderHelp(buf, name, cmd, fl)
107+
got := buf.String()
89108
assert.Equal(t, t.Name(), expected, got)
90109
})
91110
}
111+
112+
func TestSubCmdHelpOutput(t *testing.T) {
113+
withNested := `Usage: mockSubCmd Test a subcommand.
114+
115+
Aliases: s, sc, sub
116+
117+
Description: A simple mock subcommand with aliases and its own subcommand.
118+
119+
Commands:
120+
s, sc, sub, mockSubCmd - A simple mock subcommand with aliases and its own subcommand.
121+
`
122+
123+
noNested := `Usage: mockSubCmdNoNested Used for help output tests.
124+
125+
Aliases: s, sc, sub
126+
127+
Description: A simple mock subcommand with aliases and no nested subcommands.
128+
`
129+
130+
for _, test := range []struct {
131+
name, expected string
132+
cmd Command
133+
}{
134+
{
135+
name: "subcmd w/nested subcmd.",
136+
expected: withNested,
137+
cmd: new(mockSubCmd),
138+
},
139+
{
140+
name: "subcmd w/no nested subcmds.",
141+
expected: noNested,
142+
cmd: new(mockSubCmdNoNested),
143+
},
144+
} {
145+
t.Run(test.name, func(t *testing.T) {
146+
buf := new(bytes.Buffer)
147+
name := test.cmd.Spec().Name
148+
fl := pflag.NewFlagSet(name, pflag.ExitOnError)
149+
// If the help output is not written to the buffer
150+
// in the format we expect then the test will fail.
151+
renderHelp(buf, name, test.cmd, fl)
152+
got := buf.String()
153+
assert.Equal(t, t.Name(), test.expected, got)
154+
})
155+
}
156+
}

help.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,17 @@ func renderFlagHelp(fullName string, fl *pflag.FlagSet, w io.Writer) {
4545
}
4646

4747
// renderHelp generates a command's help page.
48-
func renderHelp(fullName string, cmd Command, fl *pflag.FlagSet, w io.Writer) {
48+
func renderHelp(w io.Writer, fullName string, cmd Command, fl *pflag.FlagSet) {
4949
var b strings.Builder
5050
spec := cmd.Spec()
5151
fmt.Fprintf(&b, "Usage: %v %v\n\n", fullName, spec.Usage)
5252

5353
// If the command has aliases, add them to the output as a comma-separated list.
5454
if spec.HasAliases() {
55-
fmt.Fprintf(&b, "Aliases: %v", spec.Aliases)
55+
fmt.Fprintf(&b, "Aliases: %s\n\n", strings.Join(spec.Aliases, ", "))
5656
}
57-
// Render usage and description.
58-
usageAndDesc := fmt.Sprintf("%s%s\n", b.String(), spec.Desc)
59-
fmt.Fprint(w, usageAndDesc)
57+
// Print usage and description.
58+
fmt.Fprintf(w, "%sDescription: %s\n", b.String(), spec.Desc)
6059

6160
tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', tabwriter.StripEscape)
6261
defer tw.Flush()
@@ -80,7 +79,7 @@ func renderHelp(fullName string, cmd Command, fl *pflag.FlagSet, w io.Writer) {
8079
continue
8180
}
8281

83-
allNames := strings.Join(append(spec.Aliases, spec.Name), ",")
82+
allNames := strings.Join(append(spec.Aliases, spec.Name), ", ")
8483

8584
fmt.Fprintf(tw,
8685
tabEscape+"\t"+tabEscape+"%v\t- %v\n",

run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func Run(cmd Command, args []string, parent string) {
9494
// Reassign the usage now that we've parsed the args
9595
// so that we can render it manually.
9696
fl.Usage = func() {
97-
renderHelp(name, cmd, fl, os.Stderr)
97+
renderHelp(os.Stderr, name, cmd, fl)
9898
}
9999
if err != nil {
100100
fl.Usage()

0 commit comments

Comments
 (0)