mirror of
https://github.com/django-components/django-components.git
synced 2025-07-16 04:54:59 +00:00
Reformat lines that became too long. And enforce 119 line length.
This commit is contained in:
parent
ef6a082238
commit
48fe8171b4
25 changed files with 314 additions and 733 deletions
|
@ -11,4 +11,4 @@ repos:
|
||||||
rev: 7.0.0
|
rev: 7.0.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: flake8
|
- id: flake8
|
||||||
|
additional_dependencies: [flake8-pyproject]
|
||||||
|
|
|
@ -4,10 +4,7 @@ from django.template import Context, Template
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
|
|
||||||
from django_components import component
|
from django_components import component
|
||||||
from django_components.middleware import (
|
from django_components.middleware import CSS_DEPENDENCY_PLACEHOLDER, JS_DEPENDENCY_PLACEHOLDER
|
||||||
CSS_DEPENDENCY_PLACEHOLDER,
|
|
||||||
JS_DEPENDENCY_PLACEHOLDER,
|
|
||||||
)
|
|
||||||
from tests.django_test_setup import * # NOQA
|
from tests.django_test_setup import * # NOQA
|
||||||
from tests.testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
from tests.testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
||||||
from tests.testutils import create_and_process_template_response
|
from tests.testutils import create_and_process_template_response
|
||||||
|
@ -75,9 +72,7 @@ class RenderBenchmarks(SimpleTestCase):
|
||||||
component.registry.clear()
|
component.registry.clear()
|
||||||
component.registry.register("test_component", SlottedComponent)
|
component.registry.register("test_component", SlottedComponent)
|
||||||
component.registry.register("inner_component", SimpleComponent)
|
component.registry.register("inner_component", SimpleComponent)
|
||||||
component.registry.register(
|
component.registry.register("breadcrumb_component", BreadcrumbComponent)
|
||||||
"breadcrumb_component", BreadcrumbComponent
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def timed_loop(func, iterations=1000):
|
def timed_loop(func, iterations=1000):
|
||||||
|
@ -91,22 +86,28 @@ class RenderBenchmarks(SimpleTestCase):
|
||||||
|
|
||||||
def test_render_time_for_small_component(self):
|
def test_render_time_for_small_component(self):
|
||||||
template = Template(
|
template = Template(
|
||||||
"{% load component_tags %}{% component_block 'test_component' %}"
|
"""
|
||||||
"{% slot \"header\" %}{% component_block 'inner_component' variable='foo' %}{% endslot %}{% endcomponent_block %}"
|
{% load component_tags %}
|
||||||
"{% endcomponent_block %}",
|
{% component_block 'test_component' %}
|
||||||
name="root",
|
{% slot "header" %}
|
||||||
|
{% component_block 'inner_component' variable='foo' %}{% endcomponent_block %}
|
||||||
|
{% endslot %}
|
||||||
|
{% endcomponent_block %}
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
|
|
||||||
print(
|
print(f"{self.timed_loop(lambda: template.render(Context({})))} ms per iteration")
|
||||||
f"{self.timed_loop(lambda: template.render(Context({})))} ms per iteration"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_middleware_time_with_dependency_for_small_page(self):
|
def test_middleware_time_with_dependency_for_small_page(self):
|
||||||
template = Template(
|
template = Template(
|
||||||
"{% load component_tags %}{% component_dependencies %}"
|
"""
|
||||||
"{% component_block 'test_component' %}{% slot \"header\" %}"
|
{% load component_tags %}{% component_dependencies %}
|
||||||
"{% component_block 'inner_component' variable='foo' %}{% endslot %}{% endcomponent_block %}{% endcomponent_block %}",
|
{% component_block 'test_component' %}
|
||||||
name="root",
|
{% slot "header" %}
|
||||||
|
{% component_block 'inner_component' variable='foo' %}{% endcomponent_block %}
|
||||||
|
{% endslot %}
|
||||||
|
{% endcomponent_block %}
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
# Sanity tests
|
# Sanity tests
|
||||||
response_content = create_and_process_template_response(template)
|
response_content = create_and_process_template_response(template)
|
||||||
|
@ -116,15 +117,9 @@ class RenderBenchmarks(SimpleTestCase):
|
||||||
self.assertIn("script.js", response_content)
|
self.assertIn("script.js", response_content)
|
||||||
|
|
||||||
without_middleware = self.timed_loop(
|
without_middleware = self.timed_loop(
|
||||||
lambda: create_and_process_template_response(
|
lambda: create_and_process_template_response(template, use_middleware=False)
|
||||||
template, use_middleware=False
|
|
||||||
)
|
|
||||||
)
|
|
||||||
with_middleware = self.timed_loop(
|
|
||||||
lambda: create_and_process_template_response(
|
|
||||||
template, use_middleware=True
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
with_middleware = self.timed_loop(lambda: create_and_process_template_response(template, use_middleware=True))
|
||||||
|
|
||||||
print("Small page middleware test")
|
print("Small page middleware test")
|
||||||
self.report_results(with_middleware, without_middleware)
|
self.report_results(with_middleware, without_middleware)
|
||||||
|
@ -140,14 +135,10 @@ class RenderBenchmarks(SimpleTestCase):
|
||||||
self.assertIn("test.js", response_content)
|
self.assertIn("test.js", response_content)
|
||||||
|
|
||||||
without_middleware = self.timed_loop(
|
without_middleware = self.timed_loop(
|
||||||
lambda: create_and_process_template_response(
|
lambda: create_and_process_template_response(template, {}, use_middleware=False)
|
||||||
template, {}, use_middleware=False
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
with_middleware = self.timed_loop(
|
with_middleware = self.timed_loop(
|
||||||
lambda: create_and_process_template_response(
|
lambda: create_and_process_template_response(template, {}, use_middleware=True)
|
||||||
template, {}, use_middleware=True
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
print("Large page middleware test")
|
print("Large page middleware test")
|
||||||
|
@ -156,15 +147,9 @@ class RenderBenchmarks(SimpleTestCase):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def report_results(with_middleware, without_middleware):
|
def report_results(with_middleware, without_middleware):
|
||||||
print(f"Middleware active\t\t{with_middleware:.3f} ms per iteration")
|
print(f"Middleware active\t\t{with_middleware:.3f} ms per iteration")
|
||||||
print(
|
print(f"Middleware inactive\t{without_middleware:.3f} ms per iteration")
|
||||||
f"Middleware inactive\t{without_middleware:.3f} ms per iteration"
|
|
||||||
)
|
|
||||||
time_difference = with_middleware - without_middleware
|
time_difference = with_middleware - without_middleware
|
||||||
if without_middleware > with_middleware:
|
if without_middleware > with_middleware:
|
||||||
print(
|
print(f"Decrease of {-100 * time_difference / with_middleware:.2f}%")
|
||||||
f"Decrease of {-100 * time_difference / with_middleware:.2f}%"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
print(
|
print(f"Increase of {100 * time_difference / without_middleware:.2f}%")
|
||||||
f"Increase of {100 * time_difference / without_middleware:.2f}%"
|
|
||||||
)
|
|
||||||
|
|
|
@ -26,9 +26,7 @@ class AppSettings:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def CONTEXT_BEHAVIOR(self):
|
def CONTEXT_BEHAVIOR(self):
|
||||||
raw_value = self.settings.setdefault(
|
raw_value = self.settings.setdefault("context_behavior", ContextBehavior.GLOBAL.value)
|
||||||
"context_behavior", ContextBehavior.GLOBAL.value
|
|
||||||
)
|
|
||||||
return self._validate_context_behavior(raw_value)
|
return self._validate_context_behavior(raw_value)
|
||||||
|
|
||||||
def _validate_context_behavior(self, raw_value):
|
def _validate_context_behavior(self, raw_value):
|
||||||
|
@ -36,9 +34,7 @@ class AppSettings:
|
||||||
return ContextBehavior(raw_value)
|
return ContextBehavior(raw_value)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
valid_values = [behavior.value for behavior in ContextBehavior]
|
valid_values = [behavior.value for behavior in ContextBehavior]
|
||||||
raise ValueError(
|
raise ValueError(f"Invalid context behavior: {raw_value}. Valid options are {valid_values}")
|
||||||
f"Invalid context behavior: {raw_value}. Valid options are {valid_values}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
app_settings = AppSettings()
|
app_settings = AppSettings()
|
||||||
|
|
|
@ -80,9 +80,7 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
|
||||||
self,
|
self,
|
||||||
registered_name: Optional[str] = None,
|
registered_name: Optional[str] = None,
|
||||||
outer_context: Optional[Context] = None,
|
outer_context: Optional[Context] = None,
|
||||||
fill_content: Union[
|
fill_content: Union[DefaultFillContent, Iterable[NamedFillContent]] = (),
|
||||||
DefaultFillContent, Iterable[NamedFillContent]
|
|
||||||
] = (),
|
|
||||||
):
|
):
|
||||||
self.registered_name: Optional[str] = registered_name
|
self.registered_name: Optional[str] = registered_name
|
||||||
self.outer_context: Context = outer_context or Context()
|
self.outer_context: Context = outer_context or Context()
|
||||||
|
@ -152,14 +150,10 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
|
||||||
if slots_data:
|
if slots_data:
|
||||||
self._fill_slots(slots_data, escape_slots_content)
|
self._fill_slots(slots_data, escape_slots_content)
|
||||||
|
|
||||||
updated_filled_slots_context: FilledSlotsContext = (
|
updated_filled_slots_context: FilledSlotsContext = self._process_template_and_update_filled_slot_context(
|
||||||
self._process_template_and_update_filled_slot_context(
|
context, template
|
||||||
context, template
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
with context.update(
|
with context.update({FILLED_SLOTS_CONTENT_CONTEXT_KEY: updated_filled_slots_context}):
|
||||||
{FILLED_SLOTS_CONTENT_CONTEXT_KEY: updated_filled_slots_context}
|
|
||||||
):
|
|
||||||
return template.render(context)
|
return template.render(context)
|
||||||
|
|
||||||
def render_to_response(
|
def render_to_response(
|
||||||
|
@ -201,19 +195,14 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
|
||||||
named_fills_content = {}
|
named_fills_content = {}
|
||||||
else:
|
else:
|
||||||
default_fill_content = None
|
default_fill_content = None
|
||||||
named_fills_content = {
|
named_fills_content = {name: (nodelist, alias) for name, nodelist, alias in self.fill_content}
|
||||||
name: (nodelist, alias)
|
|
||||||
for name, nodelist, alias in self.fill_content
|
|
||||||
}
|
|
||||||
|
|
||||||
# If value is `None`, then slot is unfilled.
|
# If value is `None`, then slot is unfilled.
|
||||||
slot_name2fill_content: Dict[SlotName, Optional[FillContent]] = {}
|
slot_name2fill_content: Dict[SlotName, Optional[FillContent]] = {}
|
||||||
default_slot_encountered: bool = False
|
default_slot_encountered: bool = False
|
||||||
required_slot_names: Set[str] = set()
|
required_slot_names: Set[str] = set()
|
||||||
|
|
||||||
for node in template.nodelist.get_nodes_by_type(
|
for node in template.nodelist.get_nodes_by_type((SlotNode, IfSlotFilledConditionBranchNode)): # type: ignore
|
||||||
(SlotNode, IfSlotFilledConditionBranchNode) # type: ignore
|
|
||||||
):
|
|
||||||
if isinstance(node, SlotNode):
|
if isinstance(node, SlotNode):
|
||||||
# Give slot node knowledge of its parent template.
|
# Give slot node knowledge of its parent template.
|
||||||
node.template = template
|
node.template = template
|
||||||
|
@ -225,9 +214,7 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
|
||||||
f"To fix, check template '{template.name}' "
|
f"To fix, check template '{template.name}' "
|
||||||
f"of component '{self.registered_name}'."
|
f"of component '{self.registered_name}'."
|
||||||
)
|
)
|
||||||
content_data: Optional[FillContent] = (
|
content_data: Optional[FillContent] = None # `None` -> unfilled
|
||||||
None # `None` -> unfilled
|
|
||||||
)
|
|
||||||
if node.is_required:
|
if node.is_required:
|
||||||
required_slot_names.add(node.name)
|
required_slot_names.add(node.name)
|
||||||
if node.is_default:
|
if node.is_default:
|
||||||
|
@ -245,9 +232,7 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
|
||||||
elif isinstance(node, IfSlotFilledConditionBranchNode):
|
elif isinstance(node, IfSlotFilledConditionBranchNode):
|
||||||
node.template = template
|
node.template = template
|
||||||
else:
|
else:
|
||||||
raise RuntimeError(
|
raise RuntimeError(f"Node of {type(node).__name__} does not require linking.")
|
||||||
f"Node of {type(node).__name__} does not require linking."
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check: Only component templates that include a 'default' slot
|
# Check: Only component templates that include a 'default' slot
|
||||||
# can be invoked with implicit filling.
|
# can be invoked with implicit filling.
|
||||||
|
@ -258,12 +243,8 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
|
||||||
f"even though none of its slots is marked as 'default'."
|
f"even though none of its slots is marked as 'default'."
|
||||||
)
|
)
|
||||||
|
|
||||||
unfilled_slots: Set[str] = set(
|
unfilled_slots: Set[str] = set(k for k, v in slot_name2fill_content.items() if v is None)
|
||||||
k for k, v in slot_name2fill_content.items() if v is None
|
unmatched_fills: Set[str] = named_fills_content.keys() - slot_name2fill_content.keys()
|
||||||
)
|
|
||||||
unmatched_fills: Set[str] = (
|
|
||||||
named_fills_content.keys() - slot_name2fill_content.keys()
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check that 'required' slots are filled.
|
# Check that 'required' slots are filled.
|
||||||
for slot_name in unfilled_slots:
|
for slot_name in unfilled_slots:
|
||||||
|
@ -286,9 +267,7 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
|
||||||
# Higher values make matching stricter. This is probably preferable, as it
|
# Higher values make matching stricter. This is probably preferable, as it
|
||||||
# reduces false positives.
|
# reduces false positives.
|
||||||
for fill_name in unmatched_fills:
|
for fill_name in unmatched_fills:
|
||||||
fuzzy_slot_name_matches = difflib.get_close_matches(
|
fuzzy_slot_name_matches = difflib.get_close_matches(fill_name, unfilled_slots, n=1, cutoff=0.7)
|
||||||
fill_name, unfilled_slots, n=1, cutoff=0.7
|
|
||||||
)
|
|
||||||
msg = (
|
msg = (
|
||||||
f"Component '{self.registered_name}' passed fill "
|
f"Component '{self.registered_name}' passed fill "
|
||||||
f"that refers to undefined slot: '{fill_name}'."
|
f"that refers to undefined slot: '{fill_name}'."
|
||||||
|
@ -305,9 +284,7 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
|
||||||
if content_data # Slots whose content is None (i.e. unfilled) are dropped.
|
if content_data # Slots whose content is None (i.e. unfilled) are dropped.
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
prev_context: FilledSlotsContext = context[
|
prev_context: FilledSlotsContext = context[FILLED_SLOTS_CONTENT_CONTEXT_KEY]
|
||||||
FILLED_SLOTS_CONTENT_CONTEXT_KEY
|
|
||||||
]
|
|
||||||
return prev_context.new_child(filled_slots_map)
|
return prev_context.new_child(filled_slots_map)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return ChainMap(filled_slots_map)
|
return ChainMap(filled_slots_map)
|
||||||
|
|
|
@ -12,13 +12,8 @@ class ComponentRegistry(object):
|
||||||
|
|
||||||
def register(self, name=None, component=None):
|
def register(self, name=None, component=None):
|
||||||
existing_component = self._registry.get(name)
|
existing_component = self._registry.get(name)
|
||||||
if (
|
if existing_component and existing_component.class_hash != component.class_hash:
|
||||||
existing_component
|
raise AlreadyRegistered('The component "%s" has already been registered' % name)
|
||||||
and existing_component.class_hash != component.class_hash
|
|
||||||
):
|
|
||||||
raise AlreadyRegistered(
|
|
||||||
'The component "%s" has already been registered' % name
|
|
||||||
)
|
|
||||||
self._registry[name] = component
|
self._registry[name] = component
|
||||||
|
|
||||||
def unregister(self, name):
|
def unregister(self, name):
|
||||||
|
|
|
@ -9,9 +9,7 @@ class Command(BaseCommand):
|
||||||
help = "Creates a new component"
|
help = "Creates a new component"
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument(
|
parser.add_argument("name", type=str, help="The name of the component to create")
|
||||||
"name", type=str, help="The name of the component to create"
|
|
||||||
)
|
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--path",
|
"--path",
|
||||||
type=str,
|
type=str,
|
||||||
|
@ -71,9 +69,7 @@ class Command(BaseCommand):
|
||||||
elif base_dir:
|
elif base_dir:
|
||||||
component_path = os.path.join(base_dir, "components", name)
|
component_path = os.path.join(base_dir, "components", name)
|
||||||
else:
|
else:
|
||||||
raise CommandError(
|
raise CommandError("You must specify a path or set BASE_DIR in your django settings")
|
||||||
"You must specify a path or set BASE_DIR in your django settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
if os.path.exists(component_path):
|
if os.path.exists(component_path):
|
||||||
if force:
|
if force:
|
||||||
|
@ -84,11 +80,7 @@ class Command(BaseCommand):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.stdout.write(
|
self.stdout.write(self.style.WARNING(f'The component "{name}" already exists. Overwriting...'))
|
||||||
self.style.WARNING(
|
|
||||||
f'The component "{name}" already exists. Overwriting...'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
raise CommandError(
|
raise CommandError(
|
||||||
f'The component "{name}" already exists at {component_path}. Use --force to overwrite.'
|
f'The component "{name}" already exists at {component_path}. Use --force to overwrite.'
|
||||||
|
@ -107,9 +99,7 @@ class Command(BaseCommand):
|
||||||
)
|
)
|
||||||
f.write(script_content.strip())
|
f.write(script_content.strip())
|
||||||
|
|
||||||
with open(
|
with open(os.path.join(component_path, css_filename), "w") as f:
|
||||||
os.path.join(component_path, css_filename), "w"
|
|
||||||
) as f:
|
|
||||||
style_content = dedent(
|
style_content = dedent(
|
||||||
f"""
|
f"""
|
||||||
.component-{name} {{
|
.component-{name} {{
|
||||||
|
@ -119,9 +109,7 @@ class Command(BaseCommand):
|
||||||
)
|
)
|
||||||
f.write(style_content.strip())
|
f.write(style_content.strip())
|
||||||
|
|
||||||
with open(
|
with open(os.path.join(component_path, template_filename), "w") as f:
|
||||||
os.path.join(component_path, template_filename), "w"
|
|
||||||
) as f:
|
|
||||||
template_content = dedent(
|
template_content = dedent(
|
||||||
f"""
|
f"""
|
||||||
<div class="component-{name}">
|
<div class="component-{name}">
|
||||||
|
@ -133,9 +121,7 @@ class Command(BaseCommand):
|
||||||
)
|
)
|
||||||
f.write(template_content.strip())
|
f.write(template_content.strip())
|
||||||
|
|
||||||
with open(
|
with open(os.path.join(component_path, f"{name}.py"), "w") as f:
|
||||||
os.path.join(component_path, f"{name}.py"), "w"
|
|
||||||
) as f:
|
|
||||||
py_content = dedent(
|
py_content = dedent(
|
||||||
f"""
|
f"""
|
||||||
from django_components import component
|
from django_components import component
|
||||||
|
@ -157,16 +143,8 @@ class Command(BaseCommand):
|
||||||
f.write(py_content.strip())
|
f.write(py_content.strip())
|
||||||
|
|
||||||
if verbose:
|
if verbose:
|
||||||
self.stdout.write(
|
self.stdout.write(self.style.SUCCESS(f"Successfully created {name} component at {component_path}"))
|
||||||
self.style.SUCCESS(
|
|
||||||
f"Successfully created {name} component at {component_path}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
self.stdout.write(
|
self.stdout.write(self.style.SUCCESS(f"Successfully created {name} component"))
|
||||||
self.style.SUCCESS(
|
|
||||||
f"Successfully created {name} component"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
raise CommandError("You must specify a component name")
|
raise CommandError("You must specify a component name")
|
||||||
|
|
|
@ -11,9 +11,7 @@ CSS_DEPENDENCY_PLACEHOLDER = '<link name="CSS_PLACEHOLDER">'
|
||||||
JS_DEPENDENCY_PLACEHOLDER = '<script name="JS_PLACEHOLDER"></script>'
|
JS_DEPENDENCY_PLACEHOLDER = '<script name="JS_PLACEHOLDER"></script>'
|
||||||
|
|
||||||
SCRIPT_TAG_REGEX = re.compile("<script")
|
SCRIPT_TAG_REGEX = re.compile("<script")
|
||||||
COMPONENT_COMMENT_REGEX = re.compile(
|
COMPONENT_COMMENT_REGEX = re.compile(rb"<!-- _RENDERED (?P<name>[\w\-/]+?) -->")
|
||||||
rb"<!-- _RENDERED (?P<name>[\w\-/]+?) -->"
|
|
||||||
)
|
|
||||||
PLACEHOLDER_REGEX = re.compile(
|
PLACEHOLDER_REGEX = re.compile(
|
||||||
rb"<!-- _RENDERED (?P<name>[\w\-/]+?) -->"
|
rb"<!-- _RENDERED (?P<name>[\w\-/]+?) -->"
|
||||||
rb'|<link name="CSS_PLACEHOLDER">'
|
rb'|<link name="CSS_PLACEHOLDER">'
|
||||||
|
@ -32,9 +30,7 @@ class ComponentDependencyMiddleware:
|
||||||
def __call__(self, request):
|
def __call__(self, request):
|
||||||
response = self.get_response(request)
|
response = self.get_response(request)
|
||||||
if (
|
if (
|
||||||
getattr(settings, "COMPONENTS", {}).get(
|
getattr(settings, "COMPONENTS", {}).get("RENDER_DEPENDENCIES", False)
|
||||||
"RENDER_DEPENDENCIES", False
|
|
||||||
)
|
|
||||||
and not isinstance(response, StreamingHttpResponse)
|
and not isinstance(response, StreamingHttpResponse)
|
||||||
and response.get("Content-Type", "").startswith("text/html")
|
and response.get("Content-Type", "").startswith("text/html")
|
||||||
):
|
):
|
||||||
|
@ -43,23 +39,12 @@ class ComponentDependencyMiddleware:
|
||||||
|
|
||||||
|
|
||||||
def process_response_content(content):
|
def process_response_content(content):
|
||||||
component_names_seen = {
|
component_names_seen = {match.group("name") for match in COMPONENT_COMMENT_REGEX.finditer(content)}
|
||||||
match.group("name")
|
all_components = [registry.get(name.decode("utf-8"))("") for name in component_names_seen]
|
||||||
for match in COMPONENT_COMMENT_REGEX.finditer(content)
|
|
||||||
}
|
|
||||||
all_components = [
|
|
||||||
registry.get(name.decode("utf-8"))("") for name in component_names_seen
|
|
||||||
]
|
|
||||||
all_media = join_media(all_components)
|
all_media = join_media(all_components)
|
||||||
js_dependencies = b"".join(
|
js_dependencies = b"".join(media.encode("utf-8") for media in all_media.render_js())
|
||||||
media.encode("utf-8") for media in all_media.render_js()
|
css_dependencies = b"".join(media.encode("utf-8") for media in all_media.render_css())
|
||||||
)
|
return PLACEHOLDER_REGEX.sub(DependencyReplacer(css_dependencies, js_dependencies), content)
|
||||||
css_dependencies = b"".join(
|
|
||||||
media.encode("utf-8") for media in all_media.render_css()
|
|
||||||
)
|
|
||||||
return PLACEHOLDER_REGEX.sub(
|
|
||||||
DependencyReplacer(css_dependencies, js_dependencies), content
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def add_module_attribute_to_scripts(scripts):
|
def add_module_attribute_to_scripts(scripts):
|
||||||
|
|
|
@ -12,9 +12,7 @@ class SaferStaticFilesConfig(StaticFilesConfig):
|
||||||
by the static file server.
|
by the static file server.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
default = (
|
default = True # Ensure that _this_ app is registered, as opposed to parent cls.
|
||||||
True # Ensure that _this_ app is registered, as opposed to parent cls.
|
|
||||||
)
|
|
||||||
ignore_patterns = StaticFilesConfig.ignore_patterns + [
|
ignore_patterns = StaticFilesConfig.ignore_patterns + [
|
||||||
"*.py",
|
"*.py",
|
||||||
"*.html",
|
"*.html",
|
||||||
|
|
|
@ -9,13 +9,7 @@ else:
|
||||||
import django.template
|
import django.template
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.template.base import (
|
from django.template.base import FilterExpression, Node, NodeList, TextNode, TokenType
|
||||||
FilterExpression,
|
|
||||||
Node,
|
|
||||||
NodeList,
|
|
||||||
TextNode,
|
|
||||||
TokenType,
|
|
||||||
)
|
|
||||||
from django.template.defaulttags import CommentNode
|
from django.template.defaulttags import CommentNode
|
||||||
from django.template.exceptions import TemplateSyntaxError
|
from django.template.exceptions import TemplateSyntaxError
|
||||||
from django.template.library import parse_bits
|
from django.template.library import parse_bits
|
||||||
|
@ -24,10 +18,7 @@ from django.utils.safestring import mark_safe
|
||||||
from django_components.app_settings import app_settings
|
from django_components.app_settings import app_settings
|
||||||
from django_components.component_registry import ComponentRegistry
|
from django_components.component_registry import ComponentRegistry
|
||||||
from django_components.component_registry import registry as component_registry
|
from django_components.component_registry import registry as component_registry
|
||||||
from django_components.middleware import (
|
from django_components.middleware import CSS_DEPENDENCY_PLACEHOLDER, JS_DEPENDENCY_PLACEHOLDER
|
||||||
CSS_DEPENDENCY_PLACEHOLDER,
|
|
||||||
JS_DEPENDENCY_PLACEHOLDER,
|
|
||||||
)
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from django_components.component import Component
|
from django_components.component import Component
|
||||||
|
@ -88,16 +79,8 @@ def component_dependencies_tag(preload=""):
|
||||||
if is_dependency_middleware_active():
|
if is_dependency_middleware_active():
|
||||||
preloaded_dependencies = []
|
preloaded_dependencies = []
|
||||||
for component in get_components_from_preload_str(preload):
|
for component in get_components_from_preload_str(preload):
|
||||||
preloaded_dependencies.append(
|
preloaded_dependencies.append(RENDERED_COMMENT_TEMPLATE.format(name=component.registered_name))
|
||||||
RENDERED_COMMENT_TEMPLATE.format(
|
return mark_safe("\n".join(preloaded_dependencies) + CSS_DEPENDENCY_PLACEHOLDER + JS_DEPENDENCY_PLACEHOLDER)
|
||||||
name=component.registered_name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return mark_safe(
|
|
||||||
"\n".join(preloaded_dependencies)
|
|
||||||
+ CSS_DEPENDENCY_PLACEHOLDER
|
|
||||||
+ JS_DEPENDENCY_PLACEHOLDER
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
rendered_dependencies = []
|
rendered_dependencies = []
|
||||||
for component in get_components_from_registry(component_registry):
|
for component in get_components_from_registry(component_registry):
|
||||||
|
@ -113,14 +96,8 @@ def component_css_dependencies_tag(preload=""):
|
||||||
if is_dependency_middleware_active():
|
if is_dependency_middleware_active():
|
||||||
preloaded_dependencies = []
|
preloaded_dependencies = []
|
||||||
for component in get_components_from_preload_str(preload):
|
for component in get_components_from_preload_str(preload):
|
||||||
preloaded_dependencies.append(
|
preloaded_dependencies.append(RENDERED_COMMENT_TEMPLATE.format(name=component.registered_name))
|
||||||
RENDERED_COMMENT_TEMPLATE.format(
|
return mark_safe("\n".join(preloaded_dependencies) + CSS_DEPENDENCY_PLACEHOLDER)
|
||||||
name=component.registered_name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return mark_safe(
|
|
||||||
"\n".join(preloaded_dependencies) + CSS_DEPENDENCY_PLACEHOLDER
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
rendered_dependencies = []
|
rendered_dependencies = []
|
||||||
for component in get_components_from_registry(component_registry):
|
for component in get_components_from_registry(component_registry):
|
||||||
|
@ -136,14 +113,8 @@ def component_js_dependencies_tag(preload=""):
|
||||||
if is_dependency_middleware_active():
|
if is_dependency_middleware_active():
|
||||||
preloaded_dependencies = []
|
preloaded_dependencies = []
|
||||||
for component in get_components_from_preload_str(preload):
|
for component in get_components_from_preload_str(preload):
|
||||||
preloaded_dependencies.append(
|
preloaded_dependencies.append(RENDERED_COMMENT_TEMPLATE.format(name=component.registered_name))
|
||||||
RENDERED_COMMENT_TEMPLATE.format(
|
return mark_safe("\n".join(preloaded_dependencies) + JS_DEPENDENCY_PLACEHOLDER)
|
||||||
name=component.registered_name
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return mark_safe(
|
|
||||||
"\n".join(preloaded_dependencies) + JS_DEPENDENCY_PLACEHOLDER
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
rendered_dependencies = []
|
rendered_dependencies = []
|
||||||
for component in get_components_from_registry(component_registry):
|
for component in get_components_from_registry(component_registry):
|
||||||
|
@ -151,6 +122,7 @@ def component_js_dependencies_tag(preload=""):
|
||||||
|
|
||||||
return mark_safe("\n".join(rendered_dependencies))
|
return mark_safe("\n".join(rendered_dependencies))
|
||||||
|
|
||||||
|
|
||||||
class UserSlotVar:
|
class UserSlotVar:
|
||||||
"""
|
"""
|
||||||
Extensible mechanism for offering 'fill' blocks in template access to properties
|
Extensible mechanism for offering 'fill' blocks in template access to properties
|
||||||
|
@ -216,24 +188,17 @@ class SlotNode(Node, TemplateAwareNodeMixin):
|
||||||
|
|
||||||
def render(self, context):
|
def render(self, context):
|
||||||
try:
|
try:
|
||||||
filled_slots_map: FilledSlotsContext = context[
|
filled_slots_map: FilledSlotsContext = context[FILLED_SLOTS_CONTENT_CONTEXT_KEY]
|
||||||
FILLED_SLOTS_CONTENT_CONTEXT_KEY
|
|
||||||
]
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(f"Attempted to render SlotNode '{self.name}' outside a parent component.")
|
||||||
f"Attempted to render SlotNode '{self.name}' outside a parent component."
|
|
||||||
)
|
|
||||||
|
|
||||||
extra_context = {}
|
extra_context = {}
|
||||||
try:
|
try:
|
||||||
slot_fill_content: Optional[FillContent] = filled_slots_map[
|
slot_fill_content: Optional[FillContent] = filled_slots_map[(self.name, self.template)]
|
||||||
(self.name, self.template)
|
|
||||||
]
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
if self.is_required:
|
if self.is_required:
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(
|
||||||
f"Slot '{self.name}' is marked as 'required' (i.e. non-optional), "
|
f"Slot '{self.name}' is marked as 'required' (i.e. non-optional), " f"yet no fill is provided. "
|
||||||
f"yet no fill is provided. "
|
|
||||||
)
|
)
|
||||||
nodelist = self.nodelist
|
nodelist = self.nodelist
|
||||||
else:
|
else:
|
||||||
|
@ -257,9 +222,7 @@ def do_slot(parser, token):
|
||||||
if 1 <= len(args) <= 3:
|
if 1 <= len(args) <= 3:
|
||||||
slot_name, *options = args
|
slot_name, *options = args
|
||||||
if not is_wrapped_in_quotes(slot_name):
|
if not is_wrapped_in_quotes(slot_name):
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(f"'{bits[0]}' name must be a string 'literal'.")
|
||||||
f"'{bits[0]}' name must be a string 'literal'."
|
|
||||||
)
|
|
||||||
slot_name = strip_quotes(slot_name)
|
slot_name = strip_quotes(slot_name)
|
||||||
modifiers_count = len(options)
|
modifiers_count = len(options)
|
||||||
if SLOT_REQUIRED_OPTION_KEYWORD in options:
|
if SLOT_REQUIRED_OPTION_KEYWORD in options:
|
||||||
|
@ -273,9 +236,7 @@ def do_slot(parser, token):
|
||||||
SLOT_REQUIRED_OPTION_KEYWORD,
|
SLOT_REQUIRED_OPTION_KEYWORD,
|
||||||
SLOT_DEFAULT_OPTION_KEYWORD,
|
SLOT_DEFAULT_OPTION_KEYWORD,
|
||||||
]
|
]
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(f"Invalid options passed to 'slot' tag. Valid choices: {keywords}.")
|
||||||
f"Invalid options passed to 'slot' tag. Valid choices: {keywords}."
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(
|
||||||
"'slot' tag does not match pattern "
|
"'slot' tag does not match pattern "
|
||||||
|
@ -354,14 +315,10 @@ def do_fill(parser, token):
|
||||||
elif len(args) == 3:
|
elif len(args) == 3:
|
||||||
tgt_slot_name, as_keyword, alias = args
|
tgt_slot_name, as_keyword, alias = args
|
||||||
if as_keyword.lower() != "as":
|
if as_keyword.lower() != "as":
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(f"{tag} tag args do not conform to pattern '<target slot> as <alias>'")
|
||||||
f"{tag} tag args do not conform to pattern '<target slot> as <alias>'"
|
|
||||||
)
|
|
||||||
alias_fexp = FilterExpression(alias, parser)
|
alias_fexp = FilterExpression(alias, parser)
|
||||||
else:
|
else:
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(f"'{tag}' tag takes either 1 or 3 arguments: Received {len(args)}.")
|
||||||
f"'{tag}' tag takes either 1 or 3 arguments: Received {len(args)}."
|
|
||||||
)
|
|
||||||
nodelist = parser.parse(parse_until=["endfill"])
|
nodelist = parser.parse(parse_until=["endfill"])
|
||||||
parser.delete_first_token()
|
parser.delete_first_token()
|
||||||
|
|
||||||
|
@ -397,27 +354,18 @@ class ComponentNode(Node):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<ComponentNode: %s. Contents: %r>" % (
|
return "<ComponentNode: %s. Contents: %r>" % (
|
||||||
self.name_fexp,
|
self.name_fexp,
|
||||||
getattr(
|
getattr(self, "nodelist", None), # 'nodelist' attribute only assigned later.
|
||||||
self, "nodelist", None
|
|
||||||
), # 'nodelist' attribute only assigned later.
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def render(self, context: Context):
|
def render(self, context: Context):
|
||||||
resolved_component_name = self.name_fexp.resolve(context)
|
resolved_component_name = self.name_fexp.resolve(context)
|
||||||
component_cls: Type[Component] = component_registry.get(
|
component_cls: Type[Component] = component_registry.get(resolved_component_name)
|
||||||
resolved_component_name
|
|
||||||
)
|
|
||||||
|
|
||||||
# Resolve FilterExpressions and Variables that were passed as args to the
|
# Resolve FilterExpressions and Variables that were passed as args to the
|
||||||
# component, then call component's context method
|
# component, then call component's context method
|
||||||
# to get values to insert into the context
|
# to get values to insert into the context
|
||||||
resolved_context_args = [
|
resolved_context_args = [safe_resolve(arg, context) for arg in self.context_args]
|
||||||
safe_resolve(arg, context) for arg in self.context_args
|
resolved_context_kwargs = {key: safe_resolve(kwarg, context) for key, kwarg in self.context_kwargs.items()}
|
||||||
]
|
|
||||||
resolved_context_kwargs = {
|
|
||||||
key: safe_resolve(kwarg, context)
|
|
||||||
for key, kwarg in self.context_kwargs.items()
|
|
||||||
}
|
|
||||||
|
|
||||||
if isinstance(self.fill_nodes, ImplicitFillNode):
|
if isinstance(self.fill_nodes, ImplicitFillNode):
|
||||||
fill_content = self.fill_nodes.nodelist
|
fill_content = self.fill_nodes.nodelist
|
||||||
|
@ -437,9 +385,7 @@ class ComponentNode(Node):
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
resolved_alias: None = None
|
resolved_alias: None = None
|
||||||
fill_content.append(
|
fill_content.append((resolved_name, fill_node.nodelist, resolved_alias))
|
||||||
(resolved_name, fill_node.nodelist, resolved_alias)
|
|
||||||
)
|
|
||||||
|
|
||||||
component: Component = component_cls(
|
component: Component = component_cls(
|
||||||
registered_name=resolved_component_name,
|
registered_name=resolved_component_name,
|
||||||
|
@ -447,9 +393,7 @@ class ComponentNode(Node):
|
||||||
fill_content=fill_content,
|
fill_content=fill_content,
|
||||||
)
|
)
|
||||||
|
|
||||||
component_context: dict = component.get_context_data(
|
component_context: dict = component.get_context_data(*resolved_context_args, **resolved_context_kwargs)
|
||||||
*resolved_context_args, **resolved_context_kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.isolated_context:
|
if self.isolated_context:
|
||||||
context = context.new()
|
context = context.new()
|
||||||
|
@ -457,10 +401,7 @@ class ComponentNode(Node):
|
||||||
rendered_component = component.render(context)
|
rendered_component = component.render(context)
|
||||||
|
|
||||||
if is_dependency_middleware_active():
|
if is_dependency_middleware_active():
|
||||||
return (
|
return RENDERED_COMMENT_TEMPLATE.format(name=resolved_component_name) + rendered_component
|
||||||
RENDERED_COMMENT_TEMPLATE.format(name=resolved_component_name)
|
|
||||||
+ rendered_component
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
return rendered_component
|
return rendered_component
|
||||||
|
|
||||||
|
@ -482,9 +423,7 @@ def do_component_block(parser, token):
|
||||||
|
|
||||||
bits = token.split_contents()
|
bits = token.split_contents()
|
||||||
bits, isolated_context = check_for_isolated_context_keyword(bits)
|
bits, isolated_context = check_for_isolated_context_keyword(bits)
|
||||||
component_name, context_args, context_kwargs = parse_component_with_args(
|
component_name, context_args, context_kwargs = parse_component_with_args(parser, bits, "component_block")
|
||||||
parser, bits, "component_block"
|
|
||||||
)
|
|
||||||
body: NodeList = parser.parse(parse_until=["endcomponent_block"])
|
body: NodeList = parser.parse(parse_until=["endcomponent_block"])
|
||||||
parser.delete_first_token()
|
parser.delete_first_token()
|
||||||
fill_nodes = ()
|
fill_nodes = ()
|
||||||
|
@ -575,10 +514,7 @@ def is_whitespace_token(token):
|
||||||
|
|
||||||
|
|
||||||
def is_block_tag_token(token, name):
|
def is_block_tag_token(token, name):
|
||||||
return (
|
return token.token_type == TokenType.BLOCK and token.split_contents()[0] == name
|
||||||
token.token_type == TokenType.BLOCK
|
|
||||||
and token.split_contents()[0] == name
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@register.tag(name="if_filled")
|
@register.tag(name="if_filled")
|
||||||
|
@ -610,9 +546,7 @@ def do_if_filled_block(parser, token):
|
||||||
slot_name, is_positive = parse_if_filled_bits(bits)
|
slot_name, is_positive = parse_if_filled_bits(bits)
|
||||||
nodelist = parser.parse(("elif_filled", "else_filled", "endif_filled"))
|
nodelist = parser.parse(("elif_filled", "else_filled", "endif_filled"))
|
||||||
branches: List[_IfSlotFilledBranchNode] = [
|
branches: List[_IfSlotFilledBranchNode] = [
|
||||||
IfSlotFilledConditionBranchNode(
|
IfSlotFilledConditionBranchNode(slot_name=slot_name, nodelist=nodelist, is_positive=is_positive)
|
||||||
slot_name=slot_name, nodelist=nodelist, is_positive=is_positive
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
|
|
||||||
token = parser.next_token()
|
token = parser.next_token()
|
||||||
|
@ -621,13 +555,9 @@ def do_if_filled_block(parser, token):
|
||||||
while token.contents.startswith("elif_filled"):
|
while token.contents.startswith("elif_filled"):
|
||||||
bits = token.split_contents()
|
bits = token.split_contents()
|
||||||
slot_name, is_positive = parse_if_filled_bits(bits)
|
slot_name, is_positive = parse_if_filled_bits(bits)
|
||||||
nodelist: NodeList = parser.parse(
|
nodelist: NodeList = parser.parse(("elif_filled", "else_filled", "endif_filled"))
|
||||||
("elif_filled", "else_filled", "endif_filled")
|
|
||||||
)
|
|
||||||
branches.append(
|
branches.append(
|
||||||
IfSlotFilledConditionBranchNode(
|
IfSlotFilledConditionBranchNode(slot_name=slot_name, nodelist=nodelist, is_positive=is_positive)
|
||||||
slot_name=slot_name, nodelist=nodelist, is_positive=is_positive
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
token = parser.next_token()
|
token = parser.next_token()
|
||||||
|
@ -656,9 +586,7 @@ def parse_if_filled_bits(
|
||||||
tag, args = bits[0], bits[1:]
|
tag, args = bits[0], bits[1:]
|
||||||
if tag in ("else_filled", "endif_filled"):
|
if tag in ("else_filled", "endif_filled"):
|
||||||
if len(args) != 0:
|
if len(args) != 0:
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(f"Tag '{tag}' takes no arguments. Received '{' '.join(args)}'")
|
||||||
f"Tag '{tag}' takes no arguments. Received '{' '.join(args)}'"
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
return None, None
|
return None, None
|
||||||
if len(args) == 1:
|
if len(args) == 1:
|
||||||
|
@ -669,13 +597,10 @@ def parse_if_filled_bits(
|
||||||
is_positive = bool_from_string(args[1])
|
is_positive = bool_from_string(args[1])
|
||||||
else:
|
else:
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(
|
||||||
f"{bits[0]} tag arguments '{' '.join(args)}' do not match pattern "
|
f"{bits[0]} tag arguments '{' '.join(args)}' do not match pattern " f"'<slotname> (<is_positive>)'"
|
||||||
f"'<slotname> (<is_positive>)'"
|
|
||||||
)
|
)
|
||||||
if not is_wrapped_in_quotes(slot_name):
|
if not is_wrapped_in_quotes(slot_name):
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(f"First argument of '{bits[0]}' must be a quoted string 'literal'.")
|
||||||
f"First argument of '{bits[0]}' must be a quoted string 'literal'."
|
|
||||||
)
|
|
||||||
slot_name = strip_quotes(slot_name)
|
slot_name = strip_quotes(slot_name)
|
||||||
return slot_name, is_positive
|
return slot_name, is_positive
|
||||||
|
|
||||||
|
@ -691,9 +616,7 @@ class _IfSlotFilledBranchNode(Node):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class IfSlotFilledConditionBranchNode(
|
class IfSlotFilledConditionBranchNode(_IfSlotFilledBranchNode, TemplateAwareNodeMixin):
|
||||||
_IfSlotFilledBranchNode, TemplateAwareNodeMixin
|
|
||||||
):
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
slot_name: str,
|
slot_name: str,
|
||||||
|
@ -706,9 +629,7 @@ class IfSlotFilledConditionBranchNode(
|
||||||
|
|
||||||
def evaluate(self, context) -> bool:
|
def evaluate(self, context) -> bool:
|
||||||
try:
|
try:
|
||||||
filled_slots: FilledSlotsContext = context[
|
filled_slots: FilledSlotsContext = context[FILLED_SLOTS_CONTENT_CONTEXT_KEY]
|
||||||
FILLED_SLOTS_CONTENT_CONTEXT_KEY
|
|
||||||
]
|
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(
|
||||||
f"Attempted to render {type(self).__name__} outside a Component rendering context."
|
f"Attempted to render {type(self).__name__} outside a Component rendering context."
|
||||||
|
@ -777,9 +698,7 @@ def parse_component_with_args(parser, bits, tag_name):
|
||||||
)
|
)
|
||||||
|
|
||||||
if tag_name != tag_args[0].token:
|
if tag_name != tag_args[0].token:
|
||||||
raise RuntimeError(
|
raise RuntimeError(f"Internal error: Expected tag_name to be {tag_name}, but it was {tag_args[0].token}")
|
||||||
f"Internal error: Expected tag_name to be {tag_name}, but it was {tag_args[0].token}"
|
|
||||||
)
|
|
||||||
if len(tag_args) > 1:
|
if len(tag_args) > 1:
|
||||||
# At least one position arg, so take the first as the component name
|
# At least one position arg, so take the first as the component name
|
||||||
component_name = tag_args[1].token
|
component_name = tag_args[1].token
|
||||||
|
@ -791,9 +710,7 @@ def parse_component_with_args(parser, bits, tag_name):
|
||||||
context_args = []
|
context_args = []
|
||||||
context_kwargs = tag_kwargs
|
context_kwargs = tag_kwargs
|
||||||
except IndexError:
|
except IndexError:
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(f"Call the '{tag_name}' tag with a component name as the first parameter")
|
||||||
f"Call the '{tag_name}' tag with a component name as the first parameter"
|
|
||||||
)
|
|
||||||
|
|
||||||
return component_name, context_args, context_kwargs
|
return component_name, context_args, context_kwargs
|
||||||
|
|
||||||
|
@ -801,11 +718,7 @@ def parse_component_with_args(parser, bits, tag_name):
|
||||||
def safe_resolve(context_item, context):
|
def safe_resolve(context_item, context):
|
||||||
"""Resolve FilterExpressions and Variables in context if possible. Return other items unchanged."""
|
"""Resolve FilterExpressions and Variables in context if possible. Return other items unchanged."""
|
||||||
|
|
||||||
return (
|
return context_item.resolve(context) if hasattr(context_item, "resolve") else context_item
|
||||||
context_item.resolve(context)
|
|
||||||
if hasattr(context_item, "resolve")
|
|
||||||
else context_item
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def is_wrapped_in_quotes(s):
|
def is_wrapped_in_quotes(s):
|
||||||
|
@ -813,9 +726,7 @@ def is_wrapped_in_quotes(s):
|
||||||
|
|
||||||
|
|
||||||
def is_dependency_middleware_active():
|
def is_dependency_middleware_active():
|
||||||
return getattr(settings, "COMPONENTS", {}).get(
|
return getattr(settings, "COMPONENTS", {}).get("RENDER_DEPENDENCIES", False)
|
||||||
"RENDER_DEPENDENCIES", False
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def norm_and_validate_name(name: str, tag: str, context: Optional[str] = None):
|
def norm_and_validate_name(name: str, tag: str, context: Optional[str] = None):
|
||||||
|
@ -826,10 +737,7 @@ def norm_and_validate_name(name: str, tag: str, context: Optional[str] = None):
|
||||||
name = strip_quotes(name)
|
name = strip_quotes(name)
|
||||||
if not name.isidentifier():
|
if not name.isidentifier():
|
||||||
context = f" in '{context}'" if context else ""
|
context = f" in '{context}'" if context else ""
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(f"{tag} name '{name}'{context} " "is not a valid Python identifier.")
|
||||||
f"{tag} name '{name}'{context} "
|
|
||||||
"is not a valid Python identifier."
|
|
||||||
)
|
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[tool.black]
|
[tool.black]
|
||||||
line-length = 79
|
line-length = 119
|
||||||
include = '\.pyi?$'
|
include = '\.pyi?$'
|
||||||
exclude = '''
|
exclude = '''
|
||||||
/(
|
/(
|
||||||
|
@ -18,7 +18,21 @@ exclude = '''
|
||||||
|
|
||||||
[tool.isort]
|
[tool.isort]
|
||||||
profile = "black"
|
profile = "black"
|
||||||
line_length = 79
|
line_length = 119
|
||||||
multi_line_output = 3
|
multi_line_output = 3
|
||||||
include_trailing_comma = "True"
|
include_trailing_comma = "True"
|
||||||
known_first_party = "django_components"
|
known_first_party = "django_components"
|
||||||
|
|
||||||
|
[tool.flake8]
|
||||||
|
ignore = ['E302', 'W503']
|
||||||
|
max-line-length = 119
|
||||||
|
exclude = [
|
||||||
|
'migrations',
|
||||||
|
'__pycache__',
|
||||||
|
'manage.py',
|
||||||
|
'settings.py',
|
||||||
|
'env',
|
||||||
|
'.env',
|
||||||
|
'.venv',
|
||||||
|
'.tox',
|
||||||
|
]
|
|
@ -2,5 +2,7 @@ django
|
||||||
tox
|
tox
|
||||||
pytest
|
pytest
|
||||||
flake8
|
flake8
|
||||||
|
flake8-pyproject
|
||||||
isort
|
isort
|
||||||
pre-commit
|
pre-commit
|
||||||
|
black
|
|
@ -6,12 +6,16 @@
|
||||||
#
|
#
|
||||||
asgiref==3.7.2
|
asgiref==3.7.2
|
||||||
# via django
|
# via django
|
||||||
|
black==24.1.1
|
||||||
|
# via -r requirements-dev.in
|
||||||
cachetools==5.3.2
|
cachetools==5.3.2
|
||||||
# via tox
|
# via tox
|
||||||
cfgv==3.4.0
|
cfgv==3.4.0
|
||||||
# via pre-commit
|
# via pre-commit
|
||||||
chardet==5.2.0
|
chardet==5.2.0
|
||||||
# via tox
|
# via tox
|
||||||
|
click==8.1.7
|
||||||
|
# via black
|
||||||
colorama==0.4.6
|
colorama==0.4.6
|
||||||
# via tox
|
# via tox
|
||||||
distlib==0.3.8
|
distlib==0.3.8
|
||||||
|
@ -23,6 +27,10 @@ filelock==3.13.1
|
||||||
# tox
|
# tox
|
||||||
# virtualenv
|
# virtualenv
|
||||||
flake8==7.0.0
|
flake8==7.0.0
|
||||||
|
# via
|
||||||
|
# -r requirements-dev.in
|
||||||
|
# flake8-pyproject
|
||||||
|
flake8-pyproject==1.2.3
|
||||||
# via -r requirements-dev.in
|
# via -r requirements-dev.in
|
||||||
identify==2.5.33
|
identify==2.5.33
|
||||||
# via pre-commit
|
# via pre-commit
|
||||||
|
@ -32,15 +40,21 @@ isort==5.13.2
|
||||||
# via -r requirements-dev.in
|
# via -r requirements-dev.in
|
||||||
mccabe==0.7.0
|
mccabe==0.7.0
|
||||||
# via flake8
|
# via flake8
|
||||||
|
mypy-extensions==1.0.0
|
||||||
|
# via black
|
||||||
nodeenv==1.8.0
|
nodeenv==1.8.0
|
||||||
# via pre-commit
|
# via pre-commit
|
||||||
packaging==23.2
|
packaging==23.2
|
||||||
# via
|
# via
|
||||||
|
# black
|
||||||
# pyproject-api
|
# pyproject-api
|
||||||
# pytest
|
# pytest
|
||||||
# tox
|
# tox
|
||||||
|
pathspec==0.12.1
|
||||||
|
# via black
|
||||||
platformdirs==4.1.0
|
platformdirs==4.1.0
|
||||||
# via
|
# via
|
||||||
|
# black
|
||||||
# tox
|
# tox
|
||||||
# virtualenv
|
# virtualenv
|
||||||
pluggy==1.3.0
|
pluggy==1.3.0
|
||||||
|
|
|
@ -30,9 +30,7 @@ def get_supported_versions(url):
|
||||||
django_to_python = {
|
django_to_python = {
|
||||||
version_to_tuple(python_version): [
|
version_to_tuple(python_version): [
|
||||||
version_to_tuple(version_string)
|
version_to_tuple(version_string)
|
||||||
for version_string in re.findall(
|
for version_string in re.findall(r"(?<!\.)\d+\.\d+(?!\.)", django_versions)
|
||||||
r"(?<!\.)\d+\.\d+(?!\.)", django_versions
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
for python_version, django_versions in version_dict.items()
|
for python_version, django_versions in version_dict.items()
|
||||||
}
|
}
|
||||||
|
@ -46,9 +44,7 @@ def get_latest_version(url):
|
||||||
response_content = response.read()
|
response_content = response.read()
|
||||||
|
|
||||||
content = response_content.decode("utf-8")
|
content = response_content.decode("utf-8")
|
||||||
version_string = re.findall(
|
version_string = re.findall(r"The latest official version is (\d+\.\d)", content)[0]
|
||||||
r"The latest official version is (\d+\.\d)", content
|
|
||||||
)[0]
|
|
||||||
return version_to_tuple(version_string)
|
return version_to_tuple(version_string)
|
||||||
|
|
||||||
|
|
||||||
|
@ -108,9 +104,7 @@ def build_deps_envlist(python_to_django):
|
||||||
(
|
(
|
||||||
env_format(django_version),
|
env_format(django_version),
|
||||||
env_format(django_version, divider="."),
|
env_format(django_version, divider="."),
|
||||||
env_format(
|
env_format((django_version[0], django_version[1] + 1), divider="."),
|
||||||
(django_version[0], django_version[1] + 1), divider="."
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
for django_version in sorted(all_django_versions)
|
for django_version in sorted(all_django_versions)
|
||||||
]
|
]
|
||||||
|
@ -123,9 +117,7 @@ def build_pypi_classifiers(python_to_django):
|
||||||
|
|
||||||
all_python_versions = python_to_django.keys()
|
all_python_versions = python_to_django.keys()
|
||||||
for python_version in all_python_versions:
|
for python_version in all_python_versions:
|
||||||
classifiers.append(
|
classifiers.append(f'"Programming Language :: Python :: {env_format(python_version, divider=".")}",')
|
||||||
f'"Programming Language :: Python :: {env_format(python_version, divider=".")}",'
|
|
||||||
)
|
|
||||||
|
|
||||||
all_django_versions = set()
|
all_django_versions = set()
|
||||||
for django_versions in python_to_django.values():
|
for django_versions in python_to_django.values():
|
||||||
|
@ -133,13 +125,9 @@ def build_pypi_classifiers(python_to_django):
|
||||||
all_django_versions.add(django_version)
|
all_django_versions.add(django_version)
|
||||||
|
|
||||||
for django_version in sorted(all_django_versions):
|
for django_version in sorted(all_django_versions):
|
||||||
classifiers.append(
|
classifiers.append(f'"Framework :: Django :: {env_format(django_version, divider=".")}",')
|
||||||
f'"Framework :: Django :: {env_format(django_version, divider=".")}",'
|
|
||||||
)
|
|
||||||
|
|
||||||
return textwrap.indent(
|
return textwrap.indent("classifiers=[\n", prefix=" " * 4) + textwrap.indent("\n".join(classifiers), prefix=" " * 8)
|
||||||
"classifiers=[\n", prefix=" " * 4
|
|
||||||
) + textwrap.indent("\n".join(classifiers), prefix=" " * 8)
|
|
||||||
|
|
||||||
|
|
||||||
def build_readme(python_to_django):
|
def build_readme(python_to_django):
|
||||||
|
@ -154,9 +142,7 @@ def build_readme(python_to_django):
|
||||||
lines = [
|
lines = [
|
||||||
(
|
(
|
||||||
env_format(python_version, divider="."),
|
env_format(python_version, divider="."),
|
||||||
", ".join(
|
", ".join(env_format(version, divider=".") for version in django_versions),
|
||||||
env_format(version, divider=".") for version in django_versions
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
for python_version, django_versions in python_to_django.items()
|
for python_version, django_versions in python_to_django.items()
|
||||||
]
|
]
|
||||||
|
@ -169,13 +155,9 @@ def build_pyenv(python_to_django):
|
||||||
lines = []
|
lines = []
|
||||||
all_python_versions = python_to_django.keys()
|
all_python_versions = python_to_django.keys()
|
||||||
for python_version in all_python_versions:
|
for python_version in all_python_versions:
|
||||||
lines.append(
|
lines.append(f'pyenv install -s {env_format(python_version, divider=".")}')
|
||||||
f'pyenv install -s {env_format(python_version, divider=".")}'
|
|
||||||
)
|
|
||||||
|
|
||||||
lines.append(
|
lines.append(f'pyenv local {" ".join(env_format(version, divider=".") for version in all_python_versions)}')
|
||||||
f'pyenv local {" ".join(env_format(version, divider=".") for version in all_python_versions)}'
|
|
||||||
)
|
|
||||||
|
|
||||||
lines.append("tox -p")
|
lines.append("tox -p")
|
||||||
|
|
||||||
|
@ -185,20 +167,15 @@ def build_pyenv(python_to_django):
|
||||||
def build_ci_python_versions(python_to_django):
|
def build_ci_python_versions(python_to_django):
|
||||||
# Outputs python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11']
|
# Outputs python-version: ['3.6', '3.7', '3.8', '3.9', '3.10', '3.11']
|
||||||
lines = [
|
lines = [
|
||||||
f"'{env_format(python_version, divider='.')}'"
|
f"'{env_format(python_version, divider='.')}'" for python_version, django_versions in python_to_django.items()
|
||||||
for python_version, django_versions in python_to_django.items()
|
|
||||||
]
|
]
|
||||||
lines = " " * 8 + f"python-version: [{', '.join(lines)}]"
|
lines = " " * 8 + f"python-version: [{', '.join(lines)}]"
|
||||||
return lines
|
return lines
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
django_to_python = get_supported_versions(
|
django_to_python = get_supported_versions("https://docs.djangoproject.com/en/dev/faq/install/")
|
||||||
"https://docs.djangoproject.com/en/dev/faq/install/"
|
latest_version = get_latest_version("https://www.djangoproject.com/download/")
|
||||||
)
|
|
||||||
latest_version = get_latest_version(
|
|
||||||
"https://www.djangoproject.com/download/"
|
|
||||||
)
|
|
||||||
|
|
||||||
python_to_django = build_python_to_django(django_to_python, latest_version)
|
python_to_django = build_python_to_django(django_to_python, latest_version)
|
||||||
|
|
||||||
|
|
12
setup.cfg
12
setup.cfg
|
@ -1,12 +0,0 @@
|
||||||
[flake8]
|
|
||||||
ignore = E302,W503
|
|
||||||
max-line-length = 119
|
|
||||||
exclude =
|
|
||||||
migrations
|
|
||||||
__pycache__
|
|
||||||
manage.py
|
|
||||||
settings.py
|
|
||||||
env
|
|
||||||
.env
|
|
||||||
.venv
|
|
||||||
.tox
|
|
4
setup.py
4
setup.py
|
@ -10,9 +10,7 @@ setup(
|
||||||
packages=find_packages(exclude=["tests"]),
|
packages=find_packages(exclude=["tests"]),
|
||||||
version=VERSION,
|
version=VERSION,
|
||||||
description="A way to create simple reusable template components in Django.",
|
description="A way to create simple reusable template components in Django.",
|
||||||
long_description=open(
|
long_description=open(os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf8").read(),
|
||||||
os.path.join(os.path.dirname(__file__), "README.md"), encoding="utf8"
|
|
||||||
).read(),
|
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
author="Emil Stenström",
|
author="Emil Stenström",
|
||||||
author_email="emil@emilstenstrom.se",
|
author_email="emil@emilstenstrom.se",
|
||||||
|
|
|
@ -11,9 +11,7 @@ if not settings.configured:
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
COMPONENTS={"template_cache_size": 128},
|
COMPONENTS={"template_cache_size": 128},
|
||||||
MIDDLEWARE=[
|
MIDDLEWARE=["django_components.middleware.ComponentDependencyMiddleware"],
|
||||||
"django_components.middleware.ComponentDependencyMiddleware"
|
|
||||||
],
|
|
||||||
DATABASES={
|
DATABASES={
|
||||||
"default": {
|
"default": {
|
||||||
"ENGINE": "django.db.backends.sqlite3",
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
|
|
|
@ -4,7 +4,7 @@ from django.template.engine import Engine
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
|
||||||
# isort: off
|
# isort: off
|
||||||
from .django_test_setup import * # noqa
|
from .django_test_setup import settings
|
||||||
from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
||||||
|
|
||||||
# isort: on
|
# isort: on
|
||||||
|
@ -28,9 +28,7 @@ class TestAutodiscover(SimpleTestCase):
|
||||||
try:
|
try:
|
||||||
autodiscover()
|
autodiscover()
|
||||||
except component.AlreadyRegistered:
|
except component.AlreadyRegistered:
|
||||||
self.fail(
|
self.fail("Autodiscover should not raise AlreadyRegistered exception")
|
||||||
"Autodiscover should not raise AlreadyRegistered exception"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TestLoaderSettingsModule(SimpleTestCase):
|
class TestLoaderSettingsModule(SimpleTestCase):
|
||||||
|
@ -42,26 +40,17 @@ class TestLoaderSettingsModule(SimpleTestCase):
|
||||||
current_engine = Engine.get_default()
|
current_engine = Engine.get_default()
|
||||||
loader = Loader(current_engine)
|
loader = Loader(current_engine)
|
||||||
dirs = loader.get_dirs()
|
dirs = loader.get_dirs()
|
||||||
self.assertEqual(
|
self.assertEqual(dirs, [Path(__file__).parent.resolve() / "components"])
|
||||||
dirs, [Path(__file__).parent.resolve() / "components"]
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_complex_settings_module(self):
|
def test_complex_settings_module(self):
|
||||||
settings.SETTINGS_MODULE = ( # noqa
|
settings.SETTINGS_MODULE = "tests.test_structures.test_structure_1.config.settings" # noqa
|
||||||
"tests.test_structures.test_structure_1.config.settings"
|
|
||||||
)
|
|
||||||
|
|
||||||
current_engine = Engine.get_default()
|
current_engine = Engine.get_default()
|
||||||
loader = Loader(current_engine)
|
loader = Loader(current_engine)
|
||||||
dirs = loader.get_dirs()
|
dirs = loader.get_dirs()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
dirs,
|
dirs,
|
||||||
[
|
[Path(__file__).parent.resolve() / "test_structures" / "test_structure_1" / "components"],
|
||||||
Path(__file__).parent.resolve()
|
|
||||||
/ "test_structures"
|
|
||||||
/ "test_structure_1"
|
|
||||||
/ "components"
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_complex_settings_module_2(self):
|
def test_complex_settings_module_2(self):
|
||||||
|
@ -72,13 +61,7 @@ class TestLoaderSettingsModule(SimpleTestCase):
|
||||||
dirs = loader.get_dirs()
|
dirs = loader.get_dirs()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
dirs,
|
dirs,
|
||||||
[
|
[Path(__file__).parent.resolve() / "test_structures" / "test_structure_2" / "project" / "components"],
|
||||||
Path(__file__).parent.resolve()
|
|
||||||
/ "test_structures"
|
|
||||||
/ "test_structure_2"
|
|
||||||
/ "project"
|
|
||||||
/ "components"
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_complex_settings_module_3(self):
|
def test_complex_settings_module_3(self):
|
||||||
|
@ -88,19 +71,8 @@ class TestLoaderSettingsModule(SimpleTestCase):
|
||||||
loader = Loader(current_engine)
|
loader = Loader(current_engine)
|
||||||
dirs = loader.get_dirs()
|
dirs = loader.get_dirs()
|
||||||
expected = [
|
expected = [
|
||||||
(
|
(Path(__file__).parent.resolve() / "test_structures" / "test_structure_3" / "components"),
|
||||||
Path(__file__).parent.resolve()
|
(Path(__file__).parent.resolve() / "test_structures" / "test_structure_3" / "project" / "components"),
|
||||||
/ "test_structures"
|
|
||||||
/ "test_structure_3"
|
|
||||||
/ "components"
|
|
||||||
),
|
|
||||||
(
|
|
||||||
Path(__file__).parent.resolve()
|
|
||||||
/ "test_structures"
|
|
||||||
/ "test_structure_3"
|
|
||||||
/ "project"
|
|
||||||
/ "components"
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
sorted(dirs),
|
sorted(dirs),
|
||||||
|
@ -110,11 +82,7 @@ class TestLoaderSettingsModule(SimpleTestCase):
|
||||||
|
|
||||||
class TestBaseDir(SimpleTestCase):
|
class TestBaseDir(SimpleTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
settings.BASE_DIR = ( # noqa
|
settings.BASE_DIR = Path(__file__).parent.resolve() / "test_structures" / "test_structure_1" # noqa
|
||||||
Path(__file__).parent.resolve()
|
|
||||||
/ "test_structures"
|
|
||||||
/ "test_structure_1"
|
|
||||||
)
|
|
||||||
settings.SETTINGS_MODULE = "tests_fake.test_autodiscover_fake" # noqa
|
settings.SETTINGS_MODULE = "tests_fake.test_autodiscover_fake" # noqa
|
||||||
|
|
||||||
def tearDown(self) -> None:
|
def tearDown(self) -> None:
|
||||||
|
@ -125,10 +93,5 @@ class TestBaseDir(SimpleTestCase):
|
||||||
current_engine = Engine.get_default()
|
current_engine = Engine.get_default()
|
||||||
loader = Loader(current_engine)
|
loader = Loader(current_engine)
|
||||||
dirs = loader.get_dirs()
|
dirs = loader.get_dirs()
|
||||||
expected = [
|
expected = [Path(__file__).parent.resolve() / "test_structures" / "test_structure_1" / "components"]
|
||||||
Path(__file__).parent.resolve()
|
|
||||||
/ "test_structures"
|
|
||||||
/ "test_structure_1"
|
|
||||||
/ "components"
|
|
||||||
]
|
|
||||||
self.assertEqual(dirs, expected)
|
self.assertEqual(dirs, expected)
|
||||||
|
|
|
@ -234,9 +234,7 @@ class InlineComponentTest(SimpleTestCase):
|
||||||
css = "path/to/style.css"
|
css = "path/to/style.css"
|
||||||
js = "path/to/script.js"
|
js = "path/to/script.js"
|
||||||
|
|
||||||
comp = HTMLStringFileCSSJSComponent(
|
comp = HTMLStringFileCSSJSComponent("html_string_file_css_js_component")
|
||||||
"html_string_file_css_js_component"
|
|
||||||
)
|
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(
|
||||||
comp.render(Context({})),
|
comp.render(Context({})),
|
||||||
"<div class='html-string-file'>Content</div>",
|
"<div class='html-string-file'>Content</div>",
|
||||||
|
@ -259,9 +257,7 @@ class InlineComponentTest(SimpleTestCase):
|
||||||
class Media:
|
class Media:
|
||||||
css = "path/to/style.css"
|
css = "path/to/style.css"
|
||||||
|
|
||||||
comp = HTMLStringFileCSSJSComponent(
|
comp = HTMLStringFileCSSJSComponent("html_string_file_css_js_component")
|
||||||
"html_string_file_css_js_component"
|
|
||||||
)
|
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(
|
||||||
comp.render(Context({})),
|
comp.render(Context({})),
|
||||||
"<div class='html-string-file'>Content</div>",
|
"<div class='html-string-file'>Content</div>",
|
||||||
|
@ -284,9 +280,7 @@ class InlineComponentTest(SimpleTestCase):
|
||||||
class Media:
|
class Media:
|
||||||
js = "path/to/script.js"
|
js = "path/to/script.js"
|
||||||
|
|
||||||
comp = HTMLStringFileCSSJSComponent(
|
comp = HTMLStringFileCSSJSComponent("html_string_file_css_js_component")
|
||||||
"html_string_file_css_js_component"
|
|
||||||
)
|
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(
|
||||||
comp.render(Context({})),
|
comp.render(Context({})),
|
||||||
"<div class='html-string-file'>Content</div>",
|
"<div class='html-string-file'>Content</div>",
|
||||||
|
@ -303,9 +297,7 @@ class InlineComponentTest(SimpleTestCase):
|
||||||
def test_component_with_variable_in_html(self):
|
def test_component_with_variable_in_html(self):
|
||||||
class VariableHTMLComponent(component.Component):
|
class VariableHTMLComponent(component.Component):
|
||||||
def get_template(self, context):
|
def get_template(self, context):
|
||||||
return Template(
|
return Template("<div class='variable-html'>{{ variable }}</div>")
|
||||||
"<div class='variable-html'>{{ variable }}</div>"
|
|
||||||
)
|
|
||||||
|
|
||||||
comp = VariableHTMLComponent("variable_html_component")
|
comp = VariableHTMLComponent("variable_html_component")
|
||||||
context = Context({"variable": "Dynamic Content"})
|
context = Context({"variable": "Dynamic Content"})
|
||||||
|
|
|
@ -49,9 +49,7 @@ class MockComponentSlot(component.Component):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs) -> HttpResponse:
|
def get(self, request, *args, **kwargs) -> HttpResponse:
|
||||||
return self.render_to_response(
|
return self.render_to_response({"name": "Bob"}, {"second_slot": "Nice to meet you, Bob"})
|
||||||
{"name": "Bob"}, {"second_slot": "Nice to meet you, Bob"}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@component.register("testcomponent_context_insecure")
|
@component.register("testcomponent_context_insecure")
|
||||||
|
@ -64,9 +62,7 @@ class MockInsecureComponentContext(component.Component):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs) -> HttpResponse:
|
def get(self, request, *args, **kwargs) -> HttpResponse:
|
||||||
return self.render_to_response(
|
return self.render_to_response({"variable": "<script>alert(1);</script>"})
|
||||||
{"variable": "<script>alert(1);</script>"}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@component.register("testcomponent_slot_insecure")
|
@component.register("testcomponent_slot_insecure")
|
||||||
|
@ -80,9 +76,7 @@ class MockInsecureComponentSlot(component.Component):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs) -> HttpResponse:
|
def get(self, request, *args, **kwargs) -> HttpResponse:
|
||||||
return self.render_to_response(
|
return self.render_to_response({}, {"test_slot": "<script>alert(1);</script>"})
|
||||||
{}, {"test_slot": "<script>alert(1);</script>"}
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def render_template_view(request):
|
def render_template_view(request):
|
||||||
|
|
|
@ -65,15 +65,11 @@ class OuterContextComponent(component.Component):
|
||||||
|
|
||||||
|
|
||||||
component.registry.register(name="parent_component", component=ParentComponent)
|
component.registry.register(name="parent_component", component=ParentComponent)
|
||||||
component.registry.register(
|
component.registry.register(name="parent_with_args", component=ParentComponentWithArgs)
|
||||||
name="parent_with_args", component=ParentComponentWithArgs
|
|
||||||
)
|
|
||||||
component.registry.register(name="variable_display", component=VariableDisplay)
|
component.registry.register(name="variable_display", component=VariableDisplay)
|
||||||
component.registry.register(name="incrementer", component=IncrementerComponent)
|
component.registry.register(name="incrementer", component=IncrementerComponent)
|
||||||
component.registry.register(name="simple_component", component=SimpleComponent)
|
component.registry.register(name="simple_component", component=SimpleComponent)
|
||||||
component.registry.register(
|
component.registry.register(name="outer_context_component", component=OuterContextComponent)
|
||||||
name="outer_context_component", component=OuterContextComponent
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ContextTests(SimpleTestCase):
|
class ContextTests(SimpleTestCase):
|
||||||
|
@ -86,17 +82,13 @@ class ContextTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
rendered = template.render(Context())
|
rendered = template.render(Context())
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn("<h1>Shadowing variable = override</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = override</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
"<h1>Shadowing variable = slot_default_override</h1>",
|
"<h1>Shadowing variable = slot_default_override</h1>",
|
||||||
rendered,
|
rendered,
|
||||||
rendered,
|
rendered,
|
||||||
)
|
)
|
||||||
self.assertNotIn(
|
self.assertNotIn("<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_tag(
|
def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_tag(
|
||||||
self,
|
self,
|
||||||
|
@ -107,9 +99,7 @@ class ContextTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
rendered = template.render(Context())
|
rendered = template.render(Context())
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn("<h1>Uniquely named variable = unique_val</h1>", rendered, rendered)
|
||||||
"<h1>Uniquely named variable = unique_val</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
"<h1>Uniquely named variable = slot_default_unique</h1>",
|
"<h1>Uniquely named variable = slot_default_unique</h1>",
|
||||||
rendered,
|
rendered,
|
||||||
|
@ -125,17 +115,13 @@ class ContextTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
rendered = template.render(Context())
|
rendered = template.render(Context())
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn("<h1>Shadowing variable = override</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = override</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
"<h1>Shadowing variable = slot_default_override</h1>",
|
"<h1>Shadowing variable = slot_default_override</h1>",
|
||||||
rendered,
|
rendered,
|
||||||
rendered,
|
rendered,
|
||||||
)
|
)
|
||||||
self.assertNotIn(
|
self.assertNotIn("<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_block_tag(
|
def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_block_tag(
|
||||||
self,
|
self,
|
||||||
|
@ -146,9 +132,7 @@ class ContextTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
rendered = template.render(Context())
|
rendered = template.render(Context())
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn("<h1>Uniquely named variable = unique_val</h1>", rendered, rendered)
|
||||||
"<h1>Uniquely named variable = unique_val</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
"<h1>Uniquely named variable = slot_default_unique</h1>",
|
"<h1>Uniquely named variable = slot_default_unique</h1>",
|
||||||
rendered,
|
rendered,
|
||||||
|
@ -156,7 +140,8 @@ class ContextTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_nested_component_context_shadows_parent_with_filled_slots(self):
|
def test_nested_component_context_shadows_parent_with_filled_slots(self):
|
||||||
template = Template("""
|
template = Template(
|
||||||
|
"""
|
||||||
{% load component_tags %}{% component_dependencies %}
|
{% load component_tags %}{% component_dependencies %}
|
||||||
{% component_block 'parent_component' %}
|
{% component_block 'parent_component' %}
|
||||||
{% fill 'content' %}
|
{% fill 'content' %}
|
||||||
|
@ -164,25 +149,23 @@ class ContextTests(SimpleTestCase):
|
||||||
{% endcomponent_block %}
|
{% endcomponent_block %}
|
||||||
{% endfill %}
|
{% endfill %}
|
||||||
{% endcomponent_block %}
|
{% endcomponent_block %}
|
||||||
""") # NOQA
|
""" # NOQA
|
||||||
|
)
|
||||||
rendered = template.render(Context())
|
rendered = template.render(Context())
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn("<h1>Shadowing variable = override</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = override</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
"<h1>Shadowing variable = shadow_from_slot</h1>",
|
"<h1>Shadowing variable = shadow_from_slot</h1>",
|
||||||
rendered,
|
rendered,
|
||||||
rendered,
|
rendered,
|
||||||
)
|
)
|
||||||
self.assertNotIn(
|
self.assertNotIn("<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_nested_component_instances_have_unique_context_with_filled_slots(
|
def test_nested_component_instances_have_unique_context_with_filled_slots(
|
||||||
self,
|
self,
|
||||||
):
|
):
|
||||||
template = Template("""
|
template = Template(
|
||||||
|
"""
|
||||||
{% load component_tags %}{% component_dependencies %}
|
{% load component_tags %}{% component_dependencies %}
|
||||||
{% component_block 'parent_component' %}
|
{% component_block 'parent_component' %}
|
||||||
{% fill 'content' %}
|
{% fill 'content' %}
|
||||||
|
@ -190,12 +173,11 @@ class ContextTests(SimpleTestCase):
|
||||||
{% endcomponent_block %}
|
{% endcomponent_block %}
|
||||||
{% endfill %}
|
{% endfill %}
|
||||||
{% endcomponent_block %}
|
{% endcomponent_block %}
|
||||||
""") # NOQA
|
""" # NOQA
|
||||||
|
)
|
||||||
rendered = template.render(Context())
|
rendered = template.render(Context())
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn("<h1>Uniquely named variable = unique_val</h1>", rendered, rendered)
|
||||||
"<h1>Uniquely named variable = unique_val</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
"<h1>Uniquely named variable = unique_from_slot</h1>",
|
"<h1>Uniquely named variable = unique_from_slot</h1>",
|
||||||
rendered,
|
rendered,
|
||||||
|
@ -209,21 +191,15 @@ class ContextTests(SimpleTestCase):
|
||||||
"{% load component_tags %}{% component_dependencies %}"
|
"{% load component_tags %}{% component_dependencies %}"
|
||||||
"{% component_block name='parent_component' %}{% endcomponent_block %}"
|
"{% component_block name='parent_component' %}{% endcomponent_block %}"
|
||||||
)
|
)
|
||||||
rendered = template.render(
|
rendered = template.render(Context({"shadowing_variable": "NOT SHADOWED"}))
|
||||||
Context({"shadowing_variable": "NOT SHADOWED"})
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn("<h1>Shadowing variable = override</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = override</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
"<h1>Shadowing variable = slot_default_override</h1>",
|
"<h1>Shadowing variable = slot_default_override</h1>",
|
||||||
rendered,
|
rendered,
|
||||||
rendered,
|
rendered,
|
||||||
)
|
)
|
||||||
self.assertNotIn(
|
self.assertNotIn("<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_nested_component_context_shadows_outer_context_with_unfilled_slots_and_component_block_tag(
|
def test_nested_component_context_shadows_outer_context_with_unfilled_slots_and_component_block_tag(
|
||||||
self,
|
self,
|
||||||
|
@ -232,26 +208,21 @@ class ContextTests(SimpleTestCase):
|
||||||
"{% load component_tags %}{% component_dependencies %}"
|
"{% load component_tags %}{% component_dependencies %}"
|
||||||
"{% component_block 'parent_component' %}{% endcomponent_block %}"
|
"{% component_block 'parent_component' %}{% endcomponent_block %}"
|
||||||
)
|
)
|
||||||
rendered = template.render(
|
rendered = template.render(Context({"shadowing_variable": "NOT SHADOWED"}))
|
||||||
Context({"shadowing_variable": "NOT SHADOWED"})
|
|
||||||
)
|
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn("<h1>Shadowing variable = override</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = override</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
"<h1>Shadowing variable = slot_default_override</h1>",
|
"<h1>Shadowing variable = slot_default_override</h1>",
|
||||||
rendered,
|
rendered,
|
||||||
rendered,
|
rendered,
|
||||||
)
|
)
|
||||||
self.assertNotIn(
|
self.assertNotIn("<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_nested_component_context_shadows_outer_context_with_filled_slots(
|
def test_nested_component_context_shadows_outer_context_with_filled_slots(
|
||||||
self,
|
self,
|
||||||
):
|
):
|
||||||
template = Template("""
|
template = Template(
|
||||||
|
"""
|
||||||
{% load component_tags %}{% component_dependencies %}
|
{% load component_tags %}{% component_dependencies %}
|
||||||
{% component_block 'parent_component' %}
|
{% component_block 'parent_component' %}
|
||||||
{% fill 'content' %}
|
{% fill 'content' %}
|
||||||
|
@ -259,22 +230,17 @@ class ContextTests(SimpleTestCase):
|
||||||
{% endcomponent_block %}
|
{% endcomponent_block %}
|
||||||
{% endfill %}
|
{% endfill %}
|
||||||
{% endcomponent_block %}
|
{% endcomponent_block %}
|
||||||
""") # NOQA
|
""" # NOQA
|
||||||
rendered = template.render(
|
|
||||||
Context({"shadowing_variable": "NOT SHADOWED"})
|
|
||||||
)
|
)
|
||||||
|
rendered = template.render(Context({"shadowing_variable": "NOT SHADOWED"}))
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn("<h1>Shadowing variable = override</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = override</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
self.assertIn(
|
self.assertIn(
|
||||||
"<h1>Shadowing variable = shadow_from_slot</h1>",
|
"<h1>Shadowing variable = shadow_from_slot</h1>",
|
||||||
rendered,
|
rendered,
|
||||||
rendered,
|
rendered,
|
||||||
)
|
)
|
||||||
self.assertNotIn(
|
self.assertNotIn("<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ParentArgsTests(SimpleTestCase):
|
class ParentArgsTests(SimpleTestCase):
|
||||||
|
@ -286,15 +252,9 @@ class ParentArgsTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
rendered = template.render(Context({"parent_value": "passed_in"}))
|
rendered = template.render(Context({"parent_value": "passed_in"}))
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn("<h1>Shadowing variable = passed_in</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = passed_in</h1>", rendered, rendered
|
self.assertIn("<h1>Uniquely named variable = passed_in</h1>", rendered, rendered)
|
||||||
)
|
self.assertNotIn("<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered)
|
||||||
self.assertIn(
|
|
||||||
"<h1>Uniquely named variable = passed_in</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
self.assertNotIn(
|
|
||||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_parent_args_available_outside_slots(self):
|
def test_parent_args_available_outside_slots(self):
|
||||||
template = Template(
|
template = Template(
|
||||||
|
@ -303,18 +263,13 @@ class ParentArgsTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
rendered = template.render(Context())
|
rendered = template.render(Context())
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn("<h1>Shadowing variable = passed_in</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = passed_in</h1>", rendered, rendered
|
self.assertIn("<h1>Uniquely named variable = passed_in</h1>", rendered, rendered)
|
||||||
)
|
self.assertNotIn("<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered)
|
||||||
self.assertIn(
|
|
||||||
"<h1>Uniquely named variable = passed_in</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
self.assertNotIn(
|
|
||||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_parent_args_available_in_slots(self):
|
def test_parent_args_available_in_slots(self):
|
||||||
template = Template("""
|
template = Template(
|
||||||
|
"""
|
||||||
{% load component_tags %}{% component_dependencies %}
|
{% load component_tags %}{% component_dependencies %}
|
||||||
{% component_block 'parent_with_args' parent_value='passed_in' %}
|
{% component_block 'parent_with_args' parent_value='passed_in' %}
|
||||||
{% fill 'content' %}
|
{% fill 'content' %}
|
||||||
|
@ -322,18 +277,13 @@ class ParentArgsTests(SimpleTestCase):
|
||||||
{% endcomponent_block %}
|
{% endcomponent_block %}
|
||||||
{% endfill %}
|
{% endfill %}
|
||||||
{% endcomponent_block %}
|
{% endcomponent_block %}
|
||||||
""") # NOQA
|
""" # NOQA
|
||||||
|
)
|
||||||
rendered = template.render(Context())
|
rendered = template.render(Context())
|
||||||
|
|
||||||
self.assertIn(
|
self.assertIn("<h1>Shadowing variable = value_from_slot</h1>", rendered, rendered)
|
||||||
"<h1>Shadowing variable = value_from_slot</h1>", rendered, rendered
|
self.assertIn("<h1>Uniquely named variable = passed_in</h1>", rendered, rendered)
|
||||||
)
|
self.assertNotIn("<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered)
|
||||||
self.assertIn(
|
|
||||||
"<h1>Uniquely named variable = passed_in</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
self.assertNotIn(
|
|
||||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ContextCalledOnceTests(SimpleTestCase):
|
class ContextCalledOnceTests(SimpleTestCase):
|
||||||
|
@ -344,9 +294,7 @@ class ContextCalledOnceTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
rendered = template.render(Context()).strip()
|
rendered = template.render(Context()).strip()
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(rendered, '<p class="incrementer">value=1;calls=1</p>', rendered)
|
||||||
rendered, '<p class="incrementer">value=1;calls=1</p>', rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_one_context_call_with_simple_component_and_arg(self):
|
def test_one_context_call_with_simple_component_and_arg(self):
|
||||||
template = Template(
|
template = Template(
|
||||||
|
@ -354,31 +302,21 @@ class ContextCalledOnceTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
rendered = template.render(Context()).strip()
|
rendered = template.render(Context()).strip()
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(rendered, '<p class="incrementer">value=3;calls=1</p>', rendered)
|
||||||
rendered, '<p class="incrementer">value=3;calls=1</p>', rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_one_context_call_with_component_block(self):
|
def test_one_context_call_with_component_block(self):
|
||||||
template = Template(
|
template = Template("{% load component_tags %}" "{% component_block 'incrementer' %}{% endcomponent_block %}")
|
||||||
"{% load component_tags %}"
|
|
||||||
"{% component_block 'incrementer' %}{% endcomponent_block %}"
|
|
||||||
)
|
|
||||||
rendered = template.render(Context()).strip()
|
rendered = template.render(Context()).strip()
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(rendered, '<p class="incrementer">value=1;calls=1</p>', rendered)
|
||||||
rendered, '<p class="incrementer">value=1;calls=1</p>', rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_one_context_call_with_component_block_and_arg(self):
|
def test_one_context_call_with_component_block_and_arg(self):
|
||||||
template = Template(
|
template = Template(
|
||||||
"{% load component_tags %}"
|
"{% load component_tags %}" "{% component_block 'incrementer' value='3' %}{% endcomponent_block %}"
|
||||||
"{% component_block 'incrementer' value='3' %}{% endcomponent_block %}"
|
|
||||||
)
|
)
|
||||||
rendered = template.render(Context()).strip()
|
rendered = template.render(Context()).strip()
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(rendered, '<p class="incrementer">value=4;calls=1</p>', rendered)
|
||||||
rendered, '<p class="incrementer">value=4;calls=1</p>', rendered
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_one_context_call_with_slot(self):
|
def test_one_context_call_with_slot(self):
|
||||||
template = Template(
|
template = Template(
|
||||||
|
@ -415,9 +353,7 @@ class ComponentsCanAccessOuterContext(SimpleTestCase):
|
||||||
"{% load component_tags %}{% component_dependencies %}"
|
"{% load component_tags %}{% component_dependencies %}"
|
||||||
"{% component_block 'simple_component' %}{% endcomponent_block %}"
|
"{% component_block 'simple_component' %}{% endcomponent_block %}"
|
||||||
)
|
)
|
||||||
rendered = template.render(
|
rendered = template.render(Context({"variable": "outer_value"})).strip()
|
||||||
Context({"variable": "outer_value"})
|
|
||||||
).strip()
|
|
||||||
self.assertIn("outer_value", rendered, rendered)
|
self.assertIn("outer_value", rendered, rendered)
|
||||||
|
|
||||||
|
|
||||||
|
@ -427,9 +363,7 @@ class IsolatedContextTests(SimpleTestCase):
|
||||||
"{% load component_tags %}{% component_dependencies %}"
|
"{% load component_tags %}{% component_dependencies %}"
|
||||||
"{% component_block 'simple_component' variable only %}{% endcomponent_block %}"
|
"{% component_block 'simple_component' variable only %}{% endcomponent_block %}"
|
||||||
)
|
)
|
||||||
rendered = template.render(
|
rendered = template.render(Context({"variable": "outer_value"})).strip()
|
||||||
Context({"variable": "outer_value"})
|
|
||||||
).strip()
|
|
||||||
self.assertIn("outer_value", rendered, rendered)
|
self.assertIn("outer_value", rendered, rendered)
|
||||||
|
|
||||||
def test_simple_component_cannot_use_outer_context(self):
|
def test_simple_component_cannot_use_outer_context(self):
|
||||||
|
@ -437,9 +371,7 @@ class IsolatedContextTests(SimpleTestCase):
|
||||||
"{% load component_tags %}{% component_dependencies %}"
|
"{% load component_tags %}{% component_dependencies %}"
|
||||||
"{% component_block 'simple_component' only %}{% endcomponent_block %}"
|
"{% component_block 'simple_component' only %}{% endcomponent_block %}"
|
||||||
)
|
)
|
||||||
rendered = template.render(
|
rendered = template.render(Context({"variable": "outer_value"})).strip()
|
||||||
Context({"variable": "outer_value"})
|
|
||||||
).strip()
|
|
||||||
self.assertNotIn("outer_value", rendered, rendered)
|
self.assertNotIn("outer_value", rendered, rendered)
|
||||||
|
|
||||||
|
|
||||||
|
@ -504,9 +436,7 @@ class OuterContextPropertyTests(SimpleTestCase):
|
||||||
"{% load component_tags %}{% component_dependencies %}"
|
"{% load component_tags %}{% component_dependencies %}"
|
||||||
"{% component_block 'outer_context_component' only %}{% endcomponent_block %}"
|
"{% component_block 'outer_context_component' only %}{% endcomponent_block %}"
|
||||||
)
|
)
|
||||||
rendered = template.render(
|
rendered = template.render(Context({"variable": "outer_value"})).strip()
|
||||||
Context({"variable": "outer_value"})
|
|
||||||
).strip()
|
|
||||||
self.assertIn("outer_value", rendered, rendered)
|
self.assertIn("outer_value", rendered, rendered)
|
||||||
|
|
||||||
def test_outer_context_property_with_component_block(self):
|
def test_outer_context_property_with_component_block(self):
|
||||||
|
@ -514,7 +444,5 @@ class OuterContextPropertyTests(SimpleTestCase):
|
||||||
"{% load component_tags %}{% component_dependencies %}"
|
"{% load component_tags %}{% component_dependencies %}"
|
||||||
"{% component_block 'outer_context_component' only %}{% endcomponent_block %}"
|
"{% component_block 'outer_context_component' only %}{% endcomponent_block %}"
|
||||||
)
|
)
|
||||||
rendered = template.render(
|
rendered = template.render(Context({"variable": "outer_value"})).strip()
|
||||||
Context({"variable": "outer_value"})
|
|
||||||
).strip()
|
|
||||||
self.assertIn("outer_value", rendered, rendered)
|
self.assertIn("outer_value", rendered, rendered)
|
||||||
|
|
|
@ -52,9 +52,7 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
def test_no_dependencies_when_no_components_used(self):
|
def test_no_dependencies_when_no_components_used(self):
|
||||||
component.registry.register(name="test", component=SimpleComponent)
|
component.registry.register(name="test", component=SimpleComponent)
|
||||||
|
|
||||||
template = Template(
|
template = Template("{% load component_tags %}{% component_dependencies %}")
|
||||||
"{% load component_tags %}{% component_dependencies %}"
|
|
||||||
)
|
|
||||||
rendered = create_and_process_template_response(template)
|
rendered = create_and_process_template_response(template)
|
||||||
self.assertInHTML('<script src="script.js">', rendered, count=0)
|
self.assertInHTML('<script src="script.js">', rendered, count=0)
|
||||||
self.assertInHTML(
|
self.assertInHTML(
|
||||||
|
@ -66,18 +64,14 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
def test_no_js_dependencies_when_no_components_used(self):
|
def test_no_js_dependencies_when_no_components_used(self):
|
||||||
component.registry.register(name="test", component=SimpleComponent)
|
component.registry.register(name="test", component=SimpleComponent)
|
||||||
|
|
||||||
template = Template(
|
template = Template("{% load component_tags %}{% component_js_dependencies %}")
|
||||||
"{% load component_tags %}{% component_js_dependencies %}"
|
|
||||||
)
|
|
||||||
rendered = create_and_process_template_response(template)
|
rendered = create_and_process_template_response(template)
|
||||||
self.assertInHTML('<script src="script.js">', rendered, count=0)
|
self.assertInHTML('<script src="script.js">', rendered, count=0)
|
||||||
|
|
||||||
def test_no_css_dependencies_when_no_components_used(self):
|
def test_no_css_dependencies_when_no_components_used(self):
|
||||||
component.registry.register(name="test", component=SimpleComponent)
|
component.registry.register(name="test", component=SimpleComponent)
|
||||||
|
|
||||||
template = Template(
|
template = Template("{% load component_tags %}{% component_css_dependencies %}")
|
||||||
"{% load component_tags %}{% component_css_dependencies %}"
|
|
||||||
)
|
|
||||||
rendered = create_and_process_template_response(template)
|
rendered = create_and_process_template_response(template)
|
||||||
self.assertInHTML(
|
self.assertInHTML(
|
||||||
'<link href="style.css" media="all" rel="stylesheet"/>',
|
'<link href="style.css" media="all" rel="stylesheet"/>',
|
||||||
|
@ -88,9 +82,7 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
def test_preload_dependencies_render_when_no_components_used(self):
|
def test_preload_dependencies_render_when_no_components_used(self):
|
||||||
component.registry.register(name="test", component=SimpleComponent)
|
component.registry.register(name="test", component=SimpleComponent)
|
||||||
|
|
||||||
template = Template(
|
template = Template("{% load component_tags %}{% component_dependencies preload='test' %}")
|
||||||
"{% load component_tags %}{% component_dependencies preload='test' %}"
|
|
||||||
)
|
|
||||||
rendered = create_and_process_template_response(template)
|
rendered = create_and_process_template_response(template)
|
||||||
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
||||||
self.assertInHTML(
|
self.assertInHTML(
|
||||||
|
@ -102,9 +94,7 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
def test_preload_css_dependencies_render_when_no_components_used(self):
|
def test_preload_css_dependencies_render_when_no_components_used(self):
|
||||||
component.registry.register(name="test", component=SimpleComponent)
|
component.registry.register(name="test", component=SimpleComponent)
|
||||||
|
|
||||||
template = Template(
|
template = Template("{% load component_tags %}{% component_css_dependencies preload='test' %}")
|
||||||
"{% load component_tags %}{% component_css_dependencies preload='test' %}"
|
|
||||||
)
|
|
||||||
rendered = create_and_process_template_response(template)
|
rendered = create_and_process_template_response(template)
|
||||||
self.assertInHTML(
|
self.assertInHTML(
|
||||||
'<link href="style.css" media="all" rel="stylesheet"/>',
|
'<link href="style.css" media="all" rel="stylesheet"/>',
|
||||||
|
@ -170,9 +160,7 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
def test_placeholder_removed_when_preload_rendered(self):
|
def test_placeholder_removed_when_preload_rendered(self):
|
||||||
component.registry.register(name="test", component=SimpleComponent)
|
component.registry.register(name="test", component=SimpleComponent)
|
||||||
|
|
||||||
template = Template(
|
template = Template("{% load component_tags %}{% component_dependencies preload='test' %}")
|
||||||
"{% load component_tags %}{% component_dependencies preload='test' %}"
|
|
||||||
)
|
|
||||||
rendered = create_and_process_template_response(template)
|
rendered = create_and_process_template_response(template)
|
||||||
self.assertNotIn("_RENDERED", rendered)
|
self.assertNotIn("_RENDERED", rendered)
|
||||||
|
|
||||||
|
@ -226,7 +214,10 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
):
|
):
|
||||||
component.registry.register(name="test", component=MultistyleComponent)
|
component.registry.register(name="test", component=MultistyleComponent)
|
||||||
template = Template(
|
template = Template(
|
||||||
"{% load component_tags %}{% component_js_dependencies %}{% component_block 'test' %}{% endcomponent_block %}"
|
"""
|
||||||
|
{% load component_tags %}{% component_js_dependencies %}
|
||||||
|
{% component_block 'test' %}{% endcomponent_block %}
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
rendered = create_and_process_template_response(template)
|
rendered = create_and_process_template_response(template)
|
||||||
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
||||||
|
@ -247,7 +238,10 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
):
|
):
|
||||||
component.registry.register(name="test", component=MultistyleComponent)
|
component.registry.register(name="test", component=MultistyleComponent)
|
||||||
template = Template(
|
template = Template(
|
||||||
"{% load component_tags %}{% component_css_dependencies %}{% component_block 'test' %}{% endcomponent_block %}"
|
"""
|
||||||
|
{% load component_tags %}{% component_css_dependencies %}
|
||||||
|
{% component_block 'test' %}{% endcomponent_block %}
|
||||||
|
"""
|
||||||
)
|
)
|
||||||
rendered = create_and_process_template_response(template)
|
rendered = create_and_process_template_response(template)
|
||||||
self.assertInHTML('<script src="script.js">', rendered, count=0)
|
self.assertInHTML('<script src="script.js">', rendered, count=0)
|
||||||
|
@ -265,13 +259,9 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_no_dependencies_with_multiple_unused_components(self):
|
def test_no_dependencies_with_multiple_unused_components(self):
|
||||||
component.registry.register(name="test1", component=SimpleComponent)
|
component.registry.register(name="test1", component=SimpleComponent)
|
||||||
component.registry.register(
|
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||||
name="test2", component=SimpleComponentAlternate
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template(
|
template = Template("{% load component_tags %}{% component_dependencies %}")
|
||||||
"{% load component_tags %}{% component_dependencies %}"
|
|
||||||
)
|
|
||||||
rendered = create_and_process_template_response(template)
|
rendered = create_and_process_template_response(template)
|
||||||
self.assertInHTML('<script src="script.js">', rendered, count=0)
|
self.assertInHTML('<script src="script.js">', rendered, count=0)
|
||||||
self.assertInHTML('<script src="script2.js">', rendered, count=0)
|
self.assertInHTML('<script src="script2.js">', rendered, count=0)
|
||||||
|
@ -288,9 +278,7 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_correct_css_dependencies_with_multiple_components(self):
|
def test_correct_css_dependencies_with_multiple_components(self):
|
||||||
component.registry.register(name="test1", component=SimpleComponent)
|
component.registry.register(name="test1", component=SimpleComponent)
|
||||||
component.registry.register(
|
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||||
name="test2", component=SimpleComponentAlternate
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template(
|
template = Template(
|
||||||
"{% load component_tags %}{% component_css_dependencies %}"
|
"{% load component_tags %}{% component_css_dependencies %}"
|
||||||
|
@ -310,9 +298,7 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_correct_js_dependencies_with_multiple_components(self):
|
def test_correct_js_dependencies_with_multiple_components(self):
|
||||||
component.registry.register(name="test1", component=SimpleComponent)
|
component.registry.register(name="test1", component=SimpleComponent)
|
||||||
component.registry.register(
|
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||||
name="test2", component=SimpleComponentAlternate
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template(
|
template = Template(
|
||||||
"{% load component_tags %}{% component_js_dependencies %}"
|
"{% load component_tags %}{% component_js_dependencies %}"
|
||||||
|
@ -324,9 +310,7 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_correct_dependencies_with_multiple_components(self):
|
def test_correct_dependencies_with_multiple_components(self):
|
||||||
component.registry.register(name="test1", component=SimpleComponent)
|
component.registry.register(name="test1", component=SimpleComponent)
|
||||||
component.registry.register(
|
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||||
name="test2", component=SimpleComponentAlternate
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template(
|
template = Template(
|
||||||
"{% load component_tags %}{% component_dependencies %}"
|
"{% load component_tags %}{% component_dependencies %}"
|
||||||
|
@ -348,19 +332,17 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_shared_dependencies_rendered_once(self):
|
def test_shared_dependencies_rendered_once(self):
|
||||||
component.registry.register(name="test1", component=SimpleComponent)
|
component.registry.register(name="test1", component=SimpleComponent)
|
||||||
component.registry.register(
|
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||||
name="test2", component=SimpleComponentAlternate
|
component.registry.register(name="test3", component=SimpleComponentWithSharedDependency)
|
||||||
)
|
|
||||||
component.registry.register(
|
|
||||||
name="test3", component=SimpleComponentWithSharedDependency
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template("""
|
template = Template(
|
||||||
|
"""
|
||||||
{% load component_tags %}{% component_dependencies %}
|
{% load component_tags %}{% component_dependencies %}
|
||||||
{% component_block 'test1' variable='variable' %}{% endcomponent_block %}
|
{% component_block 'test1' variable='variable' %}{% endcomponent_block %}
|
||||||
{% component_block 'test2' variable='variable' %}{% endcomponent_block %}
|
{% component_block 'test2' variable='variable' %}{% endcomponent_block %}
|
||||||
{% component_block 'test1' variable='variable' %}{% endcomponent_block %}
|
{% component_block 'test1' variable='variable' %}{% endcomponent_block %}
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
rendered = create_and_process_template_response(template)
|
rendered = create_and_process_template_response(template)
|
||||||
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
||||||
self.assertInHTML('<script src="script2.js">', rendered, count=1)
|
self.assertInHTML('<script src="script2.js">', rendered, count=1)
|
||||||
|
@ -377,27 +359,23 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
|
|
||||||
def test_placeholder_removed_when_multiple_component_rendered(self):
|
def test_placeholder_removed_when_multiple_component_rendered(self):
|
||||||
component.registry.register(name="test1", component=SimpleComponent)
|
component.registry.register(name="test1", component=SimpleComponent)
|
||||||
component.registry.register(
|
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||||
name="test2", component=SimpleComponentAlternate
|
component.registry.register(name="test3", component=SimpleComponentWithSharedDependency)
|
||||||
)
|
|
||||||
component.registry.register(
|
|
||||||
name="test3", component=SimpleComponentWithSharedDependency
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template("""
|
template = Template(
|
||||||
|
"""
|
||||||
{% load component_tags %}{% component_dependencies %}
|
{% load component_tags %}{% component_dependencies %}
|
||||||
{% component_block 'test1' variable='variable' %}{% endcomponent_block %}
|
{% component_block 'test1' variable='variable' %}{% endcomponent_block %}
|
||||||
{% component_block 'test2' variable='variable' %}{% endcomponent_block %}
|
{% component_block 'test2' variable='variable' %}{% endcomponent_block %}
|
||||||
{% component_block 'test1' variable='variable' %}{% endcomponent_block %}
|
{% component_block 'test1' variable='variable' %}{% endcomponent_block %}
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
rendered = create_and_process_template_response(template)
|
rendered = create_and_process_template_response(template)
|
||||||
self.assertNotIn("_RENDERED", rendered)
|
self.assertNotIn("_RENDERED", rendered)
|
||||||
|
|
||||||
def test_middleware_response_without_content_type(self):
|
def test_middleware_response_without_content_type(self):
|
||||||
response = HttpResponseNotModified()
|
response = HttpResponseNotModified()
|
||||||
middleware = ComponentDependencyMiddleware(
|
middleware = ComponentDependencyMiddleware(get_response=lambda _: response)
|
||||||
get_response=lambda _: response
|
|
||||||
)
|
|
||||||
request = Mock()
|
request = Mock()
|
||||||
self.assertEqual(response, middleware(request=request))
|
self.assertEqual(response, middleware(request=request))
|
||||||
|
|
||||||
|
@ -410,9 +388,7 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
||||||
"test_component",
|
"test_component",
|
||||||
]
|
]
|
||||||
for component_name in component_names:
|
for component_name in component_names:
|
||||||
component.registry.register(
|
component.registry.register(name=component_name, component=SimpleComponent)
|
||||||
name=component_name, component=SimpleComponent
|
|
||||||
)
|
|
||||||
template = Template(
|
template = Template(
|
||||||
"{% load component_tags %}"
|
"{% load component_tags %}"
|
||||||
"{% component_js_dependencies %}"
|
"{% component_js_dependencies %}"
|
||||||
|
|
|
@ -27,9 +27,7 @@ class ComponentRegistryTest(unittest.TestCase):
|
||||||
class TestComponent(component.Component):
|
class TestComponent(component.Component):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(component.registry.get("decorated_component"), TestComponent)
|
||||||
component.registry.get("decorated_component"), TestComponent
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_simple_register(self):
|
def test_simple_register(self):
|
||||||
self.registry.register(name="testcomponent", component=MockComponent)
|
self.registry.register(name="testcomponent", component=MockComponent)
|
||||||
|
@ -49,18 +47,12 @@ class ComponentRegistryTest(unittest.TestCase):
|
||||||
def test_prevent_registering_different_components_with_the_same_name(self):
|
def test_prevent_registering_different_components_with_the_same_name(self):
|
||||||
self.registry.register(name="testcomponent", component=MockComponent)
|
self.registry.register(name="testcomponent", component=MockComponent)
|
||||||
with self.assertRaises(component.AlreadyRegistered):
|
with self.assertRaises(component.AlreadyRegistered):
|
||||||
self.registry.register(
|
self.registry.register(name="testcomponent", component=MockComponent2)
|
||||||
name="testcomponent", component=MockComponent2
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_allow_duplicated_registration_of_the_same_component(self):
|
def test_allow_duplicated_registration_of_the_same_component(self):
|
||||||
try:
|
try:
|
||||||
self.registry.register(
|
self.registry.register(name="testcomponent", component=MockComponentView)
|
||||||
name="testcomponent", component=MockComponentView
|
self.registry.register(name="testcomponent", component=MockComponentView)
|
||||||
)
|
|
||||||
self.registry.register(
|
|
||||||
name="testcomponent", component=MockComponentView
|
|
||||||
)
|
|
||||||
except component.AlreadyRegistered:
|
except component.AlreadyRegistered:
|
||||||
self.fail("Should not raise AlreadyRegistered")
|
self.fail("Should not raise AlreadyRegistered")
|
||||||
|
|
||||||
|
|
|
@ -52,15 +52,11 @@ class CreateComponentCommandTest(TestCase):
|
||||||
os.path.join(self.temp_dir, component_name, "test.js"),
|
os.path.join(self.temp_dir, component_name, "test.js"),
|
||||||
os.path.join(self.temp_dir, component_name, "test.css"),
|
os.path.join(self.temp_dir, component_name, "test.css"),
|
||||||
os.path.join(self.temp_dir, component_name, "test.html"),
|
os.path.join(self.temp_dir, component_name, "test.html"),
|
||||||
os.path.join(
|
os.path.join(self.temp_dir, component_name, f"{component_name}.py"),
|
||||||
self.temp_dir, component_name, f"{component_name}.py"
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for file_path in expected_files:
|
for file_path in expected_files:
|
||||||
self.assertTrue(
|
self.assertTrue(os.path.exists(file_path), f"File {file_path} was not created")
|
||||||
os.path.exists(file_path), f"File {file_path} was not created"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_dry_run(self):
|
def test_dry_run(self):
|
||||||
component_name = "dryruncomponent"
|
component_name = "dryruncomponent"
|
||||||
|
@ -80,9 +76,7 @@ class CreateComponentCommandTest(TestCase):
|
||||||
component_path = os.path.join(self.temp_dir, component_name)
|
component_path = os.path.join(self.temp_dir, component_name)
|
||||||
os.makedirs(component_path)
|
os.makedirs(component_path)
|
||||||
|
|
||||||
with open(
|
with open(os.path.join(component_path, f"{component_name}.py"), "w") as f:
|
||||||
os.path.join(component_path, f"{component_name}.py"), "w"
|
|
||||||
) as f:
|
|
||||||
f.write("hello world")
|
f.write("hello world")
|
||||||
|
|
||||||
call_command(
|
call_command(
|
||||||
|
@ -93,9 +87,7 @@ class CreateComponentCommandTest(TestCase):
|
||||||
"--force",
|
"--force",
|
||||||
)
|
)
|
||||||
|
|
||||||
with open(
|
with open(os.path.join(component_path, f"{component_name}.py"), "r") as f:
|
||||||
os.path.join(component_path, f"{component_name}.py"), "r"
|
|
||||||
) as f:
|
|
||||||
self.assertNotIn("hello world", f.read())
|
self.assertNotIn("hello world", f.read())
|
||||||
|
|
||||||
def test_error_existing_component_no_force(self):
|
def test_error_existing_component_no_force(self):
|
||||||
|
@ -104,9 +96,7 @@ class CreateComponentCommandTest(TestCase):
|
||||||
os.makedirs(component_path)
|
os.makedirs(component_path)
|
||||||
|
|
||||||
with self.assertRaises(CommandError):
|
with self.assertRaises(CommandError):
|
||||||
call_command(
|
call_command("startcomponent", component_name, "--path", self.temp_dir)
|
||||||
"startcomponent", component_name, "--path", self.temp_dir
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_verbose_output(self):
|
def test_verbose_output(self):
|
||||||
component_name = "verbosecomponent"
|
component_name = "verbosecomponent"
|
||||||
|
|
|
@ -100,41 +100,44 @@ class ComponentTemplateTagTest(SimpleTestCase):
|
||||||
def test_single_component(self):
|
def test_single_component(self):
|
||||||
component.registry.register(name="test", component=SimpleComponent)
|
component.registry.register(name="test", component=SimpleComponent)
|
||||||
|
|
||||||
simple_tag_tempate = '{% load component_tags %}{% component_block name="test" variable="variable" %}{% endcomponent_block %}'
|
simple_tag_tempate = """
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component_block name="test" variable="variable" %}{% endcomponent_block %}
|
||||||
|
"""
|
||||||
block_tag_template = self.inline_to_block(simple_tag_tempate)
|
block_tag_template = self.inline_to_block(simple_tag_tempate)
|
||||||
|
|
||||||
for tag in [simple_tag_tempate, block_tag_template]:
|
for tag in [simple_tag_tempate, block_tag_template]:
|
||||||
template = Template(tag)
|
template = Template(tag)
|
||||||
rendered = template.render(Context({}))
|
rendered = template.render(Context({}))
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(rendered, "Variable: <strong>variable</strong>\n")
|
||||||
rendered, "Variable: <strong>variable</strong>\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_call_with_invalid_name(self):
|
def test_call_with_invalid_name(self):
|
||||||
# Note: No tag registered
|
# Note: No tag registered
|
||||||
|
|
||||||
simple_tag_tempate = '{% load component_tags %}{% component_block name="test" variable="variable" %}{% endcomponent_block %}'
|
simple_tag_tempate = """
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component_block name="test" variable="variable" %}{% endcomponent_block %}
|
||||||
|
"""
|
||||||
block_tag_template = self.inline_to_block(simple_tag_tempate)
|
block_tag_template = self.inline_to_block(simple_tag_tempate)
|
||||||
|
|
||||||
for tag in [simple_tag_tempate, block_tag_template]:
|
for tag in [simple_tag_tempate, block_tag_template]:
|
||||||
template = Template(tag)
|
template = Template(tag)
|
||||||
with self.assertRaises(
|
with self.assertRaises(django_components.component_registry.NotRegistered):
|
||||||
django_components.component_registry.NotRegistered
|
|
||||||
):
|
|
||||||
template.render(Context({}))
|
template.render(Context({}))
|
||||||
|
|
||||||
def test_component_called_with_positional_name(self):
|
def test_component_called_with_positional_name(self):
|
||||||
component.registry.register(name="test", component=SimpleComponent)
|
component.registry.register(name="test", component=SimpleComponent)
|
||||||
|
|
||||||
simple_tag_tempate = '{% load component_tags %}{% component_block "test" variable="variable" %}{% endcomponent_block %}'
|
simple_tag_tempate = """
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component_block "test" variable="variable" %}{% endcomponent_block %}
|
||||||
|
"""
|
||||||
block_tag_template = self.inline_to_block(simple_tag_tempate)
|
block_tag_template = self.inline_to_block(simple_tag_tempate)
|
||||||
|
|
||||||
for tag in [simple_tag_tempate, block_tag_template]:
|
for tag in [simple_tag_tempate, block_tag_template]:
|
||||||
template = Template(tag)
|
template = Template(tag)
|
||||||
rendered = template.render(Context({}))
|
rendered = template.render(Context({}))
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(rendered, "Variable: <strong>variable</strong>\n")
|
||||||
rendered, "Variable: <strong>variable</strong>\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_call_component_with_two_variables(self):
|
def test_call_component_with_two_variables(self):
|
||||||
component.registry.register(name="test", component=IffedComponent)
|
component.registry.register(name="test", component=IffedComponent)
|
||||||
|
@ -148,24 +151,22 @@ class ComponentTemplateTagTest(SimpleTestCase):
|
||||||
for tag in [simple_tag_tempate, block_tag_template]:
|
for tag in [simple_tag_tempate, block_tag_template]:
|
||||||
template = Template(tag)
|
template = Template(tag)
|
||||||
rendered = template.render(Context({}))
|
rendered = template.render(Context({}))
|
||||||
expected_outcome = (
|
expected_outcome = """Variable: <strong>variable</strong>\n""" """Variable2: <strong>hej</strong>"""
|
||||||
"""Variable: <strong>variable</strong>\n"""
|
|
||||||
"""Variable2: <strong>hej</strong>"""
|
|
||||||
)
|
|
||||||
self.assertHTMLEqual(rendered, textwrap.dedent(expected_outcome))
|
self.assertHTMLEqual(rendered, textwrap.dedent(expected_outcome))
|
||||||
|
|
||||||
def test_component_called_with_singlequoted_name(self):
|
def test_component_called_with_singlequoted_name(self):
|
||||||
component.registry.register(name="test", component=SimpleComponent)
|
component.registry.register(name="test", component=SimpleComponent)
|
||||||
|
|
||||||
simple_tag_tempate = """{% load component_tags %}{% component_block 'test' variable="variable" %}{% endcomponent_block %}"""
|
simple_tag_tempate = """
|
||||||
|
{% load component_tags %}
|
||||||
|
{% component_block 'test' variable="variable" %}{% endcomponent_block %}
|
||||||
|
"""
|
||||||
block_tag_template = self.inline_to_block(simple_tag_tempate)
|
block_tag_template = self.inline_to_block(simple_tag_tempate)
|
||||||
|
|
||||||
for tag in [simple_tag_tempate, block_tag_template]:
|
for tag in [simple_tag_tempate, block_tag_template]:
|
||||||
template = Template(tag)
|
template = Template(tag)
|
||||||
rendered = template.render(Context({}))
|
rendered = template.render(Context({}))
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(rendered, "Variable: <strong>variable</strong>\n")
|
||||||
rendered, "Variable: <strong>variable</strong>\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_component_called_with_variable_as_name(self):
|
def test_component_called_with_variable_as_name(self):
|
||||||
component.registry.register(name="test", component=SimpleComponent)
|
component.registry.register(name="test", component=SimpleComponent)
|
||||||
|
@ -181,9 +182,7 @@ class ComponentTemplateTagTest(SimpleTestCase):
|
||||||
for tag in [simple_tag_tempate, block_tag_template]:
|
for tag in [simple_tag_tempate, block_tag_template]:
|
||||||
template = Template(tag)
|
template = Template(tag)
|
||||||
rendered = template.render(Context({}))
|
rendered = template.render(Context({}))
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(rendered, "Variable: <strong>variable</strong>\n")
|
||||||
rendered, "Variable: <strong>variable</strong>\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_component_called_with_invalid_variable_as_name(self):
|
def test_component_called_with_invalid_variable_as_name(self):
|
||||||
component.registry.register(name="test", component=SimpleComponent)
|
component.registry.register(name="test", component=SimpleComponent)
|
||||||
|
@ -199,9 +198,7 @@ class ComponentTemplateTagTest(SimpleTestCase):
|
||||||
for tag in [simple_tag_tempate, block_tag_template]:
|
for tag in [simple_tag_tempate, block_tag_template]:
|
||||||
template = Template(tag)
|
template = Template(tag)
|
||||||
|
|
||||||
with self.assertRaises(
|
with self.assertRaises(django_components.component_registry.NotRegistered):
|
||||||
django_components.component_registry.NotRegistered
|
|
||||||
):
|
|
||||||
template.render(Context({}))
|
template.render(Context({}))
|
||||||
|
|
||||||
|
|
||||||
|
@ -241,9 +238,7 @@ class ComponentSlottedTemplateTagTest(SimpleTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_slotted_template_with_context_var(self):
|
def test_slotted_template_with_context_var(self):
|
||||||
component.registry.register(
|
component.registry.register(name="test1", component=SlottedComponentWithContext)
|
||||||
name="test1", component=SlottedComponentWithContext
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template(
|
template = Template(
|
||||||
"""
|
"""
|
||||||
|
@ -276,9 +271,7 @@ class ComponentSlottedTemplateTagTest(SimpleTestCase):
|
||||||
def test_slotted_template_no_slots_filled(self):
|
def test_slotted_template_no_slots_filled(self):
|
||||||
component.registry.register(name="test", component=SlottedComponent)
|
component.registry.register(name="test", component=SlottedComponent)
|
||||||
|
|
||||||
template = Template(
|
template = Template('{% load component_tags %}{% component_block "test" %}{% endcomponent_block %}')
|
||||||
'{% load component_tags %}{% component_block "test" %}{% endcomponent_block %}'
|
|
||||||
)
|
|
||||||
rendered = template.render(Context({}))
|
rendered = template.render(Context({}))
|
||||||
|
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(
|
||||||
|
@ -293,9 +286,7 @@ class ComponentSlottedTemplateTagTest(SimpleTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_slotted_template_without_slots(self):
|
def test_slotted_template_without_slots(self):
|
||||||
component.registry.register(
|
component.registry.register(name="test", component=SlottedComponentNoSlots)
|
||||||
name="test", component=SlottedComponentNoSlots
|
|
||||||
)
|
|
||||||
template = Template(
|
template = Template(
|
||||||
"""
|
"""
|
||||||
{% load component_tags %}
|
{% load component_tags %}
|
||||||
|
@ -307,9 +298,7 @@ class ComponentSlottedTemplateTagTest(SimpleTestCase):
|
||||||
self.assertHTMLEqual(rendered, "<custom-template></custom-template>")
|
self.assertHTMLEqual(rendered, "<custom-template></custom-template>")
|
||||||
|
|
||||||
def test_slotted_template_without_slots_and_single_quotes(self):
|
def test_slotted_template_without_slots_and_single_quotes(self):
|
||||||
component.registry.register(
|
component.registry.register(name="test", component=SlottedComponentNoSlots)
|
||||||
name="test", component=SlottedComponentNoSlots
|
|
||||||
)
|
|
||||||
template = Template(
|
template = Template(
|
||||||
"""
|
"""
|
||||||
{% load component_tags %}
|
{% load component_tags %}
|
||||||
|
@ -397,9 +386,7 @@ class ComponentSlottedTemplateTagTest(SimpleTestCase):
|
||||||
self.assertHTMLEqual(rendered, expected)
|
self.assertHTMLEqual(rendered, expected)
|
||||||
|
|
||||||
def test_error_raised_when_default_and_required_slot_not_filled(self):
|
def test_error_raised_when_default_and_required_slot_not_filled(self):
|
||||||
component.registry.register(
|
component.registry.register("test_comp", ComponentWithDefaultAndRequiredSlot)
|
||||||
"test_comp", ComponentWithDefaultAndRequiredSlot
|
|
||||||
)
|
|
||||||
template = Template(
|
template = Template(
|
||||||
"""
|
"""
|
||||||
{% load component_tags %}
|
{% load component_tags %}
|
||||||
|
@ -488,9 +475,7 @@ class ComponentSlottedTemplateTagTest(SimpleTestCase):
|
||||||
|
|
||||||
def test_component_template_cannot_have_multiple_default_slots(self):
|
def test_component_template_cannot_have_multiple_default_slots(self):
|
||||||
class BadComponent(component.Component):
|
class BadComponent(component.Component):
|
||||||
def get_template(
|
def get_template(self, context, template_name: Optional[str] = None) -> Template:
|
||||||
self, context, template_name: Optional[str] = None
|
|
||||||
) -> Template:
|
|
||||||
return Template(
|
return Template(
|
||||||
"""
|
"""
|
||||||
{% load django_components %}
|
{% load django_components %}
|
||||||
|
@ -543,9 +528,7 @@ class SlottedTemplateRegressionTests(SimpleTestCase):
|
||||||
component.registry.clear()
|
component.registry.clear()
|
||||||
|
|
||||||
def test_slotted_template_that_uses_missing_variable(self):
|
def test_slotted_template_that_uses_missing_variable(self):
|
||||||
component.registry.register(
|
component.registry.register(name="test", component=SlottedComponentWithMissingVariable)
|
||||||
name="test", component=SlottedComponentWithMissingVariable
|
|
||||||
)
|
|
||||||
template = Template(
|
template = Template(
|
||||||
"""
|
"""
|
||||||
{% load component_tags %}
|
{% load component_tags %}
|
||||||
|
@ -566,9 +549,7 @@ class SlottedTemplateRegressionTests(SimpleTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_component_block_accepts_provided_and_default_parameters(self):
|
def test_component_block_accepts_provided_and_default_parameters(self):
|
||||||
component.registry.register(
|
component.registry.register(name="test", component=ComponentWithProvidedAndDefaultParameters)
|
||||||
name="test", component=ComponentWithProvidedAndDefaultParameters
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template(
|
template = Template(
|
||||||
"""
|
"""
|
||||||
|
@ -590,32 +571,22 @@ class MultiComponentTests(SimpleTestCase):
|
||||||
|
|
||||||
def register_components(self):
|
def register_components(self):
|
||||||
component.registry.register("first_component", SlottedComponent)
|
component.registry.register("first_component", SlottedComponent)
|
||||||
component.registry.register(
|
component.registry.register("second_component", SlottedComponentWithContext)
|
||||||
"second_component", SlottedComponentWithContext
|
|
||||||
)
|
|
||||||
|
|
||||||
def make_template(self, first_component_slot="", second_component_slot=""):
|
def make_template(self, first_component_slot="", second_component_slot=""):
|
||||||
return Template(
|
return Template(
|
||||||
"{% load component_tags %}"
|
"{% load component_tags %}"
|
||||||
"{% component_block 'first_component' %}"
|
"{% component_block 'first_component' %}" + first_component_slot + "{% endcomponent_block %}"
|
||||||
+ first_component_slot
|
|
||||||
+ "{% endcomponent_block %}"
|
|
||||||
"{% component_block 'second_component' variable='xyz' %}"
|
"{% component_block 'second_component' variable='xyz' %}"
|
||||||
+ second_component_slot
|
+ second_component_slot
|
||||||
+ "{% endcomponent_block %}"
|
+ "{% endcomponent_block %}"
|
||||||
)
|
)
|
||||||
|
|
||||||
def expected_result(
|
def expected_result(self, first_component_slot="", second_component_slot=""):
|
||||||
self, first_component_slot="", second_component_slot=""
|
|
||||||
):
|
|
||||||
return (
|
return (
|
||||||
"<custom-template><header>{}</header>".format(
|
"<custom-template><header>{}</header>".format(first_component_slot or "Default header")
|
||||||
first_component_slot or "Default header"
|
|
||||||
)
|
|
||||||
+ "<main>Default main</main><footer>Default footer</footer></custom-template>"
|
+ "<main>Default main</main><footer>Default footer</footer></custom-template>"
|
||||||
+ "<custom-template><header>{}</header>".format(
|
+ "<custom-template><header>{}</header>".format(second_component_slot or "Default header")
|
||||||
second_component_slot or "Default header"
|
|
||||||
)
|
|
||||||
+ "<main>Default main</main><footer>Default footer</footer></custom-template>"
|
+ "<main>Default main</main><footer>Default footer</footer></custom-template>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -633,9 +604,7 @@ class MultiComponentTests(SimpleTestCase):
|
||||||
second_slot_content = "<div>Slot #2</div>"
|
second_slot_content = "<div>Slot #2</div>"
|
||||||
first_slot = self.wrap_with_slot_tags(first_slot_content)
|
first_slot = self.wrap_with_slot_tags(first_slot_content)
|
||||||
second_slot = self.wrap_with_slot_tags(second_slot_content)
|
second_slot = self.wrap_with_slot_tags(second_slot_content)
|
||||||
rendered = self.make_template(first_slot, second_slot).render(
|
rendered = self.make_template(first_slot, second_slot).render(Context({}))
|
||||||
Context({})
|
|
||||||
)
|
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(
|
||||||
rendered,
|
rendered,
|
||||||
self.expected_result(first_slot_content, second_slot_content),
|
self.expected_result(first_slot_content, second_slot_content),
|
||||||
|
@ -646,18 +615,14 @@ class MultiComponentTests(SimpleTestCase):
|
||||||
first_slot_content = "<p>Slot #1</p>"
|
first_slot_content = "<p>Slot #1</p>"
|
||||||
first_slot = self.wrap_with_slot_tags(first_slot_content)
|
first_slot = self.wrap_with_slot_tags(first_slot_content)
|
||||||
rendered = self.make_template(first_slot).render(Context({}))
|
rendered = self.make_template(first_slot).render(Context({}))
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(rendered, self.expected_result(first_slot_content))
|
||||||
rendered, self.expected_result(first_slot_content)
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_both_components_render_correctly_when_only_second_has_slots(self):
|
def test_both_components_render_correctly_when_only_second_has_slots(self):
|
||||||
self.register_components()
|
self.register_components()
|
||||||
second_slot_content = "<div>Slot #2</div>"
|
second_slot_content = "<div>Slot #2</div>"
|
||||||
second_slot = self.wrap_with_slot_tags(second_slot_content)
|
second_slot = self.wrap_with_slot_tags(second_slot_content)
|
||||||
rendered = self.make_template("", second_slot).render(Context({}))
|
rendered = self.make_template("", second_slot).render(Context({}))
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(rendered, self.expected_result("", second_slot_content))
|
||||||
rendered, self.expected_result("", second_slot_content)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class TemplateInstrumentationTest(SimpleTestCase):
|
class TemplateInstrumentationTest(SimpleTestCase):
|
||||||
|
@ -689,9 +654,7 @@ class TemplateInstrumentationTest(SimpleTestCase):
|
||||||
def receive_template_signal(sender, template, context, **_kwargs):
|
def receive_template_signal(sender, template, context, **_kwargs):
|
||||||
templates_used.append(template.name)
|
templates_used.append(template.name)
|
||||||
|
|
||||||
template_rendered.connect(
|
template_rendered.connect(receive_template_signal, dispatch_uid="test_method")
|
||||||
receive_template_signal, dispatch_uid="test_method"
|
|
||||||
)
|
|
||||||
subject_template.render(render_context or Context({}))
|
subject_template.render(render_context or Context({}))
|
||||||
template_rendered.disconnect(dispatch_uid="test_method")
|
template_rendered.disconnect(dispatch_uid="test_method")
|
||||||
return templates_used
|
return templates_used
|
||||||
|
@ -823,9 +786,7 @@ class ConditionalSlotTests(SimpleTestCase):
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
rendered = template.render(Context({}))
|
rendered = template.render(Context({}))
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(rendered, '<p id="a">Default A</p><p id="b">Default B</p>')
|
||||||
rendered, '<p id="a">Default A</p><p id="b">Default B</p>'
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_one_slot_overridden(self):
|
def test_one_slot_overridden(self):
|
||||||
template = Template(
|
template = Template(
|
||||||
|
@ -840,9 +801,7 @@ class ConditionalSlotTests(SimpleTestCase):
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
rendered = template.render(Context({}))
|
rendered = template.render(Context({}))
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(rendered, '<p id="a">Default A</p><p id="b">Override B</p>')
|
||||||
rendered, '<p id="a">Default A</p><p id="b">Override B</p>'
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_both_slots_overridden(self):
|
def test_both_slots_overridden(self):
|
||||||
template = Template(
|
template = Template(
|
||||||
|
@ -859,9 +818,7 @@ class ConditionalSlotTests(SimpleTestCase):
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
rendered = template.render(Context({}))
|
rendered = template.render(Context({}))
|
||||||
self.assertHTMLEqual(
|
self.assertHTMLEqual(rendered, '<p id="a">Override A</p><p id="b">Override B</p>')
|
||||||
rendered, '<p id="a">Override A</p><p id="b">Override B</p>'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class SlotSuperTests(SimpleTestCase):
|
class SlotSuperTests(SimpleTestCase):
|
||||||
|
@ -957,9 +914,7 @@ class TemplateSyntaxErrorTests(SimpleTestCase):
|
||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
component.registry.register("test", SlottedComponent)
|
component.registry.register("test", SlottedComponent)
|
||||||
component.registry.register("broken_component", BrokenComponent)
|
component.registry.register("broken_component", BrokenComponent)
|
||||||
component.registry.register(
|
component.registry.register("nonunique_slot_component", NonUniqueSlotsComponent)
|
||||||
"nonunique_slot_component", NonUniqueSlotsComponent
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls) -> None:
|
def tearDownClass(cls) -> None:
|
||||||
|
@ -1141,16 +1096,12 @@ class ConditionalIfFilledSlotsTests(SimpleTestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls) -> None:
|
def setUpClass(cls) -> None:
|
||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
component.registry.register(
|
component.registry.register("conditional_slots", cls.ComponentWithConditionalSlots)
|
||||||
"conditional_slots", cls.ComponentWithConditionalSlots
|
|
||||||
)
|
|
||||||
component.registry.register(
|
component.registry.register(
|
||||||
"complex_conditional_slots",
|
"complex_conditional_slots",
|
||||||
cls.ComponentWithComplexConditionalSlots,
|
cls.ComponentWithComplexConditionalSlots,
|
||||||
)
|
)
|
||||||
component.registry.register(
|
component.registry.register("negated_conditional_slot", cls.ComponentWithNegatedConditionalSlot)
|
||||||
"negated_conditional_slot", cls.ComponentWithNegatedConditionalSlot
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def tearDownClass(cls) -> None:
|
def tearDownClass(cls) -> None:
|
||||||
|
@ -1310,9 +1261,7 @@ class IterationFillTest(SimpleTestCase):
|
||||||
django_components.component.registry.clear()
|
django_components.component.registry.clear()
|
||||||
|
|
||||||
def test_inner_slot_iteration_basic(self):
|
def test_inner_slot_iteration_basic(self):
|
||||||
component.registry.register(
|
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||||
"slot_in_a_loop", self.ComponentSimpleSlotInALoop
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template(
|
template = Template(
|
||||||
"""
|
"""
|
||||||
|
@ -1336,9 +1285,7 @@ class IterationFillTest(SimpleTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_inner_slot_iteration_with_variable_from_outer_scope(self):
|
def test_inner_slot_iteration_with_variable_from_outer_scope(self):
|
||||||
component.registry.register(
|
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||||
"slot_in_a_loop", self.ComponentSimpleSlotInALoop
|
|
||||||
)
|
|
||||||
|
|
||||||
template = Template(
|
template = Template(
|
||||||
"""
|
"""
|
||||||
|
@ -1372,9 +1319,7 @@ class IterationFillTest(SimpleTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_inner_slot_iteration_nested(self):
|
def test_inner_slot_iteration_nested(self):
|
||||||
component.registry.register(
|
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||||
"slot_in_a_loop", self.ComponentSimpleSlotInALoop
|
|
||||||
)
|
|
||||||
|
|
||||||
objects = [
|
objects = [
|
||||||
{"inner": ["OBJECT1_ITER1", "OBJECT2_ITER1"]},
|
{"inner": ["OBJECT1_ITER1", "OBJECT2_ITER1"]},
|
||||||
|
@ -1408,9 +1353,7 @@ class IterationFillTest(SimpleTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_inner_slot_iteration_nested_with_outer_scope_variable(self):
|
def test_inner_slot_iteration_nested_with_outer_scope_variable(self):
|
||||||
component.registry.register(
|
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||||
"slot_in_a_loop", self.ComponentSimpleSlotInALoop
|
|
||||||
)
|
|
||||||
|
|
||||||
objects = [
|
objects = [
|
||||||
{"inner": ["OBJECT1_ITER1", "OBJECT2_ITER1"]},
|
{"inner": ["OBJECT1_ITER1", "OBJECT2_ITER1"]},
|
||||||
|
@ -1460,9 +1403,7 @@ class IterationFillTest(SimpleTestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_inner_slot_iteration_nested_with_slot_default(self):
|
def test_inner_slot_iteration_nested_with_slot_default(self):
|
||||||
component.registry.register(
|
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||||
"slot_in_a_loop", self.ComponentSimpleSlotInALoop
|
|
||||||
)
|
|
||||||
|
|
||||||
objects = [
|
objects = [
|
||||||
{"inner": ["OBJECT1_ITER1", "OBJECT2_ITER1"]},
|
{"inner": ["OBJECT1_ITER1", "OBJECT2_ITER1"]},
|
||||||
|
@ -1498,9 +1439,7 @@ class IterationFillTest(SimpleTestCase):
|
||||||
def test_inner_slot_iteration_nested_with_slot_default_and_outer_scope_variable(
|
def test_inner_slot_iteration_nested_with_slot_default_and_outer_scope_variable(
|
||||||
self,
|
self,
|
||||||
):
|
):
|
||||||
component.registry.register(
|
component.registry.register("slot_in_a_loop", self.ComponentSimpleSlotInALoop)
|
||||||
"slot_in_a_loop", self.ComponentSimpleSlotInALoop
|
|
||||||
)
|
|
||||||
|
|
||||||
objects = [
|
objects = [
|
||||||
{"inner": ["OBJECT1_ITER1", "OBJECT2_ITER1"]},
|
{"inner": ["OBJECT1_ITER1", "OBJECT2_ITER1"]},
|
||||||
|
|
|
@ -8,9 +8,7 @@ from django_components.middleware import ComponentDependencyMiddleware
|
||||||
|
|
||||||
# Create middleware instance
|
# Create middleware instance
|
||||||
response_stash = None
|
response_stash = None
|
||||||
middleware = ComponentDependencyMiddleware(
|
middleware = ComponentDependencyMiddleware(get_response=lambda _: response_stash)
|
||||||
get_response=lambda _: response_stash
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class Django30CompatibleSimpleTestCase(SimpleTestCase):
|
class Django30CompatibleSimpleTestCase(SimpleTestCase):
|
||||||
|
@ -19,9 +17,7 @@ class Django30CompatibleSimpleTestCase(SimpleTestCase):
|
||||||
left = left.replace(' type="text/css"', "")
|
left = left.replace(' type="text/css"', "")
|
||||||
right = right.replace(' type="text/javascript"', "")
|
right = right.replace(' type="text/javascript"', "")
|
||||||
right = right.replace(' type="text/css"', "")
|
right = right.replace(' type="text/css"', "")
|
||||||
super(Django30CompatibleSimpleTestCase, self).assertHTMLEqual(
|
super(Django30CompatibleSimpleTestCase, self).assertHTMLEqual(left, right)
|
||||||
left, right
|
|
||||||
)
|
|
||||||
|
|
||||||
def assertInHTML(self, needle, haystack, count=None, msg_prefix=""):
|
def assertInHTML(self, needle, haystack, count=None, msg_prefix=""):
|
||||||
haystack = haystack.replace(' type="text/javascript"', "")
|
haystack = haystack.replace(' type="text/javascript"', "")
|
||||||
|
@ -37,9 +33,7 @@ request = Mock()
|
||||||
mock_template = Mock()
|
mock_template = Mock()
|
||||||
|
|
||||||
|
|
||||||
def create_and_process_template_response(
|
def create_and_process_template_response(template, context=None, use_middleware=True):
|
||||||
template, context=None, use_middleware=True
|
|
||||||
):
|
|
||||||
context = context if context is not None else Context({})
|
context = context if context is not None else Context({})
|
||||||
mock_template.render = lambda context, _: template.render(context)
|
mock_template.render = lambda context, _: template.render(context)
|
||||||
response = TemplateResponse(request, mock_template, context)
|
response = TemplateResponse(request, mock_template, context)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue