github.com/maresnic/mr-kong@v1.0.0/help_test.go (about) 1 package kong_test 2 3 import ( 4 "bytes" 5 "fmt" 6 "strings" 7 "testing" 8 9 "github.com/alecthomas/assert/v2" 10 "github.com/maresnic/mr-kong" 11 ) 12 13 func panicsTrue(t *testing.T, f func()) { 14 defer func() { 15 if value := recover(); value != nil { 16 if boolval, ok := value.(bool); !ok || !boolval { 17 t.Fatalf("expected panic with true but got %v", value) 18 } 19 } 20 }() 21 f() 22 t.Fatal("expected panic did not occur") 23 } 24 25 type threeArg struct { 26 RequiredThree bool `required` 27 Three string `arg` 28 } 29 30 func (threeArg) Help() string { 31 return `Detailed help provided through the HelpProvider interface.` 32 } 33 34 func TestHelpOptionalArgs(t *testing.T) { 35 var cli struct { 36 One string `arg:"" optional:"" help:"One optional arg."` 37 Two string `arg:"" optional:"" help:"Two optional arg."` 38 } 39 w := bytes.NewBuffer(nil) 40 exited := false 41 app := mustNew(t, &cli, 42 kong.Name("test-app"), 43 kong.Writers(w, w), 44 kong.Exit(func(int) { 45 exited = true 46 panic(true) // Panic to fake "exit". 47 }), 48 ) 49 panicsTrue(t, func() { 50 _, err := app.Parse([]string{"--help"}) 51 assert.NoError(t, err) 52 }) 53 assert.True(t, exited) 54 expected := `Usage: test-app [<one> [<two>]] [flags] 55 56 Arguments: 57 [<one>] One optional arg. 58 [<two>] Two optional arg. 59 60 Flags: 61 -h, --help Show context-sensitive help. 62 ` 63 assert.Equal(t, expected, w.String()) 64 } 65 66 func TestHelp(t *testing.T) { 67 var cli struct { 68 String string `help:"A string flag."` 69 Bool bool `help:"A bool flag with very long help that wraps a lot and is verbose and is really verbose."` 70 Slice []string `help:"A slice of strings." placeholder:"STR"` 71 Map map[string]int `help:"A map of strings to ints."` 72 Required bool `required help:"A required flag."` 73 Sort bool `negatable short:"s" help:"Is sortable or not."` 74 75 One struct { 76 Flag string `help:"Nested flag."` 77 } `cmd help:"A subcommand."` 78 79 Two struct { 80 Flag string `help:"Nested flag under two."` 81 RequiredTwo bool `required` 82 83 Three threeArg `arg help:"Sub-sub-arg."` 84 85 Four struct { 86 } `cmd help:"Sub-sub-command."` 87 } `cmd help:"Another subcommand."` 88 } 89 90 w := bytes.NewBuffer(nil) 91 exited := false 92 app := mustNew(t, &cli, 93 kong.Name("test-app"), 94 kong.Description("A test app."), 95 kong.Writers(w, w), 96 kong.Exit(func(int) { 97 exited = true 98 panic(true) // Panic to fake "exit". 99 }), 100 ) 101 102 t.Run("Full", func(t *testing.T) { 103 panicsTrue(t, func() { 104 _, err := app.Parse([]string{"--help"}) 105 assert.NoError(t, err) 106 }) 107 assert.True(t, exited) 108 expected := `Usage: test-app --required <command> [flags] 109 110 A test app. 111 112 Flags: 113 -h, --help Show context-sensitive help. 114 --string=STRING A string flag. 115 --bool A bool flag with very long help that wraps a lot 116 and is verbose and is really verbose. 117 --slice=STR,... A slice of strings. 118 --map=KEY=VALUE;... A map of strings to ints. 119 --required A required flag. 120 -s, --[no-]sort Is sortable or not. 121 122 Commands: 123 one --required [flags] 124 A subcommand. 125 126 two <three> --required --required-two --required-three [flags] 127 Sub-sub-arg. 128 129 two four --required --required-two [flags] 130 Sub-sub-command. 131 132 Run "test-app <command> --help" for more information on a command. 133 ` 134 t.Log(w.String()) 135 t.Log(expected) 136 assert.Equal(t, expected, w.String()) 137 }) 138 139 t.Run("Selected", func(t *testing.T) { 140 exited = false 141 w.Truncate(0) 142 panicsTrue(t, func() { 143 _, err := app.Parse([]string{"two", "hello", "--help"}) 144 assert.NoError(t, err) 145 }) 146 assert.True(t, exited) 147 expected := `Usage: test-app two <three> --required --required-two --required-three [flags] 148 149 Sub-sub-arg. 150 151 Detailed help provided through the HelpProvider interface. 152 153 Flags: 154 -h, --help Show context-sensitive help. 155 --string=STRING A string flag. 156 --bool A bool flag with very long help that wraps a lot 157 and is verbose and is really verbose. 158 --slice=STR,... A slice of strings. 159 --map=KEY=VALUE;... A map of strings to ints. 160 --required A required flag. 161 -s, --[no-]sort Is sortable or not. 162 163 --flag=STRING Nested flag under two. 164 --required-two 165 166 --required-three 167 ` 168 t.Log(expected) 169 t.Log(w.String()) 170 assert.Equal(t, expected, w.String()) 171 }) 172 } 173 174 func TestFlagsLast(t *testing.T) { 175 var cli struct { 176 String string `help:"A string flag."` 177 Bool bool `help:"A bool flag with very long help that wraps a lot and is verbose and is really verbose."` 178 Slice []string `help:"A slice of strings." placeholder:"STR"` 179 Map map[string]int `help:"A map of strings to ints."` 180 Required bool `required help:"A required flag."` 181 182 One struct { 183 Flag string `help:"Nested flag."` 184 } `cmd help:"A subcommand."` 185 186 Two struct { 187 Flag string `help:"Nested flag under two."` 188 RequiredTwo bool `required` 189 190 Three threeArg `arg help:"Sub-sub-arg."` 191 192 Four struct { 193 } `cmd help:"Sub-sub-command."` 194 } `cmd help:"Another subcommand."` 195 } 196 197 w := bytes.NewBuffer(nil) 198 exited := false 199 app := mustNew(t, &cli, 200 kong.Name("test-app"), 201 kong.Description("A test app."), 202 kong.HelpOptions{ 203 FlagsLast: true, 204 }, 205 kong.Writers(w, w), 206 kong.Exit(func(int) { 207 exited = true 208 panic(true) // Panic to fake "exit". 209 }), 210 ) 211 212 t.Run("Full", func(t *testing.T) { 213 panicsTrue(t, func() { 214 _, err := app.Parse([]string{"--help"}) 215 assert.NoError(t, err) 216 }) 217 assert.True(t, exited) 218 expected := `Usage: test-app --required <command> [flags] 219 220 A test app. 221 222 Commands: 223 one --required [flags] 224 A subcommand. 225 226 two <three> --required --required-two --required-three [flags] 227 Sub-sub-arg. 228 229 two four --required --required-two [flags] 230 Sub-sub-command. 231 232 Flags: 233 -h, --help Show context-sensitive help. 234 --string=STRING A string flag. 235 --bool A bool flag with very long help that wraps a lot 236 and is verbose and is really verbose. 237 --slice=STR,... A slice of strings. 238 --map=KEY=VALUE;... A map of strings to ints. 239 --required A required flag. 240 241 Run "test-app <command> --help" for more information on a command. 242 ` 243 t.Log(w.String()) 244 t.Log(expected) 245 assert.Equal(t, expected, w.String()) 246 }) 247 248 t.Run("Selected", func(t *testing.T) { 249 exited = false 250 w.Truncate(0) 251 panicsTrue(t, func() { 252 _, err := app.Parse([]string{"two", "hello", "--help"}) 253 assert.NoError(t, err) 254 }) 255 assert.True(t, exited) 256 expected := `Usage: test-app two <three> --required --required-two --required-three [flags] 257 258 Sub-sub-arg. 259 260 Detailed help provided through the HelpProvider interface. 261 262 Flags: 263 -h, --help Show context-sensitive help. 264 --string=STRING A string flag. 265 --bool A bool flag with very long help that wraps a lot 266 and is verbose and is really verbose. 267 --slice=STR,... A slice of strings. 268 --map=KEY=VALUE;... A map of strings to ints. 269 --required A required flag. 270 271 --flag=STRING Nested flag under two. 272 --required-two 273 274 --required-three 275 ` 276 t.Log(expected) 277 t.Log(w.String()) 278 assert.Equal(t, expected, w.String()) 279 }) 280 } 281 282 func TestHelpTree(t *testing.T) { 283 var cli struct { 284 One struct { 285 Thing struct { 286 Arg string `arg help:"argument"` 287 } `cmd help:"subcommand thing"` 288 Other struct { 289 Other string `arg help:"other arg"` 290 } `arg help:"subcommand other"` 291 } `cmd help:"subcommand one" group:"Group A" aliases:"un,uno"` // Groups are ignored in trees 292 293 Two struct { 294 Three threeArg `arg help:"Sub-sub-arg."` 295 296 Four struct { 297 } `cmd help:"Sub-sub-command." aliases:"for,fore"` 298 } `cmd help:"Another subcommand."` 299 } 300 301 w := bytes.NewBuffer(nil) 302 exited := false 303 app := mustNew(t, &cli, 304 kong.Name("test-app"), 305 kong.Description("A test app."), 306 kong.Writers(w, w), 307 kong.ConfigureHelp(kong.HelpOptions{ 308 Tree: true, 309 Indenter: kong.LineIndenter, 310 }), 311 kong.Exit(func(int) { 312 exited = true 313 panic(true) // Panic to fake "exit". 314 }), 315 ) 316 317 t.Run("Full", func(t *testing.T) { 318 panicsTrue(t, func() { 319 _, err := app.Parse([]string{"--help"}) 320 assert.NoError(t, err) 321 }) 322 assert.True(t, exited) 323 expected := `Usage: test-app <command> [flags] 324 325 A test app. 326 327 Flags: 328 -h, --help Show context-sensitive help. 329 330 Commands: 331 one (un,uno) subcommand one 332 - thing subcommand thing 333 - <arg> argument 334 - <other> subcommand other 335 336 two Another subcommand. 337 - <three> Sub-sub-arg. 338 - four (for,fore) Sub-sub-command. 339 340 Run "test-app <command> --help" for more information on a command. 341 ` 342 if expected != w.String() { 343 t.Errorf("help command returned:\n%v\n\nwant:\n%v", w.String(), expected) 344 } 345 assert.Equal(t, expected, w.String()) 346 }) 347 348 t.Run("Selected", func(t *testing.T) { 349 exited = false 350 w.Truncate(0) 351 panicsTrue(t, func() { 352 _, err := app.Parse([]string{"one", "--help"}) 353 assert.NoError(t, err) 354 }) 355 assert.True(t, exited) 356 expected := `Usage: test-app one (un,uno) <command> [flags] 357 358 subcommand one 359 360 Flags: 361 -h, --help Show context-sensitive help. 362 363 Commands: 364 thing subcommand thing 365 - <arg> argument 366 367 <other> subcommand other 368 ` 369 if expected != w.String() { 370 t.Errorf("help command returned:\n%v\n\nwant:\n%v", w.String(), expected) 371 } 372 assert.Equal(t, expected, w.String()) 373 }) 374 } 375 376 func TestHelpCompactNoExpand(t *testing.T) { 377 var cli struct { 378 One struct { 379 Thing struct { 380 Arg string `arg help:"argument"` 381 } `cmd help:"subcommand thing"` 382 Other struct { 383 Other string `arg help:"other arg"` 384 } `arg help:"subcommand other"` 385 } `cmd help:"subcommand one" group:"Group A" aliases:"un,uno"` // Groups are ignored in trees 386 387 Two struct { 388 Three threeArg `arg help:"Sub-sub-arg."` 389 390 Four struct { 391 } `cmd help:"Sub-sub-command." aliases:"for,fore"` 392 } `cmd help:"Another subcommand."` 393 } 394 395 w := bytes.NewBuffer(nil) 396 exited := false 397 app := mustNew(t, &cli, 398 kong.Name("test-app"), 399 kong.Description("A test app."), 400 kong.Writers(w, w), 401 kong.ConfigureHelp(kong.HelpOptions{ 402 Compact: true, 403 NoExpandSubcommands: true, 404 }), 405 kong.Exit(func(int) { 406 exited = true 407 panic(true) // Panic to fake "exit". 408 }), 409 ) 410 411 t.Run("Full", func(t *testing.T) { 412 panicsTrue(t, func() { 413 _, err := app.Parse([]string{"--help"}) 414 assert.NoError(t, err) 415 }) 416 assert.True(t, exited) 417 expected := `Usage: test-app <command> [flags] 418 419 A test app. 420 421 Flags: 422 -h, --help Show context-sensitive help. 423 424 Commands: 425 two Another subcommand. 426 427 Group A 428 one (un,uno) subcommand one 429 430 Run "test-app <command> --help" for more information on a command. 431 ` 432 if expected != w.String() { 433 t.Errorf("help command returned:\n%v\n\nwant:\n%v", w.String(), expected) 434 } 435 assert.Equal(t, expected, w.String()) 436 }) 437 438 t.Run("Selected", func(t *testing.T) { 439 exited = false 440 w.Truncate(0) 441 panicsTrue(t, func() { 442 _, err := app.Parse([]string{"one", "--help"}) 443 assert.NoError(t, err) 444 }) 445 assert.True(t, exited) 446 expected := `Usage: test-app one (un,uno) <command> [flags] 447 448 subcommand one 449 450 Flags: 451 -h, --help Show context-sensitive help. 452 453 Group A 454 one (un,uno) thing subcommand thing 455 one (un,uno) <other> subcommand other 456 ` 457 if expected != w.String() { 458 t.Errorf("help command returned:\n%v\n\nwant:\n%v", w.String(), expected) 459 } 460 assert.Equal(t, expected, w.String()) 461 }) 462 } 463 464 func TestEnvarAutoHelp(t *testing.T) { 465 var cli struct { 466 Flag string `env:"FLAG" help:"A flag."` 467 } 468 w := &strings.Builder{} 469 p := mustNew(t, &cli, kong.Writers(w, w), kong.Exit(func(int) {})) 470 _, err := p.Parse([]string{"--help"}) 471 assert.NoError(t, err) 472 assert.Contains(t, w.String(), "A flag ($FLAG).") 473 } 474 475 func TestMultipleEnvarAutoHelp(t *testing.T) { 476 var cli struct { 477 Flag string `env:"FLAG1,FLAG2" help:"A flag."` 478 } 479 w := &strings.Builder{} 480 p := mustNew(t, &cli, kong.Writers(w, w), kong.Exit(func(int) {})) 481 _, err := p.Parse([]string{"--help"}) 482 assert.NoError(t, err) 483 assert.Contains(t, w.String(), "A flag ($FLAG1, $FLAG2).") 484 } 485 486 //nolint:dupl // false positive 487 func TestEnvarAutoHelpWithEnvPrefix(t *testing.T) { 488 type Anonymous struct { 489 Flag string `env:"FLAG" help:"A flag."` 490 Other string `help:"A different flag."` 491 } 492 var cli struct { 493 Anonymous `envprefix:"ANON_"` 494 } 495 w := &strings.Builder{} 496 p := mustNew(t, &cli, kong.Writers(w, w), kong.Exit(func(int) {})) 497 _, err := p.Parse([]string{"--help"}) 498 assert.NoError(t, err) 499 assert.Contains(t, w.String(), "A flag ($ANON_FLAG).") 500 assert.Contains(t, w.String(), "A different flag.") 501 } 502 503 //nolint:dupl // false positive 504 func TestMultipleEnvarAutoHelpWithEnvPrefix(t *testing.T) { 505 type Anonymous struct { 506 Flag string `env:"FLAG1,FLAG2" help:"A flag."` 507 Other string `help:"A different flag."` 508 } 509 var cli struct { 510 Anonymous `envprefix:"ANON_"` 511 } 512 w := &strings.Builder{} 513 p := mustNew(t, &cli, kong.Writers(w, w), kong.Exit(func(int) {})) 514 _, err := p.Parse([]string{"--help"}) 515 assert.NoError(t, err) 516 assert.Contains(t, w.String(), "A flag ($ANON_FLAG1, $ANON_FLAG2).") 517 assert.Contains(t, w.String(), "A different flag.") 518 } 519 520 //nolint:dupl // false positive 521 func TestCustomValueFormatter(t *testing.T) { 522 var cli struct { 523 Flag string `env:"FLAG" help:"A flag."` 524 } 525 w := &strings.Builder{} 526 p := mustNew(t, &cli, 527 kong.Writers(w, w), 528 kong.Exit(func(int) {}), 529 kong.ValueFormatter(func(value *kong.Value) string { 530 return value.Help 531 }), 532 ) 533 _, err := p.Parse([]string{"--help"}) 534 assert.NoError(t, err) 535 assert.Contains(t, w.String(), "A flag.") 536 } 537 538 //nolint:dupl // false positive 539 func TestMultipleCustomValueFormatter(t *testing.T) { 540 var cli struct { 541 Flag string `env:"FLAG1,FLAG2" help:"A flag."` 542 } 543 w := &strings.Builder{} 544 p := mustNew(t, &cli, 545 kong.Writers(w, w), 546 kong.Exit(func(int) {}), 547 kong.ValueFormatter(func(value *kong.Value) string { 548 return value.Help 549 }), 550 ) 551 _, err := p.Parse([]string{"--help"}) 552 assert.NoError(t, err) 553 assert.Contains(t, w.String(), "A flag.") 554 } 555 556 func TestAutoGroup(t *testing.T) { 557 var cli struct { 558 GroupedAString string `help:"A string flag grouped in A."` 559 FreeString string `help:"A non grouped string flag."` 560 GroupedBString string `help:"A string flag grouped in B."` 561 FreeBool bool `help:"A non grouped bool flag."` 562 GroupedABool bool `help:"A bool flag grouped in A."` 563 564 One struct { 565 Flag string `help:"Nested flag."` 566 // Group is inherited from the parent command 567 Thing struct { 568 Arg string `arg help:"argument"` 569 } `cmd help:"subcommand thing"` 570 Other struct { 571 Other string `arg help:"other arg"` 572 } `arg help:"subcommand other"` 573 // ... but a subcommand can override it 574 Stuff struct { 575 Stuff string `arg help:"argument"` 576 } `arg help:"subcommand stuff"` 577 } `cmd help:"A subcommand grouped in A."` 578 579 Two struct { 580 Grouped1String string `help:"A string flag grouped in 1."` 581 AFreeString string `help:"A non grouped string flag."` 582 Grouped2String string `help:"A string flag grouped in 2."` 583 AGroupedAString bool `help:"A string flag grouped in A."` 584 Grouped1Bool bool `help:"A bool flag grouped in 1."` 585 } `cmd help:"A non grouped subcommand."` 586 587 Four struct { 588 Flag string `help:"Nested flag."` 589 } `cmd help:"Another subcommand grouped in B."` 590 591 Three struct { 592 Flag string `help:"Nested flag."` 593 } `cmd help:"Another subcommand grouped in A."` 594 } 595 w := bytes.NewBuffer(nil) 596 app := mustNew(t, &cli, 597 kong.Writers(w, w), 598 kong.Exit(func(int) {}), 599 kong.AutoGroup(func(parent kong.Visitable, flag *kong.Flag) *kong.Group { 600 if node, ok := parent.(*kong.Node); ok { 601 return &kong.Group{ 602 Key: node.Name, 603 Title: strings.Title(node.Name) + " flags:", //nolint 604 } 605 } 606 return nil 607 }), 608 ) 609 _, _ = app.Parse([]string{"--help", "two"}) 610 assert.Equal(t, `Usage: test two [flags] 611 612 A non grouped subcommand. 613 614 Flags: 615 -h, --help Show context-sensitive help. 616 --grouped-a-string=STRING A string flag grouped in A. 617 --free-string=STRING A non grouped string flag. 618 --grouped-b-string=STRING A string flag grouped in B. 619 --free-bool A non grouped bool flag. 620 --grouped-a-bool A bool flag grouped in A. 621 622 Two flags: 623 --grouped-1-string=STRING A string flag grouped in 1. 624 --a-free-string=STRING A non grouped string flag. 625 --grouped-2-string=STRING A string flag grouped in 2. 626 --a-grouped-a-string A string flag grouped in A. 627 --grouped-1-bool A bool flag grouped in 1. 628 `, w.String()) 629 } 630 631 func TestHelpGrouping(t *testing.T) { 632 var cli struct { 633 GroupedAString string `help:"A string flag grouped in A." group:"Group A"` 634 FreeString string `help:"A non grouped string flag."` 635 GroupedBString string `help:"A string flag grouped in B." group:"Group B"` 636 FreeBool bool `help:"A non grouped bool flag."` 637 GroupedABool bool `help:"A bool flag grouped in A." group:"Group A"` 638 639 One struct { 640 Flag string `help:"Nested flag."` 641 // Group is inherited from the parent command 642 Thing struct { 643 Arg string `arg help:"argument"` 644 } `cmd help:"subcommand thing"` 645 Other struct { 646 Other string `arg help:"other arg"` 647 } `arg help:"subcommand other"` 648 // ... but a subcommand can override it 649 Stuff struct { 650 Stuff string `arg help:"argument"` 651 } `arg help:"subcommand stuff" group:"Group B"` 652 } `cmd help:"A subcommand grouped in A." group:"Group A"` 653 654 Two struct { 655 Grouped1String string `help:"A string flag grouped in 1." group:"Group 1"` 656 AFreeString string `help:"A non grouped string flag."` 657 Grouped2String string `help:"A string flag grouped in 2." group:"Group 2"` 658 AGroupedAString bool `help:"A string flag grouped in A." group:"Group A"` 659 Grouped1Bool bool `help:"A bool flag grouped in 1." group:"Group 1"` 660 } `cmd help:"A non grouped subcommand."` 661 662 Four struct { 663 Flag string `help:"Nested flag."` 664 } `cmd help:"Another subcommand grouped in B." group:"Group B"` 665 666 Three struct { 667 Flag string `help:"Nested flag."` 668 } `cmd help:"Another subcommand grouped in A." group:"Group A"` 669 } 670 671 w := bytes.NewBuffer(nil) 672 exited := false 673 app := mustNew(t, &cli, 674 kong.Name("test-app"), 675 kong.Description("A test app."), 676 kong.Groups{ 677 "Group A": "Group title taken from the kong.ExplicitGroups option\nA group header", 678 "Group 1": "Another group title, this time without header", 679 "Unknown key": "", 680 }, 681 kong.Writers(w, w), 682 kong.Exit(func(int) { 683 exited = true 684 panic(true) // Panic to fake "exit". 685 }), 686 ) 687 688 t.Run("Full", func(t *testing.T) { 689 panicsTrue(t, func() { 690 _, err := app.Parse([]string{"--help"}) 691 assert.True(t, exited) 692 assert.NoError(t, err) 693 }) 694 expected := `Usage: test-app <command> [flags] 695 696 A test app. 697 698 Flags: 699 -h, --help Show context-sensitive help. 700 --free-string=STRING A non grouped string flag. 701 --free-bool A non grouped bool flag. 702 703 Group title taken from the kong.ExplicitGroups option 704 A group header 705 706 --grouped-a-string=STRING A string flag grouped in A. 707 --grouped-a-bool A bool flag grouped in A. 708 709 Group B 710 --grouped-b-string=STRING A string flag grouped in B. 711 712 Commands: 713 two [flags] 714 A non grouped subcommand. 715 716 Group title taken from the kong.ExplicitGroups option 717 A group header 718 719 one thing <arg> [flags] 720 subcommand thing 721 722 one <other> [flags] 723 subcommand other 724 725 three [flags] 726 Another subcommand grouped in A. 727 728 Group B 729 one <stuff> [flags] 730 subcommand stuff 731 732 four [flags] 733 Another subcommand grouped in B. 734 735 Run "test-app <command> --help" for more information on a command. 736 ` 737 t.Log(w.String()) 738 t.Log(expected) 739 assert.Equal(t, expected, w.String()) 740 }) 741 742 t.Run("Selected", func(t *testing.T) { 743 exited = false 744 w.Truncate(0) 745 panicsTrue(t, func() { 746 _, err := app.Parse([]string{"two", "--help"}) 747 assert.NoError(t, err) 748 assert.True(t, exited) 749 }) 750 expected := `Usage: test-app two [flags] 751 752 A non grouped subcommand. 753 754 Flags: 755 -h, --help Show context-sensitive help. 756 --free-string=STRING A non grouped string flag. 757 --free-bool A non grouped bool flag. 758 759 --a-free-string=STRING A non grouped string flag. 760 761 Group title taken from the kong.ExplicitGroups option 762 A group header 763 764 --grouped-a-string=STRING A string flag grouped in A. 765 --grouped-a-bool A bool flag grouped in A. 766 767 --a-grouped-a-string A string flag grouped in A. 768 769 Group B 770 --grouped-b-string=STRING A string flag grouped in B. 771 772 Another group title, this time without header 773 --grouped-1-string=STRING A string flag grouped in 1. 774 --grouped-1-bool A bool flag grouped in 1. 775 776 Group 2 777 --grouped-2-string=STRING A string flag grouped in 2. 778 ` 779 t.Log(expected) 780 t.Log(w.String()) 781 assert.Equal(t, expected, w.String()) 782 }) 783 } 784 785 func TestUsageOnError(t *testing.T) { 786 var cli struct { 787 Flag string `help:"A required flag." required` 788 } 789 w := &strings.Builder{} 790 p := mustNew(t, &cli, 791 kong.Writers(w, w), 792 kong.Description("Some description."), 793 kong.Exit(func(int) {}), 794 kong.UsageOnError(), 795 ) 796 _, err := p.Parse([]string{}) 797 p.FatalIfErrorf(err) 798 799 expected := `Usage: test --flag=STRING [flags] 800 801 Some description. 802 803 Flags: 804 -h, --help Show context-sensitive help. 805 --flag=STRING A required flag. 806 807 test: error: missing flags: --flag=STRING 808 ` 809 assert.Equal(t, expected, w.String()) 810 } 811 812 func TestShortUsageOnError(t *testing.T) { 813 var cli struct { 814 Flag string `help:"A required flag." required` 815 } 816 w := &strings.Builder{} 817 p := mustNew(t, &cli, 818 kong.Writers(w, w), 819 kong.Description("Some description."), 820 kong.Exit(func(int) {}), 821 kong.ShortUsageOnError(), 822 ) 823 _, err := p.Parse([]string{}) 824 assert.Error(t, err) 825 p.FatalIfErrorf(err) 826 827 expected := `Usage: test --flag=STRING [flags] 828 Run "test --help" for more information. 829 830 test: error: missing flags: --flag=STRING 831 ` 832 assert.Equal(t, expected, w.String()) 833 } 834 835 func TestCustomShortUsageOnError(t *testing.T) { 836 var cli struct { 837 Flag string `help:"A required flag." required` 838 } 839 w := &strings.Builder{} 840 shortHelp := func(_ kong.HelpOptions, ctx *kong.Context) error { 841 fmt.Fprintln(ctx.Stdout, "🤷 wish I could help") 842 return nil 843 } 844 p := mustNew(t, &cli, 845 kong.Writers(w, w), 846 kong.Description("Some description."), 847 kong.Exit(func(int) {}), 848 kong.ShortHelp(shortHelp), 849 kong.ShortUsageOnError(), 850 ) 851 _, err := p.Parse([]string{}) 852 assert.Error(t, err) 853 p.FatalIfErrorf(err) 854 855 expected := `🤷 wish I could help 856 857 test: error: missing flags: --flag=STRING 858 ` 859 assert.Equal(t, expected, w.String()) 860 }