1package cobra 2 3import ( 4 "bytes" 5 "context" 6 "strings" 7 "testing" 8) 9 10func validArgsFunc(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 11 if len(args) != 0 { 12 return nil, ShellCompDirectiveNoFileComp 13 } 14 15 var completions []string 16 for _, comp := range []string{"one\tThe first", "two\tThe second"} { 17 if strings.HasPrefix(comp, toComplete) { 18 completions = append(completions, comp) 19 } 20 } 21 return completions, ShellCompDirectiveDefault 22} 23 24func validArgsFunc2(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 25 if len(args) != 0 { 26 return nil, ShellCompDirectiveNoFileComp 27 } 28 29 var completions []string 30 for _, comp := range []string{"three\tThe third", "four\tThe fourth"} { 31 if strings.HasPrefix(comp, toComplete) { 32 completions = append(completions, comp) 33 } 34 } 35 return completions, ShellCompDirectiveDefault 36} 37 38func TestCmdNameCompletionInGo(t *testing.T) { 39 rootCmd := &Command{ 40 Use: "root", 41 Run: emptyRun, 42 } 43 childCmd1 := &Command{ 44 Use: "firstChild", 45 Short: "First command", 46 Run: emptyRun, 47 } 48 childCmd2 := &Command{ 49 Use: "secondChild", 50 Run: emptyRun, 51 } 52 hiddenCmd := &Command{ 53 Use: "testHidden", 54 Hidden: true, // Not completed 55 Run: emptyRun, 56 } 57 deprecatedCmd := &Command{ 58 Use: "testDeprecated", 59 Deprecated: "deprecated", // Not completed 60 Run: emptyRun, 61 } 62 aliasedCmd := &Command{ 63 Use: "aliased", 64 Short: "A command with aliases", 65 Aliases: []string{"testAlias", "testSynonym"}, // Not completed 66 Run: emptyRun, 67 } 68 69 rootCmd.AddCommand(childCmd1, childCmd2, hiddenCmd, deprecatedCmd, aliasedCmd) 70 71 // Test that sub-command names are completed 72 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 73 if err != nil { 74 t.Errorf("Unexpected error: %v", err) 75 } 76 77 expected := strings.Join([]string{ 78 "aliased", 79 "completion", 80 "firstChild", 81 "help", 82 "secondChild", 83 ":4", 84 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 85 86 if output != expected { 87 t.Errorf("expected: %q, got: %q", expected, output) 88 } 89 90 // Test that sub-command names are completed with prefix 91 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "s") 92 if err != nil { 93 t.Errorf("Unexpected error: %v", err) 94 } 95 96 expected = strings.Join([]string{ 97 "secondChild", 98 ":4", 99 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 100 101 if output != expected { 102 t.Errorf("expected: %q, got: %q", expected, output) 103 } 104 105 // Test that even with no valid sub-command matches, hidden, deprecated and 106 // aliases are not completed 107 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "test") 108 if err != nil { 109 t.Errorf("Unexpected error: %v", err) 110 } 111 112 expected = strings.Join([]string{ 113 ":4", 114 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 115 116 if output != expected { 117 t.Errorf("expected: %q, got: %q", expected, output) 118 } 119 120 // Test that sub-command names are completed with description 121 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "") 122 if err != nil { 123 t.Errorf("Unexpected error: %v", err) 124 } 125 126 expected = strings.Join([]string{ 127 "aliased\tA command with aliases", 128 "completion\tgenerate the autocompletion script for the specified shell", 129 "firstChild\tFirst command", 130 "help\tHelp about any command", 131 "secondChild", 132 ":4", 133 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 134 135 if output != expected { 136 t.Errorf("expected: %q, got: %q", expected, output) 137 } 138} 139 140func TestNoCmdNameCompletionInGo(t *testing.T) { 141 rootCmd := &Command{ 142 Use: "root", 143 Run: emptyRun, 144 } 145 rootCmd.Flags().String("localroot", "", "local root flag") 146 147 childCmd1 := &Command{ 148 Use: "childCmd1", 149 Short: "First command", 150 Args: MinimumNArgs(0), 151 Run: emptyRun, 152 } 153 rootCmd.AddCommand(childCmd1) 154 childCmd1.PersistentFlags().StringP("persistent", "p", "", "persistent flag") 155 persistentFlag := childCmd1.PersistentFlags().Lookup("persistent") 156 childCmd1.Flags().StringP("nonPersistent", "n", "", "non-persistent flag") 157 nonPersistentFlag := childCmd1.Flags().Lookup("nonPersistent") 158 159 childCmd2 := &Command{ 160 Use: "childCmd2", 161 Run: emptyRun, 162 } 163 childCmd1.AddCommand(childCmd2) 164 165 // Test that sub-command names are not completed if there is an argument already 166 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "arg1", "") 167 if err != nil { 168 t.Errorf("Unexpected error: %v", err) 169 } 170 171 expected := strings.Join([]string{ 172 ":0", 173 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 174 175 if output != expected { 176 t.Errorf("expected: %q, got: %q", expected, output) 177 } 178 179 // Test that sub-command names are not completed if a local non-persistent flag is present 180 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "--nonPersistent", "value", "") 181 if err != nil { 182 t.Errorf("Unexpected error: %v", err) 183 } 184 // Reset the flag for the next command 185 nonPersistentFlag.Changed = false 186 187 expected = strings.Join([]string{ 188 ":0", 189 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 190 191 if output != expected { 192 t.Errorf("expected: %q, got: %q", expected, output) 193 } 194 195 // Test that sub-command names are completed if a local non-persistent flag is present and TraverseChildren is set to true 196 // set TraverseChildren to true on the root cmd 197 rootCmd.TraverseChildren = true 198 199 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "") 200 if err != nil { 201 t.Errorf("Unexpected error: %v", err) 202 } 203 // Reset TraverseChildren for next command 204 rootCmd.TraverseChildren = false 205 206 expected = strings.Join([]string{ 207 "childCmd1", 208 "completion", 209 "help", 210 ":4", 211 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 212 213 if output != expected { 214 t.Errorf("expected: %q, got: %q", expected, output) 215 } 216 217 // Test that sub-command names from a child cmd are completed if a local non-persistent flag is present 218 // and TraverseChildren is set to true on the root cmd 219 rootCmd.TraverseChildren = true 220 221 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "--nonPersistent", "value", "") 222 if err != nil { 223 t.Errorf("Unexpected error: %v", err) 224 } 225 // Reset TraverseChildren for next command 226 rootCmd.TraverseChildren = false 227 // Reset the flag for the next command 228 nonPersistentFlag.Changed = false 229 230 expected = strings.Join([]string{ 231 "childCmd2", 232 ":4", 233 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 234 235 if output != expected { 236 t.Errorf("expected: %q, got: %q", expected, output) 237 } 238 239 // Test that we don't use Traverse when we shouldn't. 240 // This command should not return a completion since the command line is invalid without TraverseChildren. 241 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "") 242 if err != nil { 243 t.Errorf("Unexpected error: %v", err) 244 } 245 246 expected = strings.Join([]string{ 247 ":0", 248 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 249 250 if output != expected { 251 t.Errorf("expected: %q, got: %q", expected, output) 252 } 253 254 // Test that sub-command names are not completed if a local non-persistent short flag is present 255 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "-n", "value", "") 256 if err != nil { 257 t.Errorf("Unexpected error: %v", err) 258 } 259 // Reset the flag for the next command 260 nonPersistentFlag.Changed = false 261 262 expected = strings.Join([]string{ 263 ":0", 264 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 265 266 if output != expected { 267 t.Errorf("expected: %q, got: %q", expected, output) 268 } 269 270 // Test that sub-command names are completed with a persistent flag 271 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "--persistent", "value", "") 272 if err != nil { 273 t.Errorf("Unexpected error: %v", err) 274 } 275 // Reset the flag for the next command 276 persistentFlag.Changed = false 277 278 expected = strings.Join([]string{ 279 "childCmd2", 280 ":4", 281 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 282 283 if output != expected { 284 t.Errorf("expected: %q, got: %q", expected, output) 285 } 286 287 // Test that sub-command names are completed with a persistent short flag 288 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "-p", "value", "") 289 if err != nil { 290 t.Errorf("Unexpected error: %v", err) 291 } 292 // Reset the flag for the next command 293 persistentFlag.Changed = false 294 295 expected = strings.Join([]string{ 296 "childCmd2", 297 ":4", 298 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 299 300 if output != expected { 301 t.Errorf("expected: %q, got: %q", expected, output) 302 } 303} 304 305func TestValidArgsCompletionInGo(t *testing.T) { 306 rootCmd := &Command{ 307 Use: "root", 308 ValidArgs: []string{"one", "two", "three"}, 309 Args: MinimumNArgs(1), 310 } 311 312 // Test that validArgs are completed 313 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 314 if err != nil { 315 t.Errorf("Unexpected error: %v", err) 316 } 317 318 expected := strings.Join([]string{ 319 "one", 320 "two", 321 "three", 322 ":4", 323 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 324 325 if output != expected { 326 t.Errorf("expected: %q, got: %q", expected, output) 327 } 328 329 // Test that validArgs are completed with prefix 330 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "o") 331 if err != nil { 332 t.Errorf("Unexpected error: %v", err) 333 } 334 335 expected = strings.Join([]string{ 336 "one", 337 ":4", 338 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 339 340 if output != expected { 341 t.Errorf("expected: %q, got: %q", expected, output) 342 } 343 344 // Test that validArgs don't repeat 345 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "one", "") 346 if err != nil { 347 t.Errorf("Unexpected error: %v", err) 348 } 349 350 expected = strings.Join([]string{ 351 ":0", 352 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 353 354 if output != expected { 355 t.Errorf("expected: %q, got: %q", expected, output) 356 } 357} 358 359func TestValidArgsAndCmdCompletionInGo(t *testing.T) { 360 rootCmd := &Command{ 361 Use: "root", 362 ValidArgs: []string{"one", "two"}, 363 Run: emptyRun, 364 } 365 366 childCmd := &Command{ 367 Use: "thechild", 368 Run: emptyRun, 369 } 370 371 rootCmd.AddCommand(childCmd) 372 373 // Test that both sub-commands and validArgs are completed 374 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 375 if err != nil { 376 t.Errorf("Unexpected error: %v", err) 377 } 378 379 expected := strings.Join([]string{ 380 "completion", 381 "help", 382 "thechild", 383 "one", 384 "two", 385 ":4", 386 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 387 388 if output != expected { 389 t.Errorf("expected: %q, got: %q", expected, output) 390 } 391 392 // Test that both sub-commands and validArgs are completed with prefix 393 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t") 394 if err != nil { 395 t.Errorf("Unexpected error: %v", err) 396 } 397 398 expected = strings.Join([]string{ 399 "thechild", 400 "two", 401 ":4", 402 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 403 404 if output != expected { 405 t.Errorf("expected: %q, got: %q", expected, output) 406 } 407} 408 409func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) { 410 rootCmd := &Command{ 411 Use: "root", 412 ValidArgsFunction: validArgsFunc, 413 Run: emptyRun, 414 } 415 416 childCmd := &Command{ 417 Use: "thechild", 418 Short: "The child command", 419 Run: emptyRun, 420 } 421 422 rootCmd.AddCommand(childCmd) 423 424 // Test that both sub-commands and validArgsFunction are completed 425 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 426 if err != nil { 427 t.Errorf("Unexpected error: %v", err) 428 } 429 430 expected := strings.Join([]string{ 431 "completion", 432 "help", 433 "thechild", 434 "one", 435 "two", 436 ":0", 437 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 438 439 if output != expected { 440 t.Errorf("expected: %q, got: %q", expected, output) 441 } 442 443 // Test that both sub-commands and validArgs are completed with prefix 444 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t") 445 if err != nil { 446 t.Errorf("Unexpected error: %v", err) 447 } 448 449 expected = strings.Join([]string{ 450 "thechild", 451 "two", 452 ":0", 453 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 454 455 if output != expected { 456 t.Errorf("expected: %q, got: %q", expected, output) 457 } 458 459 // Test that both sub-commands and validArgs are completed with description 460 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "t") 461 if err != nil { 462 t.Errorf("Unexpected error: %v", err) 463 } 464 465 expected = strings.Join([]string{ 466 "thechild\tThe child command", 467 "two\tThe second", 468 ":0", 469 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 470 471 if output != expected { 472 t.Errorf("expected: %q, got: %q", expected, output) 473 } 474} 475 476func TestFlagNameCompletionInGo(t *testing.T) { 477 rootCmd := &Command{ 478 Use: "root", 479 Run: emptyRun, 480 } 481 childCmd := &Command{ 482 Use: "childCmd", 483 Run: emptyRun, 484 } 485 rootCmd.AddCommand(childCmd) 486 487 rootCmd.Flags().IntP("first", "f", -1, "first flag") 488 rootCmd.PersistentFlags().BoolP("second", "s", false, "second flag") 489 childCmd.Flags().String("subFlag", "", "sub flag") 490 491 // Test that flag names are not shown if the user has not given the '-' prefix 492 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 493 if err != nil { 494 t.Errorf("Unexpected error: %v", err) 495 } 496 497 expected := strings.Join([]string{ 498 "childCmd", 499 "completion", 500 "help", 501 ":4", 502 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 503 504 if output != expected { 505 t.Errorf("expected: %q, got: %q", expected, output) 506 } 507 508 // Test that flag names are completed 509 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-") 510 if err != nil { 511 t.Errorf("Unexpected error: %v", err) 512 } 513 514 expected = strings.Join([]string{ 515 "--first", 516 "-f", 517 "--second", 518 "-s", 519 ":4", 520 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 521 522 if output != expected { 523 t.Errorf("expected: %q, got: %q", expected, output) 524 } 525 526 // Test that flag names are completed when a prefix is given 527 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--f") 528 if err != nil { 529 t.Errorf("Unexpected error: %v", err) 530 } 531 532 expected = strings.Join([]string{ 533 "--first", 534 ":4", 535 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 536 537 if output != expected { 538 t.Errorf("expected: %q, got: %q", expected, output) 539 } 540 541 // Test that flag names are completed in a sub-cmd 542 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "-") 543 if err != nil { 544 t.Errorf("Unexpected error: %v", err) 545 } 546 547 expected = strings.Join([]string{ 548 "--second", 549 "-s", 550 "--subFlag", 551 ":4", 552 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 553 554 if output != expected { 555 t.Errorf("expected: %q, got: %q", expected, output) 556 } 557} 558 559func TestFlagNameCompletionInGoWithDesc(t *testing.T) { 560 rootCmd := &Command{ 561 Use: "root", 562 Run: emptyRun, 563 } 564 childCmd := &Command{ 565 Use: "childCmd", 566 Short: "first command", 567 Run: emptyRun, 568 } 569 rootCmd.AddCommand(childCmd) 570 571 rootCmd.Flags().IntP("first", "f", -1, "first flag\nlonger description for flag") 572 rootCmd.PersistentFlags().BoolP("second", "s", false, "second flag") 573 childCmd.Flags().String("subFlag", "", "sub flag") 574 575 // Test that flag names are not shown if the user has not given the '-' prefix 576 output, err := executeCommand(rootCmd, ShellCompRequestCmd, "") 577 if err != nil { 578 t.Errorf("Unexpected error: %v", err) 579 } 580 581 expected := strings.Join([]string{ 582 "childCmd\tfirst command", 583 "completion\tgenerate the autocompletion script for the specified shell", 584 "help\tHelp about any command", 585 ":4", 586 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 587 588 if output != expected { 589 t.Errorf("expected: %q, got: %q", expected, output) 590 } 591 592 // Test that flag names are completed 593 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "-") 594 if err != nil { 595 t.Errorf("Unexpected error: %v", err) 596 } 597 598 expected = strings.Join([]string{ 599 "--first\tfirst flag", 600 "-f\tfirst flag", 601 "--second\tsecond flag", 602 "-s\tsecond flag", 603 ":4", 604 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 605 606 if output != expected { 607 t.Errorf("expected: %q, got: %q", expected, output) 608 } 609 610 // Test that flag names are completed when a prefix is given 611 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--f") 612 if err != nil { 613 t.Errorf("Unexpected error: %v", err) 614 } 615 616 expected = strings.Join([]string{ 617 "--first\tfirst flag", 618 ":4", 619 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 620 621 if output != expected { 622 t.Errorf("expected: %q, got: %q", expected, output) 623 } 624 625 // Test that flag names are completed in a sub-cmd 626 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "childCmd", "-") 627 if err != nil { 628 t.Errorf("Unexpected error: %v", err) 629 } 630 631 expected = strings.Join([]string{ 632 "--second\tsecond flag", 633 "-s\tsecond flag", 634 "--subFlag\tsub flag", 635 ":4", 636 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 637 638 if output != expected { 639 t.Errorf("expected: %q, got: %q", expected, output) 640 } 641} 642 643func TestFlagNameCompletionRepeat(t *testing.T) { 644 rootCmd := &Command{ 645 Use: "root", 646 Run: emptyRun, 647 } 648 childCmd := &Command{ 649 Use: "childCmd", 650 Short: "first command", 651 Run: emptyRun, 652 } 653 rootCmd.AddCommand(childCmd) 654 655 rootCmd.Flags().IntP("first", "f", -1, "first flag") 656 firstFlag := rootCmd.Flags().Lookup("first") 657 rootCmd.Flags().BoolP("second", "s", false, "second flag") 658 secondFlag := rootCmd.Flags().Lookup("second") 659 rootCmd.Flags().StringArrayP("array", "a", nil, "array flag") 660 arrayFlag := rootCmd.Flags().Lookup("array") 661 rootCmd.Flags().IntSliceP("slice", "l", nil, "slice flag") 662 sliceFlag := rootCmd.Flags().Lookup("slice") 663 rootCmd.Flags().BoolSliceP("bslice", "b", nil, "bool slice flag") 664 bsliceFlag := rootCmd.Flags().Lookup("bslice") 665 666 // Test that flag names are not repeated unless they are an array or slice 667 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--first", "1", "--") 668 if err != nil { 669 t.Errorf("Unexpected error: %v", err) 670 } 671 // Reset the flag for the next command 672 firstFlag.Changed = false 673 674 expected := strings.Join([]string{ 675 "--array", 676 "--bslice", 677 "--second", 678 "--slice", 679 ":4", 680 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 681 682 if output != expected { 683 t.Errorf("expected: %q, got: %q", expected, output) 684 } 685 686 // Test that flag names are not repeated unless they are an array or slice 687 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--first", "1", "--second=false", "--") 688 if err != nil { 689 t.Errorf("Unexpected error: %v", err) 690 } 691 // Reset the flag for the next command 692 firstFlag.Changed = false 693 secondFlag.Changed = false 694 695 expected = strings.Join([]string{ 696 "--array", 697 "--bslice", 698 "--slice", 699 ":4", 700 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 701 702 if output != expected { 703 t.Errorf("expected: %q, got: %q", expected, output) 704 } 705 706 // Test that flag names are not repeated unless they are an array or slice 707 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--slice", "1", "--slice=2", "--array", "val", "--bslice", "true", "--") 708 if err != nil { 709 t.Errorf("Unexpected error: %v", err) 710 } 711 // Reset the flag for the next command 712 sliceFlag.Changed = false 713 arrayFlag.Changed = false 714 bsliceFlag.Changed = false 715 716 expected = strings.Join([]string{ 717 "--array", 718 "--bslice", 719 "--first", 720 "--second", 721 "--slice", 722 ":4", 723 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 724 725 if output != expected { 726 t.Errorf("expected: %q, got: %q", expected, output) 727 } 728 729 // Test that flag names are not repeated unless they are an array or slice, using shortname 730 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a", "val", "-") 731 if err != nil { 732 t.Errorf("Unexpected error: %v", err) 733 } 734 // Reset the flag for the next command 735 sliceFlag.Changed = false 736 arrayFlag.Changed = false 737 738 expected = strings.Join([]string{ 739 "--array", 740 "-a", 741 "--bslice", 742 "-b", 743 "--first", 744 "-f", 745 "--second", 746 "-s", 747 "--slice", 748 "-l", 749 ":4", 750 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 751 752 if output != expected { 753 t.Errorf("expected: %q, got: %q", expected, output) 754 } 755 756 // Test that flag names are not repeated unless they are an array or slice, using shortname with prefix 757 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a", "val", "-a") 758 if err != nil { 759 t.Errorf("Unexpected error: %v", err) 760 } 761 // Reset the flag for the next command 762 sliceFlag.Changed = false 763 arrayFlag.Changed = false 764 765 expected = strings.Join([]string{ 766 "-a", 767 ":4", 768 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 769 770 if output != expected { 771 t.Errorf("expected: %q, got: %q", expected, output) 772 } 773} 774 775func TestRequiredFlagNameCompletionInGo(t *testing.T) { 776 rootCmd := &Command{ 777 Use: "root", 778 ValidArgs: []string{"realArg"}, 779 Run: emptyRun, 780 } 781 childCmd := &Command{ 782 Use: "childCmd", 783 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 784 return []string{"subArg"}, ShellCompDirectiveNoFileComp 785 }, 786 Run: emptyRun, 787 } 788 rootCmd.AddCommand(childCmd) 789 790 rootCmd.Flags().IntP("requiredFlag", "r", -1, "required flag") 791 assertNoErr(t, rootCmd.MarkFlagRequired("requiredFlag")) 792 requiredFlag := rootCmd.Flags().Lookup("requiredFlag") 793 794 rootCmd.PersistentFlags().IntP("requiredPersistent", "p", -1, "required persistent") 795 assertNoErr(t, rootCmd.MarkPersistentFlagRequired("requiredPersistent")) 796 requiredPersistent := rootCmd.PersistentFlags().Lookup("requiredPersistent") 797 798 rootCmd.Flags().StringP("release", "R", "", "Release name") 799 800 childCmd.Flags().BoolP("subRequired", "s", false, "sub required flag") 801 assertNoErr(t, childCmd.MarkFlagRequired("subRequired")) 802 childCmd.Flags().BoolP("subNotRequired", "n", false, "sub not required flag") 803 804 // Test that a required flag is suggested even without the - prefix 805 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 806 if err != nil { 807 t.Errorf("Unexpected error: %v", err) 808 } 809 810 expected := strings.Join([]string{ 811 "childCmd", 812 "completion", 813 "help", 814 "--requiredFlag", 815 "-r", 816 "--requiredPersistent", 817 "-p", 818 "realArg", 819 ":4", 820 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 821 822 if output != expected { 823 t.Errorf("expected: %q, got: %q", expected, output) 824 } 825 826 // Test that a required flag is suggested without other flags when using the '-' prefix 827 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-") 828 if err != nil { 829 t.Errorf("Unexpected error: %v", err) 830 } 831 832 expected = strings.Join([]string{ 833 "--requiredFlag", 834 "-r", 835 "--requiredPersistent", 836 "-p", 837 ":4", 838 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 839 840 if output != expected { 841 t.Errorf("expected: %q, got: %q", expected, output) 842 } 843 844 // Test that if no required flag matches, the normal flags are suggested 845 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--relea") 846 if err != nil { 847 t.Errorf("Unexpected error: %v", err) 848 } 849 850 expected = strings.Join([]string{ 851 "--release", 852 ":4", 853 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 854 855 if output != expected { 856 t.Errorf("expected: %q, got: %q", expected, output) 857 } 858 859 // Test required flags for sub-commands 860 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "") 861 if err != nil { 862 t.Errorf("Unexpected error: %v", err) 863 } 864 865 expected = strings.Join([]string{ 866 "--requiredPersistent", 867 "-p", 868 "--subRequired", 869 "-s", 870 "subArg", 871 ":4", 872 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 873 874 if output != expected { 875 t.Errorf("expected: %q, got: %q", expected, output) 876 } 877 878 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "-") 879 if err != nil { 880 t.Errorf("Unexpected error: %v", err) 881 } 882 883 expected = strings.Join([]string{ 884 "--requiredPersistent", 885 "-p", 886 "--subRequired", 887 "-s", 888 ":4", 889 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 890 891 if output != expected { 892 t.Errorf("expected: %q, got: %q", expected, output) 893 } 894 895 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "--subNot") 896 if err != nil { 897 t.Errorf("Unexpected error: %v", err) 898 } 899 900 expected = strings.Join([]string{ 901 "--subNotRequired", 902 ":4", 903 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 904 905 if output != expected { 906 t.Errorf("expected: %q, got: %q", expected, output) 907 } 908 909 // Test that when a required flag is present, it is not suggested anymore 910 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredFlag", "1", "") 911 if err != nil { 912 t.Errorf("Unexpected error: %v", err) 913 } 914 // Reset the flag for the next command 915 requiredFlag.Changed = false 916 917 expected = strings.Join([]string{ 918 "--requiredPersistent", 919 "-p", 920 "realArg", 921 ":4", 922 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 923 924 if output != expected { 925 t.Errorf("expected: %q, got: %q", expected, output) 926 } 927 928 // Test that when a persistent required flag is present, it is not suggested anymore 929 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredPersistent", "1", "") 930 if err != nil { 931 t.Errorf("Unexpected error: %v", err) 932 } 933 // Reset the flag for the next command 934 requiredPersistent.Changed = false 935 936 expected = strings.Join([]string{ 937 "childCmd", 938 "completion", 939 "help", 940 "--requiredFlag", 941 "-r", 942 "realArg", 943 ":4", 944 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 945 946 if output != expected { 947 t.Errorf("expected: %q, got: %q", expected, output) 948 } 949 950 // Test that when all required flags are present, normal completion is done 951 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredFlag", "1", "--requiredPersistent", "1", "") 952 if err != nil { 953 t.Errorf("Unexpected error: %v", err) 954 } 955 // Reset the flags for the next command 956 requiredFlag.Changed = false 957 requiredPersistent.Changed = false 958 959 expected = strings.Join([]string{ 960 "realArg", 961 ":4", 962 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 963 964 if output != expected { 965 t.Errorf("expected: %q, got: %q", expected, output) 966 } 967} 968 969func TestFlagFileExtFilterCompletionInGo(t *testing.T) { 970 rootCmd := &Command{ 971 Use: "root", 972 Run: emptyRun, 973 } 974 975 // No extensions. Should be ignored. 976 rootCmd.Flags().StringP("file", "f", "", "file flag") 977 assertNoErr(t, rootCmd.MarkFlagFilename("file")) 978 979 // Single extension 980 rootCmd.Flags().StringP("log", "l", "", "log flag") 981 assertNoErr(t, rootCmd.MarkFlagFilename("log", "log")) 982 983 // Multiple extensions 984 rootCmd.Flags().StringP("yaml", "y", "", "yaml flag") 985 assertNoErr(t, rootCmd.MarkFlagFilename("yaml", "yaml", "yml")) 986 987 // Directly using annotation 988 rootCmd.Flags().StringP("text", "t", "", "text flag") 989 assertNoErr(t, rootCmd.Flags().SetAnnotation("text", BashCompFilenameExt, []string{"txt"})) 990 991 // Test that the completion logic returns the proper info for the completion 992 // script to handle the file filtering 993 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--file", "") 994 if err != nil { 995 t.Errorf("Unexpected error: %v", err) 996 } 997 998 expected := strings.Join([]string{ 999 ":0", 1000 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1001 1002 if output != expected { 1003 t.Errorf("expected: %q, got: %q", expected, output) 1004 } 1005 1006 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--log", "") 1007 if err != nil { 1008 t.Errorf("Unexpected error: %v", err) 1009 } 1010 1011 expected = strings.Join([]string{ 1012 "log", 1013 ":8", 1014 "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") 1015 1016 if output != expected { 1017 t.Errorf("expected: %q, got: %q", expected, output) 1018 } 1019 1020 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--yaml", "") 1021 if err != nil { 1022 t.Errorf("Unexpected error: %v", err) 1023 } 1024 1025 expected = strings.Join([]string{ 1026 "yaml", "yml", 1027 ":8", 1028 "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") 1029 1030 if output != expected { 1031 t.Errorf("expected: %q, got: %q", expected, output) 1032 } 1033 1034 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--yaml=") 1035 if err != nil { 1036 t.Errorf("Unexpected error: %v", err) 1037 } 1038 1039 expected = strings.Join([]string{ 1040 "yaml", "yml", 1041 ":8", 1042 "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") 1043 1044 if output != expected { 1045 t.Errorf("expected: %q, got: %q", expected, output) 1046 } 1047 1048 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-y", "") 1049 if err != nil { 1050 t.Errorf("Unexpected error: %v", err) 1051 } 1052 1053 expected = strings.Join([]string{ 1054 "yaml", "yml", 1055 ":8", 1056 "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") 1057 1058 if output != expected { 1059 t.Errorf("expected: %q, got: %q", expected, output) 1060 } 1061 1062 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-y=") 1063 if err != nil { 1064 t.Errorf("Unexpected error: %v", err) 1065 } 1066 1067 expected = strings.Join([]string{ 1068 "yaml", "yml", 1069 ":8", 1070 "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") 1071 1072 if output != expected { 1073 t.Errorf("expected: %q, got: %q", expected, output) 1074 } 1075 1076 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--text", "") 1077 if err != nil { 1078 t.Errorf("Unexpected error: %v", err) 1079 } 1080 1081 expected = strings.Join([]string{ 1082 "txt", 1083 ":8", 1084 "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") 1085 1086 if output != expected { 1087 t.Errorf("expected: %q, got: %q", expected, output) 1088 } 1089} 1090 1091func TestFlagDirFilterCompletionInGo(t *testing.T) { 1092 rootCmd := &Command{ 1093 Use: "root", 1094 Run: emptyRun, 1095 } 1096 1097 // Filter directories 1098 rootCmd.Flags().StringP("dir", "d", "", "dir flag") 1099 assertNoErr(t, rootCmd.MarkFlagDirname("dir")) 1100 1101 // Filter directories within a directory 1102 rootCmd.Flags().StringP("subdir", "s", "", "subdir") 1103 assertNoErr(t, rootCmd.Flags().SetAnnotation("subdir", BashCompSubdirsInDir, []string{"themes"})) 1104 1105 // Multiple directory specification get ignored 1106 rootCmd.Flags().StringP("manydir", "m", "", "manydir") 1107 assertNoErr(t, rootCmd.Flags().SetAnnotation("manydir", BashCompSubdirsInDir, []string{"themes", "colors"})) 1108 1109 // Test that the completion logic returns the proper info for the completion 1110 // script to handle the directory filtering 1111 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--dir", "") 1112 if err != nil { 1113 t.Errorf("Unexpected error: %v", err) 1114 } 1115 1116 expected := strings.Join([]string{ 1117 ":16", 1118 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1119 1120 if output != expected { 1121 t.Errorf("expected: %q, got: %q", expected, output) 1122 } 1123 1124 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-d", "") 1125 if err != nil { 1126 t.Errorf("Unexpected error: %v", err) 1127 } 1128 1129 expected = strings.Join([]string{ 1130 ":16", 1131 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1132 1133 if output != expected { 1134 t.Errorf("expected: %q, got: %q", expected, output) 1135 } 1136 1137 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--subdir", "") 1138 if err != nil { 1139 t.Errorf("Unexpected error: %v", err) 1140 } 1141 1142 expected = strings.Join([]string{ 1143 "themes", 1144 ":16", 1145 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1146 1147 if output != expected { 1148 t.Errorf("expected: %q, got: %q", expected, output) 1149 } 1150 1151 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--subdir=") 1152 if err != nil { 1153 t.Errorf("Unexpected error: %v", err) 1154 } 1155 1156 expected = strings.Join([]string{ 1157 "themes", 1158 ":16", 1159 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1160 1161 if output != expected { 1162 t.Errorf("expected: %q, got: %q", expected, output) 1163 } 1164 1165 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s", "") 1166 if err != nil { 1167 t.Errorf("Unexpected error: %v", err) 1168 } 1169 1170 expected = strings.Join([]string{ 1171 "themes", 1172 ":16", 1173 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1174 1175 if output != expected { 1176 t.Errorf("expected: %q, got: %q", expected, output) 1177 } 1178 1179 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s=") 1180 if err != nil { 1181 t.Errorf("Unexpected error: %v", err) 1182 } 1183 1184 expected = strings.Join([]string{ 1185 "themes", 1186 ":16", 1187 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1188 1189 if output != expected { 1190 t.Errorf("expected: %q, got: %q", expected, output) 1191 } 1192 1193 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--manydir", "") 1194 if err != nil { 1195 t.Errorf("Unexpected error: %v", err) 1196 } 1197 1198 expected = strings.Join([]string{ 1199 ":16", 1200 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1201 1202 if output != expected { 1203 t.Errorf("expected: %q, got: %q", expected, output) 1204 } 1205} 1206 1207func TestValidArgsFuncCmdContext(t *testing.T) { 1208 validArgsFunc := func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1209 ctx := cmd.Context() 1210 1211 if ctx == nil { 1212 t.Error("Received nil context in completion func") 1213 } else if ctx.Value("testKey") != "123" { 1214 t.Error("Received invalid context") 1215 } 1216 1217 return nil, ShellCompDirectiveDefault 1218 } 1219 1220 rootCmd := &Command{ 1221 Use: "root", 1222 Run: emptyRun, 1223 } 1224 childCmd := &Command{ 1225 Use: "childCmd", 1226 ValidArgsFunction: validArgsFunc, 1227 Run: emptyRun, 1228 } 1229 rootCmd.AddCommand(childCmd) 1230 1231 //nolint:golint,staticcheck // We can safely use a basic type as key in tests. 1232 ctx := context.WithValue(context.Background(), "testKey", "123") 1233 1234 // Test completing an empty string on the childCmd 1235 _, output, err := executeCommandWithContextC(ctx, rootCmd, ShellCompNoDescRequestCmd, "childCmd", "") 1236 if err != nil { 1237 t.Errorf("Unexpected error: %v", err) 1238 } 1239 1240 expected := strings.Join([]string{ 1241 ":0", 1242 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1243 1244 if output != expected { 1245 t.Errorf("expected: %q, got: %q", expected, output) 1246 } 1247} 1248 1249func TestValidArgsFuncSingleCmd(t *testing.T) { 1250 rootCmd := &Command{ 1251 Use: "root", 1252 ValidArgsFunction: validArgsFunc, 1253 Run: emptyRun, 1254 } 1255 1256 // Test completing an empty string 1257 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 1258 if err != nil { 1259 t.Errorf("Unexpected error: %v", err) 1260 } 1261 1262 expected := strings.Join([]string{ 1263 "one", 1264 "two", 1265 ":0", 1266 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1267 1268 if output != expected { 1269 t.Errorf("expected: %q, got: %q", expected, output) 1270 } 1271 1272 // Check completing with a prefix 1273 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t") 1274 if err != nil { 1275 t.Errorf("Unexpected error: %v", err) 1276 } 1277 1278 expected = strings.Join([]string{ 1279 "two", 1280 ":0", 1281 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1282 1283 if output != expected { 1284 t.Errorf("expected: %q, got: %q", expected, output) 1285 } 1286} 1287 1288func TestValidArgsFuncSingleCmdInvalidArg(t *testing.T) { 1289 rootCmd := &Command{ 1290 Use: "root", 1291 // If we don't specify a value for Args, this test fails. 1292 // This is only true for a root command without any subcommands, and is caused 1293 // by the fact that the __complete command becomes a subcommand when there should not be one. 1294 // The problem is in the implementation of legacyArgs(). 1295 Args: MinimumNArgs(1), 1296 ValidArgsFunction: validArgsFunc, 1297 Run: emptyRun, 1298 } 1299 1300 // Check completing with wrong number of args 1301 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "unexpectedArg", "t") 1302 if err != nil { 1303 t.Errorf("Unexpected error: %v", err) 1304 } 1305 1306 expected := strings.Join([]string{ 1307 ":4", 1308 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1309 1310 if output != expected { 1311 t.Errorf("expected: %q, got: %q", expected, output) 1312 } 1313} 1314 1315func TestValidArgsFuncChildCmds(t *testing.T) { 1316 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1317 child1Cmd := &Command{ 1318 Use: "child1", 1319 ValidArgsFunction: validArgsFunc, 1320 Run: emptyRun, 1321 } 1322 child2Cmd := &Command{ 1323 Use: "child2", 1324 ValidArgsFunction: validArgsFunc2, 1325 Run: emptyRun, 1326 } 1327 rootCmd.AddCommand(child1Cmd, child2Cmd) 1328 1329 // Test completion of first sub-command with empty argument 1330 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "") 1331 if err != nil { 1332 t.Errorf("Unexpected error: %v", err) 1333 } 1334 1335 expected := strings.Join([]string{ 1336 "one", 1337 "two", 1338 ":0", 1339 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1340 1341 if output != expected { 1342 t.Errorf("expected: %q, got: %q", expected, output) 1343 } 1344 1345 // Test completion of first sub-command with a prefix to complete 1346 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "t") 1347 if err != nil { 1348 t.Errorf("Unexpected error: %v", err) 1349 } 1350 1351 expected = strings.Join([]string{ 1352 "two", 1353 ":0", 1354 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1355 1356 if output != expected { 1357 t.Errorf("expected: %q, got: %q", expected, output) 1358 } 1359 1360 // Check completing with wrong number of args 1361 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "unexpectedArg", "t") 1362 if err != nil { 1363 t.Errorf("Unexpected error: %v", err) 1364 } 1365 1366 expected = strings.Join([]string{ 1367 ":4", 1368 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1369 1370 if output != expected { 1371 t.Errorf("expected: %q, got: %q", expected, output) 1372 } 1373 1374 // Test completion of second sub-command with empty argument 1375 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "") 1376 if err != nil { 1377 t.Errorf("Unexpected error: %v", err) 1378 } 1379 1380 expected = strings.Join([]string{ 1381 "three", 1382 "four", 1383 ":0", 1384 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1385 1386 if output != expected { 1387 t.Errorf("expected: %q, got: %q", expected, output) 1388 } 1389 1390 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "t") 1391 if err != nil { 1392 t.Errorf("Unexpected error: %v", err) 1393 } 1394 1395 expected = strings.Join([]string{ 1396 "three", 1397 ":0", 1398 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1399 1400 if output != expected { 1401 t.Errorf("expected: %q, got: %q", expected, output) 1402 } 1403 1404 // Check completing with wrong number of args 1405 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "unexpectedArg", "t") 1406 if err != nil { 1407 t.Errorf("Unexpected error: %v", err) 1408 } 1409 1410 expected = strings.Join([]string{ 1411 ":4", 1412 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1413 1414 if output != expected { 1415 t.Errorf("expected: %q, got: %q", expected, output) 1416 } 1417} 1418 1419func TestValidArgsFuncAliases(t *testing.T) { 1420 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1421 child := &Command{ 1422 Use: "child", 1423 Aliases: []string{"son", "daughter"}, 1424 ValidArgsFunction: validArgsFunc, 1425 Run: emptyRun, 1426 } 1427 rootCmd.AddCommand(child) 1428 1429 // Test completion of first sub-command with empty argument 1430 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "son", "") 1431 if err != nil { 1432 t.Errorf("Unexpected error: %v", err) 1433 } 1434 1435 expected := strings.Join([]string{ 1436 "one", 1437 "two", 1438 ":0", 1439 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1440 1441 if output != expected { 1442 t.Errorf("expected: %q, got: %q", expected, output) 1443 } 1444 1445 // Test completion of first sub-command with a prefix to complete 1446 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "daughter", "t") 1447 if err != nil { 1448 t.Errorf("Unexpected error: %v", err) 1449 } 1450 1451 expected = strings.Join([]string{ 1452 "two", 1453 ":0", 1454 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1455 1456 if output != expected { 1457 t.Errorf("expected: %q, got: %q", expected, output) 1458 } 1459 1460 // Check completing with wrong number of args 1461 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "son", "unexpectedArg", "t") 1462 if err != nil { 1463 t.Errorf("Unexpected error: %v", err) 1464 } 1465 1466 expected = strings.Join([]string{ 1467 ":4", 1468 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1469 1470 if output != expected { 1471 t.Errorf("expected: %q, got: %q", expected, output) 1472 } 1473} 1474 1475func TestValidArgsFuncInBashScript(t *testing.T) { 1476 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1477 child := &Command{ 1478 Use: "child", 1479 ValidArgsFunction: validArgsFunc, 1480 Run: emptyRun, 1481 } 1482 rootCmd.AddCommand(child) 1483 1484 buf := new(bytes.Buffer) 1485 assertNoErr(t, rootCmd.GenBashCompletion(buf)) 1486 output := buf.String() 1487 1488 check(t, output, "has_completion_function=1") 1489} 1490 1491func TestNoValidArgsFuncInBashScript(t *testing.T) { 1492 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1493 child := &Command{ 1494 Use: "child", 1495 Run: emptyRun, 1496 } 1497 rootCmd.AddCommand(child) 1498 1499 buf := new(bytes.Buffer) 1500 assertNoErr(t, rootCmd.GenBashCompletion(buf)) 1501 output := buf.String() 1502 1503 checkOmit(t, output, "has_completion_function=1") 1504} 1505 1506func TestCompleteCmdInBashScript(t *testing.T) { 1507 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1508 child := &Command{ 1509 Use: "child", 1510 ValidArgsFunction: validArgsFunc, 1511 Run: emptyRun, 1512 } 1513 rootCmd.AddCommand(child) 1514 1515 buf := new(bytes.Buffer) 1516 assertNoErr(t, rootCmd.GenBashCompletion(buf)) 1517 output := buf.String() 1518 1519 check(t, output, ShellCompNoDescRequestCmd) 1520} 1521 1522func TestCompleteNoDesCmdInZshScript(t *testing.T) { 1523 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1524 child := &Command{ 1525 Use: "child", 1526 ValidArgsFunction: validArgsFunc, 1527 Run: emptyRun, 1528 } 1529 rootCmd.AddCommand(child) 1530 1531 buf := new(bytes.Buffer) 1532 assertNoErr(t, rootCmd.GenZshCompletionNoDesc(buf)) 1533 output := buf.String() 1534 1535 check(t, output, ShellCompNoDescRequestCmd) 1536} 1537 1538func TestCompleteCmdInZshScript(t *testing.T) { 1539 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1540 child := &Command{ 1541 Use: "child", 1542 ValidArgsFunction: validArgsFunc, 1543 Run: emptyRun, 1544 } 1545 rootCmd.AddCommand(child) 1546 1547 buf := new(bytes.Buffer) 1548 assertNoErr(t, rootCmd.GenZshCompletion(buf)) 1549 output := buf.String() 1550 1551 check(t, output, ShellCompRequestCmd) 1552 checkOmit(t, output, ShellCompNoDescRequestCmd) 1553} 1554 1555func TestFlagCompletionInGo(t *testing.T) { 1556 rootCmd := &Command{ 1557 Use: "root", 1558 Run: emptyRun, 1559 } 1560 rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot") 1561 assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1562 completions := []string{} 1563 for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} { 1564 if strings.HasPrefix(comp, toComplete) { 1565 completions = append(completions, comp) 1566 } 1567 } 1568 return completions, ShellCompDirectiveDefault 1569 })) 1570 rootCmd.Flags().String("filename", "", "Enter a filename") 1571 assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1572 completions := []string{} 1573 for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} { 1574 if strings.HasPrefix(comp, toComplete) { 1575 completions = append(completions, comp) 1576 } 1577 } 1578 return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp 1579 })) 1580 1581 // Test completing an empty string 1582 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "") 1583 if err != nil { 1584 t.Errorf("Unexpected error: %v", err) 1585 } 1586 1587 expected := strings.Join([]string{ 1588 "1", 1589 "2", 1590 "10", 1591 ":0", 1592 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1593 1594 if output != expected { 1595 t.Errorf("expected: %q, got: %q", expected, output) 1596 } 1597 1598 // Check completing with a prefix 1599 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "1") 1600 if err != nil { 1601 t.Errorf("Unexpected error: %v", err) 1602 } 1603 1604 expected = strings.Join([]string{ 1605 "1", 1606 "10", 1607 ":0", 1608 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1609 1610 if output != expected { 1611 t.Errorf("expected: %q, got: %q", expected, output) 1612 } 1613 1614 // Test completing an empty string 1615 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--filename", "") 1616 if err != nil { 1617 t.Errorf("Unexpected error: %v", err) 1618 } 1619 1620 expected = strings.Join([]string{ 1621 "file.yaml", 1622 "myfile.json", 1623 "file.xml", 1624 ":6", 1625 "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") 1626 1627 if output != expected { 1628 t.Errorf("expected: %q, got: %q", expected, output) 1629 } 1630 1631 // Check completing with a prefix 1632 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--filename", "f") 1633 if err != nil { 1634 t.Errorf("Unexpected error: %v", err) 1635 } 1636 1637 expected = strings.Join([]string{ 1638 "file.yaml", 1639 "file.xml", 1640 ":6", 1641 "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") 1642 1643 if output != expected { 1644 t.Errorf("expected: %q, got: %q", expected, output) 1645 } 1646} 1647 1648func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) { 1649 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1650 child1Cmd := &Command{ 1651 Use: "child1", 1652 ValidArgsFunction: validArgsFunc, 1653 Run: emptyRun, 1654 } 1655 child2Cmd := &Command{ 1656 Use: "child2", 1657 ValidArgsFunction: validArgsFunc2, 1658 Run: emptyRun, 1659 } 1660 rootCmd.AddCommand(child1Cmd, child2Cmd) 1661 1662 // Test completion of first sub-command with empty argument 1663 output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child1", "") 1664 if err != nil { 1665 t.Errorf("Unexpected error: %v", err) 1666 } 1667 1668 expected := strings.Join([]string{ 1669 "one\tThe first", 1670 "two\tThe second", 1671 ":0", 1672 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1673 1674 if output != expected { 1675 t.Errorf("expected: %q, got: %q", expected, output) 1676 } 1677 1678 // Test completion of first sub-command with a prefix to complete 1679 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "t") 1680 if err != nil { 1681 t.Errorf("Unexpected error: %v", err) 1682 } 1683 1684 expected = strings.Join([]string{ 1685 "two\tThe second", 1686 ":0", 1687 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1688 1689 if output != expected { 1690 t.Errorf("expected: %q, got: %q", expected, output) 1691 } 1692 1693 // Check completing with wrong number of args 1694 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "unexpectedArg", "t") 1695 if err != nil { 1696 t.Errorf("Unexpected error: %v", err) 1697 } 1698 1699 expected = strings.Join([]string{ 1700 ":4", 1701 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1702 1703 if output != expected { 1704 t.Errorf("expected: %q, got: %q", expected, output) 1705 } 1706 1707 // Test completion of second sub-command with empty argument 1708 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "") 1709 if err != nil { 1710 t.Errorf("Unexpected error: %v", err) 1711 } 1712 1713 expected = strings.Join([]string{ 1714 "three\tThe third", 1715 "four\tThe fourth", 1716 ":0", 1717 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1718 1719 if output != expected { 1720 t.Errorf("expected: %q, got: %q", expected, output) 1721 } 1722 1723 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "t") 1724 if err != nil { 1725 t.Errorf("Unexpected error: %v", err) 1726 } 1727 1728 expected = strings.Join([]string{ 1729 "three\tThe third", 1730 ":0", 1731 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1732 1733 if output != expected { 1734 t.Errorf("expected: %q, got: %q", expected, output) 1735 } 1736 1737 // Check completing with wrong number of args 1738 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "unexpectedArg", "t") 1739 if err != nil { 1740 t.Errorf("Unexpected error: %v", err) 1741 } 1742 1743 expected = strings.Join([]string{ 1744 ":4", 1745 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1746 1747 if output != expected { 1748 t.Errorf("expected: %q, got: %q", expected, output) 1749 } 1750} 1751 1752func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { 1753 rootCmd := &Command{Use: "root", Run: emptyRun} 1754 childCmd := &Command{ 1755 Use: "child", 1756 Run: emptyRun, 1757 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1758 return []string{"--validarg", "test"}, ShellCompDirectiveDefault 1759 }, 1760 } 1761 childCmd2 := &Command{ 1762 Use: "child2", 1763 Run: emptyRun, 1764 ValidArgs: []string{"arg1", "arg2"}, 1765 } 1766 rootCmd.AddCommand(childCmd, childCmd2) 1767 childCmd.Flags().Bool("bool", false, "test bool flag") 1768 childCmd.Flags().String("string", "", "test string flag") 1769 _ = childCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1770 return []string{"myval"}, ShellCompDirectiveDefault 1771 }) 1772 1773 // Test flag completion with no argument 1774 output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--") 1775 if err != nil { 1776 t.Errorf("Unexpected error: %v", err) 1777 } 1778 1779 expected := strings.Join([]string{ 1780 "--bool\ttest bool flag", 1781 "--string\ttest string flag", 1782 ":4", 1783 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1784 1785 if output != expected { 1786 t.Errorf("expected: %q, got: %q", expected, output) 1787 } 1788 1789 // Test that no flags are completed after the -- arg 1790 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "-") 1791 if err != nil { 1792 t.Errorf("Unexpected error: %v", err) 1793 } 1794 1795 expected = strings.Join([]string{ 1796 "--validarg", 1797 "test", 1798 ":0", 1799 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1800 1801 if output != expected { 1802 t.Errorf("expected: %q, got: %q", expected, output) 1803 } 1804 1805 // Test that no flags are completed after the -- arg with a flag set 1806 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--bool", "--", "-") 1807 if err != nil { 1808 t.Errorf("Unexpected error: %v", err) 1809 } 1810 1811 expected = strings.Join([]string{ 1812 "--validarg", 1813 "test", 1814 ":0", 1815 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1816 1817 if output != expected { 1818 t.Errorf("expected: %q, got: %q", expected, output) 1819 } 1820 1821 // set Interspersed to false which means that no flags should be completed after the first arg 1822 childCmd.Flags().SetInterspersed(false) 1823 1824 // Test that no flags are completed after the first arg 1825 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "arg", "--") 1826 if err != nil { 1827 t.Errorf("Unexpected error: %v", err) 1828 } 1829 1830 expected = strings.Join([]string{ 1831 "--validarg", 1832 "test", 1833 ":0", 1834 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1835 1836 if output != expected { 1837 t.Errorf("expected: %q, got: %q", expected, output) 1838 } 1839 1840 // Test that no flags are completed after the fist arg with a flag set 1841 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "t", "arg", "--") 1842 if err != nil { 1843 t.Errorf("Unexpected error: %v", err) 1844 } 1845 1846 expected = strings.Join([]string{ 1847 "--validarg", 1848 "test", 1849 ":0", 1850 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1851 1852 if output != expected { 1853 t.Errorf("expected: %q, got: %q", expected, output) 1854 } 1855 1856 // Check that args are still completed after -- 1857 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "") 1858 if err != nil { 1859 t.Errorf("Unexpected error: %v", err) 1860 } 1861 1862 expected = strings.Join([]string{ 1863 "--validarg", 1864 "test", 1865 ":0", 1866 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1867 1868 if output != expected { 1869 t.Errorf("expected: %q, got: %q", expected, output) 1870 } 1871 1872 // Check that args are still completed even if flagname with ValidArgsFunction exists 1873 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--string", "") 1874 if err != nil { 1875 t.Errorf("Unexpected error: %v", err) 1876 } 1877 1878 expected = strings.Join([]string{ 1879 "--validarg", 1880 "test", 1881 ":0", 1882 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1883 1884 if output != expected { 1885 t.Errorf("expected: %q, got: %q", expected, output) 1886 } 1887 1888 // Check that args are still completed even if flagname with ValidArgsFunction exists 1889 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "--", "a") 1890 if err != nil { 1891 t.Errorf("Unexpected error: %v", err) 1892 } 1893 1894 expected = strings.Join([]string{ 1895 "arg1", 1896 "arg2", 1897 ":4", 1898 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1899 1900 if output != expected { 1901 t.Errorf("expected: %q, got: %q", expected, output) 1902 } 1903 1904 // Check that --validarg is not parsed as flag after -- 1905 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "") 1906 if err != nil { 1907 t.Errorf("Unexpected error: %v", err) 1908 } 1909 1910 expected = strings.Join([]string{ 1911 "--validarg", 1912 "test", 1913 ":0", 1914 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1915 1916 if output != expected { 1917 t.Errorf("expected: %q, got: %q", expected, output) 1918 } 1919 1920 // Check that --validarg is not parsed as flag after an arg 1921 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "arg", "--validarg", "") 1922 if err != nil { 1923 t.Errorf("Unexpected error: %v", err) 1924 } 1925 1926 expected = strings.Join([]string{ 1927 "--validarg", 1928 "test", 1929 ":0", 1930 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1931 1932 if output != expected { 1933 t.Errorf("expected: %q, got: %q", expected, output) 1934 } 1935 1936 // Check that --validarg is added to args for the ValidArgsFunction 1937 childCmd.ValidArgsFunction = func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1938 return args, ShellCompDirectiveDefault 1939 } 1940 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "") 1941 if err != nil { 1942 t.Errorf("Unexpected error: %v", err) 1943 } 1944 1945 expected = strings.Join([]string{ 1946 "--validarg", 1947 ":0", 1948 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1949 1950 if output != expected { 1951 t.Errorf("expected: %q, got: %q", expected, output) 1952 } 1953 1954 // Check that --validarg is added to args for the ValidArgsFunction and toComplete is also set correctly 1955 childCmd.ValidArgsFunction = func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1956 return append(args, toComplete), ShellCompDirectiveDefault 1957 } 1958 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "--toComp=ab") 1959 if err != nil { 1960 t.Errorf("Unexpected error: %v", err) 1961 } 1962 1963 expected = strings.Join([]string{ 1964 "--validarg", 1965 "--toComp=ab", 1966 ":0", 1967 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1968 1969 if output != expected { 1970 t.Errorf("expected: %q, got: %q", expected, output) 1971 } 1972} 1973 1974func TestFlagCompletionWorksRootCommandAddedAfterFlags(t *testing.T) { 1975 rootCmd := &Command{Use: "root", Run: emptyRun} 1976 childCmd := &Command{ 1977 Use: "child", 1978 Run: emptyRun, 1979 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1980 return []string{"--validarg", "test"}, ShellCompDirectiveDefault 1981 }, 1982 } 1983 childCmd.Flags().Bool("bool", false, "test bool flag") 1984 childCmd.Flags().String("string", "", "test string flag") 1985 _ = childCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1986 return []string{"myval"}, ShellCompDirectiveDefault 1987 }) 1988 1989 // Important: This is a test for https://github.com/spf13/cobra/issues/1437 1990 // Only add the subcommand after RegisterFlagCompletionFunc was called, do not change this order! 1991 rootCmd.AddCommand(childCmd) 1992 1993 // Test that flag completion works for the subcmd 1994 output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "") 1995 if err != nil { 1996 t.Errorf("Unexpected error: %v", err) 1997 } 1998 1999 expected := strings.Join([]string{ 2000 "myval", 2001 ":0", 2002 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 2003 2004 if output != expected { 2005 t.Errorf("expected: %q, got: %q", expected, output) 2006 } 2007} 2008 2009func TestFlagCompletionInGoWithDesc(t *testing.T) { 2010 rootCmd := &Command{ 2011 Use: "root", 2012 Run: emptyRun, 2013 } 2014 rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot") 2015 assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2016 completions := []string{} 2017 for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} { 2018 if strings.HasPrefix(comp, toComplete) { 2019 completions = append(completions, comp) 2020 } 2021 } 2022 return completions, ShellCompDirectiveDefault 2023 })) 2024 rootCmd.Flags().String("filename", "", "Enter a filename") 2025 assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2026 completions := []string{} 2027 for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} { 2028 if strings.HasPrefix(comp, toComplete) { 2029 completions = append(completions, comp) 2030 } 2031 } 2032 return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp 2033 })) 2034 2035 // Test completing an empty string 2036 output, err := executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "") 2037 if err != nil { 2038 t.Errorf("Unexpected error: %v", err) 2039 } 2040 2041 expected := strings.Join([]string{ 2042 "1\tThe first", 2043 "2\tThe second", 2044 "10\tThe tenth", 2045 ":0", 2046 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 2047 2048 if output != expected { 2049 t.Errorf("expected: %q, got: %q", expected, output) 2050 } 2051 2052 // Check completing with a prefix 2053 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "1") 2054 if err != nil { 2055 t.Errorf("Unexpected error: %v", err) 2056 } 2057 2058 expected = strings.Join([]string{ 2059 "1\tThe first", 2060 "10\tThe tenth", 2061 ":0", 2062 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 2063 2064 if output != expected { 2065 t.Errorf("expected: %q, got: %q", expected, output) 2066 } 2067 2068 // Test completing an empty string 2069 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "") 2070 if err != nil { 2071 t.Errorf("Unexpected error: %v", err) 2072 } 2073 2074 expected = strings.Join([]string{ 2075 "file.yaml\tYAML format", 2076 "myfile.json\tJSON format", 2077 "file.xml\tXML format", 2078 ":6", 2079 "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") 2080 2081 if output != expected { 2082 t.Errorf("expected: %q, got: %q", expected, output) 2083 } 2084 2085 // Check completing with a prefix 2086 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "f") 2087 if err != nil { 2088 t.Errorf("Unexpected error: %v", err) 2089 } 2090 2091 expected = strings.Join([]string{ 2092 "file.yaml\tYAML format", 2093 "file.xml\tXML format", 2094 ":6", 2095 "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") 2096 2097 if output != expected { 2098 t.Errorf("expected: %q, got: %q", expected, output) 2099 } 2100} 2101 2102func TestValidArgsNotValidArgsFunc(t *testing.T) { 2103 rootCmd := &Command{ 2104 Use: "root", 2105 ValidArgs: []string{"one", "two"}, 2106 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2107 return []string{"three", "four"}, ShellCompDirectiveNoFileComp 2108 }, 2109 Run: emptyRun, 2110 } 2111 2112 // Test that if both ValidArgs and ValidArgsFunction are present 2113 // only ValidArgs is considered 2114 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 2115 if err != nil { 2116 t.Errorf("Unexpected error: %v", err) 2117 } 2118 2119 expected := strings.Join([]string{ 2120 "one", 2121 "two", 2122 ":4", 2123 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2124 2125 if output != expected { 2126 t.Errorf("expected: %q, got: %q", expected, output) 2127 } 2128 2129 // Check completing with a prefix 2130 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t") 2131 if err != nil { 2132 t.Errorf("Unexpected error: %v", err) 2133 } 2134 2135 expected = strings.Join([]string{ 2136 "two", 2137 ":4", 2138 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2139 2140 if output != expected { 2141 t.Errorf("expected: %q, got: %q", expected, output) 2142 } 2143} 2144 2145func TestArgAliasesCompletionInGo(t *testing.T) { 2146 rootCmd := &Command{ 2147 Use: "root", 2148 Args: OnlyValidArgs, 2149 ValidArgs: []string{"one", "two", "three"}, 2150 ArgAliases: []string{"un", "deux", "trois"}, 2151 Run: emptyRun, 2152 } 2153 2154 // Test that argaliases are not completed when there are validargs that match 2155 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 2156 if err != nil { 2157 t.Errorf("Unexpected error: %v", err) 2158 } 2159 2160 expected := strings.Join([]string{ 2161 "one", 2162 "two", 2163 "three", 2164 ":4", 2165 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2166 2167 if output != expected { 2168 t.Errorf("expected: %q, got: %q", expected, output) 2169 } 2170 2171 // Test that argaliases are not completed when there are validargs that match using a prefix 2172 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t") 2173 if err != nil { 2174 t.Errorf("Unexpected error: %v", err) 2175 } 2176 2177 expected = strings.Join([]string{ 2178 "two", 2179 "three", 2180 ":4", 2181 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2182 2183 if output != expected { 2184 t.Errorf("expected: %q, got: %q", expected, output) 2185 } 2186 2187 // Test that argaliases are completed when there are no validargs that match 2188 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "tr") 2189 if err != nil { 2190 t.Errorf("Unexpected error: %v", err) 2191 } 2192 2193 expected = strings.Join([]string{ 2194 "trois", 2195 ":4", 2196 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2197 2198 if output != expected { 2199 t.Errorf("expected: %q, got: %q", expected, output) 2200 } 2201} 2202 2203func TestCompleteHelp(t *testing.T) { 2204 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 2205 child1Cmd := &Command{ 2206 Use: "child1", 2207 Run: emptyRun, 2208 } 2209 child2Cmd := &Command{ 2210 Use: "child2", 2211 Run: emptyRun, 2212 } 2213 rootCmd.AddCommand(child1Cmd, child2Cmd) 2214 2215 child3Cmd := &Command{ 2216 Use: "child3", 2217 Run: emptyRun, 2218 } 2219 child1Cmd.AddCommand(child3Cmd) 2220 2221 // Test that completion includes the help command 2222 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 2223 if err != nil { 2224 t.Errorf("Unexpected error: %v", err) 2225 } 2226 2227 expected := strings.Join([]string{ 2228 "child1", 2229 "child2", 2230 "completion", 2231 "help", 2232 ":4", 2233 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2234 2235 if output != expected { 2236 t.Errorf("expected: %q, got: %q", expected, output) 2237 } 2238 2239 // Test sub-commands are completed on first level of help command 2240 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "") 2241 if err != nil { 2242 t.Errorf("Unexpected error: %v", err) 2243 } 2244 2245 expected = strings.Join([]string{ 2246 "child1", 2247 "child2", 2248 "completion", 2249 "help", // "<program> help help" is a valid command, so should be completed 2250 ":4", 2251 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2252 2253 if output != expected { 2254 t.Errorf("expected: %q, got: %q", expected, output) 2255 } 2256 2257 // Test sub-commands are completed on first level of help command 2258 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "child1", "") 2259 if err != nil { 2260 t.Errorf("Unexpected error: %v", err) 2261 } 2262 2263 expected = strings.Join([]string{ 2264 "child3", 2265 ":4", 2266 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2267 2268 if output != expected { 2269 t.Errorf("expected: %q, got: %q", expected, output) 2270 } 2271} 2272 2273func removeCompCmd(rootCmd *Command) { 2274 // Remove completion command for the next test 2275 for _, cmd := range rootCmd.commands { 2276 if cmd.Name() == compCmdName { 2277 rootCmd.RemoveCommand(cmd) 2278 return 2279 } 2280 } 2281} 2282 2283func TestDefaultCompletionCmd(t *testing.T) { 2284 rootCmd := &Command{ 2285 Use: "root", 2286 Args: NoArgs, 2287 Run: emptyRun, 2288 } 2289 2290 // Test that no completion command is created if there are not other sub-commands 2291 assertNoErr(t, rootCmd.Execute()) 2292 for _, cmd := range rootCmd.commands { 2293 if cmd.Name() == compCmdName { 2294 t.Errorf("Should not have a 'completion' command when there are no other sub-commands of root") 2295 break 2296 } 2297 } 2298 2299 subCmd := &Command{ 2300 Use: "sub", 2301 Run: emptyRun, 2302 } 2303 rootCmd.AddCommand(subCmd) 2304 2305 // Test that a completion command is created if there are other sub-commands 2306 found := false 2307 assertNoErr(t, rootCmd.Execute()) 2308 for _, cmd := range rootCmd.commands { 2309 if cmd.Name() == compCmdName { 2310 found = true 2311 break 2312 } 2313 } 2314 if !found { 2315 t.Errorf("Should have a 'completion' command when there are other sub-commands of root") 2316 } 2317 // Remove completion command for the next test 2318 removeCompCmd(rootCmd) 2319 2320 // Test that the default completion command can be disabled 2321 rootCmd.CompletionOptions.DisableDefaultCmd = true 2322 assertNoErr(t, rootCmd.Execute()) 2323 for _, cmd := range rootCmd.commands { 2324 if cmd.Name() == compCmdName { 2325 t.Errorf("Should not have a 'completion' command when the feature is disabled") 2326 break 2327 } 2328 } 2329 // Re-enable for next test 2330 rootCmd.CompletionOptions.DisableDefaultCmd = false 2331 2332 // Test that completion descriptions are enabled by default 2333 output, err := executeCommand(rootCmd, compCmdName, "zsh") 2334 if err != nil { 2335 t.Errorf("Unexpected error: %v", err) 2336 } 2337 2338 check(t, output, ShellCompRequestCmd) 2339 checkOmit(t, output, ShellCompNoDescRequestCmd) 2340 // Remove completion command for the next test 2341 removeCompCmd(rootCmd) 2342 2343 // Test that completion descriptions can be disabled completely 2344 rootCmd.CompletionOptions.DisableDescriptions = true 2345 output, err = executeCommand(rootCmd, compCmdName, "zsh") 2346 if err != nil { 2347 t.Errorf("Unexpected error: %v", err) 2348 } 2349 2350 check(t, output, ShellCompNoDescRequestCmd) 2351 // Re-enable for next test 2352 rootCmd.CompletionOptions.DisableDescriptions = false 2353 // Remove completion command for the next test 2354 removeCompCmd(rootCmd) 2355 2356 var compCmd *Command 2357 // Test that the --no-descriptions flag is present on all shells 2358 assertNoErr(t, rootCmd.Execute()) 2359 for _, shell := range []string{"bash", "fish", "powershell", "zsh"} { 2360 if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil { 2361 t.Errorf("Unexpected error: %v", err) 2362 } 2363 if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag == nil { 2364 t.Errorf("Missing --%s flag for %s shell", compCmdNoDescFlagName, shell) 2365 } 2366 } 2367 // Remove completion command for the next test 2368 removeCompCmd(rootCmd) 2369 2370 // Test that the '--no-descriptions' flag can be disabled 2371 rootCmd.CompletionOptions.DisableNoDescFlag = true 2372 assertNoErr(t, rootCmd.Execute()) 2373 for _, shell := range []string{"fish", "zsh", "bash", "powershell"} { 2374 if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil { 2375 t.Errorf("Unexpected error: %v", err) 2376 } 2377 if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil { 2378 t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell) 2379 } 2380 } 2381 // Re-enable for next test 2382 rootCmd.CompletionOptions.DisableNoDescFlag = false 2383 // Remove completion command for the next test 2384 removeCompCmd(rootCmd) 2385 2386 // Test that the '--no-descriptions' flag is disabled when descriptions are disabled 2387 rootCmd.CompletionOptions.DisableDescriptions = true 2388 assertNoErr(t, rootCmd.Execute()) 2389 for _, shell := range []string{"fish", "zsh", "bash", "powershell"} { 2390 if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil { 2391 t.Errorf("Unexpected error: %v", err) 2392 } 2393 if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil { 2394 t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell) 2395 } 2396 } 2397 // Re-enable for next test 2398 rootCmd.CompletionOptions.DisableDescriptions = false 2399 // Remove completion command for the next test 2400 removeCompCmd(rootCmd) 2401} 2402 2403func TestCompleteCompletion(t *testing.T) { 2404 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 2405 subCmd := &Command{ 2406 Use: "sub", 2407 Run: emptyRun, 2408 } 2409 rootCmd.AddCommand(subCmd) 2410 2411 // Test sub-commands of the completion command 2412 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "completion", "") 2413 if err != nil { 2414 t.Errorf("Unexpected error: %v", err) 2415 } 2416 2417 expected := strings.Join([]string{ 2418 "bash", 2419 "fish", 2420 "powershell", 2421 "zsh", 2422 ":4", 2423 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2424 2425 if output != expected { 2426 t.Errorf("expected: %q, got: %q", expected, output) 2427 } 2428 2429 // Test there are no completions for the sub-commands of the completion command 2430 var compCmd *Command 2431 for _, cmd := range rootCmd.Commands() { 2432 if cmd.Name() == compCmdName { 2433 compCmd = cmd 2434 break 2435 } 2436 } 2437 2438 for _, shell := range compCmd.Commands() { 2439 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, compCmdName, shell.Name(), "") 2440 if err != nil { 2441 t.Errorf("Unexpected error: %v", err) 2442 } 2443 2444 expected = strings.Join([]string{ 2445 ":4", 2446 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2447 2448 if output != expected { 2449 t.Errorf("expected: %q, got: %q", expected, output) 2450 } 2451 } 2452} 2453 2454func TestMultipleShorthandFlagCompletion(t *testing.T) { 2455 rootCmd := &Command{ 2456 Use: "root", 2457 ValidArgs: []string{"foo", "bar"}, 2458 Run: emptyRun, 2459 } 2460 f := rootCmd.Flags() 2461 f.BoolP("short", "s", false, "short flag 1") 2462 f.BoolP("short2", "d", false, "short flag 2") 2463 f.StringP("short3", "f", "", "short flag 3") 2464 _ = rootCmd.RegisterFlagCompletionFunc("short3", func(*Command, []string, string) ([]string, ShellCompDirective) { 2465 return []string{"works"}, ShellCompDirectiveNoFileComp 2466 }) 2467 2468 // Test that a single shorthand flag works 2469 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s", "") 2470 if err != nil { 2471 t.Errorf("Unexpected error: %v", err) 2472 } 2473 2474 expected := strings.Join([]string{ 2475 "foo", 2476 "bar", 2477 ":4", 2478 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2479 2480 if output != expected { 2481 t.Errorf("expected: %q, got: %q", expected, output) 2482 } 2483 2484 // Test that multiple boolean shorthand flags work 2485 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sd", "") 2486 if err != nil { 2487 t.Errorf("Unexpected error: %v", err) 2488 } 2489 2490 expected = strings.Join([]string{ 2491 "foo", 2492 "bar", 2493 ":4", 2494 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2495 2496 if output != expected { 2497 t.Errorf("expected: %q, got: %q", expected, output) 2498 } 2499 2500 // Test that multiple boolean + string shorthand flags work 2501 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf", "") 2502 if err != nil { 2503 t.Errorf("Unexpected error: %v", err) 2504 } 2505 2506 expected = strings.Join([]string{ 2507 "works", 2508 ":4", 2509 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2510 2511 if output != expected { 2512 t.Errorf("expected: %q, got: %q", expected, output) 2513 } 2514 2515 // Test that multiple boolean + string with equal sign shorthand flags work 2516 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf=") 2517 if err != nil { 2518 t.Errorf("Unexpected error: %v", err) 2519 } 2520 2521 expected = strings.Join([]string{ 2522 "works", 2523 ":4", 2524 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2525 2526 if output != expected { 2527 t.Errorf("expected: %q, got: %q", expected, output) 2528 } 2529 2530 // Test that multiple boolean + string with equal sign with value shorthand flags work 2531 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf=abc", "") 2532 if err != nil { 2533 t.Errorf("Unexpected error: %v", err) 2534 } 2535 2536 expected = strings.Join([]string{ 2537 "foo", 2538 "bar", 2539 ":4", 2540 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2541 2542 if output != expected { 2543 t.Errorf("expected: %q, got: %q", expected, output) 2544 } 2545} 2546