import sys
from pathlib import Path
from typing import Any, Dict, List, Optional
from django.core.exceptions import ImproperlyConfigured
from django.template import Context, Template
from django.test import override_settings
# isort: off
from .django_test_setup import * # NOQA
from .testutils import BaseTestCase, autodiscover_with_cleanup
# isort: on
from django_components import component
#########################
# COMPONENTS
#########################
class ParentComponent(component.Component):
template_name = "parent_template.html"
def get_context_data(self):
return {"shadowing_variable": "NOT SHADOWED"}
class VariableDisplay(component.Component):
template_name = "variable_display.html"
def get_context_data(self, shadowing_variable=None, new_variable=None):
context = {}
if shadowing_variable is not None:
context["shadowing_variable"] = shadowing_variable
if new_variable is not None:
context["unique_variable"] = new_variable
return context
class DuplicateSlotComponent(component.Component):
template_name = "template_with_nonunique_slots.html"
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]:
return {
"name": name,
}
class DuplicateSlotNestedComponent(component.Component):
template_name = "template_with_nonunique_slots_nested.html"
def get_context_data(self, items: List) -> Dict[str, Any]:
return {
"items": items,
}
class CalendarComponent(component.Component):
"""Nested in ComponentWithNestedComponent"""
template_name = "slotted_component_nesting_template_pt1_calendar.html"
#########################
# TESTS
#########################
class ComponentTest(BaseTestCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
component.registry.register(name="parent_component", component=ParentComponent)
component.registry.register(name="variable_display", component=VariableDisplay)
def test_empty_component(self):
class EmptyComponent(component.Component):
pass
with self.assertRaises(ImproperlyConfigured):
EmptyComponent("empty_component").get_template(Context({}))
def test_simple_component(self):
class SimpleComponent(component.Component):
template_name = "simple_template.html"
def get_context_data(self, variable=None):
return {
"variable": variable,
}
class Media:
css = "style.css"
js = "script.js"
comp = SimpleComponent("simple_component")
context = Context(comp.get_context_data(variable="test"))
self.assertHTMLEqual(
comp.render_dependencies(),
"""
""",
)
self.assertHTMLEqual(
comp.render(context),
"""
Variable: test
""",
)
def test_css_only_component(self):
class SimpleComponent(component.Component):
template_name = "simple_template.html"
class Media:
css = "style.css"
comp = SimpleComponent("simple_component")
self.assertHTMLEqual(
comp.render_dependencies(),
"""
""",
)
def test_js_only_component(self):
class SimpleComponent(component.Component):
template_name = "simple_template.html"
class Media:
js = "script.js"
comp = SimpleComponent("simple_component")
self.assertHTMLEqual(
comp.render_dependencies(),
"""
""",
)
def test_empty_media_component(self):
class SimpleComponent(component.Component):
template_name = "simple_template.html"
class Media:
pass
comp = SimpleComponent("simple_component")
self.assertHTMLEqual(comp.render_dependencies(), "")
def test_missing_media_component(self):
class SimpleComponent(component.Component):
template_name = "simple_template.html"
comp = SimpleComponent("simple_component")
self.assertHTMLEqual(comp.render_dependencies(), "")
def test_component_with_list_of_styles(self):
class MultistyleComponent(component.Component):
class Media:
css = ["style.css", "style2.css"]
js = ["script.js", "script2.js"]
comp = MultistyleComponent("multistyle_component")
self.assertHTMLEqual(
comp.render_dependencies(),
"""
""",
)
def test_component_with_filtered_template(self):
class FilteredComponent(component.Component):
template_name = "filtered_template.html"
def get_context_data(self, var1=None, var2=None):
return {
"var1": var1,
"var2": var2,
}
comp = FilteredComponent("filtered_component")
context = Context(comp.get_context_data(var1="test1", var2="test2"))
self.assertHTMLEqual(
comp.render(context),
"""
Var1: test1
Var2 (uppercased): TEST2
""",
)
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,
}
def get_template_name(self, context):
return f"svg_{context['name']}.svg"
comp = SvgComponent("svg_component")
self.assertHTMLEqual(
comp.render(Context(comp.get_context_data(name="dynamic1"))),
"""
""",
)
self.assertHTMLEqual(
comp.render(Context(comp.get_context_data(name="dynamic2"))),
"""
""",
)
# Settings required for autodiscover to work
@override_settings(
BASE_DIR=Path(__file__).resolve().parent,
STATICFILES_DIRS=[
Path(__file__).resolve().parent / "components",
],
)
def test_component_with_relative_paths_as_subcomponent(self):
# Ensure that the module is executed again after import in autodiscovery
if "tests.components.relative_file.relative_file" in sys.modules:
del sys.modules["tests.components.relative_file.relative_file"]
# Fix the paths, since the "components" dir is nested
with autodiscover_with_cleanup(map_import_paths=lambda p: f"tests.{p}"):
template = Template(
"""
{% load component_tags %}{% component_dependencies %}
{% component 'parent_component' %}
{% fill 'content' %}
{% component name='relative_file_component' variable='hello' %}
{% endcomponent %}
{% endfill %}
{% endcomponent %}
""" # NOQA
)
rendered = template.render(Context({}))
self.assertIn('', rendered, rendered)
def test_component_inside_slot(self):
class SlottedComponent(component.Component):
template_name = "slotted_template.html"
def get_context_data(self, name: Optional[str] = None) -> Dict[str, Any]:
return {
"name": name,
}
component.registry.register("test", SlottedComponent)
self.template = Template(
"""
{% load component_tags %}
{% component "test" name='Igor' %}
{% fill "header" %}
Name: {{ name }}
{% endfill %}
{% fill "main" %}
Day: {{ day }}
{% endfill %}
{% fill "footer" %}
{% component "test" name='Joe2' %}
{% fill "header" %}
Name2: {{ name }}
{% endfill %}
{% fill "main" %}
Day2: {{ day }}
{% endfill %}
{% endcomponent %}
{% endfill %}
{% endcomponent %}
"""
)
# {{ name }} should be "Jannete" everywhere
rendered = self.template.render(Context({"day": "Monday", "name": "Jannete"}))
self.assertHTMLEqual(
rendered,
"""