diff --git a/benchmarks/component_rendering.py b/benchmarks/component_rendering.py index e7c5cf31..2807e562 100644 --- a/benchmarks/component_rendering.py +++ b/benchmarks/component_rendering.py @@ -3,10 +3,14 @@ from time import perf_counter from django.template import Context, Template from django.test import override_settings -from django_components.middleware import CSS_DEPENDENCY_PLACEHOLDER, JS_DEPENDENCY_PLACEHOLDER -from tests.django_test_setup import * # NOQA from django_components import component -from tests.testutils import Django30CompatibleSimpleTestCase as SimpleTestCase, create_and_process_template_response +from django_components.middleware import ( + CSS_DEPENDENCY_PLACEHOLDER, + JS_DEPENDENCY_PLACEHOLDER, +) +from tests.django_test_setup import * # NOQA +from tests.testutils import Django30CompatibleSimpleTestCase as SimpleTestCase +from tests.testutils import create_and_process_template_response class SlottedComponent(component.Component): @@ -31,14 +35,22 @@ class BreadcrumbComponent(component.Component): template_name = "mdn_component_template.html" LINKS = [ - ('https://developer.mozilla.org/en-US/docs/Learn', - 'Learn web development'), - ('https://developer.mozilla.org/en-US/docs/Learn/HTML', - 'Structuring the web with HTML'), - ('https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML', - 'Introduction to HTML'), - ('https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure', - 'Document and website structure') + ( + "https://developer.mozilla.org/en-US/docs/Learn", + "Learn web development", + ), + ( + "https://developer.mozilla.org/en-US/docs/Learn/HTML", + "Structuring the web with HTML", + ), + ( + "https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML", + "Introduction to HTML", + ), + ( + "https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure", + "Document and website structure", + ), ] def get_context_data(self, items): @@ -46,7 +58,7 @@ class BreadcrumbComponent(component.Component): items = 4 elif items < 0: items = 0 - return {'links': self.LINKS[:items - 1]} + return {"links": self.LINKS[: items - 1]} class Media: css = {"all": ["test.css"]} @@ -57,13 +69,15 @@ EXPECTED_CSS = """""" EXPECTED_JS = """""" -@override_settings(COMPONENTS={'RENDER_DEPENDENCIES': True}) +@override_settings(COMPONENTS={"RENDER_DEPENDENCIES": True}) class RenderBenchmarks(SimpleTestCase): def setUp(self): component.registry.clear() - component.registry.register('test_component', SlottedComponent) - component.registry.register('inner_component', SimpleComponent) - component.registry.register('breadcrumb_component', BreadcrumbComponent) + component.registry.register("test_component", SlottedComponent) + component.registry.register("inner_component", SimpleComponent) + component.registry.register( + "breadcrumb_component", BreadcrumbComponent + ) @staticmethod def timed_loop(func, iterations=1000): @@ -76,55 +90,81 @@ class RenderBenchmarks(SimpleTestCase): return total_elapsed * 1000 / iterations def test_render_time_for_small_component(self): - template = Template("{% load component_tags %}{% component_block 'test_component' %}" - "{% slot \"header\" %}{% component 'inner_component' variable='foo' %}{% endslot %}" - "{% endcomponent_block %}", name='root') + template = Template( + "{% load component_tags %}{% component_block 'test_component' %}" + "{% slot \"header\" %}{% component 'inner_component' variable='foo' %}{% endslot %}" + "{% endcomponent_block %}", + name="root", + ) - print(f'{self.timed_loop(lambda: template.render(Context({})))} ms per iteration') + print( + f"{self.timed_loop(lambda: template.render(Context({})))} ms per iteration" + ) def test_middleware_time_with_dependency_for_small_page(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component_block 'test_component' %}{% slot \"header\" %}" - "{% component 'inner_component' variable='foo' %}{% endslot %}{% endcomponent_block %}", - name='root') + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component_block 'test_component' %}{% slot \"header\" %}" + "{% component 'inner_component' variable='foo' %}{% endslot %}{% endcomponent_block %}", + name="root", + ) # Sanity tests response_content = create_and_process_template_response(template) self.assertNotIn(CSS_DEPENDENCY_PLACEHOLDER, response_content) self.assertNotIn(JS_DEPENDENCY_PLACEHOLDER, response_content) - self.assertIn('style.css', response_content) - self.assertIn('script.js', response_content) + self.assertIn("style.css", response_content) + self.assertIn("script.js", response_content) - without_middleware = self.timed_loop(lambda: create_and_process_template_response(template, - use_middleware=False)) - with_middleware = self.timed_loop(lambda: create_and_process_template_response(template, use_middleware=True)) + without_middleware = self.timed_loop( + lambda: create_and_process_template_response( + template, use_middleware=False + ) + ) + with_middleware = self.timed_loop( + lambda: create_and_process_template_response( + template, use_middleware=True + ) + ) - print('Small page middleware test') + print("Small page middleware test") self.report_results(with_middleware, without_middleware) def test_render_time_with_dependency_for_large_page(self): from django.template.loader import get_template - template = get_template('mdn_complete_page.html') + template = get_template("mdn_complete_page.html") response_content = create_and_process_template_response(template, {}) self.assertNotIn(CSS_DEPENDENCY_PLACEHOLDER, response_content) self.assertNotIn(JS_DEPENDENCY_PLACEHOLDER, response_content) - self.assertIn('test.css', response_content) - self.assertIn('test.js', response_content) + self.assertIn("test.css", response_content) + self.assertIn("test.js", response_content) without_middleware = self.timed_loop( - lambda: create_and_process_template_response(template, {}, use_middleware=False)) + lambda: create_and_process_template_response( + template, {}, use_middleware=False + ) + ) with_middleware = self.timed_loop( - lambda: create_and_process_template_response(template, {}, use_middleware=True)) + lambda: create_and_process_template_response( + template, {}, use_middleware=True + ) + ) - print('Large page middleware test') + print("Large page middleware test") self.report_results(with_middleware, without_middleware) @staticmethod def report_results(with_middleware, without_middleware): - print(f'Middleware active\t\t{with_middleware:.3f} ms per iteration') - print(f'Middleware inactive\t{without_middleware:.3f} ms per iteration') + print(f"Middleware active\t\t{with_middleware:.3f} ms per iteration") + print( + f"Middleware inactive\t{without_middleware:.3f} ms per iteration" + ) time_difference = with_middleware - without_middleware if without_middleware > with_middleware: - print(f'Decrease of {-100 * time_difference / with_middleware:.2f}%') + print( + f"Decrease of {-100 * time_difference / with_middleware:.2f}%" + ) else: - print(f'Increase of {100 * time_difference / without_middleware:.2f}%') + print( + f"Increase of {100 * time_difference / without_middleware:.2f}%" + ) diff --git a/django_components/component.py b/django_components/component.py index db74c2d6..6c80f7cd 100644 --- a/django_components/component.py +++ b/django_components/component.py @@ -9,10 +9,16 @@ from django.template.loader import get_template from django.utils.safestring import mark_safe # Allow "component.AlreadyRegistered" instead of having to import these everywhere -from django_components.component_registry import AlreadyRegistered, ComponentRegistry, NotRegistered # noqa +from django_components.component_registry import ( # noqa + AlreadyRegistered, + ComponentRegistry, + NotRegistered, +) -TEMPLATE_CACHE_SIZE = getattr(settings, "COMPONENTS", {}).get('TEMPLATE_CACHE_SIZE', 128) -ACTIVE_SLOT_CONTEXT_KEY = '_DJANGO_COMPONENTS_ACTIVE_SLOTS' +TEMPLATE_CACHE_SIZE = getattr(settings, "COMPONENTS", {}).get( + "TEMPLATE_CACHE_SIZE", 128 +) +ACTIVE_SLOT_CONTEXT_KEY = "_DJANGO_COMPONENTS_ACTIVE_SLOTS" class SimplifiedInterfaceMediaDefiningClass(MediaDefiningClass): @@ -54,7 +60,9 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass): def get_template_name(self, context=None): if not self.template_name: - raise ImproperlyConfigured(f'Template name is not set for Component {self.__class__.__name__}') + raise ImproperlyConfigured( + f"Template name is not set for Component {self.__class__.__name__}" + ) return self.template_name @@ -75,13 +83,19 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass): @staticmethod def slots_in_template(template): - return {node.name: node.nodelist for node in template.template.nodelist if Component.is_slot_node(node)} + return { + node.name: node.nodelist + for node in template.template.nodelist + if Component.is_slot_node(node) + } @staticmethod def is_slot_node(node): - return (isinstance(node, Node) - and node.token.token_type == TokenType.BLOCK - and node.token.split_contents()[0] == "slot") + return ( + isinstance(node, Node) + and node.token.token_type == TokenType.BLOCK + and node.token.split_contents()[0] == "slot" + ) @lru_cache(maxsize=TEMPLATE_CACHE_SIZE) def get_processed_template(self, template_name): @@ -117,16 +131,16 @@ class Component(metaclass=SimplifiedInterfaceMediaDefiningClass): return component_template def render(self, context): - if hasattr(self, 'context'): + if hasattr(self, "context"): warnings.warn( - f'{self.__class__.__name__}: `context` method is deprecated, use `get_context` instead', - DeprecationWarning + f"{self.__class__.__name__}: `context` method is deprecated, use `get_context` instead", + DeprecationWarning, ) - if hasattr(self, 'template'): + if hasattr(self, "template"): warnings.warn( - f'{self.__class__.__name__}: `template` method is deprecated, set `template_name` or override `get_template_name` instead', - DeprecationWarning + f"{self.__class__.__name__}: `template` method is deprecated, set `template_name` or override `get_template_name` instead", + DeprecationWarning, ) template_name = self.template(context) else: diff --git a/django_components/component_registry.py b/django_components/component_registry.py index 68843a60..0f807ff0 100644 --- a/django_components/component_registry.py +++ b/django_components/component_registry.py @@ -12,7 +12,9 @@ class ComponentRegistry(object): def register(self, name=None, component=None): if name in self._registry: - raise AlreadyRegistered('The component "%s" is already registered' % name) + raise AlreadyRegistered( + 'The component "%s" is already registered' % name + ) self._registry[name] = component diff --git a/django_components/middleware.py b/django_components/middleware.py index 5e0b2fb8..fef98146 100644 --- a/django_components/middleware.py +++ b/django_components/middleware.py @@ -8,11 +8,13 @@ RENDERED_COMPONENTS_CONTEXT_KEY = "_COMPONENT_DEPENDENCIES" CSS_DEPENDENCY_PLACEHOLDER = '' JS_DEPENDENCY_PLACEHOLDER = ' - """).strip()) + """ + ).strip(), + ) - self.assertHTMLEqual(comp.render(context), dedent(""" + self.assertHTMLEqual( + comp.render(context), + dedent( + """ Variable: test - """).lstrip()) + """ + ).lstrip(), + ) def test_component_with_list_of_styles(self): class MultistyleComponent(component.Component): @@ -50,12 +60,17 @@ class ComponentTest(SimpleTestCase): comp = MultistyleComponent("multistyle_component") - self.assertHTMLEqual(comp.render_dependencies(), dedent(""" + self.assertHTMLEqual( + comp.render_dependencies(), + dedent( + """ - """).strip()) + """ + ).strip(), + ) def test_component_with_filtered_template(self): class FilteredComponent(component.Component): @@ -70,15 +85,25 @@ class ComponentTest(SimpleTestCase): comp = FilteredComponent("filtered_component") context = Context(comp.get_context_data(var1="test1", var2="test2")) - self.assertHTMLEqual(comp.render(context), dedent(""" + self.assertHTMLEqual( + comp.render(context), + dedent( + """ Var1: test1 Var2 (uppercased): TEST2 - """).lstrip()) + """ + ).lstrip(), + ) def test_component_with_dynamic_template(self): class SvgComponent(component.Component): def get_context_data(self, name, css_class="", title="", **attrs): - return {"name": name, "css_class": css_class, "title": title, **attrs} + return { + "name": name, + "css_class": css_class, + "title": title, + **attrs, + } def get_template_name(self, context): return f"svg_{context['name']}.svg" @@ -86,15 +111,19 @@ class ComponentTest(SimpleTestCase): comp = SvgComponent("svg_component") self.assertHTMLEqual( comp.render(Context(comp.get_context_data(name="dynamic1"))), - dedent("""\ + dedent( + """\ Dynamic1 - """) + """ + ), ) self.assertHTMLEqual( comp.render(Context(comp.get_context_data(name="dynamic2"))), - dedent("""\ + dedent( + """\ Dynamic2 - """) + """ + ), ) @@ -108,10 +137,12 @@ class ComponentMediaTests(SimpleTestCase): comp = SimpleComponent("") self.assertHTMLEqual( comp.render_dependencies(), - dedent("""\ + dedent( + """\ - """) + """ + ), ) def test_component_media_with_lists(self): @@ -123,11 +154,13 @@ class ComponentMediaTests(SimpleTestCase): comp = SimpleComponent("") self.assertHTMLEqual( comp.render_dependencies(), - dedent("""\ + dedent( + """\ - """) + """ + ), ) def test_component_media_with_dict_and_list(self): @@ -143,12 +176,14 @@ class ComponentMediaTests(SimpleTestCase): comp = SimpleComponent("") self.assertHTMLEqual( comp.render_dependencies(), - dedent("""\ + dedent( + """\ - """) + """ + ), ) def test_component_media_with_dict_with_list_and_list(self): @@ -160,10 +195,12 @@ class ComponentMediaTests(SimpleTestCase): comp = SimpleComponent("") self.assertHTMLEqual( comp.render_dependencies(), - dedent("""\ + dedent( + """\ - """) + """ + ), ) @@ -172,7 +209,7 @@ class ComponentIsolationTests(SimpleTestCase): class SlottedComponent(component.Component): template_name = "slotted_template.html" - component.registry.register('test', SlottedComponent) + component.registry.register("test", SlottedComponent) def test_instances_of_component_do_not_share_slots(self): template = Template( @@ -211,17 +248,17 @@ class ComponentIsolationTests(SimpleTestCase):
Default main
- """ + """, ) class RecursiveSlotNameTest(SimpleTestCase): def setUp(self): - @component.register('outer') + @component.register("outer") class OuterComponent(component.Component): template_name = "slotted_template.html" - @component.register('inner') + @component.register("inner") class InnerComponent(component.Component): template_name = "slotted_template.html" @@ -251,5 +288,5 @@ class RecursiveSlotNameTest(SimpleTestCase):
Default main
- """ + """, ) diff --git a/tests/test_context.py b/tests/test_context.py index 6f8e4856..29fa8ef8 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -1,9 +1,8 @@ from django.template import Context, Template -from .django_test_setup import * # NOQA - from django_components import component +from .django_test_setup import * # NOQA from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase @@ -15,16 +14,14 @@ class SimpleComponent(component.Component): @staticmethod def expected_output(variable_value): - return 'Variable: < strong > {} < / strong >'.format(variable_value) + return "Variable: < strong > {} < / strong >".format(variable_value) class ParentComponent(component.Component): template_name = "parent_template.html" def get_context_data(self): - return { - "shadowing_variable": 'NOT SHADOWED' - } + return {"shadowing_variable": "NOT SHADOWED"} class ParentComponentWithArgs(component.Component): @@ -40,9 +37,9 @@ class VariableDisplay(component.Component): def get_context_data(self, shadowing_variable=None, new_variable=None): context = {} if shadowing_variable is not None: - context['shadowing_variable'] = shadowing_variable + context["shadowing_variable"] = shadowing_variable if new_variable is not None: - context['unique_variable'] = new_variable + context["unique_variable"] = new_variable return context @@ -51,15 +48,11 @@ class IncrementerComponent(component.Component): def get_context_data(self, value=0): value = int(value) - if hasattr(self, 'call_count'): + if hasattr(self, "call_count"): self.call_count += 1 else: self.call_count = 1 - return { - "value": value + 1, - "calls": self.call_count - } - + return {"value": value + 1, "calls": self.call_count} class OuterContextComponent(component.Component): @@ -69,212 +62,394 @@ class OuterContextComponent(component.Component): return self.outer_context -component.registry.register(name='parent_component', component=ParentComponent) -component.registry.register(name='parent_with_args', component=ParentComponentWithArgs) -component.registry.register(name='variable_display', component=VariableDisplay) -component.registry.register(name='incrementer', component=IncrementerComponent) -component.registry.register(name='simple_component', component=SimpleComponent) -component.registry.register(name='outer_context_component', component=OuterContextComponent) +component.registry.register(name="parent_component", component=ParentComponent) +component.registry.register( + name="parent_with_args", component=ParentComponentWithArgs +) +component.registry.register(name="variable_display", component=VariableDisplay) +component.registry.register(name="incrementer", component=IncrementerComponent) +component.registry.register(name="simple_component", component=SimpleComponent) +component.registry.register( + name="outer_context_component", component=OuterContextComponent +) class ContextTests(SimpleTestCase): - def test_nested_component_context_shadows_parent_with_unfilled_slots_and_component_tag(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component 'parent_component' %}") + def test_nested_component_context_shadows_parent_with_unfilled_slots_and_component_tag( + self, + ): + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component 'parent_component' %}" + ) rendered = template.render(Context()) - self.assertIn('

Shadowing variable = override

', rendered, rendered) - self.assertIn('

Shadowing variable = slot_default_override

', rendered, rendered) - self.assertNotIn('

Shadowing variable = NOT SHADOWED

', rendered, rendered) + self.assertIn( + "

Shadowing variable = override

", rendered, rendered + ) + self.assertIn( + "

Shadowing variable = slot_default_override

", + rendered, + rendered, + ) + self.assertNotIn( + "

Shadowing variable = NOT SHADOWED

", rendered, rendered + ) - def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_tag(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component name='parent_component' %}") + def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_tag( + self, + ): + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component name='parent_component' %}" + ) rendered = template.render(Context()) - self.assertIn('

Uniquely named variable = unique_val

', rendered, rendered) - self.assertIn('

Uniquely named variable = slot_default_unique

', rendered, rendered) + self.assertIn( + "

Uniquely named variable = unique_val

", rendered, rendered + ) + self.assertIn( + "

Uniquely named variable = slot_default_unique

", + rendered, + rendered, + ) - def test_nested_component_context_shadows_parent_with_unfilled_slots_and_component_block_tag(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component_block 'parent_component' %}{% endcomponent_block %}") + def test_nested_component_context_shadows_parent_with_unfilled_slots_and_component_block_tag( + self, + ): + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component_block 'parent_component' %}{% endcomponent_block %}" + ) rendered = template.render(Context()) - self.assertIn('

Shadowing variable = override

', rendered, rendered) - self.assertIn('

Shadowing variable = slot_default_override

', rendered, rendered) - self.assertNotIn('

Shadowing variable = NOT SHADOWED

', rendered, rendered) + self.assertIn( + "

Shadowing variable = override

", rendered, rendered + ) + self.assertIn( + "

Shadowing variable = slot_default_override

", + rendered, + rendered, + ) + self.assertNotIn( + "

Shadowing variable = NOT SHADOWED

", rendered, rendered + ) - def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_block_tag(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component_block 'parent_component' %}{% endcomponent_block %}") + def test_nested_component_instances_have_unique_context_with_unfilled_slots_and_component_block_tag( + self, + ): + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component_block 'parent_component' %}{% endcomponent_block %}" + ) rendered = template.render(Context()) - self.assertIn('

Uniquely named variable = unique_val

', rendered, rendered) - self.assertIn('

Uniquely named variable = slot_default_unique

', rendered, rendered) + self.assertIn( + "

Uniquely named variable = unique_val

", rendered, rendered + ) + self.assertIn( + "

Uniquely named variable = slot_default_unique

", + rendered, + rendered, + ) def test_nested_component_context_shadows_parent_with_filled_slots(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component_block 'parent_component' %}" - "{% slot 'content' %}{% component name='variable_display' " - "shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}" - "{% endcomponent_block %}") + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component_block 'parent_component' %}" + "{% slot 'content' %}{% component name='variable_display' " + "shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}" + "{% endcomponent_block %}" + ) rendered = template.render(Context()) - self.assertIn('

Shadowing variable = override

', rendered, rendered) - self.assertIn('

Shadowing variable = shadow_from_slot

', rendered, rendered) - self.assertNotIn('

Shadowing variable = NOT SHADOWED

', rendered, rendered) + self.assertIn( + "

Shadowing variable = override

", rendered, rendered + ) + self.assertIn( + "

Shadowing variable = shadow_from_slot

", + rendered, + rendered, + ) + self.assertNotIn( + "

Shadowing variable = NOT SHADOWED

", rendered, rendered + ) - def test_nested_component_instances_have_unique_context_with_filled_slots(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component_block 'parent_component' %}" - "{% slot 'content' %}{% component name='variable_display' " - "shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}" - "{% endcomponent_block %}") + def test_nested_component_instances_have_unique_context_with_filled_slots( + self, + ): + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component_block 'parent_component' %}" + "{% slot 'content' %}{% component name='variable_display' " + "shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}" + "{% endcomponent_block %}" + ) rendered = template.render(Context()) - self.assertIn('

Uniquely named variable = unique_val

', rendered, rendered) - self.assertIn('

Uniquely named variable = unique_from_slot

', rendered, rendered) + self.assertIn( + "

Uniquely named variable = unique_val

", rendered, rendered + ) + self.assertIn( + "

Uniquely named variable = unique_from_slot

", + rendered, + rendered, + ) - def test_nested_component_context_shadows_outer_context_with_unfilled_slots_and_component_tag(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component name='parent_component' %}") - rendered = template.render(Context({'shadowing_variable': 'NOT SHADOWED'})) + def test_nested_component_context_shadows_outer_context_with_unfilled_slots_and_component_tag( + self, + ): + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component name='parent_component' %}" + ) + rendered = template.render( + Context({"shadowing_variable": "NOT SHADOWED"}) + ) - self.assertIn('

Shadowing variable = override

', rendered, rendered) - self.assertIn('

Shadowing variable = slot_default_override

', rendered, rendered) - self.assertNotIn('

Shadowing variable = NOT SHADOWED

', rendered, rendered) + self.assertIn( + "

Shadowing variable = override

", rendered, rendered + ) + self.assertIn( + "

Shadowing variable = slot_default_override

", + rendered, + rendered, + ) + self.assertNotIn( + "

Shadowing variable = NOT SHADOWED

", rendered, rendered + ) - def test_nested_component_context_shadows_outer_context_with_unfilled_slots_and_component_block_tag(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component_block 'parent_component' %}{% endcomponent_block %}") - rendered = template.render(Context({'shadowing_variable': 'NOT SHADOWED'})) + def test_nested_component_context_shadows_outer_context_with_unfilled_slots_and_component_block_tag( + self, + ): + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component_block 'parent_component' %}{% endcomponent_block %}" + ) + rendered = template.render( + Context({"shadowing_variable": "NOT SHADOWED"}) + ) - self.assertIn('

Shadowing variable = override

', rendered, rendered) - self.assertIn('

Shadowing variable = slot_default_override

', rendered, rendered) - self.assertNotIn('

Shadowing variable = NOT SHADOWED

', rendered, rendered) + self.assertIn( + "

Shadowing variable = override

", rendered, rendered + ) + self.assertIn( + "

Shadowing variable = slot_default_override

", + rendered, + rendered, + ) + self.assertNotIn( + "

Shadowing variable = NOT SHADOWED

", rendered, rendered + ) - def test_nested_component_context_shadows_outer_context_with_filled_slots(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component_block 'parent_component' %}" - "{% slot 'content' %}{% component name='variable_display' " - "shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}" - "{% endcomponent_block %}") - rendered = template.render(Context({'shadowing_variable': 'NOT SHADOWED'})) + def test_nested_component_context_shadows_outer_context_with_filled_slots( + self, + ): + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component_block 'parent_component' %}" + "{% slot 'content' %}{% component name='variable_display' " + "shadowing_variable='shadow_from_slot' new_variable='unique_from_slot' %}{% endslot %}" + "{% endcomponent_block %}" + ) + rendered = template.render( + Context({"shadowing_variable": "NOT SHADOWED"}) + ) - self.assertIn('

Shadowing variable = override

', rendered, rendered) - self.assertIn('

Shadowing variable = shadow_from_slot

', rendered, rendered) - self.assertNotIn('

Shadowing variable = NOT SHADOWED

', rendered, rendered) + self.assertIn( + "

Shadowing variable = override

", rendered, rendered + ) + self.assertIn( + "

Shadowing variable = shadow_from_slot

", + rendered, + rendered, + ) + self.assertNotIn( + "

Shadowing variable = NOT SHADOWED

", rendered, rendered + ) class ParentArgsTests(SimpleTestCase): def test_parent_args_can_be_drawn_from_context(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component_block 'parent_with_args' parent_value=parent_value %}" - "{% endcomponent_block %}") - rendered = template.render(Context({'parent_value': 'passed_in'})) + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component_block 'parent_with_args' parent_value=parent_value %}" + "{% endcomponent_block %}" + ) + rendered = template.render(Context({"parent_value": "passed_in"})) - self.assertIn('

Shadowing variable = passed_in

', rendered, rendered) - self.assertIn('

Uniquely named variable = passed_in

', rendered, rendered) - self.assertNotIn('

Shadowing variable = NOT SHADOWED

', rendered, rendered) + self.assertIn( + "

Shadowing variable = passed_in

", rendered, rendered + ) + self.assertIn( + "

Uniquely named variable = passed_in

", rendered, rendered + ) + self.assertNotIn( + "

Shadowing variable = NOT SHADOWED

", rendered, rendered + ) def test_parent_args_available_outside_slots(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component_block 'parent_with_args' parent_value='passed_in' %}{%endcomponent_block %}") + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component_block 'parent_with_args' parent_value='passed_in' %}{%endcomponent_block %}" + ) rendered = template.render(Context()) - self.assertIn('

Shadowing variable = passed_in

', rendered, rendered) - self.assertIn('

Uniquely named variable = passed_in

', rendered, rendered) - self.assertNotIn('

Shadowing variable = NOT SHADOWED

', rendered, rendered) + self.assertIn( + "

Shadowing variable = passed_in

", rendered, rendered + ) + self.assertIn( + "

Uniquely named variable = passed_in

", rendered, rendered + ) + self.assertNotIn( + "

Shadowing variable = NOT SHADOWED

", rendered, rendered + ) def test_parent_args_available_in_slots(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component_block 'parent_with_args' parent_value='passed_in' %}" - "{% slot 'content' %}{% component name='variable_display' " - "shadowing_variable='value_from_slot' new_variable=inner_parent_value %}{% endslot %}" - "{%endcomponent_block %}") + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component_block 'parent_with_args' parent_value='passed_in' %}" + "{% slot 'content' %}{% component name='variable_display' " + "shadowing_variable='value_from_slot' new_variable=inner_parent_value %}{% endslot %}" + "{%endcomponent_block %}" + ) rendered = template.render(Context()) - self.assertIn('

Shadowing variable = value_from_slot

', rendered, rendered) - self.assertIn('

Uniquely named variable = passed_in

', rendered, rendered) - self.assertNotIn('

Shadowing variable = NOT SHADOWED

', rendered, rendered) + self.assertIn( + "

Shadowing variable = value_from_slot

", rendered, rendered + ) + self.assertIn( + "

Uniquely named variable = passed_in

", rendered, rendered + ) + self.assertNotIn( + "

Shadowing variable = NOT SHADOWED

", rendered, rendered + ) class ContextCalledOnceTests(SimpleTestCase): def test_one_context_call_with_simple_component(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component name='incrementer' %}") + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component name='incrementer' %}" + ) rendered = template.render(Context()).strip() - self.assertEqual(rendered, '

value=1;calls=1

', rendered) + self.assertEqual( + rendered, '

value=1;calls=1

', rendered + ) def test_one_context_call_with_simple_component_and_arg(self): - template = Template("{% load component_tags %}{% component name='incrementer' value='2' %}") + template = Template( + "{% load component_tags %}{% component name='incrementer' value='2' %}" + ) rendered = template.render(Context()).strip() - self.assertEqual(rendered, '

value=3;calls=1

', rendered) + self.assertEqual( + rendered, '

value=3;calls=1

', rendered + ) def test_one_context_call_with_component_block(self): - template = Template("{% load component_tags %}" - "{% component_block 'incrementer' %}{% endcomponent_block %}") + template = Template( + "{% load component_tags %}" + "{% component_block 'incrementer' %}{% endcomponent_block %}" + ) rendered = template.render(Context()).strip() - self.assertEqual(rendered, '

value=1;calls=1

', rendered) + self.assertEqual( + rendered, '

value=1;calls=1

', rendered + ) def test_one_context_call_with_component_block_and_arg(self): - template = Template("{% load component_tags %}" - "{% component_block 'incrementer' value='3' %}{% endcomponent_block %}") + template = Template( + "{% load component_tags %}" + "{% component_block 'incrementer' value='3' %}{% endcomponent_block %}" + ) rendered = template.render(Context()).strip() - self.assertEqual(rendered, '

value=4;calls=1

', rendered) + self.assertEqual( + rendered, '

value=4;calls=1

', rendered + ) def test_one_context_call_with_slot(self): - template = Template("{% load component_tags %}" - "{% component_block 'incrementer' %}{% slot 'content' %}" - "

slot

{% endslot %}{% endcomponent_block %}") + template = Template( + "{% load component_tags %}" + "{% component_block 'incrementer' %}{% slot 'content' %}" + "

slot

{% endslot %}{% endcomponent_block %}" + ) rendered = template.render(Context()).strip() - self.assertEqual(rendered, '

value=1;calls=1

\n

slot

', rendered) + self.assertEqual( + rendered, + '

value=1;calls=1

\n

slot

', + rendered, + ) def test_one_context_call_with_slot_and_arg(self): - template = Template("{% load component_tags %}" - "{% component_block 'incrementer' value='3' %}{% slot 'content' %}" - "

slot

{% endslot %}{% endcomponent_block %}") + template = Template( + "{% load component_tags %}" + "{% component_block 'incrementer' value='3' %}{% slot 'content' %}" + "

slot

{% endslot %}{% endcomponent_block %}" + ) rendered = template.render(Context()).strip() - self.assertEqual(rendered, '

value=4;calls=1

\n

slot

', rendered) + self.assertEqual( + rendered, + '

value=4;calls=1

\n

slot

', + rendered, + ) class ComponentsCanAccessOuterContext(SimpleTestCase): def test_simple_component_can_use_outer_context(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component 'simple_component' %}") - rendered = template.render(Context({'variable': 'outer_value'})).strip() - self.assertIn('outer_value', rendered, rendered) + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component 'simple_component' %}" + ) + rendered = template.render( + Context({"variable": "outer_value"}) + ).strip() + self.assertIn("outer_value", rendered, rendered) class IsolatedContextTests(SimpleTestCase): def test_simple_component_can_pass_outer_context_in_args(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component 'simple_component' variable only %}") - rendered = template.render(Context({'variable': 'outer_value'})).strip() - self.assertIn('outer_value', rendered, rendered) + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component 'simple_component' variable only %}" + ) + rendered = template.render( + Context({"variable": "outer_value"}) + ).strip() + self.assertIn("outer_value", rendered, rendered) def test_simple_component_cannot_use_outer_context(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component 'simple_component' only %}") - rendered = template.render(Context({'variable': 'outer_value'})).strip() - self.assertNotIn('outer_value', rendered, rendered) + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component 'simple_component' only %}" + ) + rendered = template.render( + Context({"variable": "outer_value"}) + ).strip() + self.assertNotIn("outer_value", rendered, rendered) class OuterContextPropertyTests(SimpleTestCase): def test_outer_context_property_with_component(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component 'outer_context_component' only %}") - rendered = template.render(Context({'variable': 'outer_value'})).strip() - self.assertIn('outer_value', rendered, rendered) + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component 'outer_context_component' only %}" + ) + rendered = template.render( + Context({"variable": "outer_value"}) + ).strip() + self.assertIn("outer_value", rendered, rendered) def test_outer_context_property_with_component_block(self): - template = Template("{% load component_tags %}{% component_dependencies %}" - "{% component_block 'outer_context_component' only %}{% endcomponent_block %}") - rendered = template.render(Context({'variable': 'outer_value'})).strip() - self.assertIn('outer_value', rendered, rendered) + template = Template( + "{% load component_tags %}{% component_dependencies %}" + "{% component_block 'outer_context_component' only %}{% endcomponent_block %}" + ) + rendered = template.render( + Context({"variable": "outer_value"}) + ).strip() + self.assertIn("outer_value", rendered, rendered) diff --git a/tests/test_dependency_rendering.py b/tests/test_dependency_rendering.py index 7bad3b81..667636a2 100644 --- a/tests/test_dependency_rendering.py +++ b/tests/test_dependency_rendering.py @@ -1,11 +1,12 @@ from django.template import Template from django.test import override_settings -from .django_test_setup import * # NOQA from django_components import component +from .django_test_setup import * # NOQA from .test_templatetags import SimpleComponent -from .testutils import create_and_process_template_response, Django30CompatibleSimpleTestCase as SimpleTestCase +from .testutils import Django30CompatibleSimpleTestCase as SimpleTestCase +from .testutils import create_and_process_template_response class SimpleComponentAlternate(component.Component): @@ -38,7 +39,7 @@ class MultistyleComponent(component.Component): js = ["script.js", "script2.js"] -@override_settings(COMPONENTS={'RENDER_DEPENDENCIES': True}) +@override_settings(COMPONENTS={"RENDER_DEPENDENCIES": True}) class ComponentMediaRenderingTests(SimpleTestCase): def setUp(self): # NOTE: component.registry is global, so need to clear before each test @@ -47,150 +48,280 @@ class ComponentMediaRenderingTests(SimpleTestCase): def test_no_dependencies_when_no_components_used(self): component.registry.register(name="test", component=SimpleComponent) - template = Template("{% load component_tags %}{% component_dependencies %}") + template = Template( + "{% load component_tags %}{% component_dependencies %}" + ) rendered = create_and_process_template_response(template) self.assertInHTML('