[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
This commit is contained in:
pre-commit-ci[bot] 2021-09-10 11:12:25 +00:00
parent 15a0e66219
commit 0648ad9a93
14 changed files with 983 additions and 432 deletions

View file

@ -3,10 +3,14 @@ from time import perf_counter
from django.template import Context, Template from django.template import Context, Template
from django.test import override_settings 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 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): class SlottedComponent(component.Component):
@ -31,14 +35,22 @@ class BreadcrumbComponent(component.Component):
template_name = "mdn_component_template.html" template_name = "mdn_component_template.html"
LINKS = [ LINKS = [
('https://developer.mozilla.org/en-US/docs/Learn', (
'Learn web development'), "https://developer.mozilla.org/en-US/docs/Learn",
('https://developer.mozilla.org/en-US/docs/Learn/HTML', "Learn web development",
'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",
('https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure', "Structuring the web with HTML",
'Document and website structure') ),
(
"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): def get_context_data(self, items):
@ -46,7 +58,7 @@ class BreadcrumbComponent(component.Component):
items = 4 items = 4
elif items < 0: elif items < 0:
items = 0 items = 0
return {'links': self.LINKS[:items - 1]} return {"links": self.LINKS[: items - 1]}
class Media: class Media:
css = {"all": ["test.css"]} 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>""" EXPECTED_JS = """<script src="test.js"></script>"""
@override_settings(COMPONENTS={'RENDER_DEPENDENCIES': True}) @override_settings(COMPONENTS={"RENDER_DEPENDENCIES": True})
class RenderBenchmarks(SimpleTestCase): class RenderBenchmarks(SimpleTestCase):
def setUp(self): def setUp(self):
component.registry.clear() component.registry.clear()
component.registry.register('test_component', SlottedComponent) component.registry.register("test_component", SlottedComponent)
component.registry.register('inner_component', SimpleComponent) component.registry.register("inner_component", SimpleComponent)
component.registry.register('breadcrumb_component', BreadcrumbComponent) component.registry.register(
"breadcrumb_component", BreadcrumbComponent
)
@staticmethod @staticmethod
def timed_loop(func, iterations=1000): def timed_loop(func, iterations=1000):
@ -76,55 +90,81 @@ class RenderBenchmarks(SimpleTestCase):
return total_elapsed * 1000 / iterations return total_elapsed * 1000 / iterations
def test_render_time_for_small_component(self): 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 %}" "{% 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): 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_block 'test_component' %}{% slot \"header\" %}"
"{% component 'inner_component' variable='foo' %}{% endslot %}{% endcomponent_block %}", "{% component 'inner_component' variable='foo' %}{% endslot %}{% endcomponent_block %}",
name='root') name="root",
)
# Sanity tests # Sanity tests
response_content = create_and_process_template_response(template) response_content = create_and_process_template_response(template)
self.assertNotIn(CSS_DEPENDENCY_PLACEHOLDER, response_content) self.assertNotIn(CSS_DEPENDENCY_PLACEHOLDER, response_content)
self.assertNotIn(JS_DEPENDENCY_PLACEHOLDER, response_content) self.assertNotIn(JS_DEPENDENCY_PLACEHOLDER, response_content)
self.assertIn('style.css', response_content) self.assertIn("style.css", response_content)
self.assertIn('script.js', response_content) self.assertIn("script.js", response_content)
without_middleware = self.timed_loop(lambda: create_and_process_template_response(template, without_middleware = self.timed_loop(
use_middleware=False)) lambda: create_and_process_template_response(
with_middleware = self.timed_loop(lambda: create_and_process_template_response(template, use_middleware=True)) 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) self.report_results(with_middleware, without_middleware)
def test_render_time_with_dependency_for_large_page(self): def test_render_time_with_dependency_for_large_page(self):
from django.template.loader import get_template 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, {}) response_content = create_and_process_template_response(template, {})
self.assertNotIn(CSS_DEPENDENCY_PLACEHOLDER, response_content) self.assertNotIn(CSS_DEPENDENCY_PLACEHOLDER, response_content)
self.assertNotIn(JS_DEPENDENCY_PLACEHOLDER, response_content) self.assertNotIn(JS_DEPENDENCY_PLACEHOLDER, response_content)
self.assertIn('test.css', response_content) self.assertIn("test.css", response_content)
self.assertIn('test.js', response_content) self.assertIn("test.js", response_content)
without_middleware = self.timed_loop( without_middleware = self.timed_loop(
lambda: create_and_process_template_response(template, {}, use_middleware=False)) lambda: create_and_process_template_response(
template, {}, use_middleware=False
)
)
with_middleware = self.timed_loop( 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) self.report_results(with_middleware, without_middleware)
@staticmethod @staticmethod
def report_results(with_middleware, without_middleware): def report_results(with_middleware, without_middleware):
print(f'Middleware active\t\t{with_middleware:.3f} ms per iteration') print(f"Middleware active\t\t{with_middleware:.3f} ms per iteration")
print(f'Middleware inactive\t{without_middleware:.3f} ms per iteration') print(
f"Middleware inactive\t{without_middleware:.3f} ms per iteration"
)
time_difference = with_middleware - without_middleware time_difference = with_middleware - without_middleware
if without_middleware > with_middleware: if without_middleware > with_middleware:
print(f'Decrease of {-100 * time_difference / with_middleware:.2f}%') print(
f"Decrease of {-100 * time_difference / with_middleware:.2f}%"
)
else: else:
print(f'Increase of {100 * time_difference / without_middleware:.2f}%') print(
f"Increase of {100 * time_difference / without_middleware:.2f}%"
)

View file

@ -9,10 +9,16 @@ from django.template.loader import get_template
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
# Allow "component.AlreadyRegistered" instead of having to import these everywhere # 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) TEMPLATE_CACHE_SIZE = getattr(settings, "COMPONENTS", {}).get(
ACTIVE_SLOT_CONTEXT_KEY = '_DJANGO_COMPONENTS_ACTIVE_SLOTS' "TEMPLATE_CACHE_SIZE", 128
)
ACTIVE_SLOT_CONTEXT_KEY = "_DJANGO_COMPONENTS_ACTIVE_SLOTS"
class SimplifiedInterfaceMediaDefiningClass(MediaDefiningClass): class SimplifiedInterfaceMediaDefiningClass(MediaDefiningClass):
@ -54,7 +60,9 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
def get_template_name(self, context=None): def get_template_name(self, context=None):
if not self.template_name: 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 return self.template_name
@ -75,13 +83,19 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
@staticmethod @staticmethod
def slots_in_template(template): 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 @staticmethod
def is_slot_node(node): def is_slot_node(node):
return (isinstance(node, Node) return (
isinstance(node, Node)
and node.token.token_type == TokenType.BLOCK 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) @lru_cache(maxsize=TEMPLATE_CACHE_SIZE)
def get_processed_template(self, template_name): def get_processed_template(self, template_name):
@ -117,16 +131,16 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass):
return component_template return component_template
def render(self, context): def render(self, context):
if hasattr(self, 'context'): if hasattr(self, "context"):
warnings.warn( warnings.warn(
f'{self.__class__.__name__}: `context` method is deprecated, use `get_context` instead', f"{self.__class__.__name__}: `context` method is deprecated, use `get_context` instead",
DeprecationWarning DeprecationWarning,
) )
if hasattr(self, 'template'): if hasattr(self, "template"):
warnings.warn( warnings.warn(
f'{self.__class__.__name__}: `template` method is deprecated, set `template_name` or override `get_template_name` instead', f"{self.__class__.__name__}: `template` method is deprecated, set `template_name` or override `get_template_name` instead",
DeprecationWarning DeprecationWarning,
) )
template_name = self.template(context) template_name = self.template(context)
else: else:

View file

@ -12,7 +12,9 @@ class ComponentRegistry(object):
def register(self, name=None, component=None): def register(self, name=None, component=None):
if name in self._registry: 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 self._registry[name] = component

View file

@ -8,11 +8,13 @@ RENDERED_COMPONENTS_CONTEXT_KEY = "_COMPONENT_DEPENDENCIES"
CSS_DEPENDENCY_PLACEHOLDER = '<link name="CSS_PLACEHOLDER">' CSS_DEPENDENCY_PLACEHOLDER = '<link name="CSS_PLACEHOLDER">'
JS_DEPENDENCY_PLACEHOLDER = '<script name="JS_PLACEHOLDER">' JS_DEPENDENCY_PLACEHOLDER = '<script name="JS_PLACEHOLDER">'
SCRIPT_TAG_REGEX = re.compile('<script') SCRIPT_TAG_REGEX = re.compile("<script")
COMPONENT_COMMENT_REGEX = re.compile(rb'<!-- _RENDERED (?P<name>\w+?) -->') COMPONENT_COMMENT_REGEX = re.compile(rb"<!-- _RENDERED (?P<name>\w+?) -->")
PLACEHOLDER_REGEX = re.compile(rb'<!-- _RENDERED (?P<name>\w+?) -->' PLACEHOLDER_REGEX = re.compile(
rb"<!-- _RENDERED (?P<name>\w+?) -->"
rb'|<link name="CSS_PLACEHOLDER">' rb'|<link name="CSS_PLACEHOLDER">'
rb'|<script name="JS_PLACEHOLDER">') rb'|<script name="JS_PLACEHOLDER">'
)
class ComponentDependencyMiddleware: class ComponentDependencyMiddleware:
@ -25,9 +27,13 @@ class ComponentDependencyMiddleware:
def __call__(self, request): def __call__(self, request):
response = self.get_response(request) response = self.get_response(request)
if getattr(settings, "COMPONENTS", {}).get('RENDER_DEPENDENCIES', False)\ if (
and not isinstance(response, StreamingHttpResponse)\ getattr(settings, "COMPONENTS", {}).get(
and response['Content-Type'].startswith('text/html'): "RENDER_DEPENDENCIES", False
)
and not isinstance(response, StreamingHttpResponse)
and response["Content-Type"].startswith("text/html")
):
response.content = process_response_content(response.content) response.content = process_response_content(response.content)
return response return response
@ -35,12 +41,23 @@ class ComponentDependencyMiddleware:
def process_response_content(content): def process_response_content(content):
from django_components.component import registry from django_components.component import registry
component_names_seen = {match.group('name') for match in COMPONENT_COMMENT_REGEX.finditer(content)} component_names_seen = {
all_components = [registry.get(name.decode('utf-8'))('') for name in 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) all_media = join_media(all_components)
js_dependencies = b''.join(media.encode('utf-8') for media in all_media.render_js()) js_dependencies = b"".join(
css_dependencies = b''.join(media.encode('utf-8') for media in all_media.render_css()) media.encode("utf-8") for media in all_media.render_js()
return PLACEHOLDER_REGEX.sub(DependencyReplacer(css_dependencies, js_dependencies), content) )
css_dependencies = b"".join(
media.encode("utf-8") for media in all_media.render_css()
)
return PLACEHOLDER_REGEX.sub(
DependencyReplacer(css_dependencies, js_dependencies), content
)
def add_module_attribute_to_scripts(scripts): def add_module_attribute_to_scripts(scripts):
@ -51,8 +68,8 @@ class DependencyReplacer:
"""Replacer for use in re.sub that replaces the first placeholder CSS and JS """Replacer for use in re.sub that replaces the first placeholder CSS and JS
tags it encounters and removes any subsequent ones.""" tags it encounters and removes any subsequent ones."""
CSS_PLACEHOLDER = bytes(CSS_DEPENDENCY_PLACEHOLDER, encoding='utf-8') CSS_PLACEHOLDER = bytes(CSS_DEPENDENCY_PLACEHOLDER, encoding="utf-8")
JS_PLACEHOLDER = bytes(JS_DEPENDENCY_PLACEHOLDER, encoding='utf-8') JS_PLACEHOLDER = bytes(JS_DEPENDENCY_PLACEHOLDER, encoding="utf-8")
def __init__(self, css_string, js_string): def __init__(self, css_string, js_string):
self.js_string = js_string self.js_string = js_string
@ -64,7 +81,7 @@ class DependencyReplacer:
elif match[0] == self.JS_PLACEHOLDER: elif match[0] == self.JS_PLACEHOLDER:
replacement, self.js_string = self.js_string, b"" replacement, self.js_string = self.js_string, b""
else: else:
replacement = b'' replacement = b""
return replacement return replacement

View file

@ -8,4 +8,4 @@ from django.template.utils import get_app_template_dirs
class Loader(FilesystemLoader): class Loader(FilesystemLoader):
def get_dirs(self): def get_dirs(self):
return get_app_template_dirs('components') return get_app_template_dirs("components")

View file

@ -7,7 +7,10 @@ from django.template.library import parse_bits
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
from django_components.component import ACTIVE_SLOT_CONTEXT_KEY, registry 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() register = template.Library()
@ -29,7 +32,9 @@ def component_dependencies_tag():
"""Marks location where CSS link and JS script tags should be rendered.""" """Marks location where CSS link and JS script tags should be rendered."""
if is_dependency_middleware_active(): if is_dependency_middleware_active():
return mark_safe(CSS_DEPENDENCY_PLACEHOLDER + JS_DEPENDENCY_PLACEHOLDER) return mark_safe(
CSS_DEPENDENCY_PLACEHOLDER + JS_DEPENDENCY_PLACEHOLDER
)
else: else:
rendered_dependencies = [] rendered_dependencies = []
for component in get_components_from_registry(registry): for component in get_components_from_registry(registry):
@ -66,12 +71,19 @@ def component_js_dependencies_tag():
return mark_safe("\n".join(rendered_dependencies)) return mark_safe("\n".join(rendered_dependencies))
@register.tag(name='component') @register.tag(name="component")
def do_component(parser, token): def do_component(parser, token):
bits = token.split_contents() bits = token.split_contents()
bits, isolated_context = check_for_isolated_context_keyword(bits) bits, isolated_context = check_for_isolated_context_keyword(bits)
component, context_args, context_kwargs = parse_component_with_args(parser, bits, 'component') component, context_args, context_kwargs = parse_component_with_args(
return ComponentNode(component, context_args, context_kwargs, isolated_context=isolated_context) parser, bits, "component"
)
return ComponentNode(
component,
context_args,
context_kwargs,
isolated_context=isolated_context,
)
class SlotNode(Node): class SlotNode(Node):
@ -92,17 +104,25 @@ class SlotNode(Node):
cloned_node.parent_component = self.parent_component cloned_node.parent_component = self.parent_component
cloned_node.context = context cloned_node.context = context
with context.update({'slot': cloned_node}): with context.update({"slot": cloned_node}):
return self.get_nodelist(context).render(context) return self.get_nodelist(context).render(context)
def get_nodelist(self, context): def get_nodelist(self, context):
if ACTIVE_SLOT_CONTEXT_KEY not in context: if ACTIVE_SLOT_CONTEXT_KEY not in context:
raise TemplateSyntaxError(f'Attempted to render SlotNode {self.name} outside of a parent Component or ' raise TemplateSyntaxError(
'without access to context provided by its parent Component. This will not' f"Attempted to render SlotNode {self.name} outside of a parent Component or "
'work properly.') "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) overriding_nodelist = context[ACTIVE_SLOT_CONTEXT_KEY].get(
return overriding_nodelist if overriding_nodelist is not None else self.nodelist self.name, None
)
return (
overriding_nodelist
if overriding_nodelist is not None
else self.nodelist
)
def super(self): def super(self):
"""Render default slot content.""" """Render default slot content."""
@ -125,9 +145,18 @@ def do_slot(parser, token, component=None):
class ComponentNode(Node): class ComponentNode(Node):
class InvalidSlot: class InvalidSlot:
def super(self): 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_args = context_args or []
self.context_kwargs = context_kwargs or {} self.context_kwargs = context_kwargs or {}
self.component, self.isolated_context = component, isolated_context self.component, self.isolated_context = component, isolated_context
@ -139,17 +168,26 @@ class ComponentNode(Node):
self.should_render_dependencies = is_dependency_middleware_active() self.should_render_dependencies = is_dependency_middleware_active()
def __repr__(self): def __repr__(self):
return "<Component Node: %s. Contents: %r>" % (self.component, return "<Component Node: %s. Contents: %r>" % (
getattr(self.component.instance_template, 'nodelist', None)) self.component,
getattr(self.component.instance_template, "nodelist", None),
)
def render(self, context): def render(self, context):
self.component.outer_context = context.flatten() self.component.outer_context = context.flatten()
# Resolve FilterExpressions and Variables that were passed as args to the component, then call component's # 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 # 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_args = [
resolved_context_kwargs = {key: safe_resolve(kwarg, context) for key, kwarg in self.context_kwargs.items()} safe_resolve(arg, context) for arg in self.context_args
component_context = self.component.get_context_data(*resolved_context_args, **resolved_context_kwargs) ]
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 # Create a fresh context if requested
if self.isolated_context: if self.isolated_context:
@ -158,7 +196,10 @@ class ComponentNode(Node):
with context.update(component_context): with context.update(component_context):
rendered_component = self.component.render(context) rendered_component = self.component.render(context)
if self.should_render_dependencies: if self.should_render_dependencies:
return f'<!-- _RENDERED {self.component._component_name} -->' + rendered_component return (
f"<!-- _RENDERED {self.component._component_name} -->"
+ rendered_component
)
else: else:
return rendered_component return rendered_component
@ -181,12 +222,20 @@ def do_component_block(parser, token):
bits = token.split_contents() bits = token.split_contents()
bits, isolated_context = check_for_isolated_context_keyword(bits) bits, isolated_context = check_for_isolated_context_keyword(bits)
component, 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, return ComponentNode(
slots=[do_slot(parser, slot_token, component=component) component,
for slot_token in slot_tokens(parser)], context_args,
isolated_context=isolated_context) 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): 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.""" Raises TemplateSyntaxError if there are other content tokens or if there is no endcomponent_block token."""
def is_whitespace(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): 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: while True:
try: try:
token = parser.next_token() token = parser.next_token()
except IndexError: except IndexError:
raise TemplateSyntaxError('Unclosed component_block tag') raise TemplateSyntaxError("Unclosed component_block tag")
if is_block_tag(token, name='endcomponent_block'): if is_block_tag(token, name="endcomponent_block"):
return return
elif is_block_tag(token, name='slot'): elif is_block_tag(token, name="slot"):
yield token yield token
elif not is_whitespace(token) and token.token_type != TokenType.COMMENT: elif (
raise TemplateSyntaxError(f'Content tokens in component blocks must be inside of slot tags: {token}') 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): def check_for_isolated_context_keyword(bits):
"""Return True and strip the last word if token ends with 'only' keyword.""" """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[:-1], True
return bits, False return bits, False
@ -235,20 +293,26 @@ def parse_component_with_args(parser, bits, tag_name):
kwonly_defaults=None, kwonly_defaults=None,
) )
assert tag_name == tag_args[0].token, "Internal error: Expected tag_name to be {}, but it was {}".format( assert (
tag_name, tag_args[0].token) tag_name == tag_args[0].token
if len(tag_args) > 1: # At least one position arg, so take the first as the component name ), "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 component_name = tag_args[1].token
context_args = tag_args[2:] context_args = tag_args[2:]
context_kwargs = tag_kwargs context_kwargs = tag_kwargs
else: # No positional args, so look for component name as keyword arg else: # No positional args, so look for component name as keyword arg
try: try:
component_name = tag_kwargs.pop('name').token component_name = tag_kwargs.pop("name").token
context_args = [] context_args = []
context_kwargs = tag_kwargs context_kwargs = tag_kwargs
except IndexError: except IndexError:
raise TemplateSyntaxError( 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): if not is_wrapped_in_quotes(component_name):
@ -266,7 +330,11 @@ def parse_component_with_args(parser, bits, tag_name):
def safe_resolve(context_item, context): def safe_resolve(context_item, context):
"""Resolve FilterExpressions and Variables in context if possible. Return other items unchanged.""" """Resolve FilterExpressions and Variables in context if possible. Return other items unchanged."""
return 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): def is_wrapped_in_quotes(s):
@ -274,4 +342,6 @@ def is_wrapped_in_quotes(s):
def is_dependency_middleware_active(): def is_dependency_middleware_active():
return getattr(settings, "COMPONENTS", {}).get('RENDER_DEPENDENCIES', False) return getattr(settings, "COMPONENTS", {}).get(
"RENDER_DEPENDENCIES", False
)

View file

@ -3,14 +3,16 @@ import os
from setuptools import find_packages, setup from setuptools import find_packages, setup
VERSION = '0.16' VERSION = "0.16"
setup( setup(
name="django_components", name="django_components",
packages=find_packages(exclude=["tests"]), packages=find_packages(exclude=["tests"]),
version=VERSION, version=VERSION,
description="A way to create simple reusable template components in Django.", description="A way to create simple reusable template components in Django.",
long_description=open(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", long_description_content_type="text/markdown",
author=u"Emil Stenström", author=u"Emil Stenström",
author_email="emil@emilstenstrom.se", author_email="emil@emilstenstrom.se",

View file

@ -3,15 +3,17 @@ from django.conf import settings
if not settings.configured: if not settings.configured:
settings.configure( settings.configure(
INSTALLED_APPS=('django_components',), INSTALLED_APPS=("django_components",),
TEMPLATES=[{ TEMPLATES=[
'BACKEND': 'django.template.backends.django.DjangoTemplates', {
'DIRS': ["tests/templates/"], "BACKEND": "django.template.backends.django.DjangoTemplates",
}], "DIRS": ["tests/templates/"],
COMPONENTS={ }
'TEMPLATE_CACHE_SIZE': 128 ],
}, COMPONENTS={"TEMPLATE_CACHE_SIZE": 128},
MIDDLEWARE=['django_components.middleware.ComponentDependencyMiddleware'], MIDDLEWARE=[
"django_components.middleware.ComponentDependencyMiddleware"
],
DATABASES={}, DATABASES={},
) )

View file

@ -1,11 +1,11 @@
from textwrap import dedent from textwrap import dedent
from django.template import Context, Template
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.template import Context, Template
from .django_test_setup import * # NOQA
from django_components import component from django_components import component
from .django_test_setup import * # NOQA
from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
@ -33,14 +33,24 @@ class ComponentTest(SimpleTestCase):
comp = SimpleComponent("simple_component") comp = SimpleComponent("simple_component")
context = Context(comp.get_context_data(variable="test")) 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"> <link href="style.css" type="text/css" media="all" rel="stylesheet">
<script src="script.js"></script> <script src="script.js"></script>
""").strip()) """
).strip(),
)
self.assertHTMLEqual(comp.render(context), dedent(""" self.assertHTMLEqual(
comp.render(context),
dedent(
"""
Variable: <strong>test</strong> Variable: <strong>test</strong>
""").lstrip()) """
).lstrip(),
)
def test_component_with_list_of_styles(self): def test_component_with_list_of_styles(self):
class MultistyleComponent(component.Component): class MultistyleComponent(component.Component):
@ -50,12 +60,17 @@ class ComponentTest(SimpleTestCase):
comp = MultistyleComponent("multistyle_component") 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="style.css" type="text/css" media="all" rel="stylesheet">
<link href="style2.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="script.js"></script>
<script src="script2.js"></script> <script src="script2.js"></script>
""").strip()) """
).strip(),
)
def test_component_with_filtered_template(self): def test_component_with_filtered_template(self):
class FilteredComponent(component.Component): class FilteredComponent(component.Component):
@ -70,15 +85,25 @@ class ComponentTest(SimpleTestCase):
comp = FilteredComponent("filtered_component") comp = FilteredComponent("filtered_component")
context = Context(comp.get_context_data(var1="test1", var2="test2")) 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> Var1: <strong>test1</strong>
Var2 (uppercased): <strong>TEST2</strong> Var2 (uppercased): <strong>TEST2</strong>
""").lstrip()) """
).lstrip(),
)
def test_component_with_dynamic_template(self): def test_component_with_dynamic_template(self):
class SvgComponent(component.Component): class SvgComponent(component.Component):
def get_context_data(self, name, css_class="", title="", **attrs): 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): def get_template_name(self, context):
return f"svg_{context['name']}.svg" return f"svg_{context['name']}.svg"
@ -86,15 +111,19 @@ class ComponentTest(SimpleTestCase):
comp = SvgComponent("svg_component") comp = SvgComponent("svg_component")
self.assertHTMLEqual( self.assertHTMLEqual(
comp.render(Context(comp.get_context_data(name="dynamic1"))), comp.render(Context(comp.get_context_data(name="dynamic1"))),
dedent("""\ dedent(
"""\
<svg>Dynamic1</svg> <svg>Dynamic1</svg>
""") """
),
) )
self.assertHTMLEqual( self.assertHTMLEqual(
comp.render(Context(comp.get_context_data(name="dynamic2"))), comp.render(Context(comp.get_context_data(name="dynamic2"))),
dedent("""\ dedent(
"""\
<svg>Dynamic2</svg> <svg>Dynamic2</svg>
""") """
),
) )
@ -108,10 +137,12 @@ class ComponentMediaTests(SimpleTestCase):
comp = SimpleComponent("") comp = SimpleComponent("")
self.assertHTMLEqual( self.assertHTMLEqual(
comp.render_dependencies(), comp.render_dependencies(),
dedent("""\ dedent(
"""\
<link href="path/to/style.css" type="text/css" media="all" rel="stylesheet"> <link href="path/to/style.css" type="text/css" media="all" rel="stylesheet">
<script src="path/to/script.js"></script> <script src="path/to/script.js"></script>
""") """
),
) )
def test_component_media_with_lists(self): def test_component_media_with_lists(self):
@ -123,11 +154,13 @@ class ComponentMediaTests(SimpleTestCase):
comp = SimpleComponent("") comp = SimpleComponent("")
self.assertHTMLEqual( self.assertHTMLEqual(
comp.render_dependencies(), comp.render_dependencies(),
dedent("""\ dedent(
"""\
<link href="path/to/style.css" type="text/css" media="all" rel="stylesheet"> <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"> <link href="path/to/style2.css" type="text/css" media="all" rel="stylesheet">
<script src="path/to/script.js"></script> <script src="path/to/script.js"></script>
""") """
),
) )
def test_component_media_with_dict_and_list(self): def test_component_media_with_dict_and_list(self):
@ -143,12 +176,14 @@ class ComponentMediaTests(SimpleTestCase):
comp = SimpleComponent("") comp = SimpleComponent("")
self.assertHTMLEqual( self.assertHTMLEqual(
comp.render_dependencies(), comp.render_dependencies(),
dedent("""\ dedent(
"""\
<link href="path/to/style.css" type="text/css" media="all" rel="stylesheet"> <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/style2.css" type="text/css" media="print" rel="stylesheet">
<link href="path/to/style3.css" type="text/css" media="screen" rel="stylesheet"> <link href="path/to/style3.css" type="text/css" media="screen" rel="stylesheet">
<script src="path/to/script.js"></script> <script src="path/to/script.js"></script>
""") """
),
) )
def test_component_media_with_dict_with_list_and_list(self): def test_component_media_with_dict_with_list_and_list(self):
@ -160,10 +195,12 @@ class ComponentMediaTests(SimpleTestCase):
comp = SimpleComponent("") comp = SimpleComponent("")
self.assertHTMLEqual( self.assertHTMLEqual(
comp.render_dependencies(), comp.render_dependencies(),
dedent("""\ dedent(
"""\
<link href="path/to/style.css" type="text/css" media="all" rel="stylesheet"> <link href="path/to/style.css" type="text/css" media="all" rel="stylesheet">
<script src="path/to/script.js"></script> <script src="path/to/script.js"></script>
""") """
),
) )
@ -172,7 +209,7 @@ class ComponentIsolationTests(SimpleTestCase):
class SlottedComponent(component.Component): class SlottedComponent(component.Component):
template_name = "slotted_template.html" 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): def test_instances_of_component_do_not_share_slots(self):
template = Template( template = Template(
@ -211,17 +248,17 @@ class ComponentIsolationTests(SimpleTestCase):
<main>Default main</main> <main>Default main</main>
<footer>Override footer</footer> <footer>Override footer</footer>
</custom-template> </custom-template>
""" """,
) )
class RecursiveSlotNameTest(SimpleTestCase): class RecursiveSlotNameTest(SimpleTestCase):
def setUp(self): def setUp(self):
@component.register('outer') @component.register("outer")
class OuterComponent(component.Component): class OuterComponent(component.Component):
template_name = "slotted_template.html" template_name = "slotted_template.html"
@component.register('inner') @component.register("inner")
class InnerComponent(component.Component): class InnerComponent(component.Component):
template_name = "slotted_template.html" template_name = "slotted_template.html"
@ -251,5 +288,5 @@ class RecursiveSlotNameTest(SimpleTestCase):
<main>Default main</main> <main>Default main</main>
<footer>Default footer</footer> <footer>Default footer</footer>
</custom-template> </custom-template>
""" """,
) )

