Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions tests/test_arguments.py
Original file line number Diff line number Diff line change
Expand Up @@ -616,3 +616,99 @@ def cmd(myarg):
result = runner.invoke(cmd, pass_argv)
assert not result.exception
assert result.exit_code == 0


@pytest.mark.parametrize(
("args", "expected_exit_code", "expected_output"),
[
# Empty string argument should be passed through
([""], 0, "arg:''"),
# Whitespace-only argument
([" "], 0, "arg:' '"),
# Unicode characters
(["\N{SNOWMAN}"], 0, "arg:'\u2603'"),
# Very long argument
(["a" * 1000], 0, "arg:'" + "a" * 1000 + "'"),
],
)
def test_argument_edge_cases(runner, args, expected_exit_code, expected_output):
"""Test argument parsing with edge case inputs."""

@click.command()
@click.argument("arg")
def cli(arg):
click.echo(f"arg:'{arg}'")

result = runner.invoke(cli, args)
assert result.exit_code == expected_exit_code
assert expected_output in result.output


def test_argument_special_chars_with_dash_separator(runner):
"""Test that special character arguments work with -- separator."""

@click.command()
@click.argument("arg")
def cli(arg):
click.echo(f"arg:'{arg}'")

# Use -- to ensure arguments starting with - are treated as positional
result = runner.invoke(cli, ["--", "--foo"])
assert result.exit_code == 0
assert "arg:'--foo'" in result.output

result = runner.invoke(cli, ["--", "-x"])
assert result.exit_code == 0
assert "arg:'-x'" in result.output


def test_argument_with_dash_separator(runner):
"""Test that arguments after -- are treated as positional arguments."""

@click.command()
@click.option("--opt", default="default")
@click.argument("arg")
def cli(opt, arg):
click.echo(f"opt:{opt} arg:{arg}")

# Without --, -value is treated as an option
result = runner.invoke(cli, ["-value"])
assert result.exit_code == 2
assert "no such option" in result.output.lower()

# With --, -value is treated as an argument
result = runner.invoke(cli, ["--", "-value"])
assert result.exit_code == 0
assert "opt:default arg:-value" in result.output


def test_argument_nargs_zero_values(runner):
"""Test nargs=-1 with no values provided."""

@click.command()
@click.argument("args", nargs=-1)
def cli(args):
click.echo(f"count:{len(args)} args:{','.join(args)}")

result = runner.invoke(cli, [])
assert result.exit_code == 0
assert "count:0 args:" in result.output


def test_argument_required_with_default(runner):
"""Test that required argument with default works correctly."""

@click.command()
@click.argument("arg", required=True, default="fallback")
def cli(arg):
click.echo(f"arg:{arg}")

# When not provided, should use default
result = runner.invoke(cli, [])
assert result.exit_code == 0
assert "arg:fallback" in result.output

# When provided, should use provided value
result = runner.invoke(cli, ["provided"])
assert result.exit_code == 0
assert "arg:provided" in result.output
163 changes: 163 additions & 0 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -733,3 +733,166 @@ def test_help_invalid_default(runner):
result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
assert "default: not found" in result.output


@pytest.mark.parametrize(
("args", "expected_in_output"),
[
(["--unknown-option"], "No such option: --unknown-option"),
(["-x"], "No such option: -x"),
(["---triple"], "No such option: ---triple"),
],
)
def test_unknown_option_error_messages(runner, args, expected_in_output):
@click.command()
def cli():
pass

result = runner.invoke(cli, args)
assert result.exit_code == 2
assert expected_in_output in result.output


def test_missing_argument_error_message(runner):
@click.command()
@click.argument("name")
def cli(name):
pass

result = runner.invoke(cli, [])
assert result.exit_code == 2
assert "Missing argument 'NAME'" in result.output


def test_invalid_choice_error_message(runner):
@click.command()
@click.option("--choice", type=click.Choice(["a", "b", "c"]))
def cli(choice):
pass

result = runner.invoke(cli, ["--choice", "invalid"])
assert result.exit_code == 2
assert "'invalid' is not one of 'a', 'b', 'c'" in result.output


def test_invalid_integer_error_message(runner):
@click.command()
@click.option("--count", type=int)
def cli(count):
pass

result = runner.invoke(cli, ["--count", "not-a-number"])
assert result.exit_code == 2
assert "'not-a-number' is not a valid integer" in result.output


def test_invalid_float_error_message(runner):
@click.command()
@click.option("--value", type=float)
def cli(value):
pass

result = runner.invoke(cli, ["--value", "not-a-float"])
assert result.exit_code == 2
assert "'not-a-float' is not a valid float" in result.output


def test_invalid_path_error_message(runner):
@click.command()
@click.option("--path", type=click.Path(exists=True))
def cli(path):
pass

result = runner.invoke(cli, ["--path", "/nonexistent/path/that/does/not/exist"])
assert result.exit_code == 2
assert "does not exist" in result.output


def test_usage_error_with_help_suggestion(runner):
@click.command()
@click.option("--required", required=True)
def cli(required):
pass

result = runner.invoke(cli, [])
assert result.exit_code == 2
assert "Try 'cli --help' for help" in result.output


def test_error_message_with_special_chars_in_option_name(runner):
@click.command()
@click.option("--日本語-option", required=True)
def cli(japanese_option):
pass

result = runner.invoke(cli, [])
assert result.exit_code == 2
assert "Missing option" in result.output


def test_error_message_preserves_unicode(runner):
@click.command()
@click.argument("name")
def cli(name):
pass

result = runner.invoke(cli, ["🎉", "extra"])
assert result.exit_code == 2
assert "Got unexpected extra argument" in result.output


@pytest.mark.parametrize(
("option_name", "error_type"),
[
("--name", "Missing option"),
("--count", "Missing option"),
],
)
def test_missing_required_option_various_names(runner, option_name, error_type):
@click.command()
@click.option(option_name, required=True)
def cli(**kwargs):
pass

result = runner.invoke(cli, [])
assert result.exit_code == 2
assert error_type in result.output


def test_nargs_error_message(runner):
@click.command()
@click.option("--coords", nargs=2, type=int)
def cli(coords):
pass

result = runner.invoke(cli, ["--coords", "1"])
assert result.exit_code == 2
assert "--coords" in result.output


def test_range_error_message(runner):
@click.command()
@click.option("--level", type=click.IntRange(1, 10))
def cli(level):
pass

result = runner.invoke(cli, ["--level", "15"])
assert result.exit_code == 2
assert "15 is not in the range" in result.output


def test_help_output_with_multiline_description(runner):
@click.command()
@click.option(
"--config",
help="""This is a multiline
help text that should be
properly formatted in the output.""",
)
def cli(config):
pass

result = runner.invoke(cli, ["--help"])
assert result.exit_code == 0
assert "multiline" in result.output
assert "properly formatted" in result.output
Loading
Loading