git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/cobra/completions_test.go (about) 1 // Copyright 2013-2022 The Cobra Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cobra 16 17 import ( 18 "bytes" 19 "context" 20 "strings" 21 "testing" 22 ) 23 24 func validArgsFunc(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{"one\tThe first", "two\tThe second"} { 31 if strings.HasPrefix(comp, toComplete) { 32 completions = append(completions, comp) 33 } 34 } 35 return completions, ShellCompDirectiveDefault 36 } 37 38 func validArgsFunc2(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 39 if len(args) != 0 { 40 return nil, ShellCompDirectiveNoFileComp 41 } 42 43 var completions []string 44 for _, comp := range []string{"three\tThe third", "four\tThe fourth"} { 45 if strings.HasPrefix(comp, toComplete) { 46 completions = append(completions, comp) 47 } 48 } 49 return completions, ShellCompDirectiveDefault 50 } 51 52 func TestCmdNameCompletionInGo(t *testing.T) { 53 rootCmd := &Command{ 54 Use: "root", 55 Run: emptyRun, 56 } 57 childCmd1 := &Command{ 58 Use: "firstChild", 59 Short: "First command", 60 Run: emptyRun, 61 } 62 childCmd2 := &Command{ 63 Use: "secondChild", 64 Run: emptyRun, 65 } 66 hiddenCmd := &Command{ 67 Use: "testHidden", 68 Hidden: true, // Not completed 69 Run: emptyRun, 70 } 71 deprecatedCmd := &Command{ 72 Use: "testDeprecated", 73 Deprecated: "deprecated", // Not completed 74 Run: emptyRun, 75 } 76 aliasedCmd := &Command{ 77 Use: "aliased", 78 Short: "A command with aliases", 79 Aliases: []string{"testAlias", "testSynonym"}, // Not completed 80 Run: emptyRun, 81 } 82 83 rootCmd.AddCommand(childCmd1, childCmd2, hiddenCmd, deprecatedCmd, aliasedCmd) 84 85 // Test that sub-command names are completed 86 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 87 if err != nil { 88 t.Errorf("Unexpected error: %v", err) 89 } 90 91 expected := strings.Join([]string{ 92 "aliased", 93 "completion", 94 "firstChild", 95 "help", 96 "secondChild", 97 ":4", 98 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 99 100 if output != expected { 101 t.Errorf("expected: %q, got: %q", expected, output) 102 } 103 104 // Test that sub-command names are completed with prefix 105 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "s") 106 if err != nil { 107 t.Errorf("Unexpected error: %v", err) 108 } 109 110 expected = strings.Join([]string{ 111 "secondChild", 112 ":4", 113 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 114 115 if output != expected { 116 t.Errorf("expected: %q, got: %q", expected, output) 117 } 118 119 // Test that even with no valid sub-command matches, hidden, deprecated and 120 // aliases are not completed 121 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "test") 122 if err != nil { 123 t.Errorf("Unexpected error: %v", err) 124 } 125 126 expected = strings.Join([]string{ 127 ":4", 128 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 129 130 if output != expected { 131 t.Errorf("expected: %q, got: %q", expected, output) 132 } 133 134 // Test that sub-command names are completed with description 135 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "") 136 if err != nil { 137 t.Errorf("Unexpected error: %v", err) 138 } 139 140 expected = strings.Join([]string{ 141 "aliased\tA command with aliases", 142 "completion\tGenerate the autocompletion script for the specified shell", 143 "firstChild\tFirst command", 144 "help\tHelp about any command", 145 "secondChild", 146 ":4", 147 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 148 149 if output != expected { 150 t.Errorf("expected: %q, got: %q", expected, output) 151 } 152 } 153 154 func TestNoCmdNameCompletionInGo(t *testing.T) { 155 rootCmd := &Command{ 156 Use: "root", 157 Run: emptyRun, 158 } 159 rootCmd.Flags().String("localroot", "", "local root flag") 160 161 childCmd1 := &Command{ 162 Use: "childCmd1", 163 Short: "First command", 164 Args: MinimumNArgs(0), 165 Run: emptyRun, 166 } 167 rootCmd.AddCommand(childCmd1) 168 childCmd1.PersistentFlags().StringP("persistent", "p", "", "persistent flag") 169 persistentFlag := childCmd1.PersistentFlags().Lookup("persistent") 170 childCmd1.Flags().StringP("nonPersistent", "n", "", "non-persistent flag") 171 nonPersistentFlag := childCmd1.Flags().Lookup("nonPersistent") 172 173 childCmd2 := &Command{ 174 Use: "childCmd2", 175 Run: emptyRun, 176 } 177 childCmd1.AddCommand(childCmd2) 178 179 // Test that sub-command names are not completed if there is an argument already 180 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "arg1", "") 181 if err != nil { 182 t.Errorf("Unexpected error: %v", err) 183 } 184 185 expected := strings.Join([]string{ 186 ":0", 187 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 188 189 if output != expected { 190 t.Errorf("expected: %q, got: %q", expected, output) 191 } 192 193 // Test that sub-command names are not completed if a local non-persistent flag is present 194 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "--nonPersistent", "value", "") 195 if err != nil { 196 t.Errorf("Unexpected error: %v", err) 197 } 198 // Reset the flag for the next command 199 nonPersistentFlag.Changed = false 200 201 expected = strings.Join([]string{ 202 ":0", 203 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 204 205 if output != expected { 206 t.Errorf("expected: %q, got: %q", expected, output) 207 } 208 209 // Test that sub-command names are completed if a local non-persistent flag is present and TraverseChildren is set to true 210 // set TraverseChildren to true on the root cmd 211 rootCmd.TraverseChildren = true 212 213 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "") 214 if err != nil { 215 t.Errorf("Unexpected error: %v", err) 216 } 217 // Reset TraverseChildren for next command 218 rootCmd.TraverseChildren = false 219 220 expected = strings.Join([]string{ 221 "childCmd1", 222 "completion", 223 "help", 224 ":4", 225 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 226 227 if output != expected { 228 t.Errorf("expected: %q, got: %q", expected, output) 229 } 230 231 // Test that sub-command names from a child cmd are completed if a local non-persistent flag is present 232 // and TraverseChildren is set to true on the root cmd 233 rootCmd.TraverseChildren = true 234 235 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "--nonPersistent", "value", "") 236 if err != nil { 237 t.Errorf("Unexpected error: %v", err) 238 } 239 // Reset TraverseChildren for next command 240 rootCmd.TraverseChildren = false 241 // Reset the flag for the next command 242 nonPersistentFlag.Changed = false 243 244 expected = strings.Join([]string{ 245 "childCmd2", 246 ":4", 247 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 248 249 if output != expected { 250 t.Errorf("expected: %q, got: %q", expected, output) 251 } 252 253 // Test that we don't use Traverse when we shouldn't. 254 // This command should not return a completion since the command line is invalid without TraverseChildren. 255 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--localroot", "value", "childCmd1", "") 256 if err != nil { 257 t.Errorf("Unexpected error: %v", err) 258 } 259 260 expected = strings.Join([]string{ 261 ":0", 262 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 263 264 if output != expected { 265 t.Errorf("expected: %q, got: %q", expected, output) 266 } 267 268 // Test that sub-command names are not completed if a local non-persistent short flag is present 269 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "-n", "value", "") 270 if err != nil { 271 t.Errorf("Unexpected error: %v", err) 272 } 273 // Reset the flag for the next command 274 nonPersistentFlag.Changed = false 275 276 expected = strings.Join([]string{ 277 ":0", 278 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 279 280 if output != expected { 281 t.Errorf("expected: %q, got: %q", expected, output) 282 } 283 284 // Test that sub-command names are completed with a persistent flag 285 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "--persistent", "value", "") 286 if err != nil { 287 t.Errorf("Unexpected error: %v", err) 288 } 289 // Reset the flag for the next command 290 persistentFlag.Changed = false 291 292 expected = strings.Join([]string{ 293 "childCmd2", 294 ":4", 295 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 296 297 if output != expected { 298 t.Errorf("expected: %q, got: %q", expected, output) 299 } 300 301 // Test that sub-command names are completed with a persistent short flag 302 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd1", "-p", "value", "") 303 if err != nil { 304 t.Errorf("Unexpected error: %v", err) 305 } 306 // Reset the flag for the next command 307 persistentFlag.Changed = false 308 309 expected = strings.Join([]string{ 310 "childCmd2", 311 ":4", 312 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 313 314 if output != expected { 315 t.Errorf("expected: %q, got: %q", expected, output) 316 } 317 } 318 319 func TestValidArgsCompletionInGo(t *testing.T) { 320 rootCmd := &Command{ 321 Use: "root", 322 ValidArgs: []string{"one", "two", "three"}, 323 Args: MinimumNArgs(1), 324 } 325 326 // Test that validArgs are completed 327 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 328 if err != nil { 329 t.Errorf("Unexpected error: %v", err) 330 } 331 332 expected := strings.Join([]string{ 333 "one", 334 "two", 335 "three", 336 ":4", 337 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 338 339 if output != expected { 340 t.Errorf("expected: %q, got: %q", expected, output) 341 } 342 343 // Test that validArgs are completed with prefix 344 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "o") 345 if err != nil { 346 t.Errorf("Unexpected error: %v", err) 347 } 348 349 expected = strings.Join([]string{ 350 "one", 351 ":4", 352 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 353 354 if output != expected { 355 t.Errorf("expected: %q, got: %q", expected, output) 356 } 357 358 // Test that validArgs don't repeat 359 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "one", "") 360 if err != nil { 361 t.Errorf("Unexpected error: %v", err) 362 } 363 364 expected = strings.Join([]string{ 365 ":0", 366 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 367 368 if output != expected { 369 t.Errorf("expected: %q, got: %q", expected, output) 370 } 371 } 372 373 func TestValidArgsAndCmdCompletionInGo(t *testing.T) { 374 rootCmd := &Command{ 375 Use: "root", 376 ValidArgs: []string{"one", "two"}, 377 Run: emptyRun, 378 } 379 380 childCmd := &Command{ 381 Use: "thechild", 382 Run: emptyRun, 383 } 384 385 rootCmd.AddCommand(childCmd) 386 387 // Test that both sub-commands and validArgs are completed 388 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 389 if err != nil { 390 t.Errorf("Unexpected error: %v", err) 391 } 392 393 expected := strings.Join([]string{ 394 "completion", 395 "help", 396 "thechild", 397 "one", 398 "two", 399 ":4", 400 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 401 402 if output != expected { 403 t.Errorf("expected: %q, got: %q", expected, output) 404 } 405 406 // Test that both sub-commands and validArgs are completed with prefix 407 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t") 408 if err != nil { 409 t.Errorf("Unexpected error: %v", err) 410 } 411 412 expected = strings.Join([]string{ 413 "thechild", 414 "two", 415 ":4", 416 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 417 418 if output != expected { 419 t.Errorf("expected: %q, got: %q", expected, output) 420 } 421 } 422 423 func TestValidArgsFuncAndCmdCompletionInGo(t *testing.T) { 424 rootCmd := &Command{ 425 Use: "root", 426 ValidArgsFunction: validArgsFunc, 427 Run: emptyRun, 428 } 429 430 childCmd := &Command{ 431 Use: "thechild", 432 Short: "The child command", 433 Run: emptyRun, 434 } 435 436 rootCmd.AddCommand(childCmd) 437 438 // Test that both sub-commands and validArgsFunction are completed 439 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 440 if err != nil { 441 t.Errorf("Unexpected error: %v", err) 442 } 443 444 expected := strings.Join([]string{ 445 "completion", 446 "help", 447 "thechild", 448 "one", 449 "two", 450 ":0", 451 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 452 453 if output != expected { 454 t.Errorf("expected: %q, got: %q", expected, output) 455 } 456 457 // Test that both sub-commands and validArgs are completed with prefix 458 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t") 459 if err != nil { 460 t.Errorf("Unexpected error: %v", err) 461 } 462 463 expected = strings.Join([]string{ 464 "thechild", 465 "two", 466 ":0", 467 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 468 469 if output != expected { 470 t.Errorf("expected: %q, got: %q", expected, output) 471 } 472 473 // Test that both sub-commands and validArgs are completed with description 474 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "t") 475 if err != nil { 476 t.Errorf("Unexpected error: %v", err) 477 } 478 479 expected = strings.Join([]string{ 480 "thechild\tThe child command", 481 "two\tThe second", 482 ":0", 483 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 484 485 if output != expected { 486 t.Errorf("expected: %q, got: %q", expected, output) 487 } 488 } 489 490 func TestFlagNameCompletionInGo(t *testing.T) { 491 rootCmd := &Command{ 492 Use: "root", 493 Run: emptyRun, 494 } 495 childCmd := &Command{ 496 Use: "childCmd", 497 Version: "1.2.3", 498 Run: emptyRun, 499 } 500 rootCmd.AddCommand(childCmd) 501 502 rootCmd.Flags().IntP("first", "f", -1, "first flag") 503 rootCmd.PersistentFlags().BoolP("second", "s", false, "second flag") 504 childCmd.Flags().String("subFlag", "", "sub flag") 505 506 // Test that flag names are not shown if the user has not given the '-' prefix 507 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 508 if err != nil { 509 t.Errorf("Unexpected error: %v", err) 510 } 511 512 expected := strings.Join([]string{ 513 "childCmd", 514 "completion", 515 "help", 516 ":4", 517 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 518 519 if output != expected { 520 t.Errorf("expected: %q, got: %q", expected, output) 521 } 522 523 // Test that flag names are completed 524 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-") 525 if err != nil { 526 t.Errorf("Unexpected error: %v", err) 527 } 528 529 expected = strings.Join([]string{ 530 "--first", 531 "-f", 532 "--help", 533 "-h", 534 "--second", 535 "-s", 536 ":4", 537 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 538 539 if output != expected { 540 t.Errorf("expected: %q, got: %q", expected, output) 541 } 542 543 // Test that flag names are completed when a prefix is given 544 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--f") 545 if err != nil { 546 t.Errorf("Unexpected error: %v", err) 547 } 548 549 expected = strings.Join([]string{ 550 "--first", 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 // Test that flag names are completed in a sub-cmd 559 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "-") 560 if err != nil { 561 t.Errorf("Unexpected error: %v", err) 562 } 563 564 expected = strings.Join([]string{ 565 "--second", 566 "-s", 567 "--help", 568 "-h", 569 "--subFlag", 570 "--version", 571 "-v", 572 ":4", 573 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 574 575 if output != expected { 576 t.Errorf("expected: %q, got: %q", expected, output) 577 } 578 } 579 580 func TestFlagNameCompletionInGoWithDesc(t *testing.T) { 581 rootCmd := &Command{ 582 Use: "root", 583 Run: emptyRun, 584 } 585 childCmd := &Command{ 586 Use: "childCmd", 587 Short: "first command", 588 Version: "1.2.3", 589 Run: emptyRun, 590 } 591 rootCmd.AddCommand(childCmd) 592 593 rootCmd.Flags().IntP("first", "f", -1, "first flag\nlonger description for flag") 594 rootCmd.PersistentFlags().BoolP("second", "s", false, "second flag") 595 childCmd.Flags().String("subFlag", "", "sub flag") 596 597 // Test that flag names are not shown if the user has not given the '-' prefix 598 output, err := executeCommand(rootCmd, ShellCompRequestCmd, "") 599 if err != nil { 600 t.Errorf("Unexpected error: %v", err) 601 } 602 603 expected := strings.Join([]string{ 604 "childCmd\tfirst command", 605 "completion\tGenerate the autocompletion script for the specified shell", 606 "help\tHelp about any command", 607 ":4", 608 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 609 610 if output != expected { 611 t.Errorf("expected: %q, got: %q", expected, output) 612 } 613 614 // Test that flag names are completed 615 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "-") 616 if err != nil { 617 t.Errorf("Unexpected error: %v", err) 618 } 619 620 expected = strings.Join([]string{ 621 "--first\tfirst flag", 622 "-f\tfirst flag", 623 "--help\thelp for root", 624 "-h\thelp for root", 625 "--second\tsecond flag", 626 "-s\tsecond flag", 627 ":4", 628 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 629 630 if output != expected { 631 t.Errorf("expected: %q, got: %q", expected, output) 632 } 633 634 // Test that flag names are completed when a prefix is given 635 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--f") 636 if err != nil { 637 t.Errorf("Unexpected error: %v", err) 638 } 639 640 expected = strings.Join([]string{ 641 "--first\tfirst flag", 642 ":4", 643 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 644 645 if output != expected { 646 t.Errorf("expected: %q, got: %q", expected, output) 647 } 648 649 // Test that flag names are completed in a sub-cmd 650 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "childCmd", "-") 651 if err != nil { 652 t.Errorf("Unexpected error: %v", err) 653 } 654 655 expected = strings.Join([]string{ 656 "--second\tsecond flag", 657 "-s\tsecond flag", 658 "--help\thelp for childCmd", 659 "-h\thelp for childCmd", 660 "--subFlag\tsub flag", 661 "--version\tversion for childCmd", 662 "-v\tversion for childCmd", 663 ":4", 664 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 665 666 if output != expected { 667 t.Errorf("expected: %q, got: %q", expected, output) 668 } 669 } 670 671 func TestFlagNameCompletionRepeat(t *testing.T) { 672 rootCmd := &Command{ 673 Use: "root", 674 Run: emptyRun, 675 } 676 childCmd := &Command{ 677 Use: "childCmd", 678 Short: "first command", 679 Run: emptyRun, 680 } 681 rootCmd.AddCommand(childCmd) 682 683 rootCmd.Flags().IntP("first", "f", -1, "first flag") 684 firstFlag := rootCmd.Flags().Lookup("first") 685 rootCmd.Flags().BoolP("second", "s", false, "second flag") 686 secondFlag := rootCmd.Flags().Lookup("second") 687 rootCmd.Flags().StringArrayP("array", "a", nil, "array flag") 688 arrayFlag := rootCmd.Flags().Lookup("array") 689 rootCmd.Flags().IntSliceP("slice", "l", nil, "slice flag") 690 sliceFlag := rootCmd.Flags().Lookup("slice") 691 rootCmd.Flags().BoolSliceP("bslice", "b", nil, "bool slice flag") 692 bsliceFlag := rootCmd.Flags().Lookup("bslice") 693 694 // Test that flag names are not repeated unless they are an array or slice 695 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--first", "1", "--") 696 if err != nil { 697 t.Errorf("Unexpected error: %v", err) 698 } 699 // Reset the flag for the next command 700 firstFlag.Changed = false 701 702 expected := strings.Join([]string{ 703 "--array", 704 "--bslice", 705 "--help", 706 "--second", 707 "--slice", 708 ":4", 709 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 710 711 if output != expected { 712 t.Errorf("expected: %q, got: %q", expected, output) 713 } 714 715 // Test that flag names are not repeated unless they are an array or slice 716 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--first", "1", "--second=false", "--") 717 if err != nil { 718 t.Errorf("Unexpected error: %v", err) 719 } 720 // Reset the flag for the next command 721 firstFlag.Changed = false 722 secondFlag.Changed = false 723 724 expected = strings.Join([]string{ 725 "--array", 726 "--bslice", 727 "--help", 728 "--slice", 729 ":4", 730 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 731 732 if output != expected { 733 t.Errorf("expected: %q, got: %q", expected, output) 734 } 735 736 // Test that flag names are not repeated unless they are an array or slice 737 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--slice", "1", "--slice=2", "--array", "val", "--bslice", "true", "--") 738 if err != nil { 739 t.Errorf("Unexpected error: %v", err) 740 } 741 // Reset the flag for the next command 742 sliceFlag.Changed = false 743 arrayFlag.Changed = false 744 bsliceFlag.Changed = false 745 746 expected = strings.Join([]string{ 747 "--array", 748 "--bslice", 749 "--first", 750 "--help", 751 "--second", 752 "--slice", 753 ":4", 754 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 755 756 if output != expected { 757 t.Errorf("expected: %q, got: %q", expected, output) 758 } 759 760 // Test that flag names are not repeated unless they are an array or slice, using shortname 761 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a", "val", "-") 762 if err != nil { 763 t.Errorf("Unexpected error: %v", err) 764 } 765 // Reset the flag for the next command 766 sliceFlag.Changed = false 767 arrayFlag.Changed = false 768 769 expected = strings.Join([]string{ 770 "--array", 771 "-a", 772 "--bslice", 773 "-b", 774 "--first", 775 "-f", 776 "--help", 777 "-h", 778 "--second", 779 "-s", 780 "--slice", 781 "-l", 782 ":4", 783 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 784 785 if output != expected { 786 t.Errorf("expected: %q, got: %q", expected, output) 787 } 788 789 // Test that flag names are not repeated unless they are an array or slice, using shortname with prefix 790 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-l", "1", "-l=2", "-a", "val", "-a") 791 if err != nil { 792 t.Errorf("Unexpected error: %v", err) 793 } 794 // Reset the flag for the next command 795 sliceFlag.Changed = false 796 arrayFlag.Changed = false 797 798 expected = strings.Join([]string{ 799 "-a", 800 ":4", 801 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 802 803 if output != expected { 804 t.Errorf("expected: %q, got: %q", expected, output) 805 } 806 } 807 808 func TestRequiredFlagNameCompletionInGo(t *testing.T) { 809 rootCmd := &Command{ 810 Use: "root", 811 ValidArgs: []string{"realArg"}, 812 Run: emptyRun, 813 } 814 childCmd := &Command{ 815 Use: "childCmd", 816 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 817 return []string{"subArg"}, ShellCompDirectiveNoFileComp 818 }, 819 Run: emptyRun, 820 } 821 rootCmd.AddCommand(childCmd) 822 823 rootCmd.Flags().IntP("requiredFlag", "r", -1, "required flag") 824 assertNoErr(t, rootCmd.MarkFlagRequired("requiredFlag")) 825 requiredFlag := rootCmd.Flags().Lookup("requiredFlag") 826 827 rootCmd.PersistentFlags().IntP("requiredPersistent", "p", -1, "required persistent") 828 assertNoErr(t, rootCmd.MarkPersistentFlagRequired("requiredPersistent")) 829 requiredPersistent := rootCmd.PersistentFlags().Lookup("requiredPersistent") 830 831 rootCmd.Flags().StringP("release", "R", "", "Release name") 832 833 childCmd.Flags().BoolP("subRequired", "s", false, "sub required flag") 834 assertNoErr(t, childCmd.MarkFlagRequired("subRequired")) 835 childCmd.Flags().BoolP("subNotRequired", "n", false, "sub not required flag") 836 837 // Test that a required flag is suggested even without the - prefix 838 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 839 if err != nil { 840 t.Errorf("Unexpected error: %v", err) 841 } 842 843 expected := strings.Join([]string{ 844 "childCmd", 845 "completion", 846 "help", 847 "--requiredFlag", 848 "-r", 849 "--requiredPersistent", 850 "-p", 851 "realArg", 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 that a required flag is suggested without other flags when using the '-' prefix 860 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-") 861 if err != nil { 862 t.Errorf("Unexpected error: %v", err) 863 } 864 865 expected = strings.Join([]string{ 866 "--requiredFlag", 867 "-r", 868 "--requiredPersistent", 869 "-p", 870 ":4", 871 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 872 873 if output != expected { 874 t.Errorf("expected: %q, got: %q", expected, output) 875 } 876 877 // Test that if no required flag matches, the normal flags are suggested 878 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--relea") 879 if err != nil { 880 t.Errorf("Unexpected error: %v", err) 881 } 882 883 expected = strings.Join([]string{ 884 "--release", 885 ":4", 886 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 887 888 if output != expected { 889 t.Errorf("expected: %q, got: %q", expected, output) 890 } 891 892 // Test required flags for sub-commands 893 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "") 894 if err != nil { 895 t.Errorf("Unexpected error: %v", err) 896 } 897 898 expected = strings.Join([]string{ 899 "--requiredPersistent", 900 "-p", 901 "--subRequired", 902 "-s", 903 "subArg", 904 ":4", 905 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 906 907 if output != expected { 908 t.Errorf("expected: %q, got: %q", expected, output) 909 } 910 911 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "-") 912 if err != nil { 913 t.Errorf("Unexpected error: %v", err) 914 } 915 916 expected = strings.Join([]string{ 917 "--requiredPersistent", 918 "-p", 919 "--subRequired", 920 "-s", 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 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "childCmd", "--subNot") 929 if err != nil { 930 t.Errorf("Unexpected error: %v", err) 931 } 932 933 expected = strings.Join([]string{ 934 "--subNotRequired", 935 ":4", 936 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 937 938 if output != expected { 939 t.Errorf("expected: %q, got: %q", expected, output) 940 } 941 942 // Test that when a required flag is present, it is not suggested anymore 943 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredFlag", "1", "") 944 if err != nil { 945 t.Errorf("Unexpected error: %v", err) 946 } 947 // Reset the flag for the next command 948 requiredFlag.Changed = false 949 950 expected = strings.Join([]string{ 951 "--requiredPersistent", 952 "-p", 953 "realArg", 954 ":4", 955 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 956 957 if output != expected { 958 t.Errorf("expected: %q, got: %q", expected, output) 959 } 960 961 // Test that when a persistent required flag is present, it is not suggested anymore 962 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredPersistent", "1", "") 963 if err != nil { 964 t.Errorf("Unexpected error: %v", err) 965 } 966 // Reset the flag for the next command 967 requiredPersistent.Changed = false 968 969 expected = strings.Join([]string{ 970 "childCmd", 971 "completion", 972 "help", 973 "--requiredFlag", 974 "-r", 975 "realArg", 976 ":4", 977 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 978 979 if output != expected { 980 t.Errorf("expected: %q, got: %q", expected, output) 981 } 982 983 // Test that when all required flags are present, normal completion is done 984 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--requiredFlag", "1", "--requiredPersistent", "1", "") 985 if err != nil { 986 t.Errorf("Unexpected error: %v", err) 987 } 988 // Reset the flags for the next command 989 requiredFlag.Changed = false 990 requiredPersistent.Changed = false 991 992 expected = strings.Join([]string{ 993 "realArg", 994 ":4", 995 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 996 997 if output != expected { 998 t.Errorf("expected: %q, got: %q", expected, output) 999 } 1000 } 1001 1002 func TestFlagFileExtFilterCompletionInGo(t *testing.T) { 1003 rootCmd := &Command{ 1004 Use: "root", 1005 Run: emptyRun, 1006 } 1007 1008 // No extensions. Should be ignored. 1009 rootCmd.Flags().StringP("file", "f", "", "file flag") 1010 assertNoErr(t, rootCmd.MarkFlagFilename("file")) 1011 1012 // Single extension 1013 rootCmd.Flags().StringP("log", "l", "", "log flag") 1014 assertNoErr(t, rootCmd.MarkFlagFilename("log", "log")) 1015 1016 // Multiple extensions 1017 rootCmd.Flags().StringP("yaml", "y", "", "yaml flag") 1018 assertNoErr(t, rootCmd.MarkFlagFilename("yaml", "yaml", "yml")) 1019 1020 // Directly using annotation 1021 rootCmd.Flags().StringP("text", "t", "", "text flag") 1022 assertNoErr(t, rootCmd.Flags().SetAnnotation("text", BashCompFilenameExt, []string{"txt"})) 1023 1024 // Test that the completion logic returns the proper info for the completion 1025 // script to handle the file filtering 1026 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--file", "") 1027 if err != nil { 1028 t.Errorf("Unexpected error: %v", err) 1029 } 1030 1031 expected := strings.Join([]string{ 1032 ":0", 1033 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1034 1035 if output != expected { 1036 t.Errorf("expected: %q, got: %q", expected, output) 1037 } 1038 1039 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--log", "") 1040 if err != nil { 1041 t.Errorf("Unexpected error: %v", err) 1042 } 1043 1044 expected = strings.Join([]string{ 1045 "log", 1046 ":8", 1047 "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") 1048 1049 if output != expected { 1050 t.Errorf("expected: %q, got: %q", expected, output) 1051 } 1052 1053 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--yaml", "") 1054 if err != nil { 1055 t.Errorf("Unexpected error: %v", err) 1056 } 1057 1058 expected = strings.Join([]string{ 1059 "yaml", "yml", 1060 ":8", 1061 "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") 1062 1063 if output != expected { 1064 t.Errorf("expected: %q, got: %q", expected, output) 1065 } 1066 1067 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--yaml=") 1068 if err != nil { 1069 t.Errorf("Unexpected error: %v", err) 1070 } 1071 1072 expected = strings.Join([]string{ 1073 "yaml", "yml", 1074 ":8", 1075 "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") 1076 1077 if output != expected { 1078 t.Errorf("expected: %q, got: %q", expected, output) 1079 } 1080 1081 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-y", "") 1082 if err != nil { 1083 t.Errorf("Unexpected error: %v", err) 1084 } 1085 1086 expected = strings.Join([]string{ 1087 "yaml", "yml", 1088 ":8", 1089 "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") 1090 1091 if output != expected { 1092 t.Errorf("expected: %q, got: %q", expected, output) 1093 } 1094 1095 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-y=") 1096 if err != nil { 1097 t.Errorf("Unexpected error: %v", err) 1098 } 1099 1100 expected = strings.Join([]string{ 1101 "yaml", "yml", 1102 ":8", 1103 "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") 1104 1105 if output != expected { 1106 t.Errorf("expected: %q, got: %q", expected, output) 1107 } 1108 1109 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--text", "") 1110 if err != nil { 1111 t.Errorf("Unexpected error: %v", err) 1112 } 1113 1114 expected = strings.Join([]string{ 1115 "txt", 1116 ":8", 1117 "Completion ended with directive: ShellCompDirectiveFilterFileExt", ""}, "\n") 1118 1119 if output != expected { 1120 t.Errorf("expected: %q, got: %q", expected, output) 1121 } 1122 } 1123 1124 func TestFlagDirFilterCompletionInGo(t *testing.T) { 1125 rootCmd := &Command{ 1126 Use: "root", 1127 Run: emptyRun, 1128 } 1129 1130 // Filter directories 1131 rootCmd.Flags().StringP("dir", "d", "", "dir flag") 1132 assertNoErr(t, rootCmd.MarkFlagDirname("dir")) 1133 1134 // Filter directories within a directory 1135 rootCmd.Flags().StringP("subdir", "s", "", "subdir") 1136 assertNoErr(t, rootCmd.Flags().SetAnnotation("subdir", BashCompSubdirsInDir, []string{"themes"})) 1137 1138 // Multiple directory specification get ignored 1139 rootCmd.Flags().StringP("manydir", "m", "", "manydir") 1140 assertNoErr(t, rootCmd.Flags().SetAnnotation("manydir", BashCompSubdirsInDir, []string{"themes", "colors"})) 1141 1142 // Test that the completion logic returns the proper info for the completion 1143 // script to handle the directory filtering 1144 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--dir", "") 1145 if err != nil { 1146 t.Errorf("Unexpected error: %v", err) 1147 } 1148 1149 expected := strings.Join([]string{ 1150 ":16", 1151 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1152 1153 if output != expected { 1154 t.Errorf("expected: %q, got: %q", expected, output) 1155 } 1156 1157 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-d", "") 1158 if err != nil { 1159 t.Errorf("Unexpected error: %v", err) 1160 } 1161 1162 expected = strings.Join([]string{ 1163 ":16", 1164 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1165 1166 if output != expected { 1167 t.Errorf("expected: %q, got: %q", expected, output) 1168 } 1169 1170 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--subdir", "") 1171 if err != nil { 1172 t.Errorf("Unexpected error: %v", err) 1173 } 1174 1175 expected = strings.Join([]string{ 1176 "themes", 1177 ":16", 1178 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1179 1180 if output != expected { 1181 t.Errorf("expected: %q, got: %q", expected, output) 1182 } 1183 1184 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--subdir=") 1185 if err != nil { 1186 t.Errorf("Unexpected error: %v", err) 1187 } 1188 1189 expected = strings.Join([]string{ 1190 "themes", 1191 ":16", 1192 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1193 1194 if output != expected { 1195 t.Errorf("expected: %q, got: %q", expected, output) 1196 } 1197 1198 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s", "") 1199 if err != nil { 1200 t.Errorf("Unexpected error: %v", err) 1201 } 1202 1203 expected = strings.Join([]string{ 1204 "themes", 1205 ":16", 1206 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1207 1208 if output != expected { 1209 t.Errorf("expected: %q, got: %q", expected, output) 1210 } 1211 1212 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s=") 1213 if err != nil { 1214 t.Errorf("Unexpected error: %v", err) 1215 } 1216 1217 expected = strings.Join([]string{ 1218 "themes", 1219 ":16", 1220 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1221 1222 if output != expected { 1223 t.Errorf("expected: %q, got: %q", expected, output) 1224 } 1225 1226 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--manydir", "") 1227 if err != nil { 1228 t.Errorf("Unexpected error: %v", err) 1229 } 1230 1231 expected = strings.Join([]string{ 1232 ":16", 1233 "Completion ended with directive: ShellCompDirectiveFilterDirs", ""}, "\n") 1234 1235 if output != expected { 1236 t.Errorf("expected: %q, got: %q", expected, output) 1237 } 1238 } 1239 1240 func TestValidArgsFuncCmdContext(t *testing.T) { 1241 validArgsFunc := func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1242 ctx := cmd.Context() 1243 1244 if ctx == nil { 1245 t.Error("Received nil context in completion func") 1246 } else if ctx.Value("testKey") != "123" { 1247 t.Error("Received invalid context") 1248 } 1249 1250 return nil, ShellCompDirectiveDefault 1251 } 1252 1253 rootCmd := &Command{ 1254 Use: "root", 1255 Run: emptyRun, 1256 } 1257 childCmd := &Command{ 1258 Use: "childCmd", 1259 ValidArgsFunction: validArgsFunc, 1260 Run: emptyRun, 1261 } 1262 rootCmd.AddCommand(childCmd) 1263 1264 //nolint:golint,staticcheck // We can safely use a basic type as key in tests. 1265 ctx := context.WithValue(context.Background(), "testKey", "123") 1266 1267 // Test completing an empty string on the childCmd 1268 _, output, err := executeCommandWithContextC(ctx, rootCmd, ShellCompNoDescRequestCmd, "childCmd", "") 1269 if err != nil { 1270 t.Errorf("Unexpected error: %v", err) 1271 } 1272 1273 expected := strings.Join([]string{ 1274 ":0", 1275 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1276 1277 if output != expected { 1278 t.Errorf("expected: %q, got: %q", expected, output) 1279 } 1280 } 1281 1282 func TestValidArgsFuncSingleCmd(t *testing.T) { 1283 rootCmd := &Command{ 1284 Use: "root", 1285 ValidArgsFunction: validArgsFunc, 1286 Run: emptyRun, 1287 } 1288 1289 // Test completing an empty string 1290 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 1291 if err != nil { 1292 t.Errorf("Unexpected error: %v", err) 1293 } 1294 1295 expected := strings.Join([]string{ 1296 "one", 1297 "two", 1298 ":0", 1299 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1300 1301 if output != expected { 1302 t.Errorf("expected: %q, got: %q", expected, output) 1303 } 1304 1305 // Check completing with a prefix 1306 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t") 1307 if err != nil { 1308 t.Errorf("Unexpected error: %v", err) 1309 } 1310 1311 expected = strings.Join([]string{ 1312 "two", 1313 ":0", 1314 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1315 1316 if output != expected { 1317 t.Errorf("expected: %q, got: %q", expected, output) 1318 } 1319 } 1320 1321 func TestValidArgsFuncSingleCmdInvalidArg(t *testing.T) { 1322 rootCmd := &Command{ 1323 Use: "root", 1324 // If we don't specify a value for Args, this test fails. 1325 // This is only true for a root command without any subcommands, and is caused 1326 // by the fact that the __complete command becomes a subcommand when there should not be one. 1327 // The problem is in the implementation of legacyArgs(). 1328 Args: MinimumNArgs(1), 1329 ValidArgsFunction: validArgsFunc, 1330 Run: emptyRun, 1331 } 1332 1333 // Check completing with wrong number of args 1334 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "unexpectedArg", "t") 1335 if err != nil { 1336 t.Errorf("Unexpected error: %v", err) 1337 } 1338 1339 expected := strings.Join([]string{ 1340 ":4", 1341 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1342 1343 if output != expected { 1344 t.Errorf("expected: %q, got: %q", expected, output) 1345 } 1346 } 1347 1348 func TestValidArgsFuncChildCmds(t *testing.T) { 1349 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1350 child1Cmd := &Command{ 1351 Use: "child1", 1352 ValidArgsFunction: validArgsFunc, 1353 Run: emptyRun, 1354 } 1355 child2Cmd := &Command{ 1356 Use: "child2", 1357 ValidArgsFunction: validArgsFunc2, 1358 Run: emptyRun, 1359 } 1360 rootCmd.AddCommand(child1Cmd, child2Cmd) 1361 1362 // Test completion of first sub-command with empty argument 1363 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "") 1364 if err != nil { 1365 t.Errorf("Unexpected error: %v", err) 1366 } 1367 1368 expected := strings.Join([]string{ 1369 "one", 1370 "two", 1371 ":0", 1372 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1373 1374 if output != expected { 1375 t.Errorf("expected: %q, got: %q", expected, output) 1376 } 1377 1378 // Test completion of first sub-command with a prefix to complete 1379 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "t") 1380 if err != nil { 1381 t.Errorf("Unexpected error: %v", err) 1382 } 1383 1384 expected = strings.Join([]string{ 1385 "two", 1386 ":0", 1387 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1388 1389 if output != expected { 1390 t.Errorf("expected: %q, got: %q", expected, output) 1391 } 1392 1393 // Check completing with wrong number of args 1394 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child1", "unexpectedArg", "t") 1395 if err != nil { 1396 t.Errorf("Unexpected error: %v", err) 1397 } 1398 1399 expected = strings.Join([]string{ 1400 ":4", 1401 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1402 1403 if output != expected { 1404 t.Errorf("expected: %q, got: %q", expected, output) 1405 } 1406 1407 // Test completion of second sub-command with empty argument 1408 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "") 1409 if err != nil { 1410 t.Errorf("Unexpected error: %v", err) 1411 } 1412 1413 expected = strings.Join([]string{ 1414 "three", 1415 "four", 1416 ":0", 1417 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1418 1419 if output != expected { 1420 t.Errorf("expected: %q, got: %q", expected, output) 1421 } 1422 1423 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "t") 1424 if err != nil { 1425 t.Errorf("Unexpected error: %v", err) 1426 } 1427 1428 expected = strings.Join([]string{ 1429 "three", 1430 ":0", 1431 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1432 1433 if output != expected { 1434 t.Errorf("expected: %q, got: %q", expected, output) 1435 } 1436 1437 // Check completing with wrong number of args 1438 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child2", "unexpectedArg", "t") 1439 if err != nil { 1440 t.Errorf("Unexpected error: %v", err) 1441 } 1442 1443 expected = strings.Join([]string{ 1444 ":4", 1445 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1446 1447 if output != expected { 1448 t.Errorf("expected: %q, got: %q", expected, output) 1449 } 1450 } 1451 1452 func TestValidArgsFuncAliases(t *testing.T) { 1453 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1454 child := &Command{ 1455 Use: "child", 1456 Aliases: []string{"son", "daughter"}, 1457 ValidArgsFunction: validArgsFunc, 1458 Run: emptyRun, 1459 } 1460 rootCmd.AddCommand(child) 1461 1462 // Test completion of first sub-command with empty argument 1463 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "son", "") 1464 if err != nil { 1465 t.Errorf("Unexpected error: %v", err) 1466 } 1467 1468 expected := strings.Join([]string{ 1469 "one", 1470 "two", 1471 ":0", 1472 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1473 1474 if output != expected { 1475 t.Errorf("expected: %q, got: %q", expected, output) 1476 } 1477 1478 // Test completion of first sub-command with a prefix to complete 1479 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "daughter", "t") 1480 if err != nil { 1481 t.Errorf("Unexpected error: %v", err) 1482 } 1483 1484 expected = strings.Join([]string{ 1485 "two", 1486 ":0", 1487 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1488 1489 if output != expected { 1490 t.Errorf("expected: %q, got: %q", expected, output) 1491 } 1492 1493 // Check completing with wrong number of args 1494 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "son", "unexpectedArg", "t") 1495 if err != nil { 1496 t.Errorf("Unexpected error: %v", err) 1497 } 1498 1499 expected = strings.Join([]string{ 1500 ":4", 1501 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1502 1503 if output != expected { 1504 t.Errorf("expected: %q, got: %q", expected, output) 1505 } 1506 } 1507 1508 func TestValidArgsFuncInBashScript(t *testing.T) { 1509 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1510 child := &Command{ 1511 Use: "child", 1512 ValidArgsFunction: validArgsFunc, 1513 Run: emptyRun, 1514 } 1515 rootCmd.AddCommand(child) 1516 1517 buf := new(bytes.Buffer) 1518 assertNoErr(t, rootCmd.GenBashCompletion(buf)) 1519 output := buf.String() 1520 1521 check(t, output, "has_completion_function=1") 1522 } 1523 1524 func TestNoValidArgsFuncInBashScript(t *testing.T) { 1525 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1526 child := &Command{ 1527 Use: "child", 1528 Run: emptyRun, 1529 } 1530 rootCmd.AddCommand(child) 1531 1532 buf := new(bytes.Buffer) 1533 assertNoErr(t, rootCmd.GenBashCompletion(buf)) 1534 output := buf.String() 1535 1536 checkOmit(t, output, "has_completion_function=1") 1537 } 1538 1539 func TestCompleteCmdInBashScript(t *testing.T) { 1540 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1541 child := &Command{ 1542 Use: "child", 1543 ValidArgsFunction: validArgsFunc, 1544 Run: emptyRun, 1545 } 1546 rootCmd.AddCommand(child) 1547 1548 buf := new(bytes.Buffer) 1549 assertNoErr(t, rootCmd.GenBashCompletion(buf)) 1550 output := buf.String() 1551 1552 check(t, output, ShellCompNoDescRequestCmd) 1553 } 1554 1555 func TestCompleteNoDesCmdInZshScript(t *testing.T) { 1556 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1557 child := &Command{ 1558 Use: "child", 1559 ValidArgsFunction: validArgsFunc, 1560 Run: emptyRun, 1561 } 1562 rootCmd.AddCommand(child) 1563 1564 buf := new(bytes.Buffer) 1565 assertNoErr(t, rootCmd.GenZshCompletionNoDesc(buf)) 1566 output := buf.String() 1567 1568 check(t, output, ShellCompNoDescRequestCmd) 1569 } 1570 1571 func TestCompleteCmdInZshScript(t *testing.T) { 1572 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1573 child := &Command{ 1574 Use: "child", 1575 ValidArgsFunction: validArgsFunc, 1576 Run: emptyRun, 1577 } 1578 rootCmd.AddCommand(child) 1579 1580 buf := new(bytes.Buffer) 1581 assertNoErr(t, rootCmd.GenZshCompletion(buf)) 1582 output := buf.String() 1583 1584 check(t, output, ShellCompRequestCmd) 1585 checkOmit(t, output, ShellCompNoDescRequestCmd) 1586 } 1587 1588 func TestFlagCompletionInGo(t *testing.T) { 1589 rootCmd := &Command{ 1590 Use: "root", 1591 Run: emptyRun, 1592 } 1593 rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot") 1594 assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1595 completions := []string{} 1596 for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} { 1597 if strings.HasPrefix(comp, toComplete) { 1598 completions = append(completions, comp) 1599 } 1600 } 1601 return completions, ShellCompDirectiveDefault 1602 })) 1603 rootCmd.Flags().String("filename", "", "Enter a filename") 1604 assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1605 completions := []string{} 1606 for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} { 1607 if strings.HasPrefix(comp, toComplete) { 1608 completions = append(completions, comp) 1609 } 1610 } 1611 return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp 1612 })) 1613 1614 // Test completing an empty string 1615 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--introot", "") 1616 if err != nil { 1617 t.Errorf("Unexpected error: %v", err) 1618 } 1619 1620 expected := strings.Join([]string{ 1621 "1", 1622 "2", 1623 "10", 1624 ":0", 1625 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\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, "--introot", "1") 1633 if err != nil { 1634 t.Errorf("Unexpected error: %v", err) 1635 } 1636 1637 expected = strings.Join([]string{ 1638 "1", 1639 "10", 1640 ":0", 1641 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1642 1643 if output != expected { 1644 t.Errorf("expected: %q, got: %q", expected, output) 1645 } 1646 1647 // Test completing an empty string 1648 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--filename", "") 1649 if err != nil { 1650 t.Errorf("Unexpected error: %v", err) 1651 } 1652 1653 expected = strings.Join([]string{ 1654 "file.yaml", 1655 "myfile.json", 1656 "file.xml", 1657 ":6", 1658 "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") 1659 1660 if output != expected { 1661 t.Errorf("expected: %q, got: %q", expected, output) 1662 } 1663 1664 // Check completing with a prefix 1665 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "--filename", "f") 1666 if err != nil { 1667 t.Errorf("Unexpected error: %v", err) 1668 } 1669 1670 expected = strings.Join([]string{ 1671 "file.yaml", 1672 "file.xml", 1673 ":6", 1674 "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") 1675 1676 if output != expected { 1677 t.Errorf("expected: %q, got: %q", expected, output) 1678 } 1679 } 1680 1681 func TestValidArgsFuncChildCmdsWithDesc(t *testing.T) { 1682 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 1683 child1Cmd := &Command{ 1684 Use: "child1", 1685 ValidArgsFunction: validArgsFunc, 1686 Run: emptyRun, 1687 } 1688 child2Cmd := &Command{ 1689 Use: "child2", 1690 ValidArgsFunction: validArgsFunc2, 1691 Run: emptyRun, 1692 } 1693 rootCmd.AddCommand(child1Cmd, child2Cmd) 1694 1695 // Test completion of first sub-command with empty argument 1696 output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child1", "") 1697 if err != nil { 1698 t.Errorf("Unexpected error: %v", err) 1699 } 1700 1701 expected := strings.Join([]string{ 1702 "one\tThe first", 1703 "two\tThe second", 1704 ":0", 1705 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1706 1707 if output != expected { 1708 t.Errorf("expected: %q, got: %q", expected, output) 1709 } 1710 1711 // Test completion of first sub-command with a prefix to complete 1712 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "t") 1713 if err != nil { 1714 t.Errorf("Unexpected error: %v", err) 1715 } 1716 1717 expected = strings.Join([]string{ 1718 "two\tThe second", 1719 ":0", 1720 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1721 1722 if output != expected { 1723 t.Errorf("expected: %q, got: %q", expected, output) 1724 } 1725 1726 // Check completing with wrong number of args 1727 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child1", "unexpectedArg", "t") 1728 if err != nil { 1729 t.Errorf("Unexpected error: %v", err) 1730 } 1731 1732 expected = strings.Join([]string{ 1733 ":4", 1734 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1735 1736 if output != expected { 1737 t.Errorf("expected: %q, got: %q", expected, output) 1738 } 1739 1740 // Test completion of second sub-command with empty argument 1741 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "") 1742 if err != nil { 1743 t.Errorf("Unexpected error: %v", err) 1744 } 1745 1746 expected = strings.Join([]string{ 1747 "three\tThe third", 1748 "four\tThe fourth", 1749 ":0", 1750 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1751 1752 if output != expected { 1753 t.Errorf("expected: %q, got: %q", expected, output) 1754 } 1755 1756 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "t") 1757 if err != nil { 1758 t.Errorf("Unexpected error: %v", err) 1759 } 1760 1761 expected = strings.Join([]string{ 1762 "three\tThe third", 1763 ":0", 1764 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1765 1766 if output != expected { 1767 t.Errorf("expected: %q, got: %q", expected, output) 1768 } 1769 1770 // Check completing with wrong number of args 1771 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "unexpectedArg", "t") 1772 if err != nil { 1773 t.Errorf("Unexpected error: %v", err) 1774 } 1775 1776 expected = strings.Join([]string{ 1777 ":4", 1778 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1779 1780 if output != expected { 1781 t.Errorf("expected: %q, got: %q", expected, output) 1782 } 1783 } 1784 1785 func TestFlagCompletionWithNotInterspersedArgs(t *testing.T) { 1786 rootCmd := &Command{Use: "root", Run: emptyRun} 1787 childCmd := &Command{ 1788 Use: "child", 1789 Run: emptyRun, 1790 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1791 return []string{"--validarg", "test"}, ShellCompDirectiveDefault 1792 }, 1793 } 1794 childCmd2 := &Command{ 1795 Use: "child2", 1796 Run: emptyRun, 1797 ValidArgs: []string{"arg1", "arg2"}, 1798 } 1799 rootCmd.AddCommand(childCmd, childCmd2) 1800 childCmd.Flags().Bool("bool", false, "test bool flag") 1801 childCmd.Flags().String("string", "", "test string flag") 1802 _ = childCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1803 return []string{"myval"}, ShellCompDirectiveDefault 1804 }) 1805 1806 // Test flag completion with no argument 1807 output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--") 1808 if err != nil { 1809 t.Errorf("Unexpected error: %v", err) 1810 } 1811 1812 expected := strings.Join([]string{ 1813 "--bool\ttest bool flag", 1814 "--help\thelp for child", 1815 "--string\ttest string flag", 1816 ":4", 1817 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1818 1819 if output != expected { 1820 t.Errorf("expected: %q, got: %q", expected, output) 1821 } 1822 1823 // Test that no flags are completed after the -- arg 1824 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "-") 1825 if err != nil { 1826 t.Errorf("Unexpected error: %v", err) 1827 } 1828 1829 expected = strings.Join([]string{ 1830 "--validarg", 1831 "test", 1832 ":0", 1833 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1834 1835 if output != expected { 1836 t.Errorf("expected: %q, got: %q", expected, output) 1837 } 1838 1839 // Test that no flags are completed after the -- arg with a flag set 1840 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--bool", "--", "-") 1841 if err != nil { 1842 t.Errorf("Unexpected error: %v", err) 1843 } 1844 1845 expected = strings.Join([]string{ 1846 "--validarg", 1847 "test", 1848 ":0", 1849 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1850 1851 if output != expected { 1852 t.Errorf("expected: %q, got: %q", expected, output) 1853 } 1854 1855 // set Interspersed to false which means that no flags should be completed after the first arg 1856 childCmd.Flags().SetInterspersed(false) 1857 1858 // Test that no flags are completed after the first arg 1859 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "arg", "--") 1860 if err != nil { 1861 t.Errorf("Unexpected error: %v", err) 1862 } 1863 1864 expected = strings.Join([]string{ 1865 "--validarg", 1866 "test", 1867 ":0", 1868 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1869 1870 if output != expected { 1871 t.Errorf("expected: %q, got: %q", expected, output) 1872 } 1873 1874 // Test that no flags are completed after the fist arg with a flag set 1875 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "t", "arg", "--") 1876 if err != nil { 1877 t.Errorf("Unexpected error: %v", err) 1878 } 1879 1880 expected = strings.Join([]string{ 1881 "--validarg", 1882 "test", 1883 ":0", 1884 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1885 1886 if output != expected { 1887 t.Errorf("expected: %q, got: %q", expected, output) 1888 } 1889 1890 // Check that args are still completed after -- 1891 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "") 1892 if err != nil { 1893 t.Errorf("Unexpected error: %v", err) 1894 } 1895 1896 expected = strings.Join([]string{ 1897 "--validarg", 1898 "test", 1899 ":0", 1900 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1901 1902 if output != expected { 1903 t.Errorf("expected: %q, got: %q", expected, output) 1904 } 1905 1906 // Check that args are still completed even if flagname with ValidArgsFunction exists 1907 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--string", "") 1908 if err != nil { 1909 t.Errorf("Unexpected error: %v", err) 1910 } 1911 1912 expected = strings.Join([]string{ 1913 "--validarg", 1914 "test", 1915 ":0", 1916 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1917 1918 if output != expected { 1919 t.Errorf("expected: %q, got: %q", expected, output) 1920 } 1921 1922 // Check that args are still completed even if flagname with ValidArgsFunction exists 1923 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child2", "--", "a") 1924 if err != nil { 1925 t.Errorf("Unexpected error: %v", err) 1926 } 1927 1928 expected = strings.Join([]string{ 1929 "arg1", 1930 "arg2", 1931 ":4", 1932 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 1933 1934 if output != expected { 1935 t.Errorf("expected: %q, got: %q", expected, output) 1936 } 1937 1938 // Check that --validarg is not parsed as flag after -- 1939 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "") 1940 if err != nil { 1941 t.Errorf("Unexpected error: %v", err) 1942 } 1943 1944 expected = strings.Join([]string{ 1945 "--validarg", 1946 "test", 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 not parsed as flag after an arg 1955 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "arg", "--validarg", "") 1956 if err != nil { 1957 t.Errorf("Unexpected error: %v", err) 1958 } 1959 1960 expected = strings.Join([]string{ 1961 "--validarg", 1962 "test", 1963 ":0", 1964 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1965 1966 if output != expected { 1967 t.Errorf("expected: %q, got: %q", expected, output) 1968 } 1969 1970 // Check that --validarg is added to args for the ValidArgsFunction 1971 childCmd.ValidArgsFunction = func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1972 return args, ShellCompDirectiveDefault 1973 } 1974 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "") 1975 if err != nil { 1976 t.Errorf("Unexpected error: %v", err) 1977 } 1978 1979 expected = strings.Join([]string{ 1980 "--validarg", 1981 ":0", 1982 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 1983 1984 if output != expected { 1985 t.Errorf("expected: %q, got: %q", expected, output) 1986 } 1987 1988 // Check that --validarg is added to args for the ValidArgsFunction and toComplete is also set correctly 1989 childCmd.ValidArgsFunction = func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 1990 return append(args, toComplete), ShellCompDirectiveDefault 1991 } 1992 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "child", "--", "--validarg", "--toComp=ab") 1993 if err != nil { 1994 t.Errorf("Unexpected error: %v", err) 1995 } 1996 1997 expected = strings.Join([]string{ 1998 "--validarg", 1999 "--toComp=ab", 2000 ":0", 2001 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 2002 2003 if output != expected { 2004 t.Errorf("expected: %q, got: %q", expected, output) 2005 } 2006 } 2007 2008 func TestFlagCompletionWorksRootCommandAddedAfterFlags(t *testing.T) { 2009 rootCmd := &Command{Use: "root", Run: emptyRun} 2010 childCmd := &Command{ 2011 Use: "child", 2012 Run: emptyRun, 2013 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2014 return []string{"--validarg", "test"}, ShellCompDirectiveDefault 2015 }, 2016 } 2017 childCmd.Flags().Bool("bool", false, "test bool flag") 2018 childCmd.Flags().String("string", "", "test string flag") 2019 _ = childCmd.RegisterFlagCompletionFunc("string", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2020 return []string{"myval"}, ShellCompDirectiveDefault 2021 }) 2022 2023 // Important: This is a test for https://github.com/spf13/cobra/issues/1437 2024 // Only add the subcommand after RegisterFlagCompletionFunc was called, do not change this order! 2025 rootCmd.AddCommand(childCmd) 2026 2027 // Test that flag completion works for the subcmd 2028 output, err := executeCommand(rootCmd, ShellCompRequestCmd, "child", "--string", "") 2029 if err != nil { 2030 t.Errorf("Unexpected error: %v", err) 2031 } 2032 2033 expected := strings.Join([]string{ 2034 "myval", 2035 ":0", 2036 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 2037 2038 if output != expected { 2039 t.Errorf("expected: %q, got: %q", expected, output) 2040 } 2041 } 2042 2043 func TestFlagCompletionInGoWithDesc(t *testing.T) { 2044 rootCmd := &Command{ 2045 Use: "root", 2046 Run: emptyRun, 2047 } 2048 rootCmd.Flags().IntP("introot", "i", -1, "help message for flag introot") 2049 assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("introot", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2050 completions := []string{} 2051 for _, comp := range []string{"1\tThe first", "2\tThe second", "10\tThe tenth"} { 2052 if strings.HasPrefix(comp, toComplete) { 2053 completions = append(completions, comp) 2054 } 2055 } 2056 return completions, ShellCompDirectiveDefault 2057 })) 2058 rootCmd.Flags().String("filename", "", "Enter a filename") 2059 assertNoErr(t, rootCmd.RegisterFlagCompletionFunc("filename", func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2060 completions := []string{} 2061 for _, comp := range []string{"file.yaml\tYAML format", "myfile.json\tJSON format", "file.xml\tXML format"} { 2062 if strings.HasPrefix(comp, toComplete) { 2063 completions = append(completions, comp) 2064 } 2065 } 2066 return completions, ShellCompDirectiveNoSpace | ShellCompDirectiveNoFileComp 2067 })) 2068 2069 // Test completing an empty string 2070 output, err := executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "") 2071 if err != nil { 2072 t.Errorf("Unexpected error: %v", err) 2073 } 2074 2075 expected := strings.Join([]string{ 2076 "1\tThe first", 2077 "2\tThe second", 2078 "10\tThe tenth", 2079 ":0", 2080 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 2081 2082 if output != expected { 2083 t.Errorf("expected: %q, got: %q", expected, output) 2084 } 2085 2086 // Check completing with a prefix 2087 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--introot", "1") 2088 if err != nil { 2089 t.Errorf("Unexpected error: %v", err) 2090 } 2091 2092 expected = strings.Join([]string{ 2093 "1\tThe first", 2094 "10\tThe tenth", 2095 ":0", 2096 "Completion ended with directive: ShellCompDirectiveDefault", ""}, "\n") 2097 2098 if output != expected { 2099 t.Errorf("expected: %q, got: %q", expected, output) 2100 } 2101 2102 // Test completing an empty string 2103 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "") 2104 if err != nil { 2105 t.Errorf("Unexpected error: %v", err) 2106 } 2107 2108 expected = strings.Join([]string{ 2109 "file.yaml\tYAML format", 2110 "myfile.json\tJSON format", 2111 "file.xml\tXML format", 2112 ":6", 2113 "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") 2114 2115 if output != expected { 2116 t.Errorf("expected: %q, got: %q", expected, output) 2117 } 2118 2119 // Check completing with a prefix 2120 output, err = executeCommand(rootCmd, ShellCompRequestCmd, "--filename", "f") 2121 if err != nil { 2122 t.Errorf("Unexpected error: %v", err) 2123 } 2124 2125 expected = strings.Join([]string{ 2126 "file.yaml\tYAML format", 2127 "file.xml\tXML format", 2128 ":6", 2129 "Completion ended with directive: ShellCompDirectiveNoSpace, ShellCompDirectiveNoFileComp", ""}, "\n") 2130 2131 if output != expected { 2132 t.Errorf("expected: %q, got: %q", expected, output) 2133 } 2134 } 2135 2136 func TestValidArgsNotValidArgsFunc(t *testing.T) { 2137 rootCmd := &Command{ 2138 Use: "root", 2139 ValidArgs: []string{"one", "two"}, 2140 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2141 return []string{"three", "four"}, ShellCompDirectiveNoFileComp 2142 }, 2143 Run: emptyRun, 2144 } 2145 2146 // Test that if both ValidArgs and ValidArgsFunction are present 2147 // only ValidArgs is considered 2148 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 2149 if err != nil { 2150 t.Errorf("Unexpected error: %v", err) 2151 } 2152 2153 expected := strings.Join([]string{ 2154 "one", 2155 "two", 2156 ":4", 2157 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2158 2159 if output != expected { 2160 t.Errorf("expected: %q, got: %q", expected, output) 2161 } 2162 2163 // Check completing with a prefix 2164 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t") 2165 if err != nil { 2166 t.Errorf("Unexpected error: %v", err) 2167 } 2168 2169 expected = strings.Join([]string{ 2170 "two", 2171 ":4", 2172 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2173 2174 if output != expected { 2175 t.Errorf("expected: %q, got: %q", expected, output) 2176 } 2177 } 2178 2179 func TestArgAliasesCompletionInGo(t *testing.T) { 2180 rootCmd := &Command{ 2181 Use: "root", 2182 Args: OnlyValidArgs, 2183 ValidArgs: []string{"one", "two", "three"}, 2184 ArgAliases: []string{"un", "deux", "trois"}, 2185 Run: emptyRun, 2186 } 2187 2188 // Test that argaliases are not completed when there are validargs that match 2189 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 2190 if err != nil { 2191 t.Errorf("Unexpected error: %v", err) 2192 } 2193 2194 expected := strings.Join([]string{ 2195 "one", 2196 "two", 2197 "three", 2198 ":4", 2199 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2200 2201 if output != expected { 2202 t.Errorf("expected: %q, got: %q", expected, output) 2203 } 2204 2205 // Test that argaliases are not completed when there are validargs that match using a prefix 2206 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "t") 2207 if err != nil { 2208 t.Errorf("Unexpected error: %v", err) 2209 } 2210 2211 expected = strings.Join([]string{ 2212 "two", 2213 "three", 2214 ":4", 2215 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2216 2217 if output != expected { 2218 t.Errorf("expected: %q, got: %q", expected, output) 2219 } 2220 2221 // Test that argaliases are completed when there are no validargs that match 2222 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "tr") 2223 if err != nil { 2224 t.Errorf("Unexpected error: %v", err) 2225 } 2226 2227 expected = strings.Join([]string{ 2228 "trois", 2229 ":4", 2230 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2231 2232 if output != expected { 2233 t.Errorf("expected: %q, got: %q", expected, output) 2234 } 2235 } 2236 2237 func TestCompleteHelp(t *testing.T) { 2238 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 2239 child1Cmd := &Command{ 2240 Use: "child1", 2241 Run: emptyRun, 2242 } 2243 child2Cmd := &Command{ 2244 Use: "child2", 2245 Run: emptyRun, 2246 } 2247 rootCmd.AddCommand(child1Cmd, child2Cmd) 2248 2249 child3Cmd := &Command{ 2250 Use: "child3", 2251 Run: emptyRun, 2252 } 2253 child1Cmd.AddCommand(child3Cmd) 2254 2255 // Test that completion includes the help command 2256 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 2257 if err != nil { 2258 t.Errorf("Unexpected error: %v", err) 2259 } 2260 2261 expected := strings.Join([]string{ 2262 "child1", 2263 "child2", 2264 "completion", 2265 "help", 2266 ":4", 2267 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2268 2269 if output != expected { 2270 t.Errorf("expected: %q, got: %q", expected, output) 2271 } 2272 2273 // Test sub-commands are completed on first level of help command 2274 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "") 2275 if err != nil { 2276 t.Errorf("Unexpected error: %v", err) 2277 } 2278 2279 expected = strings.Join([]string{ 2280 "child1", 2281 "child2", 2282 "completion", 2283 "help", // "<program> help help" is a valid command, so should be completed 2284 ":4", 2285 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2286 2287 if output != expected { 2288 t.Errorf("expected: %q, got: %q", expected, output) 2289 } 2290 2291 // Test sub-commands are completed on first level of help command 2292 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "help", "child1", "") 2293 if err != nil { 2294 t.Errorf("Unexpected error: %v", err) 2295 } 2296 2297 expected = strings.Join([]string{ 2298 "child3", 2299 ":4", 2300 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2301 2302 if output != expected { 2303 t.Errorf("expected: %q, got: %q", expected, output) 2304 } 2305 } 2306 2307 func removeCompCmd(rootCmd *Command) { 2308 // Remove completion command for the next test 2309 for _, cmd := range rootCmd.commands { 2310 if cmd.Name() == compCmdName { 2311 rootCmd.RemoveCommand(cmd) 2312 return 2313 } 2314 } 2315 } 2316 2317 func TestDefaultCompletionCmd(t *testing.T) { 2318 rootCmd := &Command{ 2319 Use: "root", 2320 Args: NoArgs, 2321 Run: emptyRun, 2322 } 2323 2324 // Test that no completion command is created if there are not other sub-commands 2325 assertNoErr(t, rootCmd.Execute()) 2326 for _, cmd := range rootCmd.commands { 2327 if cmd.Name() == compCmdName { 2328 t.Errorf("Should not have a 'completion' command when there are no other sub-commands of root") 2329 break 2330 } 2331 } 2332 2333 subCmd := &Command{ 2334 Use: "sub", 2335 Run: emptyRun, 2336 } 2337 rootCmd.AddCommand(subCmd) 2338 2339 // Test that a completion command is created if there are other sub-commands 2340 found := false 2341 assertNoErr(t, rootCmd.Execute()) 2342 for _, cmd := range rootCmd.commands { 2343 if cmd.Name() == compCmdName { 2344 found = true 2345 break 2346 } 2347 } 2348 if !found { 2349 t.Errorf("Should have a 'completion' command when there are other sub-commands of root") 2350 } 2351 // Remove completion command for the next test 2352 removeCompCmd(rootCmd) 2353 2354 // Test that the default completion command can be disabled 2355 rootCmd.CompletionOptions.DisableDefaultCmd = true 2356 assertNoErr(t, rootCmd.Execute()) 2357 for _, cmd := range rootCmd.commands { 2358 if cmd.Name() == compCmdName { 2359 t.Errorf("Should not have a 'completion' command when the feature is disabled") 2360 break 2361 } 2362 } 2363 // Re-enable for next test 2364 rootCmd.CompletionOptions.DisableDefaultCmd = false 2365 2366 // Test that completion descriptions are enabled by default 2367 output, err := executeCommand(rootCmd, compCmdName, "zsh") 2368 if err != nil { 2369 t.Errorf("Unexpected error: %v", err) 2370 } 2371 2372 check(t, output, ShellCompRequestCmd) 2373 checkOmit(t, output, ShellCompNoDescRequestCmd) 2374 // Remove completion command for the next test 2375 removeCompCmd(rootCmd) 2376 2377 // Test that completion descriptions can be disabled completely 2378 rootCmd.CompletionOptions.DisableDescriptions = true 2379 output, err = executeCommand(rootCmd, compCmdName, "zsh") 2380 if err != nil { 2381 t.Errorf("Unexpected error: %v", err) 2382 } 2383 2384 check(t, output, ShellCompNoDescRequestCmd) 2385 // Re-enable for next test 2386 rootCmd.CompletionOptions.DisableDescriptions = false 2387 // Remove completion command for the next test 2388 removeCompCmd(rootCmd) 2389 2390 var compCmd *Command 2391 // Test that the --no-descriptions flag is present on all shells 2392 assertNoErr(t, rootCmd.Execute()) 2393 for _, shell := range []string{"bash", "fish", "powershell", "zsh"} { 2394 if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil { 2395 t.Errorf("Unexpected error: %v", err) 2396 } 2397 if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag == nil { 2398 t.Errorf("Missing --%s flag for %s shell", compCmdNoDescFlagName, shell) 2399 } 2400 } 2401 // Remove completion command for the next test 2402 removeCompCmd(rootCmd) 2403 2404 // Test that the '--no-descriptions' flag can be disabled 2405 rootCmd.CompletionOptions.DisableNoDescFlag = true 2406 assertNoErr(t, rootCmd.Execute()) 2407 for _, shell := range []string{"fish", "zsh", "bash", "powershell"} { 2408 if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil { 2409 t.Errorf("Unexpected error: %v", err) 2410 } 2411 if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil { 2412 t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell) 2413 } 2414 } 2415 // Re-enable for next test 2416 rootCmd.CompletionOptions.DisableNoDescFlag = false 2417 // Remove completion command for the next test 2418 removeCompCmd(rootCmd) 2419 2420 // Test that the '--no-descriptions' flag is disabled when descriptions are disabled 2421 rootCmd.CompletionOptions.DisableDescriptions = true 2422 assertNoErr(t, rootCmd.Execute()) 2423 for _, shell := range []string{"fish", "zsh", "bash", "powershell"} { 2424 if compCmd, _, err = rootCmd.Find([]string{compCmdName, shell}); err != nil { 2425 t.Errorf("Unexpected error: %v", err) 2426 } 2427 if flag := compCmd.Flags().Lookup(compCmdNoDescFlagName); flag != nil { 2428 t.Errorf("Unexpected --%s flag for %s shell", compCmdNoDescFlagName, shell) 2429 } 2430 } 2431 // Re-enable for next test 2432 rootCmd.CompletionOptions.DisableDescriptions = false 2433 // Remove completion command for the next test 2434 removeCompCmd(rootCmd) 2435 2436 // Test that the 'completion' command can be hidden 2437 rootCmd.CompletionOptions.HiddenDefaultCmd = true 2438 assertNoErr(t, rootCmd.Execute()) 2439 compCmd, _, err = rootCmd.Find([]string{compCmdName}) 2440 if err != nil { 2441 t.Errorf("Unexpected error: %v", err) 2442 } 2443 if compCmd.Hidden == false { 2444 t.Error("Default 'completion' command should be hidden but it is not") 2445 } 2446 // Re-enable for next test 2447 rootCmd.CompletionOptions.HiddenDefaultCmd = false 2448 // Remove completion command for the next test 2449 removeCompCmd(rootCmd) 2450 } 2451 2452 func TestCompleteCompletion(t *testing.T) { 2453 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 2454 subCmd := &Command{ 2455 Use: "sub", 2456 Run: emptyRun, 2457 } 2458 rootCmd.AddCommand(subCmd) 2459 2460 // Test sub-commands of the completion command 2461 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "completion", "") 2462 if err != nil { 2463 t.Errorf("Unexpected error: %v", err) 2464 } 2465 2466 expected := strings.Join([]string{ 2467 "bash", 2468 "fish", 2469 "powershell", 2470 "zsh", 2471 ":4", 2472 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2473 2474 if output != expected { 2475 t.Errorf("expected: %q, got: %q", expected, output) 2476 } 2477 2478 // Test there are no completions for the sub-commands of the completion command 2479 var compCmd *Command 2480 for _, cmd := range rootCmd.Commands() { 2481 if cmd.Name() == compCmdName { 2482 compCmd = cmd 2483 break 2484 } 2485 } 2486 2487 for _, shell := range compCmd.Commands() { 2488 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, compCmdName, shell.Name(), "") 2489 if err != nil { 2490 t.Errorf("Unexpected error: %v", err) 2491 } 2492 2493 expected = strings.Join([]string{ 2494 ":4", 2495 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2496 2497 if output != expected { 2498 t.Errorf("expected: %q, got: %q", expected, output) 2499 } 2500 } 2501 } 2502 2503 func TestMultipleShorthandFlagCompletion(t *testing.T) { 2504 rootCmd := &Command{ 2505 Use: "root", 2506 ValidArgs: []string{"foo", "bar"}, 2507 Run: emptyRun, 2508 } 2509 f := rootCmd.Flags() 2510 f.BoolP("short", "s", false, "short flag 1") 2511 f.BoolP("short2", "d", false, "short flag 2") 2512 f.StringP("short3", "f", "", "short flag 3") 2513 _ = rootCmd.RegisterFlagCompletionFunc("short3", func(*Command, []string, string) ([]string, ShellCompDirective) { 2514 return []string{"works"}, ShellCompDirectiveNoFileComp 2515 }) 2516 2517 // Test that a single shorthand flag works 2518 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-s", "") 2519 if err != nil { 2520 t.Errorf("Unexpected error: %v", err) 2521 } 2522 2523 expected := strings.Join([]string{ 2524 "foo", 2525 "bar", 2526 ":4", 2527 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2528 2529 if output != expected { 2530 t.Errorf("expected: %q, got: %q", expected, output) 2531 } 2532 2533 // Test that multiple boolean shorthand flags work 2534 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sd", "") 2535 if err != nil { 2536 t.Errorf("Unexpected error: %v", err) 2537 } 2538 2539 expected = strings.Join([]string{ 2540 "foo", 2541 "bar", 2542 ":4", 2543 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2544 2545 if output != expected { 2546 t.Errorf("expected: %q, got: %q", expected, output) 2547 } 2548 2549 // Test that multiple boolean + string shorthand flags work 2550 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf", "") 2551 if err != nil { 2552 t.Errorf("Unexpected error: %v", err) 2553 } 2554 2555 expected = strings.Join([]string{ 2556 "works", 2557 ":4", 2558 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2559 2560 if output != expected { 2561 t.Errorf("expected: %q, got: %q", expected, output) 2562 } 2563 2564 // Test that multiple boolean + string with equal sign shorthand flags work 2565 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf=") 2566 if err != nil { 2567 t.Errorf("Unexpected error: %v", err) 2568 } 2569 2570 expected = strings.Join([]string{ 2571 "works", 2572 ":4", 2573 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2574 2575 if output != expected { 2576 t.Errorf("expected: %q, got: %q", expected, output) 2577 } 2578 2579 // Test that multiple boolean + string with equal sign with value shorthand flags work 2580 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "-sdf=abc", "") 2581 if err != nil { 2582 t.Errorf("Unexpected error: %v", err) 2583 } 2584 2585 expected = strings.Join([]string{ 2586 "foo", 2587 "bar", 2588 ":4", 2589 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2590 2591 if output != expected { 2592 t.Errorf("expected: %q, got: %q", expected, output) 2593 } 2594 } 2595 2596 func TestCompleteWithDisableFlagParsing(t *testing.T) { 2597 2598 flagValidArgs := func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2599 return []string{"--flag", "-f"}, ShellCompDirectiveNoFileComp 2600 } 2601 2602 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 2603 childCmd := &Command{ 2604 Use: "child", 2605 Run: emptyRun, 2606 DisableFlagParsing: true, 2607 ValidArgsFunction: flagValidArgs, 2608 } 2609 rootCmd.AddCommand(childCmd) 2610 2611 rootCmd.PersistentFlags().StringP("persistent", "p", "", "persistent flag") 2612 childCmd.Flags().StringP("nonPersistent", "n", "", "non-persistent flag") 2613 2614 // Test that when DisableFlagParsing==true, ValidArgsFunction is called to complete flag names, 2615 // after Cobra tried to complete the flags it knows about. 2616 childCmd.DisableFlagParsing = true 2617 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child", "-") 2618 if err != nil { 2619 t.Errorf("Unexpected error: %v", err) 2620 } 2621 2622 expected := strings.Join([]string{ 2623 "--persistent", 2624 "-p", 2625 "--help", 2626 "-h", 2627 "--nonPersistent", 2628 "-n", 2629 "--flag", 2630 "-f", 2631 ":4", 2632 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2633 2634 if output != expected { 2635 t.Errorf("expected: %q, got: %q", expected, output) 2636 } 2637 2638 // Test that when DisableFlagParsing==false, Cobra completes the flags itself and ValidArgsFunction is not called 2639 childCmd.DisableFlagParsing = false 2640 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child", "-") 2641 if err != nil { 2642 t.Errorf("Unexpected error: %v", err) 2643 } 2644 2645 // Cobra was not told of any flags, so it returns nothing 2646 expected = strings.Join([]string{ 2647 "--persistent", 2648 "-p", 2649 "--help", 2650 "-h", 2651 "--nonPersistent", 2652 "-n", 2653 ":4", 2654 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2655 2656 if output != expected { 2657 t.Errorf("expected: %q, got: %q", expected, output) 2658 } 2659 } 2660 2661 func TestCompleteWithRootAndLegacyArgs(t *testing.T) { 2662 // Test a lonely root command which uses legacyArgs(). In such a case, the root 2663 // command should accept any number of arguments and completion should behave accordingly. 2664 rootCmd := &Command{ 2665 Use: "root", 2666 Args: nil, // Args must be nil to trigger the legacyArgs() function 2667 Run: emptyRun, 2668 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2669 return []string{"arg1", "arg2"}, ShellCompDirectiveNoFileComp 2670 }, 2671 } 2672 2673 // Make sure the first arg is completed 2674 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "") 2675 if err != nil { 2676 t.Errorf("Unexpected error: %v", err) 2677 } 2678 2679 expected := strings.Join([]string{ 2680 "arg1", 2681 "arg2", 2682 ":4", 2683 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2684 2685 if output != expected { 2686 t.Errorf("expected: %q, got: %q", expected, output) 2687 } 2688 2689 // Make sure the completion of arguments continues 2690 output, err = executeCommand(rootCmd, ShellCompNoDescRequestCmd, "arg1", "") 2691 if err != nil { 2692 t.Errorf("Unexpected error: %v", err) 2693 } 2694 2695 expected = strings.Join([]string{ 2696 "arg1", 2697 "arg2", 2698 ":4", 2699 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2700 2701 if output != expected { 2702 t.Errorf("expected: %q, got: %q", expected, output) 2703 } 2704 } 2705 2706 func TestFixedCompletions(t *testing.T) { 2707 rootCmd := &Command{Use: "root", Args: NoArgs, Run: emptyRun} 2708 choices := []string{"apple", "banana", "orange"} 2709 childCmd := &Command{ 2710 Use: "child", 2711 ValidArgsFunction: FixedCompletions(choices, ShellCompDirectiveNoFileComp), 2712 Run: emptyRun, 2713 } 2714 rootCmd.AddCommand(childCmd) 2715 2716 output, err := executeCommand(rootCmd, ShellCompNoDescRequestCmd, "child", "a") 2717 if err != nil { 2718 t.Errorf("Unexpected error: %v", err) 2719 } 2720 2721 expected := strings.Join([]string{ 2722 "apple", 2723 "banana", 2724 "orange", 2725 ":4", 2726 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n") 2727 2728 if output != expected { 2729 t.Errorf("expected: %q, got: %q", expected, output) 2730 } 2731 } 2732 2733 func TestCompletionForGroupedFlags(t *testing.T) { 2734 getCmd := func() *Command { 2735 rootCmd := &Command{ 2736 Use: "root", 2737 Run: emptyRun, 2738 } 2739 childCmd := &Command{ 2740 Use: "child", 2741 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2742 return []string{"subArg"}, ShellCompDirectiveNoFileComp 2743 }, 2744 Run: emptyRun, 2745 } 2746 rootCmd.AddCommand(childCmd) 2747 2748 rootCmd.PersistentFlags().Int("ingroup1", -1, "ingroup1") 2749 rootCmd.PersistentFlags().String("ingroup2", "", "ingroup2") 2750 2751 childCmd.Flags().Bool("ingroup3", false, "ingroup3") 2752 childCmd.Flags().Bool("nogroup", false, "nogroup") 2753 2754 // Add flags to a group 2755 childCmd.MarkFlagsRequiredTogether("ingroup1", "ingroup2", "ingroup3") 2756 2757 return rootCmd 2758 } 2759 2760 // Each test case uses a unique command from the function above. 2761 testcases := []struct { 2762 desc string 2763 args []string 2764 expectedOutput string 2765 }{ 2766 { 2767 desc: "flags in group not suggested without - prefix", 2768 args: []string{"child", ""}, 2769 expectedOutput: strings.Join([]string{ 2770 "subArg", 2771 ":4", 2772 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 2773 }, 2774 { 2775 desc: "flags in group suggested with - prefix", 2776 args: []string{"child", "-"}, 2777 expectedOutput: strings.Join([]string{ 2778 "--ingroup1", 2779 "--ingroup2", 2780 "--help", 2781 "-h", 2782 "--ingroup3", 2783 "--nogroup", 2784 ":4", 2785 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 2786 }, 2787 { 2788 desc: "when flag in group present, other flags in group suggested even without - prefix", 2789 args: []string{"child", "--ingroup2", "value", ""}, 2790 expectedOutput: strings.Join([]string{ 2791 "--ingroup1", 2792 "--ingroup3", 2793 "subArg", 2794 ":4", 2795 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 2796 }, 2797 { 2798 desc: "when all flags in group present, flags not suggested without - prefix", 2799 args: []string{"child", "--ingroup1", "8", "--ingroup2", "value2", "--ingroup3", ""}, 2800 expectedOutput: strings.Join([]string{ 2801 "subArg", 2802 ":4", 2803 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 2804 }, 2805 { 2806 desc: "group ignored if some flags not applicable", 2807 args: []string{"--ingroup2", "value", ""}, 2808 expectedOutput: strings.Join([]string{ 2809 "child", 2810 "completion", 2811 "help", 2812 ":4", 2813 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 2814 }, 2815 } 2816 2817 for _, tc := range testcases { 2818 t.Run(tc.desc, func(t *testing.T) { 2819 c := getCmd() 2820 args := []string{ShellCompNoDescRequestCmd} 2821 args = append(args, tc.args...) 2822 output, err := executeCommand(c, args...) 2823 switch { 2824 case err == nil && output != tc.expectedOutput: 2825 t.Errorf("expected: %q, got: %q", tc.expectedOutput, output) 2826 case err != nil: 2827 t.Errorf("Unexpected error %q", err) 2828 } 2829 }) 2830 } 2831 } 2832 2833 func TestCompletionForMutuallyExclusiveFlags(t *testing.T) { 2834 getCmd := func() *Command { 2835 rootCmd := &Command{ 2836 Use: "root", 2837 Run: emptyRun, 2838 } 2839 childCmd := &Command{ 2840 Use: "child", 2841 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2842 return []string{"subArg"}, ShellCompDirectiveNoFileComp 2843 }, 2844 Run: emptyRun, 2845 } 2846 rootCmd.AddCommand(childCmd) 2847 2848 rootCmd.PersistentFlags().IntSlice("ingroup1", []int{1}, "ingroup1") 2849 rootCmd.PersistentFlags().String("ingroup2", "", "ingroup2") 2850 2851 childCmd.Flags().Bool("ingroup3", false, "ingroup3") 2852 childCmd.Flags().Bool("nogroup", false, "nogroup") 2853 2854 // Add flags to a group 2855 childCmd.MarkFlagsMutuallyExclusive("ingroup1", "ingroup2", "ingroup3") 2856 2857 return rootCmd 2858 } 2859 2860 // Each test case uses a unique command from the function above. 2861 testcases := []struct { 2862 desc string 2863 args []string 2864 expectedOutput string 2865 }{ 2866 { 2867 desc: "flags in mutually exclusive group not suggested without the - prefix", 2868 args: []string{"child", ""}, 2869 expectedOutput: strings.Join([]string{ 2870 "subArg", 2871 ":4", 2872 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 2873 }, 2874 { 2875 desc: "flags in mutually exclusive group suggested with the - prefix", 2876 args: []string{"child", "-"}, 2877 expectedOutput: strings.Join([]string{ 2878 "--ingroup1", 2879 "--ingroup2", 2880 "--help", 2881 "-h", 2882 "--ingroup3", 2883 "--nogroup", 2884 ":4", 2885 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 2886 }, 2887 { 2888 desc: "when flag in mutually exclusive group present, other flags in group not suggested even with the - prefix", 2889 args: []string{"child", "--ingroup1", "8", "-"}, 2890 expectedOutput: strings.Join([]string{ 2891 "--ingroup1", // Should be suggested again since it is a slice 2892 "--help", 2893 "-h", 2894 "--nogroup", 2895 ":4", 2896 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 2897 }, 2898 { 2899 desc: "group ignored if some flags not applicable", 2900 args: []string{"--ingroup1", "8", "-"}, 2901 expectedOutput: strings.Join([]string{ 2902 "--help", 2903 "-h", 2904 "--ingroup1", 2905 "--ingroup2", 2906 ":4", 2907 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 2908 }, 2909 } 2910 2911 for _, tc := range testcases { 2912 t.Run(tc.desc, func(t *testing.T) { 2913 c := getCmd() 2914 args := []string{ShellCompNoDescRequestCmd} 2915 args = append(args, tc.args...) 2916 output, err := executeCommand(c, args...) 2917 switch { 2918 case err == nil && output != tc.expectedOutput: 2919 t.Errorf("expected: %q, got: %q", tc.expectedOutput, output) 2920 case err != nil: 2921 t.Errorf("Unexpected error %q", err) 2922 } 2923 }) 2924 } 2925 } 2926 2927 func TestCompletionCobraFlags(t *testing.T) { 2928 getCmd := func() *Command { 2929 rootCmd := &Command{ 2930 Use: "root", 2931 Version: "1.1.1", 2932 Run: emptyRun, 2933 } 2934 childCmd := &Command{ 2935 Use: "child", 2936 Version: "1.1.1", 2937 Run: emptyRun, 2938 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2939 return []string{"extra"}, ShellCompDirectiveNoFileComp 2940 }, 2941 } 2942 childCmd2 := &Command{ 2943 Use: "child2", 2944 Version: "1.1.1", 2945 Run: emptyRun, 2946 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2947 return []string{"extra2"}, ShellCompDirectiveNoFileComp 2948 }, 2949 } 2950 childCmd3 := &Command{ 2951 Use: "child3", 2952 Version: "1.1.1", 2953 Run: emptyRun, 2954 ValidArgsFunction: func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { 2955 return []string{"extra3"}, ShellCompDirectiveNoFileComp 2956 }, 2957 } 2958 2959 rootCmd.AddCommand(childCmd, childCmd2, childCmd3) 2960 2961 _ = childCmd.Flags().Bool("bool", false, "A bool flag") 2962 _ = childCmd.MarkFlagRequired("bool") 2963 2964 // Have a command that adds its own help and version flag 2965 _ = childCmd2.Flags().BoolP("help", "h", false, "My own help") 2966 _ = childCmd2.Flags().BoolP("version", "v", false, "My own version") 2967 2968 // Have a command that only adds its own -v flag 2969 _ = childCmd3.Flags().BoolP("verbose", "v", false, "Not a version flag") 2970 2971 return rootCmd 2972 } 2973 2974 // Each test case uses a unique command from the function above. 2975 testcases := []struct { 2976 desc string 2977 args []string 2978 expectedOutput string 2979 }{ 2980 { 2981 desc: "completion of help and version flags", 2982 args: []string{"-"}, 2983 expectedOutput: strings.Join([]string{ 2984 "--help", 2985 "-h", 2986 "--version", 2987 "-v", 2988 ":4", 2989 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 2990 }, 2991 { 2992 desc: "no completion after --help flag", 2993 args: []string{"--help", ""}, 2994 expectedOutput: strings.Join([]string{ 2995 ":4", 2996 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 2997 }, 2998 { 2999 desc: "no completion after -h flag", 3000 args: []string{"-h", ""}, 3001 expectedOutput: strings.Join([]string{ 3002 ":4", 3003 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3004 }, 3005 { 3006 desc: "no completion after --version flag", 3007 args: []string{"--version", ""}, 3008 expectedOutput: strings.Join([]string{ 3009 ":4", 3010 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3011 }, 3012 { 3013 desc: "no completion after -v flag", 3014 args: []string{"-v", ""}, 3015 expectedOutput: strings.Join([]string{ 3016 ":4", 3017 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3018 }, 3019 { 3020 desc: "no completion after --help flag even with other completions", 3021 args: []string{"child", "--help", ""}, 3022 expectedOutput: strings.Join([]string{ 3023 ":4", 3024 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3025 }, 3026 { 3027 desc: "no completion after -h flag even with other completions", 3028 args: []string{"child", "-h", ""}, 3029 expectedOutput: strings.Join([]string{ 3030 ":4", 3031 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3032 }, 3033 { 3034 desc: "no completion after --version flag even with other completions", 3035 args: []string{"child", "--version", ""}, 3036 expectedOutput: strings.Join([]string{ 3037 ":4", 3038 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3039 }, 3040 { 3041 desc: "no completion after -v flag even with other completions", 3042 args: []string{"child", "-v", ""}, 3043 expectedOutput: strings.Join([]string{ 3044 ":4", 3045 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3046 }, 3047 { 3048 desc: "no completion after -v flag even with other flag completions", 3049 args: []string{"child", "-v", "-"}, 3050 expectedOutput: strings.Join([]string{ 3051 ":4", 3052 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3053 }, 3054 { 3055 desc: "completion after --help flag when created by program", 3056 args: []string{"child2", "--help", ""}, 3057 expectedOutput: strings.Join([]string{ 3058 "extra2", 3059 ":4", 3060 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3061 }, 3062 { 3063 desc: "completion after -h flag when created by program", 3064 args: []string{"child2", "-h", ""}, 3065 expectedOutput: strings.Join([]string{ 3066 "extra2", 3067 ":4", 3068 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3069 }, 3070 { 3071 desc: "completion after --version flag when created by program", 3072 args: []string{"child2", "--version", ""}, 3073 expectedOutput: strings.Join([]string{ 3074 "extra2", 3075 ":4", 3076 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3077 }, 3078 { 3079 desc: "completion after -v flag when created by program", 3080 args: []string{"child2", "-v", ""}, 3081 expectedOutput: strings.Join([]string{ 3082 "extra2", 3083 ":4", 3084 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3085 }, 3086 { 3087 desc: "completion after --version when only -v flag was created by program", 3088 args: []string{"child3", "--version", ""}, 3089 expectedOutput: strings.Join([]string{ 3090 ":4", 3091 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3092 }, 3093 { 3094 desc: "completion after -v flag when only -v flag was created by program", 3095 args: []string{"child3", "-v", ""}, 3096 expectedOutput: strings.Join([]string{ 3097 "extra3", 3098 ":4", 3099 "Completion ended with directive: ShellCompDirectiveNoFileComp", ""}, "\n"), 3100 }, 3101 } 3102 3103 for _, tc := range testcases { 3104 t.Run(tc.desc, func(t *testing.T) { 3105 c := getCmd() 3106 args := []string{ShellCompNoDescRequestCmd} 3107 args = append(args, tc.args...) 3108 output, err := executeCommand(c, args...) 3109 switch { 3110 case err == nil && output != tc.expectedOutput: 3111 t.Errorf("expected: %q, got: %q", tc.expectedOutput, output) 3112 case err != nil: 3113 t.Errorf("Unexpected error %q", err) 3114 } 3115 }) 3116 } 3117 }