View file

@ -1,9 +1,8 @@
from django.template import Context, Template from django.template import Context, Template
from .django_test_setup import * # NOQA
from django_components import component from django_components import component
from .django_test_setup import * # NOQA
from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
@ -15,16 +14,14 @@ class SimpleComponent(component.Component):
@staticmethod @staticmethod
def expected_output(variable_value): def expected_output(variable_value):
return 'Variable: < strong > {} < / strong >'.format(variable_value) return "Variable: < strong > {} < / strong >".format(variable_value)
class ParentComponent(component.Component): class ParentComponent(component.Component):
template_name = "parent_template.html" template_name = "parent_template.html"
def get_context_data(self): def get_context_data(self):
return { return {"shadowing_variable": "NOT SHADOWED"}
"shadowing_variable": 'NOT SHADOWED'
}
class ParentComponentWithArgs(component.Component): class ParentComponentWithArgs(component.Component):
@ -40,9 +37,9 @@ class VariableDisplay(component.Component):
def get_context_data(self, shadowing_variable=None, new_variable=None): def get_context_data(self, shadowing_variable=None, new_variable=None):
context = {} context = {}
if shadowing_variable is not None: if shadowing_variable is not None:
context['shadowing_variable'] = shadowing_variable context["shadowing_variable"] = shadowing_variable
if new_variable is not None: if new_variable is not None:
context['unique_variable'] = new_variable context["unique_variable"] = new_variable
return context return context
@ -51,15 +48,11 @@ class IncrementerComponent(component.Component):
def get_context_data(self, value=0): def get_context_data(self, value=0):
value = int(value) value = int(value)
if hasattr(self, 'call_count'): if hasattr(self, "call_count"):
self.call_count += 1 self.call_count += 1
else: else:
self.call_count = 1 self.call_count = 1
return { return {"value": value + 1, "calls": self.call_count}
"value": value + 1,
"calls": self.call_count
}
class OuterContextComponent(component.Component): class OuterContextComponent(component.Component):
@ -69,212 +62,394 @@ class OuterContextComponent(component.Component):
return self.outer_context return self.outer_context
component.registry.register(name='parent_component', component=ParentComponent) component.registry.register(name="parent_component", component=ParentComponent)
component.registry.register(name='parent_with_args', component=ParentComponentWithArgs) component.registry.register(
component.registry.register(name='variable_display', component=VariableDisplay) name="parent_with_args", component=ParentComponentWithArgs
component.registry.register(name='incrementer', component=IncrementerComponent) )
component.registry.register(name='simple_component', component=SimpleComponent) component.registry.register(name="variable_display", component=VariableDisplay)
component.registry.register(name='outer_context_component', component=OuterContextComponent) 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): class ContextTests(SimpleTestCase):
def test_nested_component_context_shadows_parent_with_unfilled_slots_and_component_tag(self): def test_nested_component_context_shadows_parent_with_unfilled_slots_and_component_tag(
template = Template("{% load component_tags %}{% component_dependencies %}" self,
"{% component 'parent_component' %}") ):
template = Template(
"{% load component_tags %}{% component_dependencies %}"
"{% component 'parent_component' %}"
)
rendered = template.render(Context()) rendered = template.render(Context())
self.assertIn('<h1>Shadowing variable = override</h1>', rendered, rendered) self.assertIn(
self.assertIn('<h1>Shadowing variable = slot_default_override</h1>', rendered, rendered) "<h1>Shadowing variable = override</h1>", rendered, rendered
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</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): def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_tag(
template = Template("{% load component_tags %}{% component_dependencies %}" self,
"{% component name='parent_component' %}") ):
template = Template(
"{% load component_tags %}{% component_dependencies %}"
"{% component name='parent_component' %}"
)
rendered = template.render(Context()) rendered = template.render(Context())
self.assertIn('<h1>Uniquely named variable = unique_val</h1>', rendered, rendered) self.assertIn(
self.assertIn('<h1>Uniquely named variable = slot_default_unique</h1>', rendered, rendered) "<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): def test_nested_component_context_shadows_parent_with_unfilled_slots_and_component_block_tag(
template = Template("{% load component_tags %}{% component_dependencies %}" self,
"{% component_block 'parent_component' %}{% endcomponent_block %}") ):
template = Template(
"{% load component_tags %}{% component_dependencies %}"
"{% component_block 'parent_component' %}{% endcomponent_block %}"
)
rendered = template.render(Context()) rendered = template.render(Context())
self.assertIn('<h1>Shadowing variable = override</h1>', rendered, rendered) self.assertIn(
self.assertIn('<h1>Shadowing variable = slot_default_override</h1>', rendered, rendered) "<h1>Shadowing variable = override</h1>", rendered, rendered
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</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): def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_block_tag(
template = Template("{% load component_tags %}{% component_dependencies %}" self,
"{% component_block 'parent_component' %}{% endcomponent_block %}") ):
template = Template(
"{% load component_tags %}{% component_dependencies %}"
"{% component_block 'parent_component' %}{% endcomponent_block %}"
)
rendered = template.render(Context()) rendered = template.render(Context())
self.assertIn('<h1>Uniquely named variable = unique_val</h1>', rendered, rendered) self.assertIn(
self.assertIn('<h1>Uniquely named variable = slot_default_unique</h1>', rendered, rendered) "<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): 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' %}" "{% component_block 'parent_component' %}"
"{% slot 'content' %}{% component name='variable_display' " "{% slot 'content' %}{% component name='variable_display' "
"shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}" "shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}"
"{% endcomponent_block %}") "{% endcomponent_block %}"
)
rendered = template.render(Context()) rendered = template.render(Context())
self.assertIn('<h1>Shadowing variable = override</h1>', rendered, rendered) self.assertIn(
self.assertIn('<h1>Shadowing variable = shadow_from_slot</h1>', rendered, rendered) "<h1>Shadowing variable = override</h1>", rendered, rendered
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</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): def test_nested_component_instances_have_unique_context_with_filled_slots(
template = Template("{% load component_tags %}{% component_dependencies %}" self,
):
template = Template(
"{% load component_tags %}{% component_dependencies %}"
"{% component_block 'parent_component' %}" "{% component_block 'parent_component' %}"
"{% slot 'content' %}{% component name='variable_display' " "{% slot 'content' %}{% component name='variable_display' "
"shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}" "shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}"
"{% endcomponent_block %}") "{% endcomponent_block %}"
)
rendered = template.render(Context()) rendered = template.render(Context())
self.assertIn('<h1>Uniquely named variable = unique_val</h1>', rendered, rendered) self.assertIn(
self.assertIn('<h1>Uniquely named variable = unique_from_slot</h1>', rendered, rendered) "<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): def test_nested_component_context_shadows_outer_context_with_unfilled_slots_and_component_tag(
template = Template("{% load component_tags %}{% component_dependencies %}" self,
"{% component name='parent_component' %}") ):
rendered = template.render(Context({'shadowing_variable': 'NOT SHADOWED'})) 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(
self.assertIn('<h1>Shadowing variable = slot_default_override</h1>', rendered, rendered) "<h1>Shadowing variable = override</h1>", rendered, rendered
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</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): def test_nested_component_context_shadows_outer_context_with_unfilled_slots_and_component_block_tag(
template = Template("{% load component_tags %}{% component_dependencies %}" self,
"{% component_block 'parent_component' %}{% endcomponent_block %}") ):
rendered = template.render(Context({'shadowing_variable': 'NOT SHADOWED'})) 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(
self.assertIn('<h1>Shadowing variable = slot_default_override</h1>', rendered, rendered) "<h1>Shadowing variable = override</h1>", rendered, rendered
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</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): def test_nested_component_context_shadows_outer_context_with_filled_slots(
template = Template("{% load component_tags %}{% component_dependencies %}" self,
):
template = Template(
"{% load component_tags %}{% component_dependencies %}"
"{% component_block 'parent_component' %}" "{% component_block 'parent_component' %}"
"{% slot 'content' %}{% component name='variable_display' " "{% slot 'content' %}{% component name='variable_display' "
"shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}" "shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}"
"{% endcomponent_block %}") "{% endcomponent_block %}"
rendered = template.render(Context({'shadowing_variable': 'NOT SHADOWED'})) )
rendered = template.render(
Context({"shadowing_variable": "NOT SHADOWED"})
)
self.assertIn('<h1>Shadowing variable = override</h1>', rendered, rendered) self.assertIn(
self.assertIn('<h1>Shadowing variable = shadow_from_slot</h1>', rendered, rendered) "<h1>Shadowing variable = override</h1>", rendered, rendered
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</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): class ParentArgsTests(SimpleTestCase):
def test_parent_args_can_be_drawn_from_context(self): 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 %}" "{% component_block 'parent_with_args' parent_value=parent_value %}"
"{% endcomponent_block %}") "{% endcomponent_block %}"
rendered = template.render(Context({'parent_value': 'passed_in'})) )
rendered = template.render(Context({"parent_value": "passed_in"}))
self.assertIn('<h1>Shadowing variable = passed_in</h1>', rendered, rendered) self.assertIn(
self.assertIn('<h1>Uniquely named variable = passed_in</h1>', rendered, rendered) "<h1>Shadowing variable = passed_in</h1>", rendered, rendered
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</h1>', rendered, rendered) )
self.assertIn(
"<h1>Uniquely named variable = passed_in</h1>", rendered, rendered
)
self.assertNotIn(
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
)
def test_parent_args_available_outside_slots(self): def test_parent_args_available_outside_slots(self):
template = Template("{% load component_tags %}{% component_dependencies %}" template = Template(
"{% component_block 'parent_with_args' parent_value='passed_in' %}{%endcomponent_block %}") "{% load component_tags %}{% component_dependencies %}"
"{% component_block 'parent_with_args' parent_value='passed_in' %}{%endcomponent_block %}"
)
rendered = template.render(Context()) rendered = template.render(Context())
self.assertIn('<h1>Shadowing variable = passed_in</h1>', rendered, rendered) self.assertIn(
self.assertIn('<h1>Uniquely named variable = passed_in</h1>', rendered, rendered) "<h1>Shadowing variable = passed_in</h1>", rendered, rendered
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</h1>', rendered, rendered) )
self.assertIn(
"<h1>Uniquely named variable = passed_in</h1>", rendered, rendered
)
self.assertNotIn(
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
)
def test_parent_args_available_in_slots(self): def test_parent_args_available_in_slots(self):
template = Template("{% load component_tags %}{% component_dependencies %}" template = Template(
"{% load component_tags %}{% component_dependencies %}"
"{% component_block 'parent_with_args' parent_value='passed_in' %}" "{% component_block 'parent_with_args' parent_value='passed_in' %}"
"{% slot 'content' %}{% component name='variable_display' " "{% slot 'content' %}{% component name='variable_display' "
"shadowing_variable='value_from_slot' new_variable=inner_parent_value %}{% endslot %}" "shadowing_variable='value_from_slot' new_variable=inner_parent_value %}{% endslot %}"
"{%endcomponent_block %}") "{%endcomponent_block %}"
)
rendered = template.render(Context()) rendered = template.render(Context())
self.assertIn('<h1>Shadowing variable = value_from_slot</h1>', rendered, rendered) self.assertIn(
self.assertIn('<h1>Uniquely named variable = passed_in</h1>', rendered, rendered) "<h1>Shadowing variable = value_from_slot</h1>", rendered, rendered
self.assertNotIn('<h1>Shadowing variable = NOT SHADOWED</h1>', rendered, rendered) )
self.assertIn(
"<h1>Uniquely named variable = passed_in</h1>", rendered, rendered
)
self.assertNotIn(
"<h1>Shadowing variable = NOT SHADOWED</h1>", rendered, rendered
)
class ContextCalledOnceTests(SimpleTestCase): class ContextCalledOnceTests(SimpleTestCase):
def test_one_context_call_with_simple_component(self): def test_one_context_call_with_simple_component(self):
template = Template("{% load component_tags %}{% component_dependencies %}" template = Template(
"{% component name='incrementer' %}") "{% load component_tags %}{% component_dependencies %}"
"{% component name='incrementer' %}"
)
rendered = template.render(Context()).strip() 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): 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() 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): def test_one_context_call_with_component_block(self):
template = Template("{% load component_tags %}" template = Template(
"{% component_block 'incrementer' %}{% endcomponent_block %}") "{% load component_tags %}"
"{% component_block 'incrementer' %}{% endcomponent_block %}"
)
rendered = template.render(Context()).strip() rendered = template.render(Context()).strip()
self.assertEqual(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): def test_one_context_call_with_component_block_and_arg(self):
template = Template("{% load component_tags %}" template = Template(
"{% component_block 'incrementer' value='3' %}{% endcomponent_block %}") "{% load component_tags %}"
"{% component_block 'incrementer' value='3' %}{% endcomponent_block %}"
)
rendered = template.render(Context()).strip() 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): def test_one_context_call_with_slot(self):
template = Template("{% load component_tags %}" template = Template(
"{% load component_tags %}"
"{% component_block 'incrementer' %}{% slot 'content' %}" "{% component_block 'incrementer' %}{% slot 'content' %}"
"<p>slot</p>{% endslot %}{% endcomponent_block %}") "<p>slot</p>{% endslot %}{% endcomponent_block %}"
)
rendered = template.render(Context()).strip() 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): 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' %}" "{% component_block 'incrementer' value='3' %}{% slot 'content' %}"
"<p>slot</p>{% endslot %}{% endcomponent_block %}") "<p>slot</p>{% endslot %}{% endcomponent_block %}"
)
rendered = template.render(Context()).strip() 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): class ComponentsCanAccessOuterContext(SimpleTestCase):
def test_simple_component_can_use_outer_context(self): def test_simple_component_can_use_outer_context(self):
template = Template("{% load component_tags %}{% component_dependencies %}" template = Template(
"{% component 'simple_component' %}") "{% load component_tags %}{% component_dependencies %}"
rendered = template.render(Context({'variable': 'outer_value'})).strip() "{% component 'simple_component' %}"
self.assertIn('outer_value', rendered, rendered) )
rendered = template.render(
Context({"variable": "outer_value"})
).strip()
self.assertIn("outer_value", rendered, rendered)
class IsolatedContextTests(SimpleTestCase): class IsolatedContextTests(SimpleTestCase):
def test_simple_component_can_pass_outer_context_in_args(self): def test_simple_component_can_pass_outer_context_in_args(self):
template = Template("{% load component_tags %}{% component_dependencies %}" template = Template(
"{% component 'simple_component' variable only %}") "{% load component_tags %}{% component_dependencies %}"
rendered = template.render(Context({'variable': 'outer_value'})).strip() "{% component 'simple_component' variable only %}"
self.assertIn('outer_value', rendered, rendered) )
rendered = template.render(
Context({"variable": "outer_value"})
).strip()
self.assertIn("outer_value", rendered, rendered)
def test_simple_component_cannot_use_outer_context(self): def test_simple_component_cannot_use_outer_context(self):
template = Template("{% load component_tags %}{% component_dependencies %}" template = Template(
"{% component 'simple_component' only %}") "{% load component_tags %}{% component_dependencies %}"
rendered = template.render(Context({'variable': 'outer_value'})).strip() "{% component 'simple_component' only %}"
self.assertNotIn('outer_value', rendered, rendered) )
rendered = template.render(
Context({"variable": "outer_value"})
).strip()
self.assertNotIn("outer_value", rendered, rendered)
class OuterContextPropertyTests(SimpleTestCase): class OuterContextPropertyTests(SimpleTestCase):
def test_outer_context_property_with_component(self): def test_outer_context_property_with_component(self):
template = Template("{% load component_tags %}{% component_dependencies %}" template = Template(
"{% component 'outer_context_component' only %}") "{% load component_tags %}{% component_dependencies %}"
rendered = template.render(Context({'variable': 'outer_value'})).strip() "{% component 'outer_context_component' only %}"
self.assertIn('outer_value', rendered, rendered) )
rendered = template.render(
Context({"variable": "outer_value"})
).strip()
self.assertIn("outer_value", rendered, rendered)
def test_outer_context_property_with_component_block(self): def test_outer_context_property_with_component_block(self):
template = Template("{% load component_tags %}{% component_dependencies %}" template = Template(
"{% component_block 'outer_context_component' only %}{% endcomponent_block %}") "{% load component_tags %}{% component_dependencies %}"
rendered = template.render(Context({'variable': 'outer_value'})).strip() "{% component_block 'outer_context_component' only %}{% endcomponent_block %}"
self.assertIn('outer_value', rendered, rendered) )
rendered = template.render(
Context({"variable": "outer_value"})
).strip()
self.assertIn("outer_value", rendered, rendered)

