Reformat lines that became too long. And enforce 119 line length.

This commit is contained in:
Emil Stenström 2024-02-11 22:50:15 +01:00 committed by Emil Stenström
parent ef6a082238
commit 48fe8171b4
25 changed files with 314 additions and 733 deletions

View file

@ -11,4 +11,4 @@ repos:
rev: 7.0.0 rev: 7.0.0
hooks: hooks:
- id: flake8 - id: flake8
additional_dependencies: [flake8-pyproject]

View file

@ -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}%"
)

View file

@ -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()

View file

@ -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)

View file

@ -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):

View file

@ -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")

View file

@ -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):

View file

@ -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",

View file

@ -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

View file

@ -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',
]

View file

@ -2,5 +2,7 @@ django
tox tox
pytest pytest
flake8 flake8
flake8-pyproject
isort isort
pre-commit pre-commit
black

View file

@ -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

View file

@ -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)

View file

@ -1,12 +0,0 @@
[flake8]
ignore = E302,W503
max-line-length = 119
exclude =
migrations
__pycache__
manage.py
settings.py
env
.env
.venv
.tox

View file

@ -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",

View file

@ -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",

View file

@ -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)

View file

@ -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"})

View file

@ -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):

View file

@ -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)

View file

@ -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 %}"

View file

@ -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")

View file

@ -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"

View file

@ -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"]},

View file

@ -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)