summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKai Chen <kaichen120@gmail.com>2020-08-04 15:54:25 -0700
committerDavid Lord <davidism@gmail.com>2020-10-03 08:01:29 -0700
commit7029307f9ce2ee6757e8d49e3f76ebeec944779e (patch)
tree892d02f43d5d1857177ea1bf3edda415b745d712
parent3c4cacb1bba9f2b6f62628cf1783ce64aed62436 (diff)
downloadclick-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.py4
-rw-r--r--tests/test_bashcomplete.py519
-rw-r--r--tests/test_compat.py17
-rw-r--r--tests/test_shell_completion.py240
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