View file

@ -1,11 +1,12 @@
from django.template import Template from django.template import Template
from django.test import override_settings from django.test import override_settings
from .django_test_setup import * # NOQA
from django_components import component from django_components import component
from .django_test_setup import * # NOQA
from .test_templatetags import SimpleComponent 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): class SimpleComponentAlternate(component.Component):
@ -38,7 +39,7 @@ class MultistyleComponent(component.Component):
js = ["script.js", "script2.js"] js = ["script.js", "script2.js"]
@override_settings(COMPONENTS={'RENDER_DEPENDENCIES': True}) @override_settings(COMPONENTS={"RENDER_DEPENDENCIES": True})
class ComponentMediaRenderingTests(SimpleTestCase): class ComponentMediaRenderingTests(SimpleTestCase):
def setUp(self): def setUp(self):
# NOTE: component.registry is global, so need to clear before each test # 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): def test_no_dependencies_when_no_components_used(self):
component.registry.register(name="test", component=SimpleComponent) component.registry.register(name="test", component=SimpleComponent)
template = Template("{% load component_tags %}{% component_dependencies %}") template = Template(
"{% load component_tags %}{% component_dependencies %}"
)
rendered = create_and_process_template_response(template) rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js>"', rendered, count=0) self.assertInHTML('<script src="script.js>"', rendered, count=0)
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0) self.assertInHTML(
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0) '<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): def test_no_js_dependencies_when_no_components_used(self):
component.registry.register(name="test", component=SimpleComponent) component.registry.register(name="test", component=SimpleComponent)
template = Template("{% load component_tags %}{% component_js_dependencies %}") template = Template(
"{% load component_tags %}{% component_js_dependencies %}"
)
rendered = create_and_process_template_response(template) rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js>"', rendered, count=0) self.assertInHTML('<script src="script.js>"', rendered, count=0)
def test_no_css_dependencies_when_no_components_used(self): def test_no_css_dependencies_when_no_components_used(self):
component.registry.register(name="test", component=SimpleComponent) component.registry.register(name="test", component=SimpleComponent)
template = Template("{% load component_tags %}{% component_css_dependencies %}") template = Template(
"{% load component_tags %}{% component_css_dependencies %}"
)
rendered = create_and_process_template_response(template) 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): def test_single_component_dependencies_render_when_used(self):
component.registry.register(name="test", component=SimpleComponent) component.registry.register(name="test", component=SimpleComponent)
template = Template("{% load component_tags %}{% component_dependencies %}" template = Template(
"{% component 'test' variable='foo' %}") "{% load component_tags %}{% component_dependencies %}"
"{% component 'test' variable='foo' %}"
)
rendered = create_and_process_template_response(template) 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) self.assertInHTML('<script src="script.js">', rendered, count=1)
def test_placeholder_removed_when_single_component_rendered(self): def test_placeholder_removed_when_single_component_rendered(self):
component.registry.register(name="test", component=SimpleComponent) component.registry.register(name="test", component=SimpleComponent)
template = Template("{% load component_tags %}{% component_dependencies %}" template = Template(
"{% component 'test' variable='foo' %}") "{% load component_tags %}{% component_dependencies %}"
"{% component 'test' variable='foo' %}"
)
rendered = create_and_process_template_response(template) rendered = create_and_process_template_response(template)
self.assertNotIn('_RENDERED', rendered) self.assertNotIn("_RENDERED", rendered)
def test_single_component_css_dependencies(self): def test_single_component_css_dependencies(self):
component.registry.register(name="test", component=SimpleComponent) component.registry.register(name="test", component=SimpleComponent)
template = Template("{% load component_tags %}{% component_css_dependencies %}" template = Template(
"{% component 'test' variable='foo' %}") "{% load component_tags %}{% component_css_dependencies %}"
"{% component 'test' variable='foo' %}"
)
rendered = create_and_process_template_response(template) 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): def test_single_component_js_dependencies(self):
component.registry.register(name="test", component=SimpleComponent) component.registry.register(name="test", component=SimpleComponent)
template = Template("{% load component_tags %}{% component_js_dependencies %}" template = Template(
"{% component 'test' variable='foo' %}") "{% load component_tags %}{% component_js_dependencies %}"
"{% component 'test' variable='foo' %}"
)
rendered = create_and_process_template_response(template) rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js">', rendered, count=1) self.assertInHTML('<script src="script.js">', rendered, count=1)
def test_all_dependencies_are_rendered_for_component_with_multiple_dependencies(self): def test_all_dependencies_are_rendered_for_component_with_multiple_dependencies(
component.registry.register(name='test', component=MultistyleComponent) self,
template = Template("{% load component_tags %}{% component_dependencies %}{% component 'test' %}") ):
component.registry.register(name="test", component=MultistyleComponent)
template = Template(
"{% load component_tags %}{% component_dependencies %}{% component 'test' %}"
)
rendered = create_and_process_template_response(template) rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js">', rendered, count=1) self.assertInHTML('<script src="script.js">', rendered, count=1)
self.assertInHTML('<script src="script2.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(
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1) '<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): def test_all_js_dependencies_are_rendered_for_component_with_multiple_dependencies(
component.registry.register(name='test', component=MultistyleComponent) self,
template = Template("{% load component_tags %}{% component_js_dependencies %}{% component 'test' %}") ):
component.registry.register(name="test", component=MultistyleComponent)
template = Template(
"{% load component_tags %}{% component_js_dependencies %}{% component 'test' %}"
)
rendered = create_and_process_template_response(template) rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js">', rendered, count=1) self.assertInHTML('<script src="script.js">', rendered, count=1)
self.assertInHTML('<script src="script2.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(
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0) '<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): def test_all_css_dependencies_are_rendered_for_component_with_multiple_dependencies(
component.registry.register(name='test', component=MultistyleComponent) self,
template = Template("{% load component_tags %}{% component_css_dependencies %}{% component 'test' %}") ):
component.registry.register(name="test", component=MultistyleComponent)
template = Template(
"{% load component_tags %}{% component_css_dependencies %}{% component 'test' %}"
)
rendered = create_and_process_template_response(template) rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js">', rendered, count=0) self.assertInHTML('<script src="script.js">', rendered, count=0)
self.assertInHTML('<script src="script2.js">', rendered, count=0) self.assertInHTML('<script src="script2.js">', rendered, count=0)
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1) self.assertInHTML(
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1) '<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): def test_no_dependencies_with_multiple_unused_components(self):
component.registry.register(name="test1", component=SimpleComponent) component.registry.register(name="test1", component=SimpleComponent)
component.registry.register(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) rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js">', rendered, count=0) self.assertInHTML('<script src="script.js">', rendered, count=0)
self.assertInHTML('<script src="script2.js">', rendered, count=0) self.assertInHTML('<script src="script2.js">', rendered, count=0)
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0) self.assertInHTML(
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0) '<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): def test_correct_css_dependencies_with_multiple_components(self):
component.registry.register(name="test1", component=SimpleComponent) component.registry.register(name="test1", component=SimpleComponent)
component.registry.register(name="test2", component=SimpleComponentAlternate) component.registry.register(
name="test2", component=SimpleComponentAlternate
)
template = Template("{% load component_tags %}{% component_css_dependencies %}" template = Template(
"{% component 'test1' 'variable' %}") "{% load component_tags %}{% component_css_dependencies %}"
"{% component 'test1' 'variable' %}"
)
rendered = create_and_process_template_response(template) 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(
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=0) '<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): def test_correct_js_dependencies_with_multiple_components(self):
component.registry.register(name="test1", component=SimpleComponent) component.registry.register(name="test1", component=SimpleComponent)
component.registry.register(name="test2", component=SimpleComponentAlternate) component.registry.register(
name="test2", component=SimpleComponentAlternate
)
template = Template("{% load component_tags %}{% component_js_dependencies %}" template = Template(
"{% component 'test1' 'variable' %}") "{% load component_tags %}{% component_js_dependencies %}"
"{% component 'test1' 'variable' %}"
)
rendered = create_and_process_template_response(template) rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js">', rendered, count=1) self.assertInHTML('<script src="script.js">', rendered, count=1)
self.assertInHTML('<script src="script2.js">', rendered, count=0) self.assertInHTML('<script src="script2.js">', rendered, count=0)
def test_correct_dependencies_with_multiple_components(self): def test_correct_dependencies_with_multiple_components(self):
component.registry.register(name="test1", component=SimpleComponent) component.registry.register(name="test1", component=SimpleComponent)
component.registry.register(name="test2", component=SimpleComponentAlternate) component.registry.register(
name="test2", component=SimpleComponentAlternate
)
template = Template("{% load component_tags %}{% component_dependencies %}" template = Template(
"{% component 'test2' variable='variable' %}") "{% load component_tags %}{% component_dependencies %}"
"{% component 'test2' variable='variable' %}"
)
rendered = create_and_process_template_response(template) rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js">', rendered, count=0) self.assertInHTML('<script src="script.js">', rendered, count=0)
self.assertInHTML('<script src="script2.js">', rendered, count=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(
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1) '<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): def test_shared_dependencies_rendered_once(self):
component.registry.register(name="test1", component=SimpleComponent) component.registry.register(name="test1", component=SimpleComponent)
component.registry.register(name="test2", component=SimpleComponentAlternate) component.registry.register(
component.registry.register(name="test3", component=SimpleComponentWithSharedDependency) 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 'test2' variable='variable' %}"
"{% component 'test1' variable='variable' %}") "{% component 'test1' variable='variable' %}"
)
rendered = create_and_process_template_response(template) rendered = create_and_process_template_response(template)
self.assertInHTML('<script src="script.js">', rendered, count=1) self.assertInHTML('<script src="script.js">', rendered, count=1)
self.assertInHTML('<script src="script2.js">', rendered, count=1) self.assertInHTML('<script src="script2.js">', rendered, count=1)
self.assertInHTML('<link href="style.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1) self.assertInHTML(
self.assertInHTML('<link href="style2.css" type="text/css" media="all" rel="stylesheet"/>', rendered, count=1) '<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): def test_placeholder_removed_when_multiple_component_rendered(self):
component.registry.register(name="test1", component=SimpleComponent) component.registry.register(name="test1", component=SimpleComponent)
component.registry.register(name="test2", component=SimpleComponentAlternate) component.registry.register(
component.registry.register(name="test3", component=SimpleComponentWithSharedDependency) 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 'test2' variable='variable' %}"
"{% component 'test1' variable='variable' %}") "{% component 'test1' variable='variable' %}"
)
rendered = create_and_process_template_response(template) rendered = create_and_process_template_response(template)
self.assertNotIn('_RENDERED', rendered) self.assertNotIn("_RENDERED", rendered)

View file

@ -1,8 +1,9 @@
import unittest import unittest
from .django_test_setup import * # NOQA
from django_components import component from django_components import component
from .django_test_setup import * # NOQA
class MockComponent(object): class MockComponent(object):
pass pass
@ -17,14 +18,13 @@ class ComponentRegistryTest(unittest.TestCase):
class TestComponent(component.Component): class TestComponent(component.Component):
pass pass
self.assertEqual(component.registry.get("decorated_component"), TestComponent) self.assertEqual(
component.registry.get("decorated_component"), TestComponent
)
def test_simple_register(self): def test_simple_register(self):
self.registry.register(name="testcomponent", component=MockComponent) self.registry.register(name="testcomponent", component=MockComponent)
self.assertEqual( self.assertEqual(self.registry.all(), {"testcomponent": MockComponent})
self.registry.all(),
{"testcomponent": MockComponent}
)
def test_register_two_components(self): def test_register_two_components(self):
self.registry.register(name="testcomponent", component=MockComponent) self.registry.register(name="testcomponent", component=MockComponent)
@ -34,13 +34,15 @@ class ComponentRegistryTest(unittest.TestCase):
{ {
"testcomponent": MockComponent, "testcomponent": MockComponent,
"testcomponent2": MockComponent, "testcomponent2": MockComponent,
} },
) )
def test_prevent_registering_twice(self): def test_prevent_registering_twice(self):
self.registry.register(name="testcomponent", component=MockComponent) self.registry.register(name="testcomponent", component=MockComponent)
with self.assertRaises(component.AlreadyRegistered): with self.assertRaises(component.AlreadyRegistered):
self.registry.register(name="testcomponent", component=MockComponent) self.registry.register(
name="testcomponent", component=MockComponent
)
def test_simple_unregister(self): def test_simple_unregister(self):
self.registry.register(name="testcomponent", component=MockComponent) self.registry.register(name="testcomponent", component=MockComponent)

View file

@ -2,8 +2,9 @@ from textwrap import dedent
from django.template import Context, Template, TemplateSyntaxError from django.template import Context, Template, TemplateSyntaxError
from .django_test_setup import * # NOQA
from django_components import component from django_components import component
from .django_test_setup import * # NOQA
from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase
@ -52,7 +53,7 @@ class ComponentWithProvidedAndDefaultParameters(component.Component):
template_name = "template_with_provided_and_default_parameters.html" template_name = "template_with_provided_and_default_parameters.html"
def get_context_data(self, variable, default_param="default text"): 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): class ComponentTemplateTagTest(SimpleTestCase):
@ -105,7 +106,7 @@ class ComponentTemplateTagTest(SimpleTestCase):
component.registry.register(name="test", component=SimpleComponent) component.registry.register(name="test", component=SimpleComponent)
template = Template( template = Template(
'{% load component_tags %}{% component \'test\' variable="variable" %}' "{% load component_tags %}{% component 'test' variable=\"variable\" %}"
) )
rendered = template.render(Context({})) rendered = template.render(Context({}))
self.assertHTMLEqual(rendered, "Variable: <strong>variable</strong>\n") self.assertHTMLEqual(rendered, "Variable: <strong>variable</strong>\n")
@ -147,7 +148,9 @@ class ComponentSlottedTemplateTagTest(SimpleTestCase):
) )
def test_slotted_template_with_context_var(self): def test_slotted_template_with_context_var(self):
component.registry.register(name="test1", component=SlottedComponentWithContext) component.registry.register(
name="test1", component=SlottedComponentWithContext
)
template = Template( template = Template(
""" """
@ -197,7 +200,9 @@ class ComponentSlottedTemplateTagTest(SimpleTestCase):
) )
def test_slotted_template_without_slots(self): def test_slotted_template_without_slots(self):
component.registry.register(name="test", component=SlottedComponentNoSlots) component.registry.register(
name="test", component=SlottedComponentNoSlots
)
template = Template( template = Template(
""" """
{% load component_tags %} {% load component_tags %}
@ -209,7 +214,9 @@ class ComponentSlottedTemplateTagTest(SimpleTestCase):
self.assertHTMLEqual(rendered, "<custom-template></custom-template>") self.assertHTMLEqual(rendered, "<custom-template></custom-template>")
def test_slotted_template_without_slots_and_single_quotes(self): def test_slotted_template_without_slots_and_single_quotes(self):
component.registry.register(name="test", component=SlottedComponentNoSlots) component.registry.register(
name="test", component=SlottedComponentNoSlots
)
template = Template( template = Template(
""" """
{% load component_tags %} {% load component_tags %}
@ -227,7 +234,9 @@ class SlottedTemplateRegressionTests(SimpleTestCase):
component.registry.clear() component.registry.clear()
def test_slotted_template_that_uses_missing_variable(self): def test_slotted_template_that_uses_missing_variable(self):
component.registry.register(name="test", component=SlottedComponentWithMissingVariable) component.registry.register(
name="test", component=SlottedComponentWithMissingVariable
)
template = Template( template = Template(
""" """
{% load component_tags %} {% load component_tags %}
@ -236,23 +245,30 @@ class SlottedTemplateRegressionTests(SimpleTestCase):
) )
rendered = template.render(Context({})) rendered = template.render(Context({}))
self.assertHTMLEqual(rendered, """ self.assertHTMLEqual(
rendered,
"""
<custom-template> <custom-template>
<header>Default header</header> <header>Default header</header>
<main>Default main</main> <main>Default main</main>
<footer>Default footer</footer> <footer>Default footer</footer>
</custom-template> </custom-template>
""") """,
)
def test_component_block_accepts_provided_and_default_parameters(self): 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( template = Template(
'{% load component_tags %}{% component_block "test" variable="provided value" %}{% endcomponent_block %}' '{% load component_tags %}{% component_block "test" variable="provided value" %}{% endcomponent_block %}'
) )
rendered = template.render(Context({})) rendered = template.render(Context({}))
self.assertHTMLEqual(rendered, self.assertHTMLEqual(
"Provided variable: <strong>provided value</strong>\nDefault: <p>default text</p>") rendered,
"Provided variable: <strong>provided value</strong>\nDefault: <p>default text</p>",
)
class MultiComponentTests(SimpleTestCase): class MultiComponentTests(SimpleTestCase):
@ -260,24 +276,38 @@ class MultiComponentTests(SimpleTestCase):
component.registry.clear() component.registry.clear()
def register_components(self): def register_components(self):
component.registry.register('first_component', SlottedComponent) component.registry.register("first_component", SlottedComponent)
component.registry.register('second_component', SlottedComponentWithContext) component.registry.register(
"second_component", SlottedComponentWithContext
)
def make_template(self, first_component_slot='', second_component_slot=''): def make_template(self, first_component_slot="", second_component_slot=""):
return Template('{% load component_tags %}' return Template(
"{% load component_tags %}"
"{% component_block 'first_component' %}" "{% component_block 'first_component' %}"
+ first_component_slot + '{% endcomponent_block %}' + first_component_slot
+ "{% endcomponent_block %}"
"{% component_block 'second_component' variable='xyz' %}" "{% component_block 'second_component' variable='xyz' %}"
+ second_component_slot + '{% endcomponent_block %}') + second_component_slot
+ "{% endcomponent_block %}"
)
def expected_result(self, first_component_slot='', second_component_slot=''): def expected_result(
return ('<custom-template><header>{}</header>'.format(first_component_slot or "Default header") self, first_component_slot="", second_component_slot=""
+ '<main>Default main</main><footer>Default footer</footer></custom-template>' ):
+ '<custom-template><header>{}</header>'.format(second_component_slot or "Default header") return (
+ '<main>Default main</main><footer>Default footer</footer></custom-template>') "<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): 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): def test_both_components_render_correctly_with_no_slots(self):
self.register_components() self.register_components()
@ -286,26 +316,35 @@ class MultiComponentTests(SimpleTestCase):
def test_both_components_render_correctly_with_slots(self): def test_both_components_render_correctly_with_slots(self):
self.register_components() self.register_components()
first_slot_content = '<p>Slot #1</p>' first_slot_content = "<p>Slot #1</p>"
second_slot_content = '<div>Slot #2</div>' second_slot_content = "<div>Slot #2</div>"
first_slot = self.wrap_with_slot_tags(first_slot_content) first_slot = self.wrap_with_slot_tags(first_slot_content)
second_slot = self.wrap_with_slot_tags(second_slot_content) second_slot = self.wrap_with_slot_tags(second_slot_content)
rendered = self.make_template(first_slot, second_slot).render(Context({})) rendered = self.make_template(first_slot, second_slot).render(
self.assertHTMLEqual(rendered, self.expected_result(first_slot_content, second_slot_content)) 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): def test_both_components_render_correctly_when_only_first_has_slots(self):
self.register_components() 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) first_slot = self.wrap_with_slot_tags(first_slot_content)
rendered = self.make_template(first_slot).render(Context({})) rendered = self.make_template(first_slot).render(Context({}))
self.assertHTMLEqual(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): def test_both_components_render_correctly_when_only_second_has_slots(self):
self.register_components() self.register_components()
second_slot_content = '<div>Slot #2</div>' second_slot_content = "<div>Slot #2</div>"
second_slot = self.wrap_with_slot_tags(second_slot_content) second_slot = self.wrap_with_slot_tags(second_slot_content)
rendered = self.make_template('', second_slot).render(Context({})) rendered = self.make_template("", second_slot).render(Context({}))
self.assertHTMLEqual(rendered, self.expected_result('', second_slot_content)) self.assertHTMLEqual(
rendered, self.expected_result("", second_slot_content)
)
class TemplateInstrumentationTest(SimpleTestCase): class TemplateInstrumentationTest(SimpleTestCase):
@ -323,8 +362,8 @@ class TemplateInstrumentationTest(SimpleTestCase):
def setUp(self): def setUp(self):
component.registry.clear() component.registry.clear()
component.registry.register('test_component', SlottedComponent) component.registry.register("test_component", SlottedComponent)
component.registry.register('inner_component', SimpleComponent) component.registry.register("inner_component", SimpleComponent)
def templates_used_to_render(self, subject_template, render_context=None): def templates_used_to_render(self, subject_template, render_context=None):
"""Emulate django.test.client.Client (see request method).""" """Emulate django.test.client.Client (see request method)."""
@ -335,23 +374,31 @@ class TemplateInstrumentationTest(SimpleTestCase):
def receive_template_signal(sender, template, context, **_kwargs): def receive_template_signal(sender, template, context, **_kwargs):
templates_used.append(template.name) templates_used.append(template.name)
template_rendered.connect(receive_template_signal, dispatch_uid='test_method') template_rendered.connect(
receive_template_signal, dispatch_uid="test_method"
)
subject_template.render(render_context or Context({})) subject_template.render(render_context or Context({}))
template_rendered.disconnect(dispatch_uid='test_method') template_rendered.disconnect(dispatch_uid="test_method")
return templates_used return templates_used
def test_template_shown_as_used(self): 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) 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): 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 %}" "{% slot \"header\" %}{% component 'inner_component' variable='foo' %}{% endslot %}"
"{% endcomponent_block %}", name='root') "{% endcomponent_block %}",
name="root",
)
templates_used = self.templates_used_to_render(template) templates_used = self.templates_used_to_render(template)
self.assertIn('slotted_template.html', templates_used) self.assertIn("slotted_template.html", templates_used)
self.assertIn('simple_template.html', templates_used) self.assertIn("simple_template.html", templates_used)
class NestedSlotTests(SimpleTestCase): class NestedSlotTests(SimpleTestCase):
@ -362,7 +409,7 @@ class NestedSlotTests(SimpleTestCase):
def setUpClass(cls): def setUpClass(cls):
super().setUpClass() super().setUpClass()
component.registry.clear() component.registry.clear()
component.registry.register('test', cls.NestedComponent) component.registry.register("test", cls.NestedComponent)
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
@ -397,7 +444,7 @@ class NestedSlotTests(SimpleTestCase):
""" """
) )
rendered = template.render(Context({})) rendered = template.render(Context({}))
self.assertHTMLEqual(rendered, '<p>Override</p>') self.assertHTMLEqual(rendered, "<p>Override</p>")
def test_both_overriden_and_inner_removed(self): def test_both_overriden_and_inner_removed(self):
template = Template( template = Template(
@ -410,7 +457,7 @@ class NestedSlotTests(SimpleTestCase):
""" """
) )
rendered = template.render(Context({})) rendered = template.render(Context({}))
self.assertHTMLEqual(rendered, '<p>Override</p>') self.assertHTMLEqual(rendered, "<p>Override</p>")
class ConditionalSlotTests(SimpleTestCase): class ConditionalSlotTests(SimpleTestCase):
@ -418,13 +465,13 @@ class ConditionalSlotTests(SimpleTestCase):
template_name = "conditional_template.html" template_name = "conditional_template.html"
def get_context_data(self, branch=None): def get_context_data(self, branch=None):
return {'branch': branch} return {"branch": branch}
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super().setUpClass() super().setUpClass()
component.registry.clear() component.registry.clear()
component.registry.register('test', cls.ConditionalComponent) component.registry.register("test", cls.ConditionalComponent)
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
@ -442,7 +489,7 @@ class ConditionalSlotTests(SimpleTestCase):
""" """
) )
rendered = template.render(Context({})) rendered = template.render(Context({}))
self.assertHTMLEqual(rendered, '') self.assertHTMLEqual(rendered, "")
def test_default_content_if_no_slots(self): def test_default_content_if_no_slots(self):
template = Template( template = Template(
@ -453,7 +500,9 @@ class ConditionalSlotTests(SimpleTestCase):
""" """
) )
rendered = template.render(Context({})) 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): def test_one_slot_overridden(self):
template = Template( template = Template(
@ -468,7 +517,9 @@ class ConditionalSlotTests(SimpleTestCase):
""" """
) )
rendered = template.render(Context({})) 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): def test_both_slots_overridden(self):
template = Template( template = Template(
@ -485,7 +536,9 @@ class ConditionalSlotTests(SimpleTestCase):
""" """
) )
rendered = template.render(Context({})) 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): class SlotSuperTests(SimpleTestCase):
@ -493,7 +546,7 @@ class SlotSuperTests(SimpleTestCase):
def setUpClass(cls): def setUpClass(cls):
super().setUpClass() super().setUpClass()
component.registry.clear() component.registry.clear()
component.registry.register('test', SlottedComponent) component.registry.register("test", SlottedComponent)
@classmethod @classmethod
def tearDownClass(cls): def tearDownClass(cls):
@ -561,7 +614,7 @@ class SlotSuperTests(SimpleTestCase):
{% endcomponent_block %} {% endcomponent_block %}
""" """
) )
rendered = template.render(Context({'range': range(3)})) rendered = template.render(Context({"range": range(3)}))
self.assertHTMLEqual( self.assertHTMLEqual(
rendered, rendered,
@ -579,8 +632,8 @@ class TemplateSyntaxErrorTests(SimpleTestCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super().setUpClass() super().setUpClass()
component.registry.register('test', SlottedComponent) component.registry.register("test", SlottedComponent)
component.registry.register('broken_component', BrokenComponent) component.registry.register("broken_component", BrokenComponent)
def test_variable_outside_slot_tag_is_error(self): def test_variable_outside_slot_tag_is_error(self):
with self.assertRaises(TemplateSyntaxError): with self.assertRaises(TemplateSyntaxError):

View file

@ -8,16 +8,20 @@ from django_components.middleware import ComponentDependencyMiddleware
# Create middleware instance # Create middleware instance
response_stash = None response_stash = None
middleware = ComponentDependencyMiddleware(get_response=lambda _: response_stash) middleware = ComponentDependencyMiddleware(
get_response=lambda _: response_stash
)
class Django30CompatibleSimpleTestCase(SimpleTestCase): class Django30CompatibleSimpleTestCase(SimpleTestCase):
def assertHTMLEqual(self, left, right): def assertHTMLEqual(self, left, right):
left = left.replace(' type="text/javascript"', '') left = left.replace(' type="text/javascript"', "")
super(Django30CompatibleSimpleTestCase, self).assertHTMLEqual(left, right) super(Django30CompatibleSimpleTestCase, self).assertHTMLEqual(
left, right
)
def assertInHTML(self, needle, haystack, count=None, msg_prefix=''): def assertInHTML(self, needle, haystack, count=None, msg_prefix=""):
haystack = haystack.replace(' type="text/javascript"', '') haystack = haystack.replace(' type="text/javascript"', "")
super().assertInHTML(needle, haystack, count, msg_prefix) super().assertInHTML(needle, haystack, count, msg_prefix)
@ -29,7 +33,9 @@ request = Mock()
mock_template = 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({}) context = context if context is not None else Context({})
mock_template.render = lambda context, _: template.render(context) mock_template.render = lambda context, _: template.render(context)
response = TemplateResponse(request, mock_template, context) response = TemplateResponse(request, mock_template, context)
@ -40,4 +46,4 @@ def create_and_process_template_response(template, context=None, use_middleware=
response = middleware(request) response = middleware(request)
else: else:
response.render() response.render()
return response.content.decode('utf-8') return response.content.decode("utf-8")