diff options
author | Kai Chen <kaichen120@gmail.com> | 2020-08-04 15:54:25 -0700 |
---|---|---|
committer | David Lord <davidism@gmail.com> | 2020-10-03 08:01:29 -0700 |
commit | 7029307f9ce2ee6757e8d49e3f76ebeec944779e (patch) | |
tree | 892d02f43d5d1857177ea1bf3edda415b745d712 | |
parent | 3c4cacb1bba9f2b6f62628cf1783ce64aed62436 (diff) | |
download | click-7029307f9ce2ee6757e8d49e3f76ebeec944779e.tar.gz |
tests for new shell completion system
Co-authored-by: Amy <leiamy12@gmail.com>
Co-authored-by: David Lord <davidism@gmail.com>
-rw-r--r-- | src/click/shell_completion.py | 4 | ||||
-rw-r--r-- | tests/test_bashcomplete.py | 519 | ||||
-rw-r--r-- | tests/test_compat.py | 17 | ||||
-rw-r--r-- | tests/test_shell_completion.py | 240 |
4 files changed, 243 insertions, 537 deletions
diff --git a/src/click/shell_completion.py b/src/click/shell_completion.py index a8a33b6..e165fbc 100644 --- a/src/click/shell_completion.py +++ b/src/click/shell_completion.py @@ -242,7 +242,7 @@ class BashComplete(ShellComplete): name = "bash" source_template = _SOURCE_BASH - def source(self): + def _check_version(self): import subprocess output = subprocess.run(["bash", "--version"], stdout=subprocess.PIPE) @@ -261,6 +261,8 @@ class BashComplete(ShellComplete): "Couldn't detect Bash version, shell completion is not supported." ) + def source(self): + self._check_version() return super().source() def get_completion_args(self): diff --git a/tests/test_bashcomplete.py b/tests/test_bashcomplete.py deleted file mode 100644 index f346982..0000000 --- a/tests/test_bashcomplete.py +++ /dev/null @@ -1,519 +0,0 @@ -import pytest - -import click -from click._bashcomplete import get_choices - - -def choices_without_help(cli, args, incomplete): - completions = get_choices(cli, "dummy", args, incomplete) - return [c[0] for c in completions] - - -def choices_with_help(cli, args, incomplete): - return list(get_choices(cli, "dummy", args, incomplete)) - - -def test_single_command(): - @click.command() - @click.option("--local-opt") - def cli(local_opt): - pass - - assert choices_without_help(cli, [], "-") == ["--local-opt", "--help"] - assert choices_without_help(cli, [], "") == [] - - -def test_boolean_flag(): - @click.command() - @click.option("--shout/--no-shout", default=False) - def cli(local_opt): - pass - - assert choices_without_help(cli, [], "-") == ["--shout", "--no-shout", "--help"] - - -def test_multi_value_option(): - @click.group() - @click.option("--pos", nargs=2, type=float) - def cli(local_opt): - pass - - @cli.command() - @click.option("--local-opt") - def sub(local_opt): - pass - - assert choices_without_help(cli, [], "-") == ["--pos", "--help"] - assert choices_without_help(cli, ["--pos"], "") == [] - assert choices_without_help(cli, ["--pos", "1.0"], "") == [] - assert choices_without_help(cli, ["--pos", "1.0", "1.0"], "") == ["sub"] - - -def test_multi_option(): - @click.command() - @click.option("--message", "-m", multiple=True) - def cli(local_opt): - pass - - assert choices_without_help(cli, [], "-") == ["--message", "-m", "--help"] - assert choices_without_help(cli, ["-m"], "") == [] - - -def test_small_chain(): - @click.group() - @click.option("--global-opt") - def cli(global_opt): - pass - - @cli.command() - @click.option("--local-opt") - def sub(local_opt): - pass - - assert choices_without_help(cli, [], "") == ["sub"] - assert choices_without_help(cli, [], "-") == ["--global-opt", "--help"] - assert choices_without_help(cli, ["sub"], "") == [] - assert choices_without_help(cli, ["sub"], "-") == ["--local-opt", "--help"] - - -def test_long_chain(): - @click.group("cli") - @click.option("--cli-opt") - def cli(cli_opt): - pass - - @cli.group("asub") - @click.option("--asub-opt") - def asub(asub_opt): - pass - - @asub.group("bsub") - @click.option("--bsub-opt") - def bsub(bsub_opt): - pass - - COLORS = ["red", "green", "blue"] - - def get_colors(ctx, args, incomplete): - for c in COLORS: - if c.startswith(incomplete): - yield c - - def search_colors(ctx, args, incomplete): - for c in COLORS: - if incomplete in c: - yield c - - CSUB_OPT_CHOICES = ["foo", "bar"] - CSUB_CHOICES = ["bar", "baz"] - - @bsub.command("csub") - @click.option("--csub-opt", type=click.Choice(CSUB_OPT_CHOICES)) - @click.option("--csub", type=click.Choice(CSUB_CHOICES)) - @click.option("--search-color", autocompletion=search_colors) - @click.argument("color", autocompletion=get_colors) - def csub(csub_opt, color): - pass - - assert choices_without_help(cli, [], "-") == ["--cli-opt", "--help"] - assert choices_without_help(cli, [], "") == ["asub"] - assert choices_without_help(cli, ["asub"], "-") == ["--asub-opt", "--help"] - assert choices_without_help(cli, ["asub"], "") == ["bsub"] - assert choices_without_help(cli, ["asub", "bsub"], "-") == ["--bsub-opt", "--help"] - assert choices_without_help(cli, ["asub", "bsub"], "") == ["csub"] - assert choices_without_help(cli, ["asub", "bsub", "csub"], "-") == [ - "--csub-opt", - "--csub", - "--search-color", - "--help", - ] - assert ( - choices_without_help(cli, ["asub", "bsub", "csub", "--csub-opt"], "") - == CSUB_OPT_CHOICES - ) - assert choices_without_help(cli, ["asub", "bsub", "csub"], "--csub") == [ - "--csub-opt", - "--csub", - ] - assert ( - choices_without_help(cli, ["asub", "bsub", "csub", "--csub"], "") - == CSUB_CHOICES - ) - assert choices_without_help(cli, ["asub", "bsub", "csub", "--csub-opt"], "f") == [ - "foo" - ] - assert choices_without_help(cli, ["asub", "bsub", "csub"], "") == COLORS - assert choices_without_help(cli, ["asub", "bsub", "csub"], "b") == ["blue"] - assert choices_without_help( - cli, ["asub", "bsub", "csub", "--search-color"], "een" - ) == ["green"] - - -def test_chaining(): - @click.group("cli", chain=True) - @click.option("--cli-opt") - @click.argument("arg", type=click.Choice(["cliarg1", "cliarg2"])) - def cli(cli_opt, arg): - pass - - @cli.command() - @click.option("--asub-opt") - def asub(asub_opt): - pass - - @cli.command(help="bsub help") - @click.option("--bsub-opt") - @click.argument("arg", type=click.Choice(["arg1", "arg2"])) - def bsub(bsub_opt, arg): - pass - - @cli.command() - @click.option("--csub-opt") - @click.argument("arg", type=click.Choice(["carg1", "carg2"]), default="carg1") - def csub(csub_opt, arg): - pass - - assert choices_without_help(cli, [], "-") == ["--cli-opt", "--help"] - assert choices_without_help(cli, [], "") == ["cliarg1", "cliarg2"] - assert choices_without_help(cli, ["cliarg1", "asub"], "-") == [ - "--asub-opt", - "--help", - ] - assert choices_without_help(cli, ["cliarg1", "asub"], "") == ["bsub", "csub"] - assert choices_without_help(cli, ["cliarg1", "bsub"], "") == ["arg1", "arg2"] - assert choices_without_help(cli, ["cliarg1", "asub", "--asub-opt"], "") == [] - assert choices_without_help( - cli, ["cliarg1", "asub", "--asub-opt", "5", "bsub"], "-" - ) == ["--bsub-opt", "--help"] - assert choices_without_help(cli, ["cliarg1", "asub", "bsub"], "-") == [ - "--bsub-opt", - "--help", - ] - assert choices_without_help(cli, ["cliarg1", "asub", "csub"], "") == [ - "carg1", - "carg2", - ] - assert choices_without_help(cli, ["cliarg1", "bsub", "arg1", "csub"], "") == [ - "carg1", - "carg2", - ] - assert choices_without_help(cli, ["cliarg1", "asub", "csub"], "-") == [ - "--csub-opt", - "--help", - ] - assert choices_with_help(cli, ["cliarg1", "asub"], "b") == [("bsub", "bsub help")] - - -def test_argument_choice(): - @click.command() - @click.argument("arg1", required=True, type=click.Choice(["arg11", "arg12"])) - @click.argument("arg2", type=click.Choice(["arg21", "arg22"]), default="arg21") - @click.argument("arg3", type=click.Choice(["arg", "argument"]), default="arg") - def cli(): - pass - - assert choices_without_help(cli, [], "") == ["arg11", "arg12"] - assert choices_without_help(cli, [], "arg") == ["arg11", "arg12"] - assert choices_without_help(cli, ["arg11"], "") == ["arg21", "arg22"] - assert choices_without_help(cli, ["arg12", "arg21"], "") == ["arg", "argument"] - assert choices_without_help(cli, ["arg12", "arg21"], "argu") == ["argument"] - - -def test_option_choice(): - @click.command() - @click.option("--opt1", type=click.Choice(["opt11", "opt12"]), help="opt1 help") - @click.option("--opt2", type=click.Choice(["opt21", "opt22"]), default="opt21") - @click.option("--opt3", type=click.Choice(["opt", "option"])) - def cli(): - pass - - assert choices_with_help(cli, [], "-") == [ - ("--opt1", "opt1 help"), - ("--opt2", None), - ("--opt3", None), - ("--help", "Show this message and exit."), - ] - assert choices_without_help(cli, [], "--opt") == ["--opt1", "--opt2", "--opt3"] - assert choices_without_help(cli, [], "--opt1=") == ["opt11", "opt12"] - assert choices_without_help(cli, [], "--opt2=") == ["opt21", "opt22"] - assert choices_without_help(cli, ["--opt2"], "=") == ["opt21", "opt22"] - assert choices_without_help(cli, ["--opt2", "="], "opt") == ["opt21", "opt22"] - assert choices_without_help(cli, ["--opt1"], "") == ["opt11", "opt12"] - assert choices_without_help(cli, ["--opt2"], "") == ["opt21", "opt22"] - assert choices_without_help(cli, ["--opt1", "opt11", "--opt2"], "") == [ - "opt21", - "opt22", - ] - assert choices_without_help(cli, ["--opt2", "opt21"], "-") == [ - "--opt1", - "--opt3", - "--help", - ] - assert choices_without_help(cli, ["--opt1", "opt11"], "-") == [ - "--opt2", - "--opt3", - "--help", - ] - assert choices_without_help(cli, ["--opt1"], "opt") == ["opt11", "opt12"] - assert choices_without_help(cli, ["--opt3"], "opti") == ["option"] - - assert choices_without_help(cli, ["--opt1", "invalid_opt"], "-") == [ - "--opt2", - "--opt3", - "--help", - ] - - -def test_option_and_arg_choice(): - @click.command() - @click.option("--opt1", type=click.Choice(["opt11", "opt12"])) - @click.argument("arg1", required=False, type=click.Choice(["arg11", "arg12"])) - @click.option("--opt2", type=click.Choice(["opt21", "opt22"])) - def cli(): - pass - - assert choices_without_help(cli, ["--opt1"], "") == ["opt11", "opt12"] - assert choices_without_help(cli, [""], "--opt1=") == ["opt11", "opt12"] - assert choices_without_help(cli, [], "") == ["arg11", "arg12"] - assert choices_without_help(cli, ["--opt2"], "") == ["opt21", "opt22"] - assert choices_without_help(cli, ["arg11"], "--opt") == ["--opt1", "--opt2"] - assert choices_without_help(cli, [], "--opt") == ["--opt1", "--opt2"] - - -def test_boolean_flag_choice(): - @click.command() - @click.option("--shout/--no-shout", default=False) - @click.argument("arg", required=False, type=click.Choice(["arg1", "arg2"])) - def cli(local_opt): - pass - - assert choices_without_help(cli, [], "-") == ["--shout", "--no-shout", "--help"] - assert choices_without_help(cli, ["--shout"], "") == ["arg1", "arg2"] - - -def test_multi_value_option_choice(): - @click.command() - @click.option("--pos", nargs=2, type=click.Choice(["pos1", "pos2"])) - @click.argument("arg", required=False, type=click.Choice(["arg1", "arg2"])) - def cli(local_opt): - pass - - assert choices_without_help(cli, ["--pos"], "") == ["pos1", "pos2"] - assert choices_without_help(cli, ["--pos", "pos1"], "") == ["pos1", "pos2"] - assert choices_without_help(cli, ["--pos", "pos1", "pos2"], "") == ["arg1", "arg2"] - assert choices_without_help(cli, ["--pos", "pos1", "pos2", "arg1"], "") == [] - - -def test_multi_option_choice(): - @click.command() - @click.option("--message", "-m", multiple=True, type=click.Choice(["m1", "m2"])) - @click.argument("arg", required=False, type=click.Choice(["arg1", "arg2"])) - def cli(local_opt): - pass - - assert choices_without_help(cli, ["-m"], "") == ["m1", "m2"] - assert choices_without_help(cli, ["-m", "m1", "-m"], "") == ["m1", "m2"] - assert choices_without_help(cli, ["-m", "m1"], "") == ["arg1", "arg2"] - - -def test_variadic_argument_choice(): - @click.command() - @click.option("--opt", type=click.Choice(["opt1", "opt2"])) - @click.argument("src", nargs=-1, type=click.Choice(["src1", "src2"])) - def cli(local_opt): - pass - - assert choices_without_help(cli, ["src1", "src2"], "") == ["src1", "src2"] - assert choices_without_help(cli, ["src1", "src2"], "--o") == ["--opt"] - assert choices_without_help(cli, ["src1", "src2", "--opt"], "") == ["opt1", "opt2"] - assert choices_without_help(cli, ["src1", "src2"], "") == ["src1", "src2"] - - -def test_variadic_argument_complete(): - def _complete(ctx, args, incomplete): - return ["abc", "def", "ghi", "jkl", "mno", "pqr", "stu", "vwx", "yz"] - - @click.group() - def entrypoint(): - pass - - @click.command() - @click.option("--opt", autocompletion=_complete) - @click.argument("arg", nargs=-1) - def subcommand(opt, arg): - pass - - entrypoint.add_command(subcommand) - - assert choices_without_help(entrypoint, ["subcommand", "--opt"], "") == _complete( - 0, 0, 0 - ) - assert choices_without_help( - entrypoint, ["subcommand", "whatever", "--opt"], "" - ) == _complete(0, 0, 0) - assert ( - choices_without_help(entrypoint, ["subcommand", "whatever", "--opt", "abc"], "") - == [] - ) - - -def test_long_chain_choice(): - @click.group() - def cli(): - pass - - @cli.group() - @click.option("--sub-opt", type=click.Choice(["subopt1", "subopt2"])) - @click.argument( - "sub-arg", required=False, type=click.Choice(["subarg1", "subarg2"]) - ) - def sub(sub_opt, sub_arg): - pass - - @sub.command(short_help="bsub help") - @click.option("--bsub-opt", type=click.Choice(["bsubopt1", "bsubopt2"])) - @click.argument( - "bsub-arg1", required=False, type=click.Choice(["bsubarg1", "bsubarg2"]) - ) - @click.argument( - "bbsub-arg2", required=False, type=click.Choice(["bbsubarg1", "bbsubarg2"]) - ) - def bsub(bsub_opt): - pass - - @sub.group("csub") - def csub(): - pass - - @csub.command() - def dsub(): - pass - - assert choices_with_help(cli, ["sub", "subarg1"], "") == [ - ("bsub", "bsub help"), - ("csub", ""), - ] - assert choices_without_help(cli, ["sub"], "") == ["subarg1", "subarg2"] - assert choices_without_help(cli, ["sub", "--sub-opt"], "") == ["subopt1", "subopt2"] - assert choices_without_help(cli, ["sub", "--sub-opt", "subopt1"], "") == [ - "subarg1", - "subarg2", - ] - assert choices_without_help( - cli, ["sub", "--sub-opt", "subopt1", "subarg1", "bsub"], "-" - ) == ["--bsub-opt", "--help"] - assert choices_without_help( - cli, ["sub", "--sub-opt", "subopt1", "subarg1", "bsub"], "" - ) == ["bsubarg1", "bsubarg2"] - assert choices_without_help( - cli, ["sub", "--sub-opt", "subopt1", "subarg1", "bsub", "--bsub-opt"], "" - ) == ["bsubopt1", "bsubopt2"] - assert choices_without_help( - cli, - [ - "sub", - "--sub-opt", - "subopt1", - "subarg1", - "bsub", - "--bsub-opt", - "bsubopt1", - "bsubarg1", - ], - "", - ) == ["bbsubarg1", "bbsubarg2"] - assert choices_without_help( - cli, ["sub", "--sub-opt", "subopt1", "subarg1", "csub"], "" - ) == ["dsub"] - - -def test_chained_multi(): - @click.group() - def cli(): - pass - - @cli.group() - def sub(): - pass - - @sub.group() - def bsub(): - pass - - @sub.group(chain=True) - def csub(): - pass - - @csub.command() - def dsub(): - pass - - @csub.command() - def esub(): - pass - - assert choices_without_help(cli, ["sub"], "") == ["bsub", "csub"] - assert choices_without_help(cli, ["sub"], "c") == ["csub"] - assert choices_without_help(cli, ["sub", "csub"], "") == ["dsub", "esub"] - assert choices_without_help(cli, ["sub", "csub", "dsub"], "") == ["esub"] - - -def test_hidden(): - @click.group() - @click.option("--name", hidden=True) - @click.option("--choices", type=click.Choice([1, 2]), hidden=True) - def cli(name): - pass - - @cli.group(hidden=True) - def hgroup(): - pass - - @hgroup.group() - def hgroupsub(): - pass - - @cli.command() - def asub(): - pass - - @cli.command(hidden=True) - @click.option("--hname") - def hsub(): - pass - - assert choices_without_help(cli, [], "--n") == [] - assert choices_without_help(cli, [], "--c") == [] - # If the user exactly types out the hidden param, complete its options. - assert choices_without_help(cli, ["--choices"], "") == [1, 2] - assert choices_without_help(cli, [], "") == ["asub"] - assert choices_without_help(cli, [], "") == ["asub"] - assert choices_without_help(cli, [], "h") == [] - # If the user exactly types out the hidden command, complete its subcommands. - assert choices_without_help(cli, ["hgroup"], "") == ["hgroupsub"] - assert choices_without_help(cli, ["hsub"], "--h") == ["--hname", "--help"] - - -@pytest.mark.parametrize( - ("args", "part", "expect"), - [ - ([], "-", ["--opt", "--help"]), - (["value"], "--", ["--opt", "--help"]), - ([], "-o", []), - (["--opt"], "-o", []), - (["--"], "", ["name", "-o", "--opt", "--"]), - (["--"], "--o", ["--opt"]), - ], -) -def test_args_with_double_dash_complete(args, part, expect): - def _complete(ctx, args, incomplete): - values = ["name", "-o", "--opt", "--"] - return [x for x in values if x.startswith(incomplete)] - - @click.command() - @click.option("--opt") - @click.argument("args", nargs=-1, autocompletion=_complete) - def cli(opt, args): - pass - - assert choices_without_help(cli, args, part) == expect diff --git a/tests/test_compat.py b/tests/test_compat.py index 3c458e9..825e046 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -4,23 +4,6 @@ from click._compat import should_strip_ansi from click._compat import WIN -def test_bash_func_name(): - from click._bashcomplete import get_completion_script - - script = get_completion_script("foo-bar baz_blah", "_COMPLETE_VAR", "bash").strip() - assert script.startswith("_foo_barbaz_blah_completion()") - assert "_COMPLETE_VAR=complete $1" in script - - -def test_zsh_func_name(): - from click._bashcomplete import get_completion_script - - script = get_completion_script("foo-bar", "_COMPLETE_VAR", "zsh").strip() - assert script.startswith("#compdef foo-bar") - assert "compdef _foo_bar_completion foo-bar;" in script - assert "(( ! $+commands[foo-bar] )) && return 1" in script - - @pytest.mark.xfail(WIN, reason="Jupyter not tested/supported on Windows") def test_is_jupyter_kernel_output(): class JupyterKernelFakeStream: diff --git a/tests/test_shell_completion.py b/tests/test_shell_completion.py new file mode 100644 index 0000000..0113b6e --- /dev/null +++ b/tests/test_shell_completion.py @@ -0,0 +1,240 @@ +import sys + +import pytest + +from click.core import Argument +from click.core import Command +from click.core import Group +from click.core import Option +from click.shell_completion import ShellComplete +from click.types import Choice +from click.types import File +from click.types import Path + + +def _get_completions(cli, args, incomplete): + comp = ShellComplete(cli, cli.name, "_CLICK_COMPLETE") + return comp.get_completions(args, incomplete) + + +def _get_words(cli, args, incomplete): + return [c[1] for c in _get_completions(cli, args, incomplete)] + + +def test_command(): + cli = Command("cli", params=[Option(["-t", "--test"])]) + assert _get_words(cli, [], "") == [] + assert _get_words(cli, [], "-") == ["-t", "--test", "--help"] + assert _get_words(cli, [], "--") == ["--test", "--help"] + assert _get_words(cli, [], "--t") == ["--test"] + assert _get_words(cli, ["-t", "a"], "-") == ["--test", "--help"] + + +def test_group(): + cli = Group("cli", params=[Option(["-a"])], commands=[Command("x"), Command("y")]) + assert _get_words(cli, [], "") == ["x", "y"] + assert _get_words(cli, [], "-") == ["-a", "--help"] + + +def test_chained(): + cli = Group( + "cli", + chain=True, + commands=[ + Command("set", params=[Option(["-y"])]), + Command("start"), + Group("get", commands=[Command("full")]), + ], + ) + assert _get_words(cli, [], "") == ["get", "set", "start"] + assert _get_words(cli, [], "s") == ["set", "start"] + assert _get_words(cli, ["set", "start"], "") == ["get"] + # subcommands and parent subcommands + assert _get_words(cli, ["get"], "") == ["full", "set", "start"] + assert _get_words(cli, ["get", "full"], "") == ["set", "start"] + assert _get_words(cli, ["get"], "s") == ["set", "start"] + + +def test_help_option(): + cli = Group("cli", commands=[Command("with"), Command("no", add_help_option=False)]) + assert _get_words(cli, ["with"], "--") == ["--help"] + assert _get_words(cli, ["no"], "--") == [] + + +def test_argument_order(): + cli = Command( + "cli", + params=[ + Argument(["plain"]), + Argument(["c1"], type=Choice(["a1", "a2", "b"])), + Argument(["c2"], type=Choice(["c1", "c2", "d"])), + ], + ) + # first argument has no completions + assert _get_words(cli, [], "") == [] + assert _get_words(cli, [], "a") == [] + # first argument filled, now completion can happen + assert _get_words(cli, ["x"], "a") == ["a1", "a2"] + assert _get_words(cli, ["x", "b"], "d") == ["d"] + + +def test_type_choice(): + cli = Command("cli", params=[Option(["-c"], type=Choice(["a1", "a2", "b"]))]) + assert _get_words(cli, ["-c"], "") == ["a1", "a2", "b"] + assert _get_words(cli, ["-c"], "a") == ["a1", "a2"] + assert _get_words(cli, ["-c"], "a2") == ["a2"] + + +def test_type_file(): + cli = Command("cli", params=[Option(["-f"], type=File())]) + assert _get_completions(cli, ["-f"], "ab") == [("file", "ab", None)] + + +def test_type_path_file(): + cli = Command("cli", params=[Option(["-p"], type=Path())]) + assert _get_completions(cli, ["-p"], "ab") == [("file", "ab", None)] + + +def test_type_path_dir(): + cli = Command("cli", params=[Option(["-d"], type=Path(file_okay=False))]) + assert _get_completions(cli, ["-d"], "ab") == [("dir", "ab", None)] + + +def test_option_flag(): + cli = Command( + "cli", + add_help_option=False, + params=[ + Option(["--on/--off"]), + Argument(["a"], type=Choice(["a1", "a2", "b"])), + ], + ) + assert _get_words(cli, ["type"], "--") == ["--on", "--off"] + # flag option doesn't take value, use choice argument + assert _get_words(cli, ["x", "--on"], "a") == ["a1", "a2"] + + +def test_option_custom(): + def custom(ctx, args, incomplete): + return [incomplete.upper()] + + cli = Command( + "cli", + params=[ + Argument(["x"]), + Argument(["y"]), + Argument(["z"], autocompletion=custom), + ], + ) + assert _get_words(cli, ["a", "b"], "") == [""] + assert _get_words(cli, ["a", "b"], "c") == ["C"] + + +def test_option_multiple(): + cli = Command( + "type", + params=[Option(["-m"], type=Choice(["a", "b"]), multiple=True), Option(["-f"])], + ) + assert _get_words(cli, ["-m"], "") == ["a", "b"] + assert "-m" in _get_words(cli, ["-m", "a"], "-") + assert _get_words(cli, ["-m", "a", "-m"], "") == ["a", "b"] + # used single options aren't suggested again + assert "-c" not in _get_words(cli, ["-c", "f"], "-") + + +def test_option_nargs(): + cli = Command("cli", params=[Option(["-c"], type=Choice(["a", "b"]), nargs=2)]) + assert _get_words(cli, ["-c"], "") == ["a", "b"] + assert _get_words(cli, ["-c", "a"], "") == ["a", "b"] + assert _get_words(cli, ["-c", "a", "b"], "") == [] + + +def test_argument_nargs(): + cli = Command( + "cli", + params=[ + Argument(["x"], type=Choice(["a", "b"]), nargs=2), + Argument(["y"], type=Choice(["c", "d"]), nargs=-1), + Option(["-z"]), + ], + ) + assert _get_words(cli, [], "") == ["a", "b"] + assert _get_words(cli, ["a"], "") == ["a", "b"] + assert _get_words(cli, ["a", "b"], "") == ["c", "d"] + assert _get_words(cli, ["a", "b", "c"], "") == ["c", "d"] + assert _get_words(cli, ["a", "b", "c", "d"], "") == ["c", "d"] + assert _get_words(cli, ["a", "-z", "1"], "") == ["a", "b"] + assert _get_words(cli, ["a", "-z", "1", "b"], "") == ["c", "d"] + + +def test_double_dash(): + cli = Command( + "cli", + add_help_option=False, + params=[ + Option(["--opt"]), + Argument(["name"], type=Choice(["name", "--", "-o", "--opt"])), + ], + ) + assert _get_words(cli, [], "-") == ["--opt"] + assert _get_words(cli, ["value"], "-") == ["--opt"] + assert _get_words(cli, [], "") == ["name", "--", "-o", "--opt"] + assert _get_words(cli, ["--"], "") == ["name", "--", "-o", "--opt"] + + +def test_hidden(): + cli = Group( + "cli", + commands=[ + Command( + "hidden", + add_help_option=False, + hidden=True, + params=[ + Option(["-a"]), + Option(["-b"], type=Choice(["a", "b"]), hidden=True), + ], + ) + ], + ) + assert "hidden" not in _get_words(cli, [], "") + assert "hidden" not in _get_words(cli, [], "hidden") + assert _get_words(cli, ["hidden"], "-") == ["-a"] + assert _get_words(cli, ["hidden", "-b"], "") == ["a", "b"] + + +@pytest.fixture() +def _patch_for_completion(monkeypatch): + monkeypatch.setattr("click.core._fast_exit", sys.exit) + monkeypatch.setattr( + "click.shell_completion.BashComplete._check_version", lambda self: True + ) + + +@pytest.mark.parametrize( + "shell", ["bash", "zsh", "fish"], +) +@pytest.mark.usefixtures("_patch_for_completion") +def test_full_source(runner, shell): + cli = Group("cli", commands=[Command("a"), Command("b")]) + result = runner.invoke(cli, env={"_CLI_COMPLETE": f"source_{shell}"}) + assert f"_CLI_COMPLETE=complete_{shell}" in result.output + + +@pytest.mark.parametrize( + ("shell", "env", "expect"), + [ + ("bash", {"COMP_WORDS": "", "COMP_CWORD": "0"}, "plain,a\nplain,b\n"), + ("bash", {"COMP_WORDS": "a b", "COMP_CWORD": "1"}, "plain,b\n"), + ("zsh", {"COMP_WORDS": "", "COMP_CWORD": "0"}, "plain\na\n_\nplain\nb\nbee\n"), + ("zsh", {"COMP_WORDS": "a b", "COMP_CWORD": "1"}, "plain\nb\nbee\n"), + ("fish", {"COMP_WORDS": "", "COMP_CWORD": ""}, "plain,a\nplain,b\tbee\n"), + ("fish", {"COMP_WORDS": "a b", "COMP_CWORD": "b"}, "plain,b\tbee\n"), + ], +) +@pytest.mark.usefixtures("_patch_for_completion") +def test_full_complete(runner, shell, env, expect): + cli = Group("cli", commands=[Command("a"), Command("b", help="bee")]) + env["_CLI_COMPLETE"] = f"complete_{shell}" + result = runner.invoke(cli, env=env) + assert result.output == expect |