...

Source file src/github.com/urfave/cli/v2/help_test.go

Documentation: github.com/urfave/cli/v2

     1  package cli
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"runtime"
    10  	"strings"
    11  	"testing"
    12  )
    13  
    14  func Test_ShowAppHelp_NoAuthor(t *testing.T) {
    15  	output := new(bytes.Buffer)
    16  	app := &App{Writer: output}
    17  
    18  	c := NewContext(app, nil, nil)
    19  
    20  	_ = ShowAppHelp(c)
    21  
    22  	if bytes.Contains(output.Bytes(), []byte("AUTHOR(S):")) {
    23  		t.Errorf("expected\n%snot to include %s", output.String(), "AUTHOR(S):")
    24  	}
    25  }
    26  
    27  func Test_ShowAppHelp_NoVersion(t *testing.T) {
    28  	output := new(bytes.Buffer)
    29  	app := &App{Writer: output}
    30  
    31  	app.Version = ""
    32  
    33  	c := NewContext(app, nil, nil)
    34  
    35  	_ = ShowAppHelp(c)
    36  
    37  	if bytes.Contains(output.Bytes(), []byte("VERSION:")) {
    38  		t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:")
    39  	}
    40  }
    41  
    42  func Test_ShowAppHelp_HideVersion(t *testing.T) {
    43  	output := new(bytes.Buffer)
    44  	app := &App{Writer: output}
    45  
    46  	app.HideVersion = true
    47  
    48  	c := NewContext(app, nil, nil)
    49  
    50  	_ = ShowAppHelp(c)
    51  
    52  	if bytes.Contains(output.Bytes(), []byte("VERSION:")) {
    53  		t.Errorf("expected\n%snot to include %s", output.String(), "VERSION:")
    54  	}
    55  }
    56  
    57  func Test_ShowAppHelp_MultiLineDescription(t *testing.T) {
    58  	output := new(bytes.Buffer)
    59  	app := &App{Writer: output}
    60  
    61  	app.HideVersion = true
    62  	app.Description = "multi\n  line"
    63  
    64  	c := NewContext(app, nil, nil)
    65  
    66  	_ = ShowAppHelp(c)
    67  
    68  	if !bytes.Contains(output.Bytes(), []byte("DESCRIPTION:\n   multi\n     line")) {
    69  		t.Errorf("expected\n%s\nto include\n%s", output.String(), "DESCRIPTION:\n   multi\n     line")
    70  	}
    71  }
    72  
    73  func Test_Help_Custom_Flags(t *testing.T) {
    74  	oldFlag := HelpFlag
    75  	defer func() {
    76  		HelpFlag = oldFlag
    77  	}()
    78  
    79  	HelpFlag = &BoolFlag{
    80  		Name:    "help",
    81  		Aliases: []string{"x"},
    82  		Usage:   "show help",
    83  	}
    84  
    85  	app := App{
    86  		Flags: []Flag{
    87  			&BoolFlag{Name: "foo", Aliases: []string{"h"}},
    88  		},
    89  		Action: func(ctx *Context) error {
    90  			if ctx.Bool("h") != true {
    91  				t.Errorf("custom help flag not set")
    92  			}
    93  			return nil
    94  		},
    95  	}
    96  	output := new(bytes.Buffer)
    97  	app.Writer = output
    98  	_ = app.Run([]string{"test", "-h"})
    99  	if output.Len() > 0 {
   100  		t.Errorf("unexpected output: %s", output.String())
   101  	}
   102  }
   103  
   104  func Test_Help_Nil_Flags(t *testing.T) {
   105  	oldFlag := HelpFlag
   106  	defer func() {
   107  		HelpFlag = oldFlag
   108  	}()
   109  	HelpFlag = nil
   110  
   111  	app := App{
   112  		Action: func(context *Context) error {
   113  			return nil
   114  		},
   115  	}
   116  	output := new(bytes.Buffer)
   117  	app.Writer = output
   118  	_ = app.Run([]string{"test"})
   119  	if output.Len() > 0 {
   120  		t.Errorf("unexpected output: %s", output.String())
   121  	}
   122  }
   123  
   124  func Test_Version_Custom_Flags(t *testing.T) {
   125  	oldFlag := VersionFlag
   126  	defer func() {
   127  		VersionFlag = oldFlag
   128  	}()
   129  
   130  	VersionFlag = &BoolFlag{
   131  		Name:    "version",
   132  		Aliases: []string{"V"},
   133  		Usage:   "show version",
   134  	}
   135  
   136  	app := App{
   137  		Flags: []Flag{
   138  			&BoolFlag{Name: "foo", Aliases: []string{"v"}},
   139  		},
   140  		Action: func(ctx *Context) error {
   141  			if ctx.Bool("v") != true {
   142  				t.Errorf("custom version flag not set")
   143  			}
   144  			return nil
   145  		},
   146  	}
   147  	output := new(bytes.Buffer)
   148  	app.Writer = output
   149  	_ = app.Run([]string{"test", "-v"})
   150  	if output.Len() > 0 {
   151  		t.Errorf("unexpected output: %s", output.String())
   152  	}
   153  }
   154  
   155  func Test_helpCommand_Action_ErrorIfNoTopic(t *testing.T) {
   156  	app := &App{}
   157  
   158  	set := flag.NewFlagSet("test", 0)
   159  	_ = set.Parse([]string{"foo"})
   160  
   161  	c := NewContext(app, set, nil)
   162  
   163  	err := helpCommand.Action(c)
   164  
   165  	if err == nil {
   166  		t.Fatalf("expected error from helpCommand.Action(), but got nil")
   167  	}
   168  
   169  	exitErr, ok := err.(*exitError)
   170  	if !ok {
   171  		t.Fatalf("expected *exitError from helpCommand.Action(), but instead got: %v", err.Error())
   172  	}
   173  
   174  	if !strings.HasPrefix(exitErr.Error(), "No help topic for") {
   175  		t.Fatalf("expected an unknown help topic error, but got: %v", exitErr.Error())
   176  	}
   177  
   178  	if exitErr.exitCode != 3 {
   179  		t.Fatalf("expected exit value = 3, got %d instead", exitErr.exitCode)
   180  	}
   181  }
   182  
   183  func Test_helpCommand_InHelpOutput(t *testing.T) {
   184  	app := &App{}
   185  	output := &bytes.Buffer{}
   186  	app.Writer = output
   187  	_ = app.Run([]string{"test", "--help"})
   188  
   189  	s := output.String()
   190  
   191  	if strings.Contains(s, "\nCOMMANDS:\nGLOBAL OPTIONS:\n") {
   192  		t.Fatalf("empty COMMANDS section detected: %q", s)
   193  	}
   194  
   195  	if !strings.Contains(s, "help, h") {
   196  		t.Fatalf("missing \"help, h\": %q", s)
   197  	}
   198  }
   199  
   200  func Test_helpSubcommand_Action_ErrorIfNoTopic(t *testing.T) {
   201  	app := &App{}
   202  
   203  	set := flag.NewFlagSet("test", 0)
   204  	_ = set.Parse([]string{"foo"})
   205  
   206  	c := NewContext(app, set, nil)
   207  
   208  	err := helpCommand.Action(c)
   209  
   210  	if err == nil {
   211  		t.Fatalf("expected error from helpCommand.Action(), but got nil")
   212  	}
   213  
   214  	exitErr, ok := err.(*exitError)
   215  	if !ok {
   216  		t.Fatalf("expected *exitError from helpCommand.Action(), but instead got: %v", err.Error())
   217  	}
   218  
   219  	if !strings.HasPrefix(exitErr.Error(), "No help topic for") {
   220  		t.Fatalf("expected an unknown help topic error, but got: %v", exitErr.Error())
   221  	}
   222  
   223  	if exitErr.exitCode != 3 {
   224  		t.Fatalf("expected exit value = 3, got %d instead", exitErr.exitCode)
   225  	}
   226  }
   227  
   228  func TestShowAppHelp_CommandAliases(t *testing.T) {
   229  	app := &App{
   230  		Commands: []*Command{
   231  			{
   232  				Name:    "frobbly",
   233  				Aliases: []string{"fr", "frob"},
   234  				Action: func(ctx *Context) error {
   235  					return nil
   236  				},
   237  			},
   238  		},
   239  	}
   240  
   241  	output := &bytes.Buffer{}
   242  	app.Writer = output
   243  	_ = app.Run([]string{"foo", "--help"})
   244  
   245  	if !strings.Contains(output.String(), "frobbly, fr, frob") {
   246  		t.Errorf("expected output to include all command aliases; got: %q", output.String())
   247  	}
   248  }
   249  
   250  func TestShowCommandHelp_HelpPrinter(t *testing.T) {
   251  	/*doublecho := func(text string) string {
   252  		return text + " " + text
   253  	}*/
   254  
   255  	tests := []struct {
   256  		name         string
   257  		template     string
   258  		printer      helpPrinter
   259  		command      string
   260  		wantTemplate string
   261  		wantOutput   string
   262  	}{
   263  		{
   264  			name:     "no-command",
   265  			template: "",
   266  			printer: func(w io.Writer, templ string, data interface{}) {
   267  				fmt.Fprint(w, "yo")
   268  			},
   269  			command:      "",
   270  			wantTemplate: AppHelpTemplate,
   271  			wantOutput:   "yo",
   272  		},
   273  		/*{
   274  			name:     "standard-command",
   275  			template: "",
   276  			printer: func(w io.Writer, templ string, data interface{}) {
   277  				fmt.Fprint(w, "yo")
   278  			},
   279  			command:      "my-command",
   280  			wantTemplate: CommandHelpTemplate,
   281  			wantOutput:   "yo",
   282  		},
   283  		{
   284  			name:     "custom-template-command",
   285  			template: "{{doublecho .Name}}",
   286  			printer: func(w io.Writer, templ string, data interface{}) {
   287  				// Pass a custom function to ensure it gets used
   288  				fm := map[string]interface{}{"doublecho": doublecho}
   289  				HelpPrinterCustom(w, templ, data, fm)
   290  			},
   291  			command:      "my-command",
   292  			wantTemplate: "{{doublecho .Name}}",
   293  			wantOutput:   "my-command my-command",
   294  		},*/
   295  	}
   296  
   297  	for _, tt := range tests {
   298  		t.Run(tt.name, func(t *testing.T) {
   299  			defer func(old helpPrinter) {
   300  				HelpPrinter = old
   301  			}(HelpPrinter)
   302  			HelpPrinter = func(w io.Writer, templ string, data interface{}) {
   303  				if templ != tt.wantTemplate {
   304  					t.Errorf("want template:\n%s\ngot template:\n%s", tt.wantTemplate, templ)
   305  				}
   306  
   307  				tt.printer(w, templ, data)
   308  			}
   309  
   310  			var buf bytes.Buffer
   311  			app := &App{
   312  				Name:   "my-app",
   313  				Writer: &buf,
   314  				Commands: []*Command{
   315  					{
   316  						Name:               "my-command",
   317  						CustomHelpTemplate: tt.template,
   318  					},
   319  				},
   320  			}
   321  
   322  			err := app.Run([]string{"my-app", "help", tt.command})
   323  			if err != nil {
   324  				t.Fatal(err)
   325  			}
   326  
   327  			got := buf.String()
   328  			if got != tt.wantOutput {
   329  				t.Errorf("want output %q, got %q", tt.wantOutput, got)
   330  			}
   331  		})
   332  	}
   333  }
   334  
   335  func TestShowCommandHelp_HelpPrinterCustom(t *testing.T) {
   336  	doublecho := func(text string) string {
   337  		return text + " " + text
   338  	}
   339  
   340  	tests := []struct {
   341  		name         string
   342  		template     string
   343  		printer      helpPrinterCustom
   344  		command      string
   345  		wantTemplate string
   346  		wantOutput   string
   347  	}{
   348  		{
   349  			name:     "no-command",
   350  			template: "",
   351  			printer: func(w io.Writer, templ string, data interface{}, fm map[string]interface{}) {
   352  				fmt.Fprint(w, "yo")
   353  			},
   354  			command:      "",
   355  			wantTemplate: AppHelpTemplate,
   356  			wantOutput:   "yo",
   357  		},
   358  		{
   359  			name:     "standard-command",
   360  			template: "",
   361  			printer: func(w io.Writer, templ string, data interface{}, fm map[string]interface{}) {
   362  				fmt.Fprint(w, "yo")
   363  			},
   364  			command:      "my-command",
   365  			wantTemplate: CommandHelpTemplate,
   366  			wantOutput:   "yo",
   367  		},
   368  		{
   369  			name:     "custom-template-command",
   370  			template: "{{doublecho .Name}}",
   371  			printer: func(w io.Writer, templ string, data interface{}, _ map[string]interface{}) {
   372  				// Pass a custom function to ensure it gets used
   373  				fm := map[string]interface{}{"doublecho": doublecho}
   374  				printHelpCustom(w, templ, data, fm)
   375  			},
   376  			command:      "my-command",
   377  			wantTemplate: "{{doublecho .Name}}",
   378  			wantOutput:   "my-command my-command",
   379  		},
   380  	}
   381  
   382  	for _, tt := range tests {
   383  		t.Run(tt.name, func(t *testing.T) {
   384  			defer func(old helpPrinterCustom) {
   385  				HelpPrinterCustom = old
   386  			}(HelpPrinterCustom)
   387  			HelpPrinterCustom = func(w io.Writer, templ string, data interface{}, fm map[string]interface{}) {
   388  				if fm != nil {
   389  					t.Error("unexpected function map passed")
   390  				}
   391  
   392  				if templ != tt.wantTemplate {
   393  					t.Errorf("want template:\n%s\ngot template:\n%s", tt.wantTemplate, templ)
   394  				}
   395  
   396  				tt.printer(w, templ, data, fm)
   397  			}
   398  
   399  			var buf bytes.Buffer
   400  			app := &App{
   401  				Name:   "my-app",
   402  				Writer: &buf,
   403  				Commands: []*Command{
   404  					{
   405  						Name:               "my-command",
   406  						CustomHelpTemplate: tt.template,
   407  					},
   408  				},
   409  			}
   410  
   411  			err := app.Run([]string{"my-app", "help", tt.command})
   412  			if err != nil {
   413  				t.Fatal(err)
   414  			}
   415  
   416  			got := buf.String()
   417  			if got != tt.wantOutput {
   418  				t.Errorf("want output %q, got %q", tt.wantOutput, got)
   419  			}
   420  		})
   421  	}
   422  }
   423  
   424  func TestShowCommandHelp_CommandAliases(t *testing.T) {
   425  	app := &App{
   426  		Commands: []*Command{
   427  			{
   428  				Name:    "frobbly",
   429  				Aliases: []string{"fr", "frob", "bork"},
   430  				Action: func(ctx *Context) error {
   431  					return nil
   432  				},
   433  			},
   434  		},
   435  	}
   436  
   437  	output := &bytes.Buffer{}
   438  	app.Writer = output
   439  	_ = app.Run([]string{"foo", "help", "fr"})
   440  
   441  	if !strings.Contains(output.String(), "frobbly") {
   442  		t.Errorf("expected output to include command name; got: %q", output.String())
   443  	}
   444  
   445  	if strings.Contains(output.String(), "bork") {
   446  		t.Errorf("expected output to exclude command aliases; got: %q", output.String())
   447  	}
   448  }
   449  
   450  func TestHelpNameConsistency(t *testing.T) {
   451  	// Setup some very basic templates based on actual AppHelp, CommandHelp
   452  	// and SubcommandHelp templates to display the help name
   453  	// The inconsistency shows up when users use NewApp() as opposed to
   454  	// using App{...} directly
   455  	tmpTemplate := SubcommandHelpTemplate
   456  	SubcommandHelpTemplate = `{{.HelpName}}`
   457  	defer func() {
   458  		SubcommandHelpTemplate = tmpTemplate
   459  	}()
   460  
   461  	app := NewApp()
   462  	app.Name = "bar"
   463  	app.CustomAppHelpTemplate = `{{.HelpName}}`
   464  	app.Commands = []*Command{
   465  		{
   466  			Name:               "command1",
   467  			CustomHelpTemplate: `{{.HelpName}}`,
   468  			Subcommands: []*Command{
   469  				{
   470  					Name:               "subcommand1",
   471  					CustomHelpTemplate: `{{.HelpName}}`,
   472  				},
   473  			},
   474  		},
   475  	}
   476  
   477  	tests := []struct {
   478  		name string
   479  		args []string
   480  	}{
   481  		{
   482  			name: "App help",
   483  			args: []string{"foo"},
   484  		},
   485  		{
   486  			name: "Command help",
   487  			args: []string{"foo", "command1"},
   488  		},
   489  		{
   490  			name: "Subcommand help",
   491  			args: []string{"foo", "command1", "subcommand1"},
   492  		},
   493  	}
   494  
   495  	for _, tt := range tests {
   496  		output := &bytes.Buffer{}
   497  		app.Writer = output
   498  		if err := app.Run(tt.args); err != nil {
   499  			t.Error(err)
   500  		}
   501  		if !strings.Contains(output.String(), "bar") {
   502  			t.Errorf("expected output to contain bar; got: %q", output.String())
   503  		}
   504  	}
   505  }
   506  
   507  func TestShowSubcommandHelp_CommandAliases(t *testing.T) {
   508  	app := &App{
   509  		Commands: []*Command{
   510  			{
   511  				Name:    "frobbly",
   512  				Aliases: []string{"fr", "frob", "bork"},
   513  				Action: func(ctx *Context) error {
   514  					return nil
   515  				},
   516  			},
   517  		},
   518  	}
   519  
   520  	output := &bytes.Buffer{}
   521  	app.Writer = output
   522  	_ = app.Run([]string{"foo", "help"})
   523  
   524  	if !strings.Contains(output.String(), "frobbly, fr, frob, bork") {
   525  		t.Errorf("expected output to include all command aliases; got: %q", output.String())
   526  	}
   527  }
   528  
   529  func TestShowCommandHelp_Customtemplate(t *testing.T) {
   530  	app := &App{
   531  		Name: "foo",
   532  		Commands: []*Command{
   533  			{
   534  				Name: "frobbly",
   535  				Action: func(ctx *Context) error {
   536  					return nil
   537  				},
   538  				CustomHelpTemplate: `NAME:
   539     {{.HelpName}} - {{.Usage}}
   540  
   541  USAGE:
   542     {{.HelpName}} [FLAGS] TARGET [TARGET ...]
   543  
   544  FLAGS:
   545    {{range .VisibleFlags}}{{.}}
   546    {{end}}
   547  EXAMPLES:
   548     1. Frobbly runs with this param locally.
   549        $ {{.HelpName}} wobbly
   550  `,
   551  			},
   552  		},
   553  	}
   554  	output := &bytes.Buffer{}
   555  	app.Writer = output
   556  	_ = app.Run([]string{"foo", "help", "frobbly"})
   557  
   558  	if strings.Contains(output.String(), "2. Frobbly runs without this param locally.") {
   559  		t.Errorf("expected output to exclude \"2. Frobbly runs without this param locally.\"; got: %q", output.String())
   560  	}
   561  
   562  	if !strings.Contains(output.String(), "1. Frobbly runs with this param locally.") {
   563  		t.Errorf("expected output to include \"1. Frobbly runs with this param locally.\"; got: %q", output.String())
   564  	}
   565  
   566  	if !strings.Contains(output.String(), "$ foo frobbly wobbly") {
   567  		t.Errorf("expected output to include \"$ foo frobbly wobbly\"; got: %q", output.String())
   568  	}
   569  }
   570  
   571  func TestShowSubcommandHelp_CommandUsageText(t *testing.T) {
   572  	app := &App{
   573  		Commands: []*Command{
   574  			{
   575  				Name:      "frobbly",
   576  				UsageText: "this is usage text",
   577  			},
   578  		},
   579  	}
   580  
   581  	output := &bytes.Buffer{}
   582  	app.Writer = output
   583  
   584  	_ = app.Run([]string{"foo", "frobbly", "--help"})
   585  
   586  	if !strings.Contains(output.String(), "this is usage text") {
   587  		t.Errorf("expected output to include usage text; got: %q", output.String())
   588  	}
   589  }
   590  
   591  func TestShowSubcommandHelp_MultiLine_CommandUsageText(t *testing.T) {
   592  	app := &App{
   593  		Commands: []*Command{
   594  			{
   595  				Name: "frobbly",
   596  				UsageText: `This is a
   597  multi
   598  line
   599  UsageText`,
   600  			},
   601  		},
   602  	}
   603  
   604  	output := &bytes.Buffer{}
   605  	app.Writer = output
   606  
   607  	_ = app.Run([]string{"foo", "frobbly", "--help"})
   608  
   609  	expected := `USAGE:
   610     This is a
   611     multi
   612     line
   613     UsageText
   614  `
   615  
   616  	if !strings.Contains(output.String(), expected) {
   617  		t.Errorf("expected output to include usage text; got: %q", output.String())
   618  	}
   619  }
   620  
   621  func TestShowSubcommandHelp_SubcommandUsageText(t *testing.T) {
   622  	app := &App{
   623  		Commands: []*Command{
   624  			{
   625  				Name: "frobbly",
   626  				Subcommands: []*Command{
   627  					{
   628  						Name:      "bobbly",
   629  						UsageText: "this is usage text",
   630  					},
   631  				},
   632  			},
   633  		},
   634  	}
   635  
   636  	output := &bytes.Buffer{}
   637  	app.Writer = output
   638  	_ = app.Run([]string{"foo", "frobbly", "bobbly", "--help"})
   639  
   640  	if !strings.Contains(output.String(), "this is usage text") {
   641  		t.Errorf("expected output to include usage text; got: %q", output.String())
   642  	}
   643  }
   644  
   645  func TestShowSubcommandHelp_MultiLine_SubcommandUsageText(t *testing.T) {
   646  	app := &App{
   647  		Commands: []*Command{
   648  			{
   649  				Name: "frobbly",
   650  				Subcommands: []*Command{
   651  					{
   652  						Name: "bobbly",
   653  						UsageText: `This is a
   654  multi
   655  line
   656  UsageText`,
   657  					},
   658  				},
   659  			},
   660  		},
   661  	}
   662  
   663  	output := &bytes.Buffer{}
   664  	app.Writer = output
   665  	_ = app.Run([]string{"foo", "frobbly", "bobbly", "--help"})
   666  
   667  	expected := `USAGE:
   668     This is a
   669     multi
   670     line
   671     UsageText
   672  `
   673  
   674  	if !strings.Contains(output.String(), expected) {
   675  		t.Errorf("expected output to include usage text; got: %q", output.String())
   676  	}
   677  }
   678  
   679  func TestShowAppHelp_HiddenCommand(t *testing.T) {
   680  	app := &App{
   681  		Commands: []*Command{
   682  			{
   683  				Name: "frobbly",
   684  				Action: func(ctx *Context) error {
   685  					return nil
   686  				},
   687  			},
   688  			{
   689  				Name:   "secretfrob",
   690  				Hidden: true,
   691  				Action: func(ctx *Context) error {
   692  					return nil
   693  				},
   694  			},
   695  		},
   696  	}
   697  
   698  	output := &bytes.Buffer{}
   699  	app.Writer = output
   700  	_ = app.Run([]string{"app", "--help"})
   701  
   702  	if strings.Contains(output.String(), "secretfrob") {
   703  		t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String())
   704  	}
   705  
   706  	if !strings.Contains(output.String(), "frobbly") {
   707  		t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
   708  	}
   709  }
   710  
   711  func TestShowAppHelp_HelpPrinter(t *testing.T) {
   712  	doublecho := func(text string) string {
   713  		return text + " " + text
   714  	}
   715  
   716  	tests := []struct {
   717  		name         string
   718  		template     string
   719  		printer      helpPrinter
   720  		wantTemplate string
   721  		wantOutput   string
   722  	}{
   723  		{
   724  			name:     "standard-command",
   725  			template: "",
   726  			printer: func(w io.Writer, templ string, data interface{}) {
   727  				fmt.Fprint(w, "yo")
   728  			},
   729  			wantTemplate: AppHelpTemplate,
   730  			wantOutput:   "yo",
   731  		},
   732  		{
   733  			name:     "custom-template-command",
   734  			template: "{{doublecho .Name}}",
   735  			printer: func(w io.Writer, templ string, data interface{}) {
   736  				// Pass a custom function to ensure it gets used
   737  				fm := map[string]interface{}{"doublecho": doublecho}
   738  				printHelpCustom(w, templ, data, fm)
   739  			},
   740  			wantTemplate: "{{doublecho .Name}}",
   741  			wantOutput:   "my-app my-app",
   742  		},
   743  	}
   744  
   745  	for _, tt := range tests {
   746  		t.Run(tt.name, func(t *testing.T) {
   747  			defer func(old helpPrinter) {
   748  				HelpPrinter = old
   749  			}(HelpPrinter)
   750  			HelpPrinter = func(w io.Writer, templ string, data interface{}) {
   751  				if templ != tt.wantTemplate {
   752  					t.Errorf("want template:\n%s\ngot template:\n%s", tt.wantTemplate, templ)
   753  				}
   754  
   755  				tt.printer(w, templ, data)
   756  			}
   757  
   758  			var buf bytes.Buffer
   759  			app := &App{
   760  				Name:                  "my-app",
   761  				Writer:                &buf,
   762  				CustomAppHelpTemplate: tt.template,
   763  			}
   764  
   765  			err := app.Run([]string{"my-app", "help"})
   766  			if err != nil {
   767  				t.Fatal(err)
   768  			}
   769  
   770  			got := buf.String()
   771  			if got != tt.wantOutput {
   772  				t.Errorf("want output %q, got %q", tt.wantOutput, got)
   773  			}
   774  		})
   775  	}
   776  }
   777  
   778  func TestShowAppHelp_HelpPrinterCustom(t *testing.T) {
   779  	doublecho := func(text string) string {
   780  		return text + " " + text
   781  	}
   782  
   783  	tests := []struct {
   784  		name         string
   785  		template     string
   786  		printer      helpPrinterCustom
   787  		wantTemplate string
   788  		wantOutput   string
   789  	}{
   790  		{
   791  			name:     "standard-command",
   792  			template: "",
   793  			printer: func(w io.Writer, templ string, data interface{}, fm map[string]interface{}) {
   794  				fmt.Fprint(w, "yo")
   795  			},
   796  			wantTemplate: AppHelpTemplate,
   797  			wantOutput:   "yo",
   798  		},
   799  		{
   800  			name:     "custom-template-command",
   801  			template: "{{doublecho .Name}}",
   802  			printer: func(w io.Writer, templ string, data interface{}, _ map[string]interface{}) {
   803  				// Pass a custom function to ensure it gets used
   804  				fm := map[string]interface{}{"doublecho": doublecho}
   805  				printHelpCustom(w, templ, data, fm)
   806  			},
   807  			wantTemplate: "{{doublecho .Name}}",
   808  			wantOutput:   "my-app my-app",
   809  		},
   810  	}
   811  
   812  	for _, tt := range tests {
   813  		t.Run(tt.name, func(t *testing.T) {
   814  			defer func(old helpPrinterCustom) {
   815  				HelpPrinterCustom = old
   816  			}(HelpPrinterCustom)
   817  			HelpPrinterCustom = func(w io.Writer, templ string, data interface{}, fm map[string]interface{}) {
   818  				if fm != nil {
   819  					t.Error("unexpected function map passed")
   820  				}
   821  
   822  				if templ != tt.wantTemplate {
   823  					t.Errorf("want template:\n%s\ngot template:\n%s", tt.wantTemplate, templ)
   824  				}
   825  
   826  				tt.printer(w, templ, data, fm)
   827  			}
   828  
   829  			var buf bytes.Buffer
   830  			app := &App{
   831  				Name:                  "my-app",
   832  				Writer:                &buf,
   833  				CustomAppHelpTemplate: tt.template,
   834  			}
   835  
   836  			err := app.Run([]string{"my-app", "help"})
   837  			if err != nil {
   838  				t.Fatal(err)
   839  			}
   840  
   841  			got := buf.String()
   842  			if got != tt.wantOutput {
   843  				t.Errorf("want output %q, got %q", tt.wantOutput, got)
   844  			}
   845  		})
   846  	}
   847  }
   848  
   849  func TestShowAppHelp_CustomAppTemplate(t *testing.T) {
   850  	app := &App{
   851  		Commands: []*Command{
   852  			{
   853  				Name: "frobbly",
   854  				Action: func(ctx *Context) error {
   855  					return nil
   856  				},
   857  			},
   858  			{
   859  				Name:   "secretfrob",
   860  				Hidden: true,
   861  				Action: func(ctx *Context) error {
   862  					return nil
   863  				},
   864  			},
   865  		},
   866  		ExtraInfo: func() map[string]string {
   867  			platform := fmt.Sprintf("OS: %s | Arch: %s", runtime.GOOS, runtime.GOARCH)
   868  			goruntime := fmt.Sprintf("Version: %s | CPUs: %d", runtime.Version(), runtime.NumCPU())
   869  			return map[string]string{
   870  				"PLATFORM": platform,
   871  				"RUNTIME":  goruntime,
   872  			}
   873  		},
   874  		CustomAppHelpTemplate: `NAME:
   875    {{.Name}} - {{.Usage}}
   876  
   877  USAGE:
   878    {{.Name}} {{if .VisibleFlags}}[FLAGS] {{end}}COMMAND{{if .VisibleFlags}} [COMMAND FLAGS | -h]{{end}} [ARGUMENTS...]
   879  
   880  COMMANDS:
   881    {{range .VisibleCommands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
   882    {{end}}{{if .VisibleFlags}}
   883  GLOBAL FLAGS:
   884    {{range .VisibleFlags}}{{.}}
   885    {{end}}{{end}}
   886  VERSION:
   887    2.0.0
   888  {{"\n"}}{{range $key, $value := ExtraInfo}}
   889  {{$key}}:
   890    {{$value}}
   891  {{end}}`,
   892  	}
   893  
   894  	output := &bytes.Buffer{}
   895  	app.Writer = output
   896  	_ = app.Run([]string{"app", "--help"})
   897  
   898  	if strings.Contains(output.String(), "secretfrob") {
   899  		t.Errorf("expected output to exclude \"secretfrob\"; got: %q", output.String())
   900  	}
   901  
   902  	if !strings.Contains(output.String(), "frobbly") {
   903  		t.Errorf("expected output to include \"frobbly\"; got: %q", output.String())
   904  	}
   905  
   906  	if !strings.Contains(output.String(), "PLATFORM:") ||
   907  		!strings.Contains(output.String(), "OS:") ||
   908  		!strings.Contains(output.String(), "Arch:") {
   909  		t.Errorf("expected output to include \"PLATFORM:, OS: and Arch:\"; got: %q", output.String())
   910  	}
   911  
   912  	if !strings.Contains(output.String(), "RUNTIME:") ||
   913  		!strings.Contains(output.String(), "Version:") ||
   914  		!strings.Contains(output.String(), "CPUs:") {
   915  		t.Errorf("expected output to include \"RUNTIME:, Version: and CPUs:\"; got: %q", output.String())
   916  	}
   917  
   918  	if !strings.Contains(output.String(), "VERSION:") ||
   919  		!strings.Contains(output.String(), "2.0.0") {
   920  		t.Errorf("expected output to include \"VERSION:, 2.0.0\"; got: %q", output.String())
   921  	}
   922  }
   923  
   924  func TestShowAppHelp_UsageText(t *testing.T) {
   925  	app := &App{
   926  		UsageText: "This is a single line of UsageText",
   927  		Commands: []*Command{
   928  			{
   929  				Name: "frobbly",
   930  			},
   931  		},
   932  	}
   933  
   934  	output := &bytes.Buffer{}
   935  	app.Writer = output
   936  
   937  	_ = app.Run([]string{"foo"})
   938  
   939  	if !strings.Contains(output.String(), "This is a single line of UsageText") {
   940  		t.Errorf("expected output to include usage text; got: %q", output.String())
   941  	}
   942  }
   943  
   944  func TestShowAppHelp_MultiLine_UsageText(t *testing.T) {
   945  	app := &App{
   946  		UsageText: `This is a
   947  multi
   948  line
   949  App UsageText`,
   950  		Commands: []*Command{
   951  			{
   952  				Name: "frobbly",
   953  			},
   954  		},
   955  	}
   956  
   957  	output := &bytes.Buffer{}
   958  	app.Writer = output
   959  
   960  	_ = app.Run([]string{"foo"})
   961  
   962  	expected := `USAGE:
   963     This is a
   964     multi
   965     line
   966     App UsageText
   967  `
   968  
   969  	if !strings.Contains(output.String(), expected) {
   970  		t.Errorf("expected output to include usage text; got: %q", output.String())
   971  	}
   972  }
   973  
   974  func TestShowAppHelp_CommandMultiLine_UsageText(t *testing.T) {
   975  	app := &App{
   976  		UsageText: `This is a
   977  multi
   978  line
   979  App UsageText`,
   980  		Commands: []*Command{
   981  			{
   982  				Name:    "frobbly",
   983  				Aliases: []string{"frb1", "frbb2", "frl2"},
   984  				Usage:   "this is a long help output for the run command, long usage \noutput, long usage output, long usage output, long usage output\noutput, long usage output, long usage output",
   985  			},
   986  			{
   987  				Name:    "grobbly",
   988  				Aliases: []string{"grb1", "grbb2"},
   989  				Usage:   "this is another long help output for the run command, long usage \noutput, long usage output",
   990  			},
   991  		},
   992  	}
   993  
   994  	output := &bytes.Buffer{}
   995  	app.Writer = output
   996  
   997  	_ = app.Run([]string{"foo"})
   998  
   999  	expected := "COMMANDS:\n" +
  1000  		"   frobbly, frb1, frbb2, frl2  this is a long help output for the run command, long usage \n" +
  1001  		"                               output, long usage output, long usage output, long usage output\n" +
  1002  		"                               output, long usage output, long usage output\n" +
  1003  		"   grobbly, grb1, grbb2        this is another long help output for the run command, long usage \n" +
  1004  		"                               output, long usage output"
  1005  	if !strings.Contains(output.String(), expected) {
  1006  		t.Errorf("expected output to include usage text; got: %q", output.String())
  1007  	}
  1008  }
  1009  
  1010  func TestHideHelpCommand(t *testing.T) {
  1011  	app := &App{
  1012  		HideHelpCommand: true,
  1013  		Writer:          io.Discard,
  1014  	}
  1015  
  1016  	err := app.Run([]string{"foo", "help"})
  1017  	if err == nil {
  1018  		t.Fatalf("expected a non-nil error")
  1019  	}
  1020  	if !strings.Contains(err.Error(), "No help topic for 'help'") {
  1021  		t.Errorf("Run returned unexpected error: %v", err)
  1022  	}
  1023  
  1024  	err = app.Run([]string{"foo", "--help"})
  1025  	if err != nil {
  1026  		t.Errorf("Run returned unexpected error: %v", err)
  1027  	}
  1028  }
  1029  
  1030  func TestHideHelpCommand_False(t *testing.T) {
  1031  	app := &App{
  1032  		HideHelpCommand: false,
  1033  		Writer:          io.Discard,
  1034  	}
  1035  
  1036  	err := app.Run([]string{"foo", "help"})
  1037  	if err != nil {
  1038  		t.Errorf("Run returned unexpected error: %v", err)
  1039  	}
  1040  
  1041  	err = app.Run([]string{"foo", "--help"})
  1042  	if err != nil {
  1043  		t.Errorf("Run returned unexpected error: %v", err)
  1044  	}
  1045  }
  1046  
  1047  func TestHideHelpCommand_WithHideHelp(t *testing.T) {
  1048  	app := &App{
  1049  		HideHelp:        true, // effective (hides both command and flag)
  1050  		HideHelpCommand: true, // ignored
  1051  		Writer:          io.Discard,
  1052  	}
  1053  
  1054  	err := app.Run([]string{"foo", "help"})
  1055  	if err == nil {
  1056  		t.Fatalf("expected a non-nil error")
  1057  	}
  1058  	if !strings.Contains(err.Error(), "No help topic for 'help'") {
  1059  		t.Errorf("Run returned unexpected error: %v", err)
  1060  	}
  1061  
  1062  	err = app.Run([]string{"foo", "--help"})
  1063  	if err == nil {
  1064  		t.Fatalf("expected a non-nil error")
  1065  	}
  1066  	if !strings.Contains(err.Error(), "flag: help requested") {
  1067  		t.Errorf("Run returned unexpected error: %v", err)
  1068  	}
  1069  }
  1070  
  1071  func newContextFromStringSlice(ss []string) *Context {
  1072  	set := flag.NewFlagSet("", flag.ContinueOnError)
  1073  	_ = set.Parse(ss)
  1074  	return &Context{flagSet: set}
  1075  }
  1076  
  1077  func TestHideHelpCommand_RunAsSubcommand(t *testing.T) {
  1078  	app := &App{
  1079  		HideHelpCommand: true,
  1080  		Writer:          io.Discard,
  1081  		Commands: []*Command{
  1082  			{
  1083  				Name: "dummy",
  1084  			},
  1085  		},
  1086  	}
  1087  
  1088  	err := app.RunAsSubcommand(newContextFromStringSlice([]string{"", "help"}))
  1089  	if err == nil {
  1090  		t.Fatalf("expected a non-nil error")
  1091  	}
  1092  	if !strings.Contains(err.Error(), "No help topic for 'help'") {
  1093  		t.Errorf("Run returned unexpected error: %v", err)
  1094  	}
  1095  
  1096  	err = app.RunAsSubcommand(newContextFromStringSlice([]string{"", "--help"}))
  1097  	if err != nil {
  1098  		t.Errorf("Run returned unexpected error: %v", err)
  1099  	}
  1100  }
  1101  
  1102  func TestHideHelpCommand_RunAsSubcommand_False(t *testing.T) {
  1103  	app := &App{
  1104  		HideHelpCommand: false,
  1105  		Writer:          io.Discard,
  1106  		Commands: []*Command{
  1107  			{
  1108  				Name: "dummy",
  1109  			},
  1110  		},
  1111  	}
  1112  
  1113  	err := app.RunAsSubcommand(newContextFromStringSlice([]string{"", "help"}))
  1114  	if err != nil {
  1115  		t.Errorf("Run returned unexpected error: %v", err)
  1116  	}
  1117  
  1118  	err = app.RunAsSubcommand(newContextFromStringSlice([]string{"", "--help"}))
  1119  	if err != nil {
  1120  		t.Errorf("Run returned unexpected error: %v", err)
  1121  	}
  1122  }
  1123  
  1124  func TestHideHelpCommand_WithSubcommands(t *testing.T) {
  1125  	app := &App{
  1126  		Writer: io.Discard,
  1127  		Commands: []*Command{
  1128  			{
  1129  				Name: "dummy",
  1130  				Subcommands: []*Command{
  1131  					{
  1132  						Name: "dummy2",
  1133  					},
  1134  				},
  1135  				HideHelpCommand: true,
  1136  			},
  1137  		},
  1138  	}
  1139  
  1140  	err := app.Run([]string{"foo", "dummy", "help"})
  1141  	if err == nil {
  1142  		t.Fatalf("expected a non-nil error")
  1143  	}
  1144  	if !strings.Contains(err.Error(), "No help topic for 'help'") {
  1145  		t.Errorf("Run returned unexpected error: %v", err)
  1146  	}
  1147  
  1148  	err = app.Run([]string{"foo", "dummy", "--help"})
  1149  	if err != nil {
  1150  		t.Errorf("Run returned unexpected error: %v", err)
  1151  	}
  1152  
  1153  	var buf bytes.Buffer
  1154  	app.Writer = &buf
  1155  
  1156  	err = app.Run([]string{"foo", "dummy"})
  1157  	if err != nil {
  1158  		t.Errorf("Run returned unexpected error: %v", err)
  1159  	}
  1160  
  1161  	if !strings.Contains(buf.String(), "dummy2") {
  1162  		t.Errorf("Expected out to contain \"dummy2\" %v", buf.String())
  1163  	}
  1164  }
  1165  
  1166  func TestHideHelpCommand_RunAsSubcommand_True_CustomTemplate(t *testing.T) {
  1167  	var buf bytes.Buffer
  1168  
  1169  	app := &App{
  1170  		Writer: &buf,
  1171  		Commands: []*Command{
  1172  			{
  1173  				Name:               "dummy",
  1174  				CustomHelpTemplate: "TEMPLATE",
  1175  				HideHelpCommand:    true,
  1176  			},
  1177  		},
  1178  	}
  1179  
  1180  	err := app.RunAsSubcommand(newContextFromStringSlice([]string{"", "dummy", "-h"}))
  1181  	if err != nil {
  1182  		t.Errorf("Run returned unexpected error: %v", err)
  1183  	}
  1184  	if !strings.Contains(buf.String(), "TEMPLATE") {
  1185  		t.Errorf("Custom Help template ignored")
  1186  	}
  1187  }
  1188  
  1189  func TestDefaultCompleteWithFlags(t *testing.T) {
  1190  	origEnv := os.Environ()
  1191  	origArgv := os.Args
  1192  
  1193  	t.Cleanup(func() {
  1194  		os.Args = origArgv
  1195  		resetEnv(origEnv)
  1196  	})
  1197  
  1198  	os.Setenv("SHELL", "bash")
  1199  
  1200  	for _, tc := range []struct {
  1201  		name     string
  1202  		a        *App
  1203  		argv     []string
  1204  		expected string
  1205  	}{
  1206  		{
  1207  			name:     "empty",
  1208  			a:        &App{},
  1209  			argv:     []string{"prog", "cmd"},
  1210  			expected: "",
  1211  		},
  1212  		{
  1213  			name: "typical-flag-suggestion",
  1214  			a: &App{
  1215  				Name: "cmd",
  1216  				Flags: []Flag{
  1217  					&BoolFlag{Name: "happiness"},
  1218  					&Int64Flag{Name: "everybody-jump-on"},
  1219  				},
  1220  				Commands: []*Command{
  1221  					{Name: "putz"},
  1222  				},
  1223  			},
  1224  			argv:     []string{"cmd", "--e", "--generate-bash-completion"},
  1225  			expected: "--everybody-jump-on\n",
  1226  		},
  1227  		{
  1228  			name: "typical-command-suggestion",
  1229  			a: &App{
  1230  				Name: "cmd",
  1231  				Flags: []Flag{
  1232  					&BoolFlag{Name: "happiness"},
  1233  					&Int64Flag{Name: "everybody-jump-on"},
  1234  				},
  1235  				Commands: []*Command{
  1236  					{
  1237  						Name: "putz",
  1238  						Subcommands: []*Command{
  1239  							{Name: "futz"},
  1240  						},
  1241  						Flags: []Flag{
  1242  							&BoolFlag{Name: "excitement"},
  1243  							&StringFlag{Name: "hat-shape"},
  1244  						},
  1245  					},
  1246  				},
  1247  			},
  1248  			argv:     []string{"cmd", "--generate-bash-completion"},
  1249  			expected: "putz\n",
  1250  		},
  1251  		{
  1252  			name: "typical-subcommand-suggestion",
  1253  			a: &App{
  1254  				Name: "cmd",
  1255  				Flags: []Flag{
  1256  					&BoolFlag{Name: "happiness"},
  1257  					&Int64Flag{Name: "everybody-jump-on"},
  1258  				},
  1259  				Commands: []*Command{
  1260  					{
  1261  						Name: "putz",
  1262  						Subcommands: []*Command{
  1263  							{Name: "futz"},
  1264  						},
  1265  						Flags: []Flag{
  1266  							&BoolFlag{Name: "excitement"},
  1267  							&StringFlag{Name: "hat-shape"},
  1268  						},
  1269  					},
  1270  				},
  1271  			},
  1272  			argv:     []string{"cmd", "--happiness", "putz", "--generate-bash-completion"},
  1273  			expected: "futz\nhelp\nh\n",
  1274  		},
  1275  		{
  1276  			name: "typical-subcommand-subcommand-suggestion",
  1277  			a: &App{
  1278  				Name: "cmd",
  1279  				Flags: []Flag{
  1280  					&BoolFlag{Name: "happiness"},
  1281  					&Int64Flag{Name: "everybody-jump-on"},
  1282  				},
  1283  				Commands: []*Command{
  1284  					{
  1285  						Name: "putz",
  1286  						Subcommands: []*Command{
  1287  							{
  1288  								Name: "futz",
  1289  								Flags: []Flag{
  1290  									&BoolFlag{Name: "excitement"},
  1291  									&StringFlag{Name: "hat-shape"},
  1292  								},
  1293  							},
  1294  						},
  1295  					},
  1296  				},
  1297  			},
  1298  			argv:     []string{"cmd", "--happiness", "putz", "futz", "-e", "--generate-bash-completion"},
  1299  			expected: "--excitement\n",
  1300  		},
  1301  		{
  1302  			name: "autocomplete-with-spaces",
  1303  			a: &App{
  1304  				Name: "cmd",
  1305  				Flags: []Flag{
  1306  					&BoolFlag{Name: "happiness"},
  1307  					&Int64Flag{Name: "everybody-jump-on"},
  1308  				},
  1309  				Commands: []*Command{
  1310  					{
  1311  						Name: "putz",
  1312  						Subcommands: []*Command{
  1313  							{Name: "help"},
  1314  						},
  1315  						Flags: []Flag{
  1316  							&BoolFlag{Name: "excitement"},
  1317  							&StringFlag{Name: "hat-shape"},
  1318  						},
  1319  					},
  1320  				},
  1321  			},
  1322  			argv:     []string{"cmd", "--happiness", "putz", "h", "--generate-bash-completion"},
  1323  			expected: "help\n",
  1324  		},
  1325  	} {
  1326  		t.Run(tc.name, func(ct *testing.T) {
  1327  			writer := &bytes.Buffer{}
  1328  
  1329  			tc.a.EnableBashCompletion = true
  1330  			tc.a.HideHelp = true
  1331  			tc.a.Writer = writer
  1332  			os.Args = tc.argv
  1333  			_ = tc.a.Run(tc.argv)
  1334  
  1335  			written := writer.String()
  1336  
  1337  			if written != tc.expected {
  1338  				ct.Errorf("written help does not match expected %q != %q", written, tc.expected)
  1339  			}
  1340  		})
  1341  	}
  1342  }
  1343  
  1344  func TestWrap(t *testing.T) {
  1345  	emptywrap := wrap("", 4, 16)
  1346  	if emptywrap != "" {
  1347  		t.Errorf("Wrapping empty line should return empty line. Got '%s'.", emptywrap)
  1348  	}
  1349  }
  1350  
  1351  func TestWrappedHelp(t *testing.T) {
  1352  
  1353  	// Reset HelpPrinter after this test.
  1354  	defer func(old helpPrinter) {
  1355  		HelpPrinter = old
  1356  	}(HelpPrinter)
  1357  
  1358  	output := new(bytes.Buffer)
  1359  	app := &App{
  1360  		Writer: output,
  1361  		Flags: []Flag{
  1362  			&BoolFlag{Name: "foo",
  1363  				Aliases: []string{"h"},
  1364  				Usage:   "here's a really long help text line, let's see where it wraps. blah blah blah and so on.",
  1365  			},
  1366  		},
  1367  		Usage:     "here's a sample App.Usage string long enough that it should be wrapped in this test",
  1368  		UsageText: "i'm not sure how App.UsageText differs from App.Usage, but this should also be wrapped in this test",
  1369  		// TODO: figure out how to make ArgsUsage appear in the help text, and test that
  1370  		Description: `here's a sample App.Description string long enough that it should be wrapped in this test
  1371  
  1372  with a newline
  1373     and an indented line`,
  1374  		Copyright: `Here's a sample copyright text string long enough that it should be wrapped.
  1375  Including newlines.
  1376     And also indented lines.
  1377  
  1378  
  1379  And then another long line. Blah blah blah does anybody ever read these things?`,
  1380  	}
  1381  
  1382  	c := NewContext(app, nil, nil)
  1383  
  1384  	HelpPrinter = func(w io.Writer, templ string, data interface{}) {
  1385  		funcMap := map[string]interface{}{
  1386  			"wrapAt": func() int {
  1387  				return 30
  1388  			},
  1389  		}
  1390  
  1391  		HelpPrinterCustom(w, templ, data, funcMap)
  1392  	}
  1393  
  1394  	_ = ShowAppHelp(c)
  1395  
  1396  	expected := `NAME:
  1397      - here's a sample
  1398        App.Usage string long
  1399        enough that it should be
  1400        wrapped in this test
  1401  
  1402  USAGE:
  1403     i'm not sure how
  1404     App.UsageText differs from
  1405     App.Usage, but this should
  1406     also be wrapped in this
  1407     test
  1408  
  1409  DESCRIPTION:
  1410     here's a sample
  1411     App.Description string long
  1412     enough that it should be
  1413     wrapped in this test
  1414  
  1415     with a newline
  1416        and an indented line
  1417  
  1418  GLOBAL OPTIONS:
  1419     --foo, -h here's a
  1420        really long help text
  1421        line, let's see where it
  1422        wraps. blah blah blah
  1423        and so on. (default:
  1424        false)
  1425  
  1426  COPYRIGHT:
  1427     Here's a sample copyright
  1428     text string long enough
  1429     that it should be wrapped.
  1430     Including newlines.
  1431        And also indented lines.
  1432  
  1433  
  1434     And then another long line.
  1435     Blah blah blah does anybody
  1436     ever read these things?
  1437  `
  1438  
  1439  	if output.String() != expected {
  1440  		t.Errorf("Unexpected wrapping, got:\n%s\nexpected: %s",
  1441  			output.String(), expected)
  1442  	}
  1443  }
  1444  
  1445  func TestWrappedCommandHelp(t *testing.T) {
  1446  
  1447  	// Reset HelpPrinter after this test.
  1448  	defer func(old helpPrinter) {
  1449  		HelpPrinter = old
  1450  	}(HelpPrinter)
  1451  
  1452  	output := new(bytes.Buffer)
  1453  	app := &App{
  1454  		Writer: output,
  1455  		Commands: []*Command{
  1456  			{
  1457  				Name:        "add",
  1458  				Aliases:     []string{"a"},
  1459  				Usage:       "add a task to the list",
  1460  				UsageText:   "this is an even longer way of describing adding a task to the list",
  1461  				Description: "and a description long enough to wrap in this test case",
  1462  				Action: func(c *Context) error {
  1463  					return nil
  1464  				},
  1465  			},
  1466  		},
  1467  	}
  1468  
  1469  	c := NewContext(app, nil, nil)
  1470  
  1471  	HelpPrinter = func(w io.Writer, templ string, data interface{}) {
  1472  		funcMap := map[string]interface{}{
  1473  			"wrapAt": func() int {
  1474  				return 30
  1475  			},
  1476  		}
  1477  
  1478  		HelpPrinterCustom(w, templ, data, funcMap)
  1479  	}
  1480  
  1481  	_ = ShowCommandHelp(c, "add")
  1482  
  1483  	expected := `NAME:
  1484      - add a task to the list
  1485  
  1486  USAGE:
  1487     this is an even longer way
  1488     of describing adding a task
  1489     to the list
  1490  
  1491  DESCRIPTION:
  1492     and a description long
  1493     enough to wrap in this test
  1494     case
  1495  
  1496  OPTIONS:
  1497     --help, -h  show help
  1498  `
  1499  
  1500  	if output.String() != expected {
  1501  		t.Errorf("Unexpected wrapping, got:\n%s\nexpected:\n%s",
  1502  			output.String(), expected)
  1503  	}
  1504  }
  1505  
  1506  func TestWrappedSubcommandHelp(t *testing.T) {
  1507  
  1508  	// Reset HelpPrinter after this test.
  1509  	defer func(old helpPrinter) {
  1510  		HelpPrinter = old
  1511  	}(HelpPrinter)
  1512  
  1513  	output := new(bytes.Buffer)
  1514  	app := &App{
  1515  		Name:   "cli.test",
  1516  		Writer: output,
  1517  		Commands: []*Command{
  1518  			{
  1519  				Name:        "bar",
  1520  				Aliases:     []string{"a"},
  1521  				Usage:       "add a task to the list",
  1522  				UsageText:   "this is an even longer way of describing adding a task to the list",
  1523  				Description: "and a description long enough to wrap in this test case",
  1524  				Action: func(c *Context) error {
  1525  					return nil
  1526  				},
  1527  				Subcommands: []*Command{
  1528  					{
  1529  						Name:      "grok",
  1530  						Usage:     "remove an existing template",
  1531  						UsageText: "longer usage text goes here, la la la, hopefully this is long enough to wrap even more",
  1532  						Action: func(c *Context) error {
  1533  							return nil
  1534  						},
  1535  					},
  1536  				},
  1537  			},
  1538  		},
  1539  	}
  1540  
  1541  	HelpPrinter = func(w io.Writer, templ string, data interface{}) {
  1542  		funcMap := map[string]interface{}{
  1543  			"wrapAt": func() int {
  1544  				return 30
  1545  			},
  1546  		}
  1547  
  1548  		HelpPrinterCustom(w, templ, data, funcMap)
  1549  	}
  1550  
  1551  	_ = app.Run([]string{"foo", "bar", "grok", "--help"})
  1552  
  1553  	expected := `NAME:
  1554     cli.test bar grok - remove
  1555                         an
  1556                         existing
  1557                         template
  1558  
  1559  USAGE:
  1560     longer usage text goes
  1561     here, la la la, hopefully
  1562     this is long enough to wrap
  1563     even more
  1564  
  1565  OPTIONS:
  1566     --help, -h  show help
  1567  `
  1568  
  1569  	if output.String() != expected {
  1570  		t.Errorf("Unexpected wrapping, got:\n%s\nexpected: %s",
  1571  			output.String(), expected)
  1572  	}
  1573  }
  1574  
  1575  func TestWrappedHelpSubcommand(t *testing.T) {
  1576  
  1577  	// Reset HelpPrinter after this test.
  1578  	defer func(old helpPrinter) {
  1579  		HelpPrinter = old
  1580  	}(HelpPrinter)
  1581  
  1582  	output := new(bytes.Buffer)
  1583  	app := &App{
  1584  		Name:   "cli.test",
  1585  		Writer: output,
  1586  		Commands: []*Command{
  1587  			{
  1588  				Name:        "bar",
  1589  				Aliases:     []string{"a"},
  1590  				Usage:       "add a task to the list",
  1591  				UsageText:   "this is an even longer way of describing adding a task to the list",
  1592  				Description: "and a description long enough to wrap in this test case",
  1593  				Action: func(c *Context) error {
  1594  					return nil
  1595  				},
  1596  				Subcommands: []*Command{
  1597  					{
  1598  						Name:      "grok",
  1599  						Usage:     "remove an existing template",
  1600  						UsageText: "longer usage text goes here, la la la, hopefully this is long enough to wrap even more",
  1601  						Action: func(c *Context) error {
  1602  							return nil
  1603  						},
  1604  						Flags: []Flag{
  1605  							&StringFlag{
  1606  								Name:  "test-f",
  1607  								Usage: "my test usage",
  1608  							},
  1609  						},
  1610  					},
  1611  				},
  1612  			},
  1613  		},
  1614  	}
  1615  
  1616  	HelpPrinter = func(w io.Writer, templ string, data interface{}) {
  1617  		funcMap := map[string]interface{}{
  1618  			"wrapAt": func() int {
  1619  				return 30
  1620  			},
  1621  		}
  1622  
  1623  		HelpPrinterCustom(w, templ, data, funcMap)
  1624  	}
  1625  
  1626  	_ = app.Run([]string{"foo", "bar", "help", "grok"})
  1627  
  1628  	expected := `NAME:
  1629     cli.test bar grok - remove
  1630                         an
  1631                         existing
  1632                         template
  1633  
  1634  USAGE:
  1635     longer usage text goes
  1636     here, la la la, hopefully
  1637     this is long enough to wrap
  1638     even more
  1639  
  1640  OPTIONS:
  1641     --test-f value my test
  1642        usage
  1643     --help, -h  show help
  1644  `
  1645  
  1646  	if output.String() != expected {
  1647  		t.Errorf("Unexpected wrapping, got:\n%s\nexpected: %s",
  1648  			output.String(), expected)
  1649  	}
  1650  }
  1651  
  1652  func TestCategorizedHelp(t *testing.T) {
  1653  	// Reset HelpPrinter after this test.
  1654  	defer func(old helpPrinter) {
  1655  		HelpPrinter = old
  1656  	}(HelpPrinter)
  1657  
  1658  	output := new(bytes.Buffer)
  1659  	app := &App{
  1660  		Name:   "cli.test",
  1661  		Args:   true,
  1662  		Writer: output,
  1663  		Action: func(ctx *Context) error { return nil },
  1664  		Flags: []Flag{
  1665  			&StringFlag{
  1666  				Name: "strd", // no category set
  1667  			},
  1668  			&Int64Flag{
  1669  				Name:     "intd",
  1670  				Aliases:  []string{"altd1", "altd2"},
  1671  				Category: "cat1",
  1672  			},
  1673  		},
  1674  	}
  1675  
  1676  	c := NewContext(app, nil, nil)
  1677  	app.Setup()
  1678  
  1679  	HelpPrinter = func(w io.Writer, templ string, data interface{}) {
  1680  		funcMap := map[string]interface{}{
  1681  			"wrapAt": func() int {
  1682  				return 30
  1683  			},
  1684  		}
  1685  
  1686  		HelpPrinterCustom(w, templ, data, funcMap)
  1687  	}
  1688  
  1689  	_ = ShowAppHelp(c)
  1690  
  1691  	expected := `NAME:
  1692     cli.test - A new cli
  1693                application
  1694  
  1695  USAGE:
  1696     cli.test [global options] command [command options] [arguments...]
  1697  
  1698  COMMANDS:
  1699     help, h  Shows a list of
  1700              commands or help
  1701              for one command
  1702  
  1703  GLOBAL OPTIONS:
  1704     --help, -h    show help
  1705     --strd value  
  1706  
  1707     cat1
  1708  
  1709     --intd value, --altd1 value, --altd2 value  (default: 0)
  1710  
  1711  `
  1712  	if output.String() != expected {
  1713  		t.Errorf("Unexpected wrapping, got:\n%s\nexpected:\n%s",
  1714  			output.String(), expected)
  1715  	}
  1716  }
  1717  

View as plain text