django-components/tests/test_command_ext.py
Juro Oravec b6994e9ad3
feat: component caching (#1097)
* feat: allow to set defaults

* refactor: remove input validation and link to it

* feat: component URL

* feat: component caching

* refactor: Mark `OnComponentRenderedContext` as extension hook for docs

* docs: update changelog

* refactor: simplify hash methods
2025-04-08 11:54:39 +02:00

261 lines
8.1 KiB
Python

import sys
from io import StringIO
from textwrap import dedent
from unittest.mock import patch
from django.core.management import call_command
from django_components import CommandArg, CommandArgGroup, ComponentCommand, ComponentExtension
from django_components.testing import djc_test
from .testutils import setup_test_config
setup_test_config({"autodiscover": False})
# NOTE: Argparse changed how the optional args are displayed in Python 3.11+
if sys.version_info >= (3, 10):
OPTIONS_TITLE = "options"
else:
OPTIONS_TITLE = "optional arguments"
class EmptyExtension(ComponentExtension):
name = "empty"
class DummyCommand(ComponentCommand):
name = "dummy_cmd"
help = "Dummy command description."
arguments = [
CommandArg(
name_or_flags="--foo",
help="Foo description.",
),
CommandArgGroup(
title="group bar",
description="Group description.",
arguments=[
CommandArg(
name_or_flags="--bar",
help="Bar description.",
),
CommandArg(
name_or_flags="--baz",
help="Baz description.",
),
],
),
]
def handle(self, *args, **kwargs):
kwargs.pop("_command")
kwargs.pop("_parser")
sorted_kwargs = dict(sorted(kwargs.items()))
print(f"DummyCommand.handle: args={args}, kwargs={sorted_kwargs}")
class DummyExtension(ComponentExtension):
name = "dummy"
commands = [
DummyCommand,
]
@djc_test
class TestExtensionsCommand:
def test_root_command(self):
out = StringIO()
with patch("sys.stdout", new=out):
call_command("components", "ext")
output = out.getvalue()
assert (
output
== dedent(
f"""
usage: components ext [-h] {{list,run}} ...
Run extension commands.
{OPTIONS_TITLE}:
-h, --help show this help message and exit
subcommands:
{{list,run}}
list List all extensions.
run Run a command added by an extension.
"""
).lstrip()
)
@djc_test
class TestExtensionsListCommand:
def test_list_default_extensions(self):
out = StringIO()
with patch("sys.stdout", new=out):
call_command("components", "ext", "list")
output = out.getvalue()
assert output.strip() == "name \n========\ncache \ndefaults\nview \nurl"
@djc_test(
components_settings={"extensions": [EmptyExtension, DummyExtension]},
)
def test_list_extra_extensions(self):
out = StringIO()
with patch("sys.stdout", new=out):
call_command("components", "ext", "list")
output = out.getvalue()
assert output.strip() == "name \n========\ncache \ndefaults\nview \nurl \nempty \ndummy"
@djc_test(
components_settings={"extensions": [EmptyExtension, DummyExtension]},
)
def test_list_all(self):
out = StringIO()
with patch("sys.stdout", new=out):
call_command("components", "ext", "list", "--all")
output = out.getvalue()
assert output.strip() == "name \n========\ncache \ndefaults\nview \nurl \nempty \ndummy"
@djc_test(
components_settings={"extensions": [EmptyExtension, DummyExtension]},
)
def test_list_specific_columns(self):
out = StringIO()
with patch("sys.stdout", new=out):
call_command("components", "ext", "list", "--columns", "name")
output = out.getvalue()
assert output.strip() == "name \n========\ncache \ndefaults\nview \nurl \nempty \ndummy"
@djc_test(
components_settings={"extensions": [EmptyExtension, DummyExtension]},
)
def test_list_simple(self):
out = StringIO()
with patch("sys.stdout", new=out):
call_command("components", "ext", "list", "--simple")
output = out.getvalue()
assert output.strip() == "cache \ndefaults\nview \nurl \nempty \ndummy"
@djc_test
class TestExtensionsRunCommand:
@djc_test(
components_settings={"extensions": [EmptyExtension, DummyExtension]},
)
def test_run_command_root(self):
out = StringIO()
with patch("sys.stdout", new=out):
call_command("components", "ext", "run")
output = out.getvalue()
assert (
output
== dedent(
f"""
usage: components ext run [-h] {{cache,defaults,view,url,empty,dummy}} ...
Run a command added by an extension.
{OPTIONS_TITLE}:
-h, --help show this help message and exit
subcommands:
{{cache,defaults,view,url,empty,dummy}}
cache Run commands added by the 'cache' extension.
defaults Run commands added by the 'defaults' extension.
view Run commands added by the 'view' extension.
url Run commands added by the 'url' extension.
empty Run commands added by the 'empty' extension.
dummy Run commands added by the 'dummy' extension.
"""
).lstrip()
)
@djc_test(
components_settings={"extensions": [EmptyExtension, DummyExtension]},
)
def test_run_command_ext_empty(self):
out = StringIO()
with patch("sys.stdout", new=out):
call_command("components", "ext", "run", "empty")
output = out.getvalue()
assert (
output
== dedent(
f"""
usage: components ext run empty [-h]
Run commands added by the 'empty' extension.
{OPTIONS_TITLE}:
-h, --help show this help message and exit
"""
).lstrip()
)
@djc_test(
components_settings={"extensions": [EmptyExtension, DummyExtension]},
)
def test_run_command_ext_with_commands(self):
out = StringIO()
with patch("sys.stdout", new=out):
call_command("components", "ext", "run", "dummy")
output = out.getvalue()
assert (
output
== dedent(
f"""
usage: components ext run dummy [-h] {{dummy_cmd}} ...
Run commands added by the 'dummy' extension.
{OPTIONS_TITLE}:
-h, --help show this help message and exit
subcommands:
{{dummy_cmd}}
dummy_cmd Dummy command description.
"""
).lstrip()
)
@djc_test(
components_settings={"extensions": [EmptyExtension, DummyExtension]},
)
def test_run_command_ext_command(self):
out = StringIO()
with patch("sys.stdout", new=out):
call_command("components", "ext", "run", "dummy", "dummy_cmd")
output = out.getvalue()
# NOTE: The dummy command prints out the kwargs, which is what we check for here
assert (
output
== dedent(
"""
DummyCommand.handle: args=(), kwargs={'bar': None, 'baz': None, 'foo': None, 'force_color': False, 'no_color': False, 'pythonpath': None, 'settings': None, 'skip_checks': True, 'traceback': False, 'verbosity': 1}
""" # noqa: E501
).lstrip()
)
@djc_test(
components_settings={"extensions": [EmptyExtension, DummyExtension]},
)
def test_prints_error_if_command_not_found(self):
out = StringIO()
with patch("sys.stderr", new=out):
try:
call_command("components", "ext", "run", "dummy", "dummy_cmd_not_found")
except SystemExit:
output = out.getvalue()
assert "invalid choice: 'dummy_cmd_not_found'" in output