mirror of
https://github.com/django-components/django-components.git
synced 2025-08-03 13:58:16 +00:00
[pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
This commit is contained in:
parent
15a0e66219
commit
0648ad9a93
14 changed files with 983 additions and 432 deletions
|
@ -3,10 +3,14 @@ from time import perf_counter
|
|||
from django.template import Context, Template
|
||||
from django.test import override_settings
|
||||
|
||||
from django_components.middleware import CSS_DEPENDENCY_PLACEHOLDER, JS_DEPENDENCY_PLACEHOLDER
|
||||
from tests.django_test_setup import * # NOQA
|
||||
from django_components import component
|
||||
from tests.testutils import Django30CompatibleSimpleTestCase as SimpleTestCase, create_and_process_template_response
|
||||
from django_components.middleware import (
|
||||
CSS_DEPENDENCY_PLACEHOLDER,
|
||||
JS_DEPENDENCY_PLACEHOLDER,
|
||||
)
|
||||
from tests.django_test_setup import * # NOQA
|
||||
from tests.testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
||||
from tests.testutils import create_and_process_template_response
|
||||
|
||||
|
||||
class SlottedComponent(component.Component):
|
||||
|
@ -31,14 +35,22 @@ class BreadcrumbComponent(component.Component):
|
|||
template_name = "mdn_component_template.html"
|
||||
|
||||
LINKS = [
|
||||
('https://developer.mozilla.org/en-US/docs/Learn',
|
||||
'Learn web development'),
|
||||
('https://developer.mozilla.org/en-US/docs/Learn/HTML',
|
||||
'Structuring the web with HTML'),
|
||||
('https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML',
|
||||
'Introduction to HTML'),
|
||||
('https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure',
|
||||
'Document and website structure')
|
||||
(
|
||||
"https://developer.mozilla.org/en-US/docs/Learn",
|
||||
"Learn web development",
|
||||
),
|
||||
(
|
||||
"https://developer.mozilla.org/en-US/docs/Learn/HTML",
|
||||
"Structuring the web with HTML",
|
||||
),
|
||||
(
|
||||
"https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML",
|
||||
"Introduction to HTML",
|
||||
),
|
||||
(
|
||||
"https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure",
|
||||
"Document and website structure",
|
||||
),
|
||||
]
|
||||
|
||||
def get_context_data(self, items):
|
||||
|
@ -46,7 +58,7 @@ class BreadcrumbComponent(component.Component):
|
|||
items = 4
|
||||
elif items < 0:
|
||||
items = 0
|
||||
return {'links': self.LINKS[:items - 1]}
|
||||
return {"links": self.LINKS[: items - 1]}
|
||||
|
||||
class Media:
|
||||
css = {"all": ["test.css"]}
|
||||
|
@ -57,13 +69,15 @@ EXPECTED_CSS = """<link href="test.css" media="all" rel="stylesheet">"""
|
|||
EXPECTED_JS = """<script src="test.js"></script>"""
|
||||
|
||||
|
||||
@override_settings(COMPONENTS={'RENDER_DEPENDENCIES': True})
|
||||
@override_settings(COMPONENTS={"RENDER_DEPENDENCIES": True})
|
||||
class RenderBenchmarks(SimpleTestCase):
|
||||
def setUp(self):
|
||||
component.registry.clear()
|
||||
component.registry.register('test_component', SlottedComponent)
|
||||
component.registry.register('inner_component', SimpleComponent)
|
||||
component.registry.register('breadcrumb_component', BreadcrumbComponent)
|
||||
component.registry.register("test_component", SlottedComponent)
|
||||
component.registry.register("inner_component", SimpleComponent)
|
||||
component.registry.register(
|
||||
"breadcrumb_component", BreadcrumbComponent
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def timed_loop(func, iterations=1000):
|
||||
|
@ -76,55 +90,81 @@ class RenderBenchmarks(SimpleTestCase):
|
|||
return total_elapsed * 1000 / iterations
|
||||
|
||||
def test_render_time_for_small_component(self):
|
||||
template = Template("{% load component_tags %}{% component_block 'test_component' %}"
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_block 'test_component' %}"
|
||||
"{% slot \"header\" %}{% component 'inner_component' variable='foo' %}{% endslot %}"
|
||||
"{% endcomponent_block %}", name='root')
|
||||
"{% endcomponent_block %}",
|
||||
name="root",
|
||||
)
|
||||
|
||||
print(f'{self.timed_loop(lambda: template.render(Context({})))} ms per iteration')
|
||||
print(
|
||||
f"{self.timed_loop(lambda: template.render(Context({})))} ms per iteration"
|
||||
)
|
||||
|
||||
def test_middleware_time_with_dependency_for_small_page(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'test_component' %}{% slot \"header\" %}"
|
||||
"{% component 'inner_component' variable='foo' %}{% endslot %}{% endcomponent_block %}",
|
||||
name='root')
|
||||
name="root",
|
||||
)
|
||||
# Sanity tests
|
||||
response_content = create_and_process_template_response(template)
|
||||
self.assertNotIn(CSS_DEPENDENCY_PLACEHOLDER, response_content)
|
||||
self.assertNotIn(JS_DEPENDENCY_PLACEHOLDER, response_content)
|
||||
self.assertIn('style.css', response_content)
|
||||
self.assertIn('script.js', response_content)
|
||||
self.assertIn("style.css", response_content)
|
||||
self.assertIn("script.js", response_content)
|
||||
|
||||
without_middleware = self.timed_loop(lambda: create_and_process_template_response(template,
|
||||
use_middleware=False))
|
||||
with_middleware = self.timed_loop(lambda: create_and_process_template_response(template, use_middleware=True))
|
||||
without_middleware = self.timed_loop(
|
||||
lambda: create_and_process_template_response(
|
||||
template, use_middleware=False
|
||||
)
|
||||
)
|
||||
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)
|
||||
|
||||
def test_render_time_with_dependency_for_large_page(self):
|
||||
from django.template.loader import get_template
|
||||
|
||||
template = get_template('mdn_complete_page.html')
|
||||
template = get_template("mdn_complete_page.html")
|
||||
response_content = create_and_process_template_response(template, {})
|
||||
self.assertNotIn(CSS_DEPENDENCY_PLACEHOLDER, response_content)
|
||||
self.assertNotIn(JS_DEPENDENCY_PLACEHOLDER, response_content)
|
||||
self.assertIn('test.css', response_content)
|
||||
self.assertIn('test.js', response_content)
|
||||
self.assertIn("test.css", response_content)
|
||||
self.assertIn("test.js", response_content)
|
||||
|
||||
without_middleware = self.timed_loop(
|
||||
lambda: create_and_process_template_response(template, {}, use_middleware=False))
|
||||
lambda: create_and_process_template_response(
|
||||
template, {}, use_middleware=False
|
||||
)
|
||||
)
|
||||
with_middleware = self.timed_loop(
|
||||
lambda: create_and_process_template_response(template, {}, use_middleware=True))
|
||||
lambda: create_and_process_template_response(
|
||||
template, {}, use_middleware=True
|
||||
)
|
||||
)
|
||||
|
||||
print('Large page middleware test')
|
||||
print("Large page middleware test")
|
||||
self.report_results(with_middleware, without_middleware)
|
||||
|
||||
@staticmethod
|
||||
def report_results(with_middleware, without_middleware):
|
||||
print(f'Middleware active\t\t{with_middleware:.3f} ms per iteration')
|
||||
print(f'Middleware inactive\t{without_middleware:.3f} ms per iteration')
|
||||
print(f"Middleware active\t\t{with_middleware:.3f} ms per iteration")
|
||||
print(
|
||||
f"Middleware inactive\t{without_middleware:.3f} ms per iteration"
|
||||
)
|
||||
time_difference = with_middleware - without_middleware
|
||||
if without_middleware > with_middleware:
|
||||
print(f'Decrease of {-100 * time_difference / with_middleware:.2f}%')
|
||||
print(
|
||||
f"Decrease of {-100 * time_difference / with_middleware:.2f}%"
|
||||
)
|
||||
else:
|
||||
print(f'Increase of {100 * time_difference / without_middleware:.2f}%')
|
||||
print(
|
||||
f"Increase of {100 * time_difference / without_middleware:.2f}%"
|
||||
)
|
||||
|
|
|
@ -9,10 +9,16 @@ from django.template.loader import get_template
|
|||
from django.utils.safestring import mark_safe
|
||||
|
||||
# Allow "component.AlreadyRegistered" instead of having to import these everywhere
|
||||
from django_components.component_registry import AlreadyRegistered, ComponentRegistry, NotRegistered # noqa
|
||||
from django_components.component_registry import ( # noqa
|
||||
AlreadyRegistered,
|
||||
ComponentRegistry,
|
||||
NotRegistered,
|
||||
)
|
||||
|
||||
TEMPLATE_CACHE_SIZE = getattr(settings, "COMPONENTS", {}).get('TEMPLATE_CACHE_SIZE', 128)
|
||||
ACTIVE_SLOT_CONTEXT_KEY = '_DJANGO_COMPONENTS_ACTIVE_SLOTS'
|
||||
TEMPLATE_CACHE_SIZE = getattr(settings, "COMPONENTS", {}).get(
|
||||
"TEMPLATE_CACHE_SIZE", 128
|
||||
)
|
||||
ACTIVE_SLOT_CONTEXT_KEY = "_DJANGO_COMPONENTS_ACTIVE_SLOTS"
|
||||
|
||||
|
||||
class SimplifiedInterfaceMediaDefiningClass(MediaDefiningClass):
|
||||
|
@ -54,7 +60,9 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
|
|||
|
||||
def get_template_name(self, context=None):
|
||||
if not self.template_name:
|
||||
raise ImproperlyConfigured(f'Template name is not set for Component {self.__class__.__name__}')
|
||||
raise ImproperlyConfigured(
|
||||
f"Template name is not set for Component {self.__class__.__name__}"
|
||||
)
|
||||
|
||||
return self.template_name
|
||||
|
||||
|
@ -75,13 +83,19 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
|
|||
|
||||
@staticmethod
|
||||
def slots_in_template(template):
|
||||
return {node.name: node.nodelist for node in template.template.nodelist if Component.is_slot_node(node)}
|
||||
return {
|
||||
node.name: node.nodelist
|
||||
for node in template.template.nodelist
|
||||
if Component.is_slot_node(node)
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def is_slot_node(node):
|
||||
return (isinstance(node, Node)
|
||||
return (
|
||||
isinstance(node, Node)
|
||||
and node.token.token_type == TokenType.BLOCK
|
||||
and node.token.split_contents()[0] == "slot")
|
||||
and node.token.split_contents()[0] == "slot"
|
||||
)
|
||||
|
||||
@lru_cache(maxsize=TEMPLATE_CACHE_SIZE)
|
||||
def get_processed_template(self, template_name):
|
||||
|
@ -117,16 +131,16 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
|
|||
return component_template
|
||||
|
||||
def render(self, context):
|
||||
if hasattr(self, 'context'):
|
||||
if hasattr(self, "context"):
|
||||
warnings.warn(
|
||||
f'{self.__class__.__name__}: `context` method is deprecated, use `get_context` instead',
|
||||
DeprecationWarning
|
||||
f"{self.__class__.__name__}: `context` method is deprecated, use `get_context` instead",
|
||||
DeprecationWarning,
|
||||
)
|
||||
|
||||
if hasattr(self, 'template'):
|
||||
if hasattr(self, "template"):
|
||||
warnings.warn(
|
||||
f'{self.__class__.__name__}: `template` method is deprecated, set `template_name` or override `get_template_name` instead',
|
||||
DeprecationWarning
|
||||
f"{self.__class__.__name__}: `template` method is deprecated, set `template_name` or override `get_template_name` instead",
|
||||
DeprecationWarning,
|
||||
)
|
||||
template_name = self.template(context)
|
||||
else:
|
||||
|
|
|
@ -12,7 +12,9 @@ class ComponentRegistry(object):
|
|||
|
||||
def register(self, name=None, component=None):
|
||||
if name in self._registry:
|
||||
raise AlreadyRegistered('The component "%s" is already registered' % name)
|
||||
raise AlreadyRegistered(
|
||||
'The component "%s" is already registered' % name
|
||||
)
|
||||
|
||||
self._registry[name] = component
|
||||
|
||||
|
|
|
@ -8,11 +8,13 @@ RENDERED_COMPONENTS_CONTEXT_KEY = "_COMPONENT_DEPENDENCIES"
|
|||
CSS_DEPENDENCY_PLACEHOLDER = '<link name="CSS_PLACEHOLDER">'
|
||||
JS_DEPENDENCY_PLACEHOLDER = '<script name="JS_PLACEHOLDER">'
|
||||
|
||||
SCRIPT_TAG_REGEX = re.compile('<script')
|
||||
COMPONENT_COMMENT_REGEX = re.compile(rb'<!-- _RENDERED (?P<name>\w+?) -->')
|
||||
PLACEHOLDER_REGEX = re.compile(rb'<!-- _RENDERED (?P<name>\w+?) -->'
|
||||
SCRIPT_TAG_REGEX = re.compile("<script")
|
||||
COMPONENT_COMMENT_REGEX = re.compile(rb"<!-- _RENDERED (?P<name>\w+?) -->")
|
||||
PLACEHOLDER_REGEX = re.compile(
|
||||
rb"<!-- _RENDERED (?P<name>\w+?) -->"
|
||||
rb'|<link name="CSS_PLACEHOLDER">'
|
||||
rb'|<script name="JS_PLACEHOLDER">')
|
||||
rb'|<script name="JS_PLACEHOLDER">'
|
||||
)
|
||||
|
||||
|
||||
class ComponentDependencyMiddleware:
|
||||
|
@ -25,9 +27,13 @@ class ComponentDependencyMiddleware:
|
|||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
if getattr(settings, "COMPONENTS", {}).get('RENDER_DEPENDENCIES', False)\
|
||||
and not isinstance(response, StreamingHttpResponse)\
|
||||
and response['Content-Type'].startswith('text/html'):
|
||||
if (
|
||||
getattr(settings, "COMPONENTS", {}).get(
|
||||
"RENDER_DEPENDENCIES", False
|
||||
)
|
||||
and not isinstance(response, StreamingHttpResponse)
|
||||
and response["Content-Type"].startswith("text/html")
|
||||
):
|
||||
response.content = process_response_content(response.content)
|
||||
return response
|
||||
|
||||
|
@ -35,12 +41,23 @@ class ComponentDependencyMiddleware:
|
|||
def process_response_content(content):
|
||||
from django_components.component import registry
|
||||
|
||||
component_names_seen = {match.group('name') for match in COMPONENT_COMMENT_REGEX.finditer(content)}
|
||||
all_components = [registry.get(name.decode('utf-8'))('') for name in component_names_seen]
|
||||
component_names_seen = {
|
||||
match.group("name")
|
||||
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)
|
||||
js_dependencies = b''.join(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)
|
||||
js_dependencies = b"".join(
|
||||
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
|
||||
)
|
||||
|
||||
|
||||
def add_module_attribute_to_scripts(scripts):
|
||||
|
@ -51,8 +68,8 @@ class DependencyReplacer:
|
|||
"""Replacer for use in re.sub that replaces the first placeholder CSS and JS
|
||||
tags it encounters and removes any subsequent ones."""
|
||||
|
||||
CSS_PLACEHOLDER = bytes(CSS_DEPENDENCY_PLACEHOLDER, encoding='utf-8')
|
||||
JS_PLACEHOLDER = bytes(JS_DEPENDENCY_PLACEHOLDER, encoding='utf-8')
|
||||
CSS_PLACEHOLDER = bytes(CSS_DEPENDENCY_PLACEHOLDER, encoding="utf-8")
|
||||
JS_PLACEHOLDER = bytes(JS_DEPENDENCY_PLACEHOLDER, encoding="utf-8")
|
||||
|
||||
def __init__(self, css_string, js_string):
|
||||
self.js_string = js_string
|
||||
|
@ -64,7 +81,7 @@ class DependencyReplacer:
|
|||
elif match[0] == self.JS_PLACEHOLDER:
|
||||
replacement, self.js_string = self.js_string, b""
|
||||
else:
|
||||
replacement = b''
|
||||
replacement = b""
|
||||
return replacement
|
||||
|
||||
|
||||
|
|
|
@ -8,4 +8,4 @@ from django.template.utils import get_app_template_dirs
|
|||
|
||||
class Loader(FilesystemLoader):
|
||||
def get_dirs(self):
|
||||
return get_app_template_dirs('components')
|
||||
return get_app_template_dirs("components")
|
||||
|
|
|
@ -7,7 +7,10 @@ from django.template.library import parse_bits
|
|||
from django.utils.safestring import mark_safe
|
||||
|
||||
from django_components.component import ACTIVE_SLOT_CONTEXT_KEY, registry
|
||||
from django_components.middleware import CSS_DEPENDENCY_PLACEHOLDER, JS_DEPENDENCY_PLACEHOLDER
|
||||
from django_components.middleware import (
|
||||
CSS_DEPENDENCY_PLACEHOLDER,
|
||||
JS_DEPENDENCY_PLACEHOLDER,
|
||||
)
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
@ -29,7 +32,9 @@ def component_dependencies_tag():
|
|||
"""Marks location where CSS link and JS script tags should be rendered."""
|
||||
|
||||
if is_dependency_middleware_active():
|
||||
return mark_safe(CSS_DEPENDENCY_PLACEHOLDER + JS_DEPENDENCY_PLACEHOLDER)
|
||||
return mark_safe(
|
||||
CSS_DEPENDENCY_PLACEHOLDER + JS_DEPENDENCY_PLACEHOLDER
|
||||
)
|
||||
else:
|
||||
rendered_dependencies = []
|
||||
for component in get_components_from_registry(registry):
|
||||
|
@ -66,12 +71,19 @@ def component_js_dependencies_tag():
|
|||
return mark_safe("\n".join(rendered_dependencies))
|
||||
|
||||
|
||||
@register.tag(name='component')
|
||||
@register.tag(name="component")
|
||||
def do_component(parser, token):
|
||||
bits = token.split_contents()
|
||||
bits, isolated_context = check_for_isolated_context_keyword(bits)
|
||||
component, context_args, context_kwargs = parse_component_with_args(parser, bits, 'component')
|
||||
return ComponentNode(component, context_args, context_kwargs, isolated_context=isolated_context)
|
||||
component, context_args, context_kwargs = parse_component_with_args(
|
||||
parser, bits, "component"
|
||||
)
|
||||
return ComponentNode(
|
||||
component,
|
||||
context_args,
|
||||
context_kwargs,
|
||||
isolated_context=isolated_context,
|
||||
)
|
||||
|
||||
|
||||
class SlotNode(Node):
|
||||
|
@ -92,17 +104,25 @@ class SlotNode(Node):
|
|||
cloned_node.parent_component = self.parent_component
|
||||
cloned_node.context = context
|
||||
|
||||
with context.update({'slot': cloned_node}):
|
||||
with context.update({"slot": cloned_node}):
|
||||
return self.get_nodelist(context).render(context)
|
||||
|
||||
def get_nodelist(self, context):
|
||||
if ACTIVE_SLOT_CONTEXT_KEY not in context:
|
||||
raise TemplateSyntaxError(f'Attempted to render SlotNode {self.name} outside of a parent Component or '
|
||||
'without access to context provided by its parent Component. This will not'
|
||||
'work properly.')
|
||||
raise TemplateSyntaxError(
|
||||
f"Attempted to render SlotNode {self.name} outside of a parent Component or "
|
||||
"without access to context provided by its parent Component. This will not"
|
||||
"work properly."
|
||||
)
|
||||
|
||||
overriding_nodelist = context[ACTIVE_SLOT_CONTEXT_KEY].get(self.name, None)
|
||||
return overriding_nodelist if overriding_nodelist is not None else self.nodelist
|
||||
overriding_nodelist = context[ACTIVE_SLOT_CONTEXT_KEY].get(
|
||||
self.name, None
|
||||
)
|
||||
return (
|
||||
overriding_nodelist
|
||||
if overriding_nodelist is not None
|
||||
else self.nodelist
|
||||
)
|
||||
|
||||
def super(self):
|
||||
"""Render default slot content."""
|
||||
|
@ -125,9 +145,18 @@ def do_slot(parser, token, component=None):
|
|||
class ComponentNode(Node):
|
||||
class InvalidSlot:
|
||||
def super(self):
|
||||
raise TemplateSyntaxError('slot.super may only be called within a {% slot %}/{% endslot %} block.')
|
||||
raise TemplateSyntaxError(
|
||||
"slot.super may only be called within a {% slot %}/{% endslot %} block."
|
||||
)
|
||||
|
||||
def __init__(self, component, context_args, context_kwargs, slots=None, isolated_context=False):
|
||||
def __init__(
|
||||
self,
|
||||
component,
|
||||
context_args,
|
||||
context_kwargs,
|
||||
slots=None,
|
||||
isolated_context=False,
|
||||
):
|
||||
self.context_args = context_args or []
|
||||
self.context_kwargs = context_kwargs or {}
|
||||
self.component, self.isolated_context = component, isolated_context
|
||||
|
@ -139,17 +168,26 @@ class ComponentNode(Node):
|
|||
self.should_render_dependencies = is_dependency_middleware_active()
|
||||
|
||||
def __repr__(self):
|
||||
return "<Component Node: %s. Contents: %r>" % (self.component,
|
||||
getattr(self.component.instance_template, 'nodelist', None))
|
||||
return "<Component Node: %s. Contents: %r>" % (
|
||||
self.component,
|
||||
getattr(self.component.instance_template, "nodelist", None),
|
||||
)
|
||||
|
||||
def render(self, context):
|
||||
self.component.outer_context = context.flatten()
|
||||
|
||||
# Resolve FilterExpressions and Variables that were passed as args to the component, then call component's
|
||||
# context method to get values to insert into the context
|
||||
resolved_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()}
|
||||
component_context = self.component.get_context_data(*resolved_context_args, **resolved_context_kwargs)
|
||||
resolved_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()
|
||||
}
|
||||
component_context = self.component.get_context_data(
|
||||
*resolved_context_args, **resolved_context_kwargs
|
||||
)
|
||||
|
||||
# Create a fresh context if requested
|
||||
if self.isolated_context:
|
||||
|
@ -158,7 +196,10 @@ class ComponentNode(Node):
|
|||
with context.update(component_context):
|
||||
rendered_component = self.component.render(context)
|
||||
if self.should_render_dependencies:
|
||||
return f'<!-- _RENDERED {self.component._component_name} -->' + rendered_component
|
||||
return (
|
||||
f"<!-- _RENDERED {self.component._component_name} -->"
|
||||
+ rendered_component
|
||||
)
|
||||
else:
|
||||
return rendered_component
|
||||
|
||||
|
@ -181,12 +222,20 @@ def do_component_block(parser, token):
|
|||
bits = token.split_contents()
|
||||
bits, isolated_context = check_for_isolated_context_keyword(bits)
|
||||
|
||||
component, context_args, context_kwargs = parse_component_with_args(parser, bits, 'component_block')
|
||||
component, context_args, context_kwargs = parse_component_with_args(
|
||||
parser, bits, "component_block"
|
||||
)
|
||||
|
||||
return ComponentNode(component, context_args, context_kwargs,
|
||||
slots=[do_slot(parser, slot_token, component=component)
|
||||
for slot_token in slot_tokens(parser)],
|
||||
isolated_context=isolated_context)
|
||||
return ComponentNode(
|
||||
component,
|
||||
context_args,
|
||||
context_kwargs,
|
||||
slots=[
|
||||
do_slot(parser, slot_token, component=component)
|
||||
for slot_token in slot_tokens(parser)
|
||||
],
|
||||
isolated_context=isolated_context,
|
||||
)
|
||||
|
||||
|
||||
def slot_tokens(parser):
|
||||
|
@ -195,28 +244,37 @@ def slot_tokens(parser):
|
|||
Raises TemplateSyntaxError if there are other content tokens or if there is no endcomponent_block token."""
|
||||
|
||||
def is_whitespace(token):
|
||||
return token.token_type == TokenType.TEXT and not token.contents.strip()
|
||||
return (
|
||||
token.token_type == TokenType.TEXT and not token.contents.strip()
|
||||
)
|
||||
|
||||
def is_block_tag(token, name):
|
||||
return token.token_type == TokenType.BLOCK and token.split_contents()[0] == name
|
||||
return (
|
||||
token.token_type == TokenType.BLOCK
|
||||
and token.split_contents()[0] == name
|
||||
)
|
||||
|
||||
while True:
|
||||
try:
|
||||
token = parser.next_token()
|
||||
except IndexError:
|
||||
raise TemplateSyntaxError('Unclosed component_block tag')
|
||||
if is_block_tag(token, name='endcomponent_block'):
|
||||
raise TemplateSyntaxError("Unclosed component_block tag")
|
||||
if is_block_tag(token, name="endcomponent_block"):
|
||||
return
|
||||
elif is_block_tag(token, name='slot'):
|
||||
elif is_block_tag(token, name="slot"):
|
||||
yield token
|
||||
elif not is_whitespace(token) and token.token_type != TokenType.COMMENT:
|
||||
raise TemplateSyntaxError(f'Content tokens in component blocks must be inside of slot tags: {token}')
|
||||
elif (
|
||||
not is_whitespace(token) and token.token_type != TokenType.COMMENT
|
||||
):
|
||||
raise TemplateSyntaxError(
|
||||
f"Content tokens in component blocks must be inside of slot tags: {token}"
|
||||
)
|
||||
|
||||
|
||||
def check_for_isolated_context_keyword(bits):
|
||||
"""Return True and strip the last word if token ends with 'only' keyword."""
|
||||
|
||||
if bits[-1] == 'only':
|
||||
if bits[-1] == "only":
|
||||
return bits[:-1], True
|
||||
return bits, False
|
||||
|
||||
|
@ -235,20 +293,26 @@ def parse_component_with_args(parser, bits, tag_name):
|
|||
kwonly_defaults=None,
|
||||
)
|
||||
|
||||
assert tag_name == tag_args[0].token, "Internal error: Expected tag_name to be {}, but it was {}".format(
|
||||
tag_name, tag_args[0].token)
|
||||
if len(tag_args) > 1: # At least one position arg, so take the first as the component name
|
||||
assert (
|
||||
tag_name == tag_args[0].token
|
||||
), "Internal error: Expected tag_name to be {}, but it was {}".format(
|
||||
tag_name, tag_args[0].token
|
||||
)
|
||||
if (
|
||||
len(tag_args) > 1
|
||||
): # At least one position arg, so take the first as the component name
|
||||
component_name = tag_args[1].token
|
||||
context_args = tag_args[2:]
|
||||
context_kwargs = tag_kwargs
|
||||
else: # No positional args, so look for component name as keyword arg
|
||||
try:
|
||||
component_name = tag_kwargs.pop('name').token
|
||||
component_name = tag_kwargs.pop("name").token
|
||||
context_args = []
|
||||
context_kwargs = tag_kwargs
|
||||
except IndexError:
|
||||
raise TemplateSyntaxError(
|
||||
"Call the '%s' tag with a component name as the first parameter" % tag_name
|
||||
"Call the '%s' tag with a component name as the first parameter"
|
||||
% tag_name
|
||||
)
|
||||
|
||||
if not is_wrapped_in_quotes(component_name):
|
||||
|
@ -256,7 +320,7 @@ def parse_component_with_args(parser, bits, tag_name):
|
|||
"Component name '%s' should be in quotes" % component_name
|
||||
)
|
||||
|
||||
trimmed_component_name = component_name[1: -1]
|
||||
trimmed_component_name = component_name[1:-1]
|
||||
component_class = registry.get(trimmed_component_name)
|
||||
component = component_class(trimmed_component_name)
|
||||
|
||||
|
@ -266,7 +330,11 @@ def parse_component_with_args(parser, bits, tag_name):
|
|||
def safe_resolve(context_item, context):
|
||||
"""Resolve FilterExpressions and Variables in context if possible. Return other items unchanged."""
|
||||
|
||||
return context_item.resolve(context) if hasattr(context_item, 'resolve') else context_item
|
||||
return (
|
||||
context_item.resolve(context)
|
||||
if hasattr(context_item, "resolve")
|
||||
else context_item
|
||||
)
|
||||
|
||||
|
||||
def is_wrapped_in_quotes(s):
|
||||
|
@ -274,4 +342,6 @@ def is_wrapped_in_quotes(s):
|
|||
|
||||
|
||||
def is_dependency_middleware_active():
|
||||
return getattr(settings, "COMPONENTS", {}).get('RENDER_DEPENDENCIES', False)
|
||||
return getattr(settings, "COMPONENTS", {}).get(
|
||||
"RENDER_DEPENDENCIES", False
|
||||
)
|
||||
|
|
6
setup.py
6
setup.py
|
@ -3,14 +3,16 @@ import os
|
|||
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
VERSION = '0.16'
|
||||
VERSION = "0.16"
|
||||
|
||||
setup(
|
||||
name="django_components",
|
||||
packages=find_packages(exclude=["tests"]),
|
||||
version=VERSION,
|
||||
description="A way to create simple reusable template components in Django.",
|
||||
long_description=open(os.path.join(os.path.dirname(__file__), "README.md")).read(),
|
||||
long_description=open(
|
||||
os.path.join(os.path.dirname(__file__), "README.md")
|
||||
).read(),
|
||||
long_description_content_type="text/markdown",
|
||||
author=u"Emil Stenström",
|
||||
author_email="emil@emilstenstrom.se",
|
||||
|
|
|
@ -3,15 +3,17 @@ from django.conf import settings
|
|||
|
||||
if not settings.configured:
|
||||
settings.configure(
|
||||
INSTALLED_APPS=('django_components',),
|
||||
TEMPLATES=[{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': ["tests/templates/"],
|
||||
}],
|
||||
COMPONENTS={
|
||||
'TEMPLATE_CACHE_SIZE': 128
|
||||
},
|
||||
MIDDLEWARE=['django_components.middleware.ComponentDependencyMiddleware'],
|
||||
INSTALLED_APPS=("django_components",),
|
||||
TEMPLATES=[
|
||||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": ["tests/templates/"],
|
||||
}
|
||||
],
|
||||
COMPONENTS={"TEMPLATE_CACHE_SIZE": 128},
|
||||
MIDDLEWARE=[
|
||||
"django_components.middleware.ComponentDependencyMiddleware"
|
||||
],
|
||||
DATABASES={},
|
||||
)
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from textwrap import dedent
|
||||
|
||||
from django.template import Context, Template
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from .django_test_setup import * # NOQA
|
||||
from django.template import Context, Template
|
||||
|
||||
from django_components import component
|
||||
|
||||
from .django_test_setup import * # NOQA
|
||||
from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
||||
|
||||
|
||||
|
@ -33,14 +33,24 @@ class ComponentTest(SimpleTestCase):
|
|||
comp = SimpleComponent("simple_component")
|
||||
context = Context(comp.get_context_data(variable="test"))
|
||||
|
||||
self.assertHTMLEqual(comp.render_dependencies(), dedent("""
|
||||
self.assertHTMLEqual(
|
||||
comp.render_dependencies(),
|
||||
dedent(
|
||||
"""
|
||||
<link href="style.css" type="text/css" media="all" rel="stylesheet">
|
||||
<script src="script.js"></script>
|
||||
""").strip())
|
||||
"""
|
||||
).strip(),
|
||||
)
|
||||
|
||||
self.assertHTMLEqual(comp.render(context), dedent("""
|
||||
self.assertHTMLEqual(
|
||||
comp.render(context),
|
||||
dedent(
|
||||
"""
|
||||
Variable: <strong>test</strong>
|
||||
""").lstrip())
|
||||
"""
|
||||
).lstrip(),
|
||||
)
|
||||
|
||||
def test_component_with_list_of_styles(self):
|
||||
class MultistyleComponent(component.Component):
|
||||
|
@ -50,12 +60,17 @@ class ComponentTest(SimpleTestCase):
|
|||
|
||||
comp = MultistyleComponent("multistyle_component")
|
||||
|
||||
self.assertHTMLEqual(comp.render_dependencies(), dedent("""
|
||||
self.assertHTMLEqual(
|
||||
comp.render_dependencies(),
|
||||
dedent(
|
||||
"""
|
||||
<link href="style.css" type="text/css" media="all" rel="stylesheet">
|
||||
<link href="style2.css" type="text/css" media="all" rel="stylesheet">
|
||||
<script src="script.js"></script>
|
||||
<script src="script2.js"></script>
|
||||
""").strip())
|
||||
"""
|
||||
).strip(),
|
||||
)
|
||||
|
||||
def test_component_with_filtered_template(self):
|
||||
class FilteredComponent(component.Component):
|
||||
|
@ -70,15 +85,25 @@ class ComponentTest(SimpleTestCase):
|
|||
comp = FilteredComponent("filtered_component")
|
||||
context = Context(comp.get_context_data(var1="test1", var2="test2"))
|
||||
|
||||
self.assertHTMLEqual(comp.render(context), dedent("""
|
||||
self.assertHTMLEqual(
|
||||
comp.render(context),
|
||||
dedent(
|
||||
"""
|
||||
Var1: <strong>test1</strong>
|
||||
Var2 (uppercased): <strong>TEST2</strong>
|
||||
""").lstrip())
|
||||
"""
|
||||
).lstrip(),
|
||||
)
|
||||
|
||||
def test_component_with_dynamic_template(self):
|
||||
class SvgComponent(component.Component):
|
||||
def get_context_data(self, name, css_class="", title="", **attrs):
|
||||
return {"name": name, "css_class": css_class, "title": title, **attrs}
|
||||
return {
|
||||
"name": name,
|
||||
"css_class": css_class,
|
||||
"title": title,
|
||||
**attrs,
|
||||
}
|
||||
|
||||
def get_template_name(self, context):
|
||||
return f"svg_{context['name']}.svg"
|
||||
|
@ -86,15 +111,19 @@ class ComponentTest(SimpleTestCase):
|
|||
comp = SvgComponent("svg_component")
|
||||
self.assertHTMLEqual(
|
||||
comp.render(Context(comp.get_context_data(name="dynamic1"))),
|
||||
dedent("""\
|
||||
dedent(
|
||||
"""\
|
||||
<svg>Dynamic1</svg>
|
||||
""")
|
||||
"""
|
||||
),
|
||||
)
|
||||
self.assertHTMLEqual(
|
||||
comp.render(Context(comp.get_context_data(name="dynamic2"))),
|
||||
dedent("""\
|
||||
dedent(
|
||||
"""\
|
||||
<svg>Dynamic2</svg>
|
||||
""")
|
||||
"""
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
@ -108,10 +137,12 @@ class ComponentMediaTests(SimpleTestCase):
|
|||
comp = SimpleComponent("")
|
||||
self.assertHTMLEqual(
|
||||
comp.render_dependencies(),
|
||||
dedent("""\
|
||||
dedent(
|
||||
"""\
|
||||
<link href="path/to/style.css" type="text/css" media="all" rel="stylesheet">
|
||||
<script src="path/to/script.js"></script>
|
||||
""")
|
||||
"""
|
||||
),
|
||||
)
|
||||
|
||||
def test_component_media_with_lists(self):
|
||||
|
@ -123,11 +154,13 @@ class ComponentMediaTests(SimpleTestCase):
|
|||
comp = SimpleComponent("")
|
||||
self.assertHTMLEqual(
|
||||
comp.render_dependencies(),
|
||||
dedent("""\
|
||||
dedent(
|
||||
"""\
|
||||
<link href="path/to/style.css" type="text/css" media="all" rel="stylesheet">
|
||||
<link href="path/to/style2.css" type="text/css" media="all" rel="stylesheet">
|
||||
<script src="path/to/script.js"></script>
|
||||
""")
|
||||
"""
|
||||
),
|
||||
)
|
||||
|
||||
def test_component_media_with_dict_and_list(self):
|
||||
|
@ -143,12 +176,14 @@ class ComponentMediaTests(SimpleTestCase):
|
|||
comp = SimpleComponent("")
|
||||
self.assertHTMLEqual(
|
||||
comp.render_dependencies(),
|
||||
dedent("""\
|
||||
dedent(
|
||||
"""\
|
||||
<link href="path/to/style.css" type="text/css" media="all" rel="stylesheet">
|
||||
<link href="path/to/style2.css" type="text/css" media="print" rel="stylesheet">
|
||||
<link href="path/to/style3.css" type="text/css" media="screen" rel="stylesheet">
|
||||
<script src="path/to/script.js"></script>
|
||||
""")
|
||||
"""
|
||||
),
|
||||
)
|
||||
|
||||
def test_component_media_with_dict_with_list_and_list(self):
|
||||
|
@ -160,10 +195,12 @@ class ComponentMediaTests(SimpleTestCase):
|
|||
comp = SimpleComponent("")
|
||||
self.assertHTMLEqual(
|
||||
comp.render_dependencies(),
|
||||
dedent("""\
|
||||
dedent(
|
||||
"""\
|
||||
<link href="path/to/style.css" type="text/css" media="all" rel="stylesheet">
|
||||
<script src="path/to/script.js"></script>
|
||||
""")
|
||||
"""
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
@ -172,7 +209,7 @@ class ComponentIsolationTests(SimpleTestCase):
|
|||
class SlottedComponent(component.Component):
|
||||
template_name = "slotted_template.html"
|
||||
|
||||
component.registry.register('test', SlottedComponent)
|
||||
component.registry.register("test", SlottedComponent)
|
||||
|
||||
def test_instances_of_component_do_not_share_slots(self):
|
||||
template = Template(
|
||||
|
@ -211,17 +248,17 @@ class ComponentIsolationTests(SimpleTestCase):
|
|||
<main>Default main</main>
|
||||
<footer>Override footer</footer>
|
||||
</custom-template>
|
||||
"""
|
||||
""",
|
||||
)
|
||||
|
||||
|
||||
class RecursiveSlotNameTest(SimpleTestCase):
|
||||
def setUp(self):
|
||||
@component.register('outer')
|
||||
@component.register("outer")
|
||||
class OuterComponent(component.Component):
|
||||
template_name = "slotted_template.html"
|
||||
|
||||
@component.register('inner')
|
||||
@component.register("inner")
|
||||
class InnerComponent(component.Component):
|
||||
template_name = "slotted_template.html"
|
||||
|
||||
|
@ -251,5 +288,5 @@ class RecursiveSlotNameTest(SimpleTestCase):
|
|||
<main>Default main</main>
|
||||
<footer>Default footer</footer>
|
||||
</custom-template>
|
||||
"""
|
||||
""",
|
||||
)
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
from django.template import Context, Template
|
||||
|
||||
from .django_test_setup import * # NOQA
|
||||
|
||||
from django_components import component
|
||||
|
||||
from .django_test_setup import * # NOQA
|
||||
from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
||||
|
||||
|
||||
|
@ -15,16 +14,14 @@ class SimpleComponent(component.Component):
|
|||
|
||||
@staticmethod
|
||||
def expected_output(variable_value):
|
||||
return 'Variable: < strong > {} < / strong >'.format(variable_value)
|
||||
return "Variable: < strong > {} < / strong >".format(variable_value)
|
||||
|
||||
|
||||
class ParentComponent(component.Component):
|
||||
template_name = "parent_template.html"
|
||||
|
||||
def get_context_data(self):
|
||||
return {
|
||||
"shadowing_variable": 'NOT SHADOWED'
|
||||
}
|
||||
return {"shadowing_variable": "NOT SHADOWED"}
|
||||
|
||||
|
||||
class ParentComponentWithArgs(component.Component):
|
||||
|
@ -40,9 +37,9 @@ class VariableDisplay(component.Component):
|
|||
def get_context_data(self, shadowing_variable=None, new_variable=None):
|
||||
context = {}
|
||||
if shadowing_variable is not None:
|
||||
context['shadowing_variable'] = shadowing_variable
|
||||
context["shadowing_variable"] = shadowing_variable
|
||||
if new_variable is not None:
|
||||
context['unique_variable'] = new_variable
|
||||
context["unique_variable"] = new_variable
|
||||
return context
|
||||
|
||||
|
||||
|
@ -51,15 +48,11 @@ class IncrementerComponent(component.Component):
|
|||
|
||||
def get_context_data(self, value=0):
|
||||
value = int(value)
|
||||
if hasattr(self, 'call_count'):
|
||||
if hasattr(self, "call_count"):
|
||||
self.call_count += 1
|
||||
else:
|
||||
self.call_count = 1
|
||||
return {
|
||||
"value": value + 1,
|
||||
"calls": self.call_count
|
||||
}
|
||||
|
||||
return {"value": value + 1, "calls": self.call_count}
|
||||
|
||||
|
||||
class OuterContextComponent(component.Component):
|
||||
|
@ -69,212 +62,394 @@ class OuterContextComponent(component.Component):
|
|||
return self.outer_context
|
||||
|
||||
|
||||
component.registry.register(name='parent_component', component=ParentComponent)
|
||||
component.registry.register(name='parent_with_args', component=ParentComponentWithArgs)
|
||||
component.registry.register(name='variable_display', component=VariableDisplay)
|
||||
component.registry.register(name='incrementer', component=IncrementerComponent)
|
||||
component.registry.register(name='simple_component', component=SimpleComponent)
|
||||
component.registry.register(name='outer_context_component', component=OuterContextComponent)
|
||||
component.registry.register(name="parent_component", component=ParentComponent)
|
||||
component.registry.register(
|
||||
name="parent_with_args", component=ParentComponentWithArgs
|
||||
)
|
||||
component.registry.register(name="variable_display", component=VariableDisplay)
|
||||
component.registry.register(name="incrementer", component=IncrementerComponent)
|
||||
component.registry.register(name="simple_component", component=SimpleComponent)
|
||||
component.registry.register(
|
||||
name="outer_context_component", component=OuterContextComponent
|
||||
)
|
||||
|
||||
|
||||
class ContextTests(SimpleTestCase):
|
||||
def test_nested_component_context_shadows_parent_with_unfilled_slots_and_component_tag(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'parent_component' %}")
|
||||
def test_nested_component_context_shadows_parent_with_unfilled_slots_and_component_tag(
|
||||
self,
|
||||
):
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'parent_component' %}"
|
||||
)
|
||||
rendered = template.render(Context())
|
||||
|
||||
self.assertIn('<h1>Shadowing variable = override</h1>', rendered, rendered)
|
||||
self.assertIn('<h1>Shadowing variable = slot_default_override</h1>', rendered, rendered)
|
||||
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</h1>', rendered, rendered)
|
||||
self.assertIn(
|
||||
"<h1>Shadowing variable = override</h1>", rendered, rendered
|
||||
)
|
||||
self.assertIn(
|
||||
"<h1>Shadowing variable = slot_default_override</h1>",
|
||||
rendered,
|
||||
rendered,
|
||||
)
|
||||
self.assertNotIn(
|
||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
||||
)
|
||||
|
||||
def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_tag(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component name='parent_component' %}")
|
||||
def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_tag(
|
||||
self,
|
||||
):
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component name='parent_component' %}"
|
||||
)
|
||||
rendered = template.render(Context())
|
||||
|
||||
self.assertIn('<h1>Uniquely named variable = unique_val</h1>', rendered, rendered)
|
||||
self.assertIn('<h1>Uniquely named variable = slot_default_unique</h1>', rendered, rendered)
|
||||
self.assertIn(
|
||||
"<h1>Uniquely named variable = unique_val</h1>", rendered, rendered
|
||||
)
|
||||
self.assertIn(
|
||||
"<h1>Uniquely named variable = slot_default_unique</h1>",
|
||||
rendered,
|
||||
rendered,
|
||||
)
|
||||
|
||||
def test_nested_component_context_shadows_parent_with_unfilled_slots_and_component_block_tag(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_component' %}{% endcomponent_block %}")
|
||||
def test_nested_component_context_shadows_parent_with_unfilled_slots_and_component_block_tag(
|
||||
self,
|
||||
):
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_component' %}{% endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(Context())
|
||||
|
||||
self.assertIn('<h1>Shadowing variable = override</h1>', rendered, rendered)
|
||||
self.assertIn('<h1>Shadowing variable = slot_default_override</h1>', rendered, rendered)
|
||||
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</h1>', rendered, rendered)
|
||||
self.assertIn(
|
||||
"<h1>Shadowing variable = override</h1>", rendered, rendered
|
||||
)
|
||||
self.assertIn(
|
||||
"<h1>Shadowing variable = slot_default_override</h1>",
|
||||
rendered,
|
||||
rendered,
|
||||
)
|
||||
self.assertNotIn(
|
||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
||||
)
|
||||
|
||||
def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_block_tag(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_component' %}{% endcomponent_block %}")
|
||||
def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_block_tag(
|
||||
self,
|
||||
):
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_component' %}{% endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(Context())
|
||||
|
||||
self.assertIn('<h1>Uniquely named variable = unique_val</h1>', rendered, rendered)
|
||||
self.assertIn('<h1>Uniquely named variable = slot_default_unique</h1>', rendered, rendered)
|
||||
self.assertIn(
|
||||
"<h1>Uniquely named variable = unique_val</h1>", rendered, rendered
|
||||
)
|
||||
self.assertIn(
|
||||
"<h1>Uniquely named variable = slot_default_unique</h1>",
|
||||
rendered,
|
||||
rendered,
|
||||
)
|
||||
|
||||
def test_nested_component_context_shadows_parent_with_filled_slots(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_component' %}"
|
||||
"{% slot 'content' %}{% component name='variable_display' "
|
||||
"shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}"
|
||||
"{% endcomponent_block %}")
|
||||
"{% endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(Context())
|
||||
|
||||
self.assertIn('<h1>Shadowing variable = override</h1>', rendered, rendered)
|
||||
self.assertIn('<h1>Shadowing variable = shadow_from_slot</h1>', rendered, rendered)
|
||||
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</h1>', rendered, rendered)
|
||||
self.assertIn(
|
||||
"<h1>Shadowing variable = override</h1>", rendered, rendered
|
||||
)
|
||||
self.assertIn(
|
||||
"<h1>Shadowing variable = shadow_from_slot</h1>",
|
||||
rendered,
|
||||
rendered,
|
||||
)
|
||||
self.assertNotIn(
|
||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
||||
)
|
||||
|
||||
def test_nested_component_instances_have_unique_context_with_filled_slots(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
def test_nested_component_instances_have_unique_context_with_filled_slots(
|
||||
self,
|
||||
):
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_component' %}"
|
||||
"{% slot 'content' %}{% component name='variable_display' "
|
||||
"shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}"
|
||||
"{% endcomponent_block %}")
|
||||
"{% endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(Context())
|
||||
|
||||
self.assertIn('<h1>Uniquely named variable = unique_val</h1>', rendered, rendered)
|
||||
self.assertIn('<h1>Uniquely named variable = unique_from_slot</h1>', rendered, rendered)
|
||||
self.assertIn(
|
||||
"<h1>Uniquely named variable = unique_val</h1>", rendered, rendered
|
||||
)
|
||||
self.assertIn(
|
||||
"<h1>Uniquely named variable = unique_from_slot</h1>",
|
||||
rendered,
|
||||
rendered,
|
||||
)
|
||||
|
||||
def test_nested_component_context_shadows_outer_context_with_unfilled_slots_and_component_tag(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component name='parent_component' %}")
|
||||
rendered = template.render(Context({'shadowing_variable': 'NOT SHADOWED'}))
|
||||
def test_nested_component_context_shadows_outer_context_with_unfilled_slots_and_component_tag(
|
||||
self,
|
||||
):
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component name='parent_component' %}"
|
||||
)
|
||||
rendered = template.render(
|
||||
Context({"shadowing_variable": "NOT SHADOWED"})
|
||||
)
|
||||
|
||||
self.assertIn('<h1>Shadowing variable = override</h1>', rendered, rendered)
|
||||
self.assertIn('<h1>Shadowing variable = slot_default_override</h1>', rendered, rendered)
|
||||
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</h1>', rendered, rendered)
|
||||
self.assertIn(
|
||||
"<h1>Shadowing variable = override</h1>", rendered, rendered
|
||||
)
|
||||
self.assertIn(
|
||||
"<h1>Shadowing variable = slot_default_override</h1>",
|
||||
rendered,
|
||||
rendered,
|
||||
)
|
||||
self.assertNotIn(
|
||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
||||
)
|
||||
|
||||
def test_nested_component_context_shadows_outer_context_with_unfilled_slots_and_component_block_tag(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_component' %}{% endcomponent_block %}")
|
||||
rendered = template.render(Context({'shadowing_variable': 'NOT SHADOWED'}))
|
||||
def test_nested_component_context_shadows_outer_context_with_unfilled_slots_and_component_block_tag(
|
||||
self,
|
||||
):
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_component' %}{% endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(
|
||||
Context({"shadowing_variable": "NOT SHADOWED"})
|
||||
)
|
||||
|
||||
self.assertIn('<h1>Shadowing variable = override</h1>', rendered, rendered)
|
||||
self.assertIn('<h1>Shadowing variable = slot_default_override</h1>', rendered, rendered)
|
||||
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</h1>', rendered, rendered)
|
||||
self.assertIn(
|
||||
"<h1>Shadowing variable = override</h1>", rendered, rendered
|
||||
)
|
||||
self.assertIn(
|
||||
"<h1>Shadowing variable = slot_default_override</h1>",
|
||||
rendered,
|
||||
rendered,
|
||||
)
|
||||
self.assertNotIn(
|
||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
||||
)
|
||||
|
||||
def test_nested_component_context_shadows_outer_context_with_filled_slots(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
def test_nested_component_context_shadows_outer_context_with_filled_slots(
|
||||
self,
|
||||
):
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_component' %}"
|
||||
"{% slot 'content' %}{% component name='variable_display' "
|
||||
"shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}"
|
||||
"{% endcomponent_block %}")
|
||||
rendered = template.render(Context({'shadowing_variable': 'NOT SHADOWED'}))
|
||||
"{% endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(
|
||||
Context({"shadowing_variable": "NOT SHADOWED"})
|
||||
)
|
||||
|
||||
self.assertIn('<h1>Shadowing variable = override</h1>', rendered, rendered)
|
||||
self.assertIn('<h1>Shadowing variable = shadow_from_slot</h1>', rendered, rendered)
|
||||
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</h1>', rendered, rendered)
|
||||
self.assertIn(
|
||||
"<h1>Shadowing variable = override</h1>", rendered, rendered
|
||||
)
|
||||
self.assertIn(
|
||||
"<h1>Shadowing variable = shadow_from_slot</h1>",
|
||||
rendered,
|
||||
rendered,
|
||||
)
|
||||
self.assertNotIn(
|
||||
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
|
||||
)
|
||||
|
||||
|
||||
class ParentArgsTests(SimpleTestCase):
|
||||
def test_parent_args_can_be_drawn_from_context(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_with_args' parent_value=parent_value %}"
|
||||
"{% endcomponent_block %}")
|
||||
rendered = template.render(Context({'parent_value': 'passed_in'}))
|
||||
"{% endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(Context({"parent_value": "passed_in"}))
|
||||
|
||||
self.assertIn('<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>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
|
||||
)
|
||||
|
||||
def test_parent_args_available_outside_slots(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_with_args' parent_value='passed_in' %}{%endcomponent_block %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_with_args' parent_value='passed_in' %}{%endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(Context())
|
||||
|
||||
self.assertIn('<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>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
|
||||
)
|
||||
|
||||
def test_parent_args_available_in_slots(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'parent_with_args' parent_value='passed_in' %}"
|
||||
"{% slot 'content' %}{% component name='variable_display' "
|
||||
"shadowing_variable='value_from_slot' new_variable=inner_parent_value %}{% endslot %}"
|
||||
"{%endcomponent_block %}")
|
||||
"{%endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(Context())
|
||||
|
||||
self.assertIn('<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>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
|
||||
)
|
||||
|
||||
|
||||
class ContextCalledOnceTests(SimpleTestCase):
|
||||
def test_one_context_call_with_simple_component(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component name='incrementer' %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component name='incrementer' %}"
|
||||
)
|
||||
rendered = template.render(Context()).strip()
|
||||
|
||||
self.assertEqual(rendered, '<p class="incrementer">value=1;calls=1</p>', rendered)
|
||||
self.assertEqual(
|
||||
rendered, '<p class="incrementer">value=1;calls=1</p>', rendered
|
||||
)
|
||||
|
||||
def test_one_context_call_with_simple_component_and_arg(self):
|
||||
template = Template("{% load component_tags %}{% component name='incrementer' value='2' %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component name='incrementer' value='2' %}"
|
||||
)
|
||||
rendered = template.render(Context()).strip()
|
||||
|
||||
self.assertEqual(rendered, '<p class="incrementer">value=3;calls=1</p>', rendered)
|
||||
self.assertEqual(
|
||||
rendered, '<p class="incrementer">value=3;calls=1</p>', rendered
|
||||
)
|
||||
|
||||
def test_one_context_call_with_component_block(self):
|
||||
template = Template("{% load component_tags %}"
|
||||
"{% component_block 'incrementer' %}{% endcomponent_block %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}"
|
||||
"{% component_block 'incrementer' %}{% endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(Context()).strip()
|
||||
|
||||
self.assertEqual(rendered, '<p class="incrementer">value=1;calls=1</p>', rendered)
|
||||
self.assertEqual(
|
||||
rendered, '<p class="incrementer">value=1;calls=1</p>', rendered
|
||||
)
|
||||
|
||||
def test_one_context_call_with_component_block_and_arg(self):
|
||||
template = Template("{% load component_tags %}"
|
||||
"{% component_block 'incrementer' value='3' %}{% endcomponent_block %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}"
|
||||
"{% component_block 'incrementer' value='3' %}{% endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(Context()).strip()
|
||||
|
||||
self.assertEqual(rendered, '<p class="incrementer">value=4;calls=1</p>', rendered)
|
||||
self.assertEqual(
|
||||
rendered, '<p class="incrementer">value=4;calls=1</p>', rendered
|
||||
)
|
||||
|
||||
def test_one_context_call_with_slot(self):
|
||||
template = Template("{% load component_tags %}"
|
||||
template = Template(
|
||||
"{% load component_tags %}"
|
||||
"{% component_block 'incrementer' %}{% slot 'content' %}"
|
||||
"<p>slot</p>{% endslot %}{% endcomponent_block %}")
|
||||
"<p>slot</p>{% endslot %}{% endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(Context()).strip()
|
||||
|
||||
self.assertEqual(rendered, '<p class="incrementer">value=1;calls=1</p>\n<p>slot</p>', rendered)
|
||||
self.assertEqual(
|
||||
rendered,
|
||||
'<p class="incrementer">value=1;calls=1</p>\n<p>slot</p>',
|
||||
rendered,
|
||||
)
|
||||
|
||||
def test_one_context_call_with_slot_and_arg(self):
|
||||
template = Template("{% load component_tags %}"
|
||||
template = Template(
|
||||
"{% load component_tags %}"
|
||||
"{% component_block 'incrementer' value='3' %}{% slot 'content' %}"
|
||||
"<p>slot</p>{% endslot %}{% endcomponent_block %}")
|
||||
"<p>slot</p>{% endslot %}{% endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(Context()).strip()
|
||||
|
||||
self.assertEqual(rendered, '<p class="incrementer">value=4;calls=1</p>\n<p>slot</p>', rendered)
|
||||
self.assertEqual(
|
||||
rendered,
|
||||
'<p class="incrementer">value=4;calls=1</p>\n<p>slot</p>',
|
||||
rendered,
|
||||
)
|
||||
|
||||
|
||||
class ComponentsCanAccessOuterContext(SimpleTestCase):
|
||||
def test_simple_component_can_use_outer_context(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'simple_component' %}")
|
||||
rendered = template.render(Context({'variable': 'outer_value'})).strip()
|
||||
self.assertIn('outer_value', rendered, rendered)
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'simple_component' %}"
|
||||
)
|
||||
rendered = template.render(
|
||||
Context({"variable": "outer_value"})
|
||||
).strip()
|
||||
self.assertIn("outer_value", rendered, rendered)
|
||||
|
||||
|
||||
class IsolatedContextTests(SimpleTestCase):
|
||||
def test_simple_component_can_pass_outer_context_in_args(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'simple_component' variable only %}")
|
||||
rendered = template.render(Context({'variable': 'outer_value'})).strip()
|
||||
self.assertIn('outer_value', rendered, rendered)
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'simple_component' variable only %}"
|
||||
)
|
||||
rendered = template.render(
|
||||
Context({"variable": "outer_value"})
|
||||
).strip()
|
||||
self.assertIn("outer_value", rendered, rendered)
|
||||
|
||||
def test_simple_component_cannot_use_outer_context(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'simple_component' only %}")
|
||||
rendered = template.render(Context({'variable': 'outer_value'})).strip()
|
||||
self.assertNotIn('outer_value', rendered, rendered)
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'simple_component' only %}"
|
||||
)
|
||||
rendered = template.render(
|
||||
Context({"variable": "outer_value"})
|
||||
).strip()
|
||||
self.assertNotIn("outer_value", rendered, rendered)
|
||||
|
||||
|
||||
class OuterContextPropertyTests(SimpleTestCase):
|
||||
def test_outer_context_property_with_component(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'outer_context_component' only %}")
|
||||
rendered = template.render(Context({'variable': 'outer_value'})).strip()
|
||||
self.assertIn('outer_value', rendered, rendered)
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'outer_context_component' only %}"
|
||||
)
|
||||
rendered = template.render(
|
||||
Context({"variable": "outer_value"})
|
||||
).strip()
|
||||
self.assertIn("outer_value", rendered, rendered)
|
||||
|
||||
def test_outer_context_property_with_component_block(self):
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'outer_context_component' only %}{% endcomponent_block %}")
|
||||
rendered = template.render(Context({'variable': 'outer_value'})).strip()
|
||||
self.assertIn('outer_value', rendered, rendered)
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component_block 'outer_context_component' only %}{% endcomponent_block %}"
|
||||
)
|
||||
rendered = template.render(
|
||||
Context({"variable": "outer_value"})
|
||||
).strip()
|
||||
self.assertIn("outer_value", rendered, rendered)
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
from django.template import Template
|
||||
from django.test import override_settings
|
||||
|
||||
from .django_test_setup import * # NOQA
|
||||
from django_components import component
|
||||
|
||||
from .django_test_setup import * # NOQA
|
||||
from .test_templatetags import SimpleComponent
|
||||
from .testutils import create_and_process_template_response, Django30CompatibleSimpleTestCase as SimpleTestCase
|
||||
from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
||||
from .testutils import create_and_process_template_response
|
||||
|
||||
|
||||
class SimpleComponentAlternate(component.Component):
|
||||
|
@ -38,7 +39,7 @@ class MultistyleComponent(component.Component):
|
|||
js = ["script.js", "script2.js"]
|
||||
|
||||
|
||||
@override_settings(COMPONENTS={'RENDER_DEPENDENCIES': True})
|
||||
@override_settings(COMPONENTS={"RENDER_DEPENDENCIES": True})
|
||||
class ComponentMediaRenderingTests(SimpleTestCase):
|
||||
def setUp(self):
|
||||
# NOTE: component.registry is global, so need to clear before each test
|
||||
|
@ -47,150 +48,280 @@ class ComponentMediaRenderingTests(SimpleTestCase):
|
|||
def test_no_dependencies_when_no_components_used(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<script src="script.js>"', rendered, count=0)
|
||||
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0)
|
||||
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0)
|
||||
self.assertInHTML(
|
||||
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=0,
|
||||
)
|
||||
self.assertInHTML(
|
||||
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=0,
|
||||
)
|
||||
|
||||
def test_no_js_dependencies_when_no_components_used(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_js_dependencies %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_js_dependencies %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<script src="script.js>"', rendered, count=0)
|
||||
|
||||
def test_no_css_dependencies_when_no_components_used(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_css_dependencies %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_css_dependencies %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0)
|
||||
self.assertInHTML(
|
||||
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=0,
|
||||
)
|
||||
|
||||
def test_single_component_dependencies_render_when_used(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'test' variable='foo' %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'test' variable='foo' %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1)
|
||||
self.assertInHTML(
|
||||
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=1,
|
||||
)
|
||||
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
||||
|
||||
def test_placeholder_removed_when_single_component_rendered(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'test' variable='foo' %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'test' variable='foo' %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertNotIn('_RENDERED', rendered)
|
||||
self.assertNotIn("_RENDERED", rendered)
|
||||
|
||||
def test_single_component_css_dependencies(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_css_dependencies %}"
|
||||
"{% component 'test' variable='foo' %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_css_dependencies %}"
|
||||
"{% component 'test' variable='foo' %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1)
|
||||
self.assertInHTML(
|
||||
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=1,
|
||||
)
|
||||
|
||||
def test_single_component_js_dependencies(self):
|
||||
component.registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_js_dependencies %}"
|
||||
"{% component 'test' variable='foo' %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_js_dependencies %}"
|
||||
"{% component 'test' variable='foo' %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
||||
|
||||
def test_all_dependencies_are_rendered_for_component_with_multiple_dependencies(self):
|
||||
component.registry.register(name='test', component=MultistyleComponent)
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}{% component 'test' %}")
|
||||
def test_all_dependencies_are_rendered_for_component_with_multiple_dependencies(
|
||||
self,
|
||||
):
|
||||
component.registry.register(name="test", component=MultistyleComponent)
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}{% component 'test' %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
||||
self.assertInHTML('<script src="script2.js">', rendered, count=1)
|
||||
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1)
|
||||
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1)
|
||||
self.assertInHTML(
|
||||
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=1,
|
||||
)
|
||||
self.assertInHTML(
|
||||
'<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=1,
|
||||
)
|
||||
|
||||
def test_all_js_dependencies_are_rendered_for_component_with_multiple_dependencies(self):
|
||||
component.registry.register(name='test', component=MultistyleComponent)
|
||||
template = Template("{% load component_tags %}{% component_js_dependencies %}{% component 'test' %}")
|
||||
def test_all_js_dependencies_are_rendered_for_component_with_multiple_dependencies(
|
||||
self,
|
||||
):
|
||||
component.registry.register(name="test", component=MultistyleComponent)
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_js_dependencies %}{% component 'test' %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
||||
self.assertInHTML('<script src="script2.js">', rendered, count=1)
|
||||
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0)
|
||||
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0)
|
||||
self.assertInHTML(
|
||||
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=0,
|
||||
)
|
||||
self.assertInHTML(
|
||||
'<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=0,
|
||||
)
|
||||
|
||||
def test_all_css_dependencies_are_rendered_for_component_with_multiple_dependencies(self):
|
||||
component.registry.register(name='test', component=MultistyleComponent)
|
||||
template = Template("{% load component_tags %}{% component_css_dependencies %}{% component 'test' %}")
|
||||
def test_all_css_dependencies_are_rendered_for_component_with_multiple_dependencies(
|
||||
self,
|
||||
):
|
||||
component.registry.register(name="test", component=MultistyleComponent)
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_css_dependencies %}{% component 'test' %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<script src="script.js">', rendered, count=0)
|
||||
self.assertInHTML('<script src="script2.js">', rendered, count=0)
|
||||
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1)
|
||||
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1)
|
||||
self.assertInHTML(
|
||||
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=1,
|
||||
)
|
||||
self.assertInHTML(
|
||||
'<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=1,
|
||||
)
|
||||
|
||||
def test_no_dependencies_with_multiple_unused_components(self):
|
||||
component.registry.register(name="test1", component=SimpleComponent)
|
||||
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
component.registry.register(
|
||||
name="test2", component=SimpleComponentAlternate
|
||||
)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<script src="script.js">', rendered, count=0)
|
||||
self.assertInHTML('<script src="script2.js">', rendered, count=0)
|
||||
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0)
|
||||
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0)
|
||||
self.assertInHTML(
|
||||
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=0,
|
||||
)
|
||||
self.assertInHTML(
|
||||
'<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=0,
|
||||
)
|
||||
|
||||
def test_correct_css_dependencies_with_multiple_components(self):
|
||||
component.registry.register(name="test1", component=SimpleComponent)
|
||||
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
component.registry.register(
|
||||
name="test2", component=SimpleComponentAlternate
|
||||
)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_css_dependencies %}"
|
||||
"{% component 'test1' 'variable' %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_css_dependencies %}"
|
||||
"{% component 'test1' 'variable' %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1)
|
||||
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0)
|
||||
self.assertInHTML(
|
||||
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=1,
|
||||
)
|
||||
self.assertInHTML(
|
||||
'<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=0,
|
||||
)
|
||||
|
||||
def test_correct_js_dependencies_with_multiple_components(self):
|
||||
component.registry.register(name="test1", component=SimpleComponent)
|
||||
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
component.registry.register(
|
||||
name="test2", component=SimpleComponentAlternate
|
||||
)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_js_dependencies %}"
|
||||
"{% component 'test1' 'variable' %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_js_dependencies %}"
|
||||
"{% component 'test1' 'variable' %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
||||
self.assertInHTML('<script src="script2.js">', rendered, count=0)
|
||||
|
||||
def test_correct_dependencies_with_multiple_components(self):
|
||||
component.registry.register(name="test1", component=SimpleComponent)
|
||||
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
component.registry.register(
|
||||
name="test2", component=SimpleComponentAlternate
|
||||
)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'test2' variable='variable' %}")
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'test2' variable='variable' %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<script src="script.js">', rendered, count=0)
|
||||
self.assertInHTML('<script src="script2.js">', rendered, count=1)
|
||||
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0)
|
||||
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1)
|
||||
self.assertInHTML(
|
||||
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=0,
|
||||
)
|
||||
self.assertInHTML(
|
||||
'<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=1,
|
||||
)
|
||||
|
||||
def test_shared_dependencies_rendered_once(self):
|
||||
component.registry.register(name="test1", component=SimpleComponent)
|
||||
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
component.registry.register(name="test3", component=SimpleComponentWithSharedDependency)
|
||||
component.registry.register(
|
||||
name="test2", component=SimpleComponentAlternate
|
||||
)
|
||||
component.registry.register(
|
||||
name="test3", component=SimpleComponentWithSharedDependency
|
||||
)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'test1' variable='variable' %}{% component 'test2' variable='variable' %}"
|
||||
"{% component 'test1' variable='variable' %}")
|
||||
"{% component 'test1' variable='variable' %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertInHTML('<script src="script.js">', rendered, count=1)
|
||||
self.assertInHTML('<script src="script2.js">', rendered, count=1)
|
||||
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1)
|
||||
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1)
|
||||
self.assertInHTML(
|
||||
'<link href="style.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=1,
|
||||
)
|
||||
self.assertInHTML(
|
||||
'<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>',
|
||||
rendered,
|
||||
count=1,
|
||||
)
|
||||
|
||||
def test_placeholder_removed_when_multiple_component_rendered(self):
|
||||
component.registry.register(name="test1", component=SimpleComponent)
|
||||
component.registry.register(name="test2", component=SimpleComponentAlternate)
|
||||
component.registry.register(name="test3", component=SimpleComponentWithSharedDependency)
|
||||
component.registry.register(
|
||||
name="test2", component=SimpleComponentAlternate
|
||||
)
|
||||
component.registry.register(
|
||||
name="test3", component=SimpleComponentWithSharedDependency
|
||||
)
|
||||
|
||||
template = Template("{% load component_tags %}{% component_dependencies %}"
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_dependencies %}"
|
||||
"{% component 'test1' variable='variable' %}{% component 'test2' variable='variable' %}"
|
||||
"{% component 'test1' variable='variable' %}")
|
||||
"{% component 'test1' variable='variable' %}"
|
||||
)
|
||||
rendered = create_and_process_template_response(template)
|
||||
self.assertNotIn('_RENDERED', rendered)
|
||||
self.assertNotIn("_RENDERED", rendered)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import unittest
|
||||
|
||||
from .django_test_setup import * # NOQA
|
||||
from django_components import component
|
||||
|
||||
from .django_test_setup import * # NOQA
|
||||
|
||||
|
||||
class MockComponent(object):
|
||||
pass
|
||||
|
@ -17,14 +18,13 @@ class ComponentRegistryTest(unittest.TestCase):
|
|||
class TestComponent(component.Component):
|
||||
pass
|
||||
|
||||
self.assertEqual(component.registry.get("decorated_component"), TestComponent)
|
||||
self.assertEqual(
|
||||
component.registry.get("decorated_component"), TestComponent
|
||||
)
|
||||
|
||||
def test_simple_register(self):
|
||||
self.registry.register(name="testcomponent", component=MockComponent)
|
||||
self.assertEqual(
|
||||
self.registry.all(),
|
||||
{"testcomponent": MockComponent}
|
||||
)
|
||||
self.assertEqual(self.registry.all(), {"testcomponent": MockComponent})
|
||||
|
||||
def test_register_two_components(self):
|
||||
self.registry.register(name="testcomponent", component=MockComponent)
|
||||
|
@ -34,13 +34,15 @@ class ComponentRegistryTest(unittest.TestCase):
|
|||
{
|
||||
"testcomponent": MockComponent,
|
||||
"testcomponent2": MockComponent,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
def test_prevent_registering_twice(self):
|
||||
self.registry.register(name="testcomponent", component=MockComponent)
|
||||
with self.assertRaises(component.AlreadyRegistered):
|
||||
self.registry.register(name="testcomponent", component=MockComponent)
|
||||
self.registry.register(
|
||||
name="testcomponent", component=MockComponent
|
||||
)
|
||||
|
||||
def test_simple_unregister(self):
|
||||
self.registry.register(name="testcomponent", component=MockComponent)
|
||||
|
|
|
@ -2,8 +2,9 @@ from textwrap import dedent
|
|||
|
||||
from django.template import Context, Template, TemplateSyntaxError
|
||||
|
||||
from .django_test_setup import * # NOQA
|
||||
from django_components import component
|
||||
|
||||
from .django_test_setup import * # NOQA
|
||||
from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
|
||||
|
||||
|
||||
|
@ -52,7 +53,7 @@ class ComponentWithProvidedAndDefaultParameters(component.Component):
|
|||
template_name = "template_with_provided_and_default_parameters.html"
|
||||
|
||||
def get_context_data(self, variable, default_param="default text"):
|
||||
return {"variable": variable, 'default_param': default_param}
|
||||
return {"variable": variable, "default_param": default_param}
|
||||
|
||||
|
||||
class ComponentTemplateTagTest(SimpleTestCase):
|
||||
|
@ -105,7 +106,7 @@ class ComponentTemplateTagTest(SimpleTestCase):
|
|||
component.registry.register(name="test", component=SimpleComponent)
|
||||
|
||||
template = Template(
|
||||
'{% load component_tags %}{% component \'test\' variable="variable" %}'
|
||||
"{% load component_tags %}{% component 'test' variable=\"variable\" %}"
|
||||
)
|
||||
rendered = template.render(Context({}))
|
||||
self.assertHTMLEqual(rendered, "Variable: <strong>variable</strong>\n")
|
||||
|
@ -147,7 +148,9 @@ class ComponentSlottedTemplateTagTest(SimpleTestCase):
|
|||
)
|
||||
|
||||
def test_slotted_template_with_context_var(self):
|
||||
component.registry.register(name="test1", component=SlottedComponentWithContext)
|
||||
component.registry.register(
|
||||
name="test1", component=SlottedComponentWithContext
|
||||
)
|
||||
|
||||
template = Template(
|
||||
"""
|
||||
|
@ -197,7 +200,9 @@ class ComponentSlottedTemplateTagTest(SimpleTestCase):
|
|||
)
|
||||
|
||||
def test_slotted_template_without_slots(self):
|
||||
component.registry.register(name="test", component=SlottedComponentNoSlots)
|
||||
component.registry.register(
|
||||
name="test", component=SlottedComponentNoSlots
|
||||
)
|
||||
template = Template(
|
||||
"""
|
||||
{% load component_tags %}
|
||||
|
@ -209,7 +214,9 @@ class ComponentSlottedTemplateTagTest(SimpleTestCase):
|
|||
self.assertHTMLEqual(rendered, "<custom-template></custom-template>")
|
||||
|
||||
def test_slotted_template_without_slots_and_single_quotes(self):
|
||||
component.registry.register(name="test", component=SlottedComponentNoSlots)
|
||||
component.registry.register(
|
||||
name="test", component=SlottedComponentNoSlots
|
||||
)
|
||||
template = Template(
|
||||
"""
|
||||
{% load component_tags %}
|
||||
|
@ -227,7 +234,9 @@ class SlottedTemplateRegressionTests(SimpleTestCase):
|
|||
component.registry.clear()
|
||||
|
||||
def test_slotted_template_that_uses_missing_variable(self):
|
||||
component.registry.register(name="test", component=SlottedComponentWithMissingVariable)
|
||||
component.registry.register(
|
||||
name="test", component=SlottedComponentWithMissingVariable
|
||||
)
|
||||
template = Template(
|
||||
"""
|
||||
{% load component_tags %}
|
||||
|
@ -236,23 +245,30 @@ class SlottedTemplateRegressionTests(SimpleTestCase):
|
|||
)
|
||||
rendered = template.render(Context({}))
|
||||
|
||||
self.assertHTMLEqual(rendered, """
|
||||
self.assertHTMLEqual(
|
||||
rendered,
|
||||
"""
|
||||
<custom-template>
|
||||
<header>Default header</header>
|
||||
<main>Default main</main>
|
||||
<footer>Default footer</footer>
|
||||
</custom-template>
|
||||
""")
|
||||
""",
|
||||
)
|
||||
|
||||
def test_component_block_accepts_provided_and_default_parameters(self):
|
||||
component.registry.register(name="test", component=ComponentWithProvidedAndDefaultParameters)
|
||||
component.registry.register(
|
||||
name="test", component=ComponentWithProvidedAndDefaultParameters
|
||||
)
|
||||
|
||||
template = Template(
|
||||
'{% load component_tags %}{% component_block "test" variable="provided value" %}{% endcomponent_block %}'
|
||||
)
|
||||
rendered = template.render(Context({}))
|
||||
self.assertHTMLEqual(rendered,
|
||||
"Provided variable: <strong>provided value</strong>\nDefault: <p>default text</p>")
|
||||
self.assertHTMLEqual(
|
||||
rendered,
|
||||
"Provided variable: <strong>provided value</strong>\nDefault: <p>default text</p>",
|
||||
)
|
||||
|
||||
|
||||
class MultiComponentTests(SimpleTestCase):
|
||||
|
@ -260,24 +276,38 @@ class MultiComponentTests(SimpleTestCase):
|
|||
component.registry.clear()
|
||||
|
||||
def register_components(self):
|
||||
component.registry.register('first_component', SlottedComponent)
|
||||
component.registry.register('second_component', SlottedComponentWithContext)
|
||||
component.registry.register("first_component", SlottedComponent)
|
||||
component.registry.register(
|
||||
"second_component", SlottedComponentWithContext
|
||||
)
|
||||
|
||||
def make_template(self, first_component_slot='', second_component_slot=''):
|
||||
return Template('{% load component_tags %}'
|
||||
def make_template(self, first_component_slot="", second_component_slot=""):
|
||||
return Template(
|
||||
"{% load component_tags %}"
|
||||
"{% component_block 'first_component' %}"
|
||||
+ first_component_slot + '{% endcomponent_block %}'
|
||||
+ first_component_slot
|
||||
+ "{% endcomponent_block %}"
|
||||
"{% component_block 'second_component' variable='xyz' %}"
|
||||
+ second_component_slot + '{% endcomponent_block %}')
|
||||
+ second_component_slot
|
||||
+ "{% endcomponent_block %}"
|
||||
)
|
||||
|
||||
def expected_result(self, first_component_slot='', second_component_slot=''):
|
||||
return ('<custom-template><header>{}</header>'.format(first_component_slot or "Default header")
|
||||
+ '<main>Default main</main><footer>Default footer</footer></custom-template>'
|
||||
+ '<custom-template><header>{}</header>'.format(second_component_slot or "Default header")
|
||||
+ '<main>Default main</main><footer>Default footer</footer></custom-template>')
|
||||
def expected_result(
|
||||
self, first_component_slot="", second_component_slot=""
|
||||
):
|
||||
return (
|
||||
"<custom-template><header>{}</header>".format(
|
||||
first_component_slot or "Default header"
|
||||
)
|
||||
+ "<main>Default main</main><footer>Default footer</footer></custom-template>"
|
||||
+ "<custom-template><header>{}</header>".format(
|
||||
second_component_slot or "Default header"
|
||||
)
|
||||
+ "<main>Default main</main><footer>Default footer</footer></custom-template>"
|
||||
)
|
||||
|
||||
def wrap_with_slot_tags(self, s):
|
||||
return '{% slot "header" %}' + s + '{% endslot %}'
|
||||
return '{% slot "header" %}' + s + "{% endslot %}"
|
||||
|
||||
def test_both_components_render_correctly_with_no_slots(self):
|
||||
self.register_components()
|
||||
|
@ -286,26 +316,35 @@ class MultiComponentTests(SimpleTestCase):
|
|||
|
||||
def test_both_components_render_correctly_with_slots(self):
|
||||
self.register_components()
|
||||
first_slot_content = '<p>Slot #1</p>'
|
||||
second_slot_content = '<div>Slot #2</div>'
|
||||
first_slot_content = "<p>Slot #1</p>"
|
||||
second_slot_content = "<div>Slot #2</div>"
|
||||
first_slot = self.wrap_with_slot_tags(first_slot_content)
|
||||
second_slot = self.wrap_with_slot_tags(second_slot_content)
|
||||
rendered = self.make_template(first_slot, second_slot).render(Context({}))
|
||||
self.assertHTMLEqual(rendered, self.expected_result(first_slot_content, second_slot_content))
|
||||
rendered = self.make_template(first_slot, second_slot).render(
|
||||
Context({})
|
||||
)
|
||||
self.assertHTMLEqual(
|
||||
rendered,
|
||||
self.expected_result(first_slot_content, second_slot_content),
|
||||
)
|
||||
|
||||
def test_both_components_render_correctly_when_only_first_has_slots(self):
|
||||
self.register_components()
|
||||
first_slot_content = '<p>Slot #1</p>'
|
||||
first_slot_content = "<p>Slot #1</p>"
|
||||
first_slot = self.wrap_with_slot_tags(first_slot_content)
|
||||
rendered = self.make_template(first_slot).render(Context({}))
|
||||
self.assertHTMLEqual(rendered, self.expected_result(first_slot_content))
|
||||
self.assertHTMLEqual(
|
||||
rendered, self.expected_result(first_slot_content)
|
||||
)
|
||||
|
||||
def test_both_components_render_correctly_when_only_second_has_slots(self):
|
||||
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)
|
||||
rendered = self.make_template('', second_slot).render(Context({}))
|
||||
self.assertHTMLEqual(rendered, self.expected_result('', second_slot_content))
|
||||
rendered = self.make_template("", second_slot).render(Context({}))
|
||||
self.assertHTMLEqual(
|
||||
rendered, self.expected_result("", second_slot_content)
|
||||
)
|
||||
|
||||
|
||||
class TemplateInstrumentationTest(SimpleTestCase):
|
||||
|
@ -323,8 +362,8 @@ class TemplateInstrumentationTest(SimpleTestCase):
|
|||
|
||||
def setUp(self):
|
||||
component.registry.clear()
|
||||
component.registry.register('test_component', SlottedComponent)
|
||||
component.registry.register('inner_component', SimpleComponent)
|
||||
component.registry.register("test_component", SlottedComponent)
|
||||
component.registry.register("inner_component", SimpleComponent)
|
||||
|
||||
def templates_used_to_render(self, subject_template, render_context=None):
|
||||
"""Emulate django.test.client.Client (see request method)."""
|
||||
|
@ -335,23 +374,31 @@ class TemplateInstrumentationTest(SimpleTestCase):
|
|||
def receive_template_signal(sender, template, context, **_kwargs):
|
||||
templates_used.append(template.name)
|
||||
|
||||
template_rendered.connect(receive_template_signal, dispatch_uid='test_method')
|
||||
template_rendered.connect(
|
||||
receive_template_signal, dispatch_uid="test_method"
|
||||
)
|
||||
subject_template.render(render_context or Context({}))
|
||||
template_rendered.disconnect(dispatch_uid='test_method')
|
||||
template_rendered.disconnect(dispatch_uid="test_method")
|
||||
return templates_used
|
||||
|
||||
def test_template_shown_as_used(self):
|
||||
template = Template("{% load component_tags %}{% component 'test_component' %}", name='root')
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component 'test_component' %}",
|
||||
name="root",
|
||||
)
|
||||
templates_used = self.templates_used_to_render(template)
|
||||
self.assertIn('slotted_template.html', templates_used)
|
||||
self.assertIn("slotted_template.html", templates_used)
|
||||
|
||||
def test_nested_component_templates_all_shown_as_used(self):
|
||||
template = Template("{% load component_tags %}{% component_block 'test_component' %}"
|
||||
template = Template(
|
||||
"{% load component_tags %}{% component_block 'test_component' %}"
|
||||
"{% slot \"header\" %}{% component 'inner_component' variable='foo' %}{% endslot %}"
|
||||
"{% endcomponent_block %}", name='root')
|
||||
"{% endcomponent_block %}",
|
||||
name="root",
|
||||
)
|
||||
templates_used = self.templates_used_to_render(template)
|
||||
self.assertIn('slotted_template.html', templates_used)
|
||||
self.assertIn('simple_template.html', templates_used)
|
||||
self.assertIn("slotted_template.html", templates_used)
|
||||
self.assertIn("simple_template.html", templates_used)
|
||||
|
||||
|
||||
class NestedSlotTests(SimpleTestCase):
|
||||
|
@ -362,7 +409,7 @@ class NestedSlotTests(SimpleTestCase):
|
|||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
component.registry.clear()
|
||||
component.registry.register('test', cls.NestedComponent)
|
||||
component.registry.register("test", cls.NestedComponent)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
|
@ -397,7 +444,7 @@ class NestedSlotTests(SimpleTestCase):
|
|||
"""
|
||||
)
|
||||
rendered = template.render(Context({}))
|
||||
self.assertHTMLEqual(rendered, '<p>Override</p>')
|
||||
self.assertHTMLEqual(rendered, "<p>Override</p>")
|
||||
|
||||
def test_both_overriden_and_inner_removed(self):
|
||||
template = Template(
|
||||
|
@ -410,7 +457,7 @@ class NestedSlotTests(SimpleTestCase):
|
|||
"""
|
||||
)
|
||||
rendered = template.render(Context({}))
|
||||
self.assertHTMLEqual(rendered, '<p>Override</p>')
|
||||
self.assertHTMLEqual(rendered, "<p>Override</p>")
|
||||
|
||||
|
||||
class ConditionalSlotTests(SimpleTestCase):
|
||||
|
@ -418,13 +465,13 @@ class ConditionalSlotTests(SimpleTestCase):
|
|||
template_name = "conditional_template.html"
|
||||
|
||||
def get_context_data(self, branch=None):
|
||||
return {'branch': branch}
|
||||
return {"branch": branch}
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
component.registry.clear()
|
||||
component.registry.register('test', cls.ConditionalComponent)
|
||||
component.registry.register("test", cls.ConditionalComponent)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
|
@ -442,7 +489,7 @@ class ConditionalSlotTests(SimpleTestCase):
|
|||
"""
|
||||
)
|
||||
rendered = template.render(Context({}))
|
||||
self.assertHTMLEqual(rendered, '')
|
||||
self.assertHTMLEqual(rendered, "")
|
||||
|
||||
def test_default_content_if_no_slots(self):
|
||||
template = Template(
|
||||
|
@ -453,7 +500,9 @@ class ConditionalSlotTests(SimpleTestCase):
|
|||
"""
|
||||
)
|
||||
rendered = template.render(Context({}))
|
||||
self.assertHTMLEqual(rendered, '<p id="a">Default A</p><p id="b">Default B</p>')
|
||||
self.assertHTMLEqual(
|
||||
rendered, '<p id="a">Default A</p><p id="b">Default B</p>'
|
||||
)
|
||||
|
||||
def test_one_slot_overridden(self):
|
||||
template = Template(
|
||||
|
@ -468,7 +517,9 @@ class ConditionalSlotTests(SimpleTestCase):
|
|||
"""
|
||||
)
|
||||
rendered = template.render(Context({}))
|
||||
self.assertHTMLEqual(rendered, '<p id="a">Default A</p><p id="b">Override B</p>')
|
||||
self.assertHTMLEqual(
|
||||
rendered, '<p id="a">Default A</p><p id="b">Override B</p>'
|
||||
)
|
||||
|
||||
def test_both_slots_overridden(self):
|
||||
template = Template(
|
||||
|
@ -485,7 +536,9 @@ class ConditionalSlotTests(SimpleTestCase):
|
|||
"""
|
||||
)
|
||||
rendered = template.render(Context({}))
|
||||
self.assertHTMLEqual(rendered, '<p id="a">Override A</p><p id="b">Override B</p>')
|
||||
self.assertHTMLEqual(
|
||||
rendered, '<p id="a">Override A</p><p id="b">Override B</p>'
|
||||
)
|
||||
|
||||
|
||||
class SlotSuperTests(SimpleTestCase):
|
||||
|
@ -493,7 +546,7 @@ class SlotSuperTests(SimpleTestCase):
|
|||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
component.registry.clear()
|
||||
component.registry.register('test', SlottedComponent)
|
||||
component.registry.register("test", SlottedComponent)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
|
@ -561,7 +614,7 @@ class SlotSuperTests(SimpleTestCase):
|
|||
{% endcomponent_block %}
|
||||
"""
|
||||
)
|
||||
rendered = template.render(Context({'range': range(3)}))
|
||||
rendered = template.render(Context({"range": range(3)}))
|
||||
|
||||
self.assertHTMLEqual(
|
||||
rendered,
|
||||
|
@ -579,8 +632,8 @@ class TemplateSyntaxErrorTests(SimpleTestCase):
|
|||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
component.registry.register('test', SlottedComponent)
|
||||
component.registry.register('broken_component', BrokenComponent)
|
||||
component.registry.register("test", SlottedComponent)
|
||||
component.registry.register("broken_component", BrokenComponent)
|
||||
|
||||
def test_variable_outside_slot_tag_is_error(self):
|
||||
with self.assertRaises(TemplateSyntaxError):
|
||||
|
|
|
@ -8,16 +8,20 @@ from django_components.middleware import ComponentDependencyMiddleware
|
|||
|
||||
# Create middleware instance
|
||||
response_stash = None
|
||||
middleware = ComponentDependencyMiddleware(get_response=lambda _: response_stash)
|
||||
middleware = ComponentDependencyMiddleware(
|
||||
get_response=lambda _: response_stash
|
||||
)
|
||||
|
||||
|
||||
class Django30CompatibleSimpleTestCase(SimpleTestCase):
|
||||
def assertHTMLEqual(self, left, right):
|
||||
left = left.replace(' type="text/javascript"', '')
|
||||
super(Django30CompatibleSimpleTestCase, self).assertHTMLEqual(left, right)
|
||||
left = left.replace(' type="text/javascript"', "")
|
||||
super(Django30CompatibleSimpleTestCase, self).assertHTMLEqual(
|
||||
left, right
|
||||
)
|
||||
|
||||
def assertInHTML(self, needle, haystack, count=None, msg_prefix=''):
|
||||
haystack = haystack.replace(' type="text/javascript"', '')
|
||||
def assertInHTML(self, needle, haystack, count=None, msg_prefix=""):
|
||||
haystack = haystack.replace(' type="text/javascript"', "")
|
||||
super().assertInHTML(needle, haystack, count, msg_prefix)
|
||||
|
||||
|
||||
|
@ -29,7 +33,9 @@ request = Mock()
|
|||
mock_template = Mock()
|
||||
|
||||
|
||||
def create_and_process_template_response(template, context=None, use_middleware=True):
|
||||
def create_and_process_template_response(
|
||||
template, context=None, use_middleware=True
|
||||
):
|
||||
context = context if context is not None else Context({})
|
||||
mock_template.render = lambda context, _: template.render(context)
|
||||
response = TemplateResponse(request, mock_template, context)
|
||||
|
@ -40,4 +46,4 @@ def create_and_process_template_response(template, context=None, use_middleware=
|
|||
response = middleware(request)
|
||||
else:
|
||||
response.render()
|
||||
return response.content.decode('utf-8')
|
||||
return response.content.decode("utf-8")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue