import os
import sys
from pathlib import Path
from django.forms.widgets import Media
from django.template import Context, Template
from django.templatetags.static import static
from django.test import override_settings
from django.utils.html import format_html, html_safe
from django.utils.safestring import mark_safe
from django_components import Component, registry, render_dependencies, types
from .django_test_setup import setup_test_config
from .testutils import BaseTestCase, autodiscover_with_cleanup
setup_test_config({"autodiscover": False})
# "Main media" refer to the HTML, JS, and CSS set on the Component class itself
# (as opposed via the `Media` class). These have special handling in the Component.
class MainMediaTest(BaseTestCase):
def test_html_js_css_inlined(self):
class TestComponent(Component):
template = """
{% load component_tags %}
{% component_js_dependencies %}
{% component_css_dependencies %}
Content
"""
css = ".html-css-only { color: blue; }"
js = "console.log('HTML and JS only');"
self.assertEqual(
TestComponent.css,
".html-css-only { color: blue; }",
)
self.assertEqual(
TestComponent.js,
"console.log('HTML and JS only');",
)
rendered = TestComponent.render()
self.assertInHTML(
'Content
',
rendered,
)
self.assertInHTML(
"",
rendered,
)
self.assertInHTML(
"",
rendered,
)
# Check that the HTML / JS / CSS can be accessed on the component class
self.assertEqual(
TestComponent.template,
"""
{% load component_tags %}
{% component_js_dependencies %}
{% component_css_dependencies %}
Content
""",
)
self.assertEqual(
TestComponent.css,
".html-css-only { color: blue; }",
)
self.assertEqual(
TestComponent.js,
"console.log('HTML and JS only');",
)
@override_settings(
STATICFILES_DIRS=[
os.path.join(Path(__file__).resolve().parent, "static_root"),
],
)
def test_html_js_css_filepath_rel_to_component(self):
from tests.test_app.components.app_lvl_comp.app_lvl_comp import AppLvlCompComponent
class TestComponent(AppLvlCompComponent):
pass
registry.register("test", TestComponent)
self.assertIn(
".html-css-only {\n color: blue;\n}",
TestComponent.css,
)
self.assertIn(
'console.log("JS file");',
TestComponent.js,
)
rendered_raw = Template(
"""
{% load component_tags %}
{% component_js_dependencies %}
{% component_css_dependencies %}
{% component "test" variable="test" / %}
"""
).render(Context())
rendered = render_dependencies(rendered_raw)
self.assertInHTML(
"""
""",
rendered,
)
self.assertInHTML(
"",
rendered,
)
self.assertInHTML(
'',
rendered,
)
# Check that the HTML / JS / CSS can be accessed on the component class
self.assertEqual(
TestComponent.template,
(
'\n"
),
)
self.assertEqual(TestComponent.css, ".html-css-only {\n" " color: blue;\n" "}\n")
self.assertEqual(
TestComponent.js,
'console.log("JS file");\n',
)
@override_settings(
STATICFILES_DIRS=[
os.path.join(Path(__file__).resolve().parent, "static_root"),
],
)
def test_html_js_css_filepath_from_static(self):
class TestComponent(Component):
template_file = "test_app_simple_template.html"
css_file = "style.css"
js_file = "script.js"
def get_context_data(self, variable):
return {
"variable": variable,
}
registry.register("test", TestComponent)
self.assertIn(
"Variable: {{ variable }}",
TestComponent.template,
)
self.assertIn(
".html-css-only {\n color: blue;\n}",
TestComponent.css,
)
self.assertIn(
'console.log("HTML and JS only");',
TestComponent.js,
)
rendered_raw = Template(
"""
{% load component_tags %}
{% component_js_dependencies %}
{% component_css_dependencies %}
{% component "test" variable="test" / %}
"""
).render(Context())
rendered = render_dependencies(rendered_raw)
self.assertIn(
"Variable: test",
rendered,
)
self.assertInHTML(
"",
rendered,
)
self.assertInHTML(
'',
rendered,
)
# Check that the HTML / JS / CSS can be accessed on the component class
self.assertEqual(
TestComponent.template,
"Variable: {{ variable }}\n",
)
self.assertEqual(
TestComponent.css,
(
"/* Used in `MainMediaTest` tests in `test_component_media.py` */\n"
".html-css-only {\n"
" color: blue;\n"
"}"
),
)
self.assertEqual(
TestComponent.js,
(
"/* Used in `MainMediaTest` tests in `test_component_media.py` */\n"
'console.log("HTML and JS only");\n'
),
)
@override_settings(
STATICFILES_DIRS=[
os.path.join(Path(__file__).resolve().parent, "static_root"),
],
)
def test_html_js_css_filepath_lazy_loaded(self):
from tests.test_app.components.app_lvl_comp.app_lvl_comp import AppLvlCompComponent
class TestComponent(AppLvlCompComponent):
pass
# NOTE: Since this is a subclass, actual CSS is defined on the parent class, and thus
# the corresponding ComponentMedia instance is also on the parent class.
self.assertEqual(
AppLvlCompComponent._component_media.css, # type: ignore[attr-defined]
None,
)
self.assertEqual(
AppLvlCompComponent._component_media.css_file, # type: ignore[attr-defined]
"app_lvl_comp.css",
)
# Access the property to load the CSS
_ = TestComponent.css
self.assertEqual(
AppLvlCompComponent._component_media.css, # type: ignore[attr-defined]
(".html-css-only {\n" " color: blue;\n" "}\n"),
)
self.assertEqual(
AppLvlCompComponent._component_media.css_file, # type: ignore[attr-defined]
"app_lvl_comp/app_lvl_comp.css",
)
# Also check JS and HTML while we're at it
self.assertEqual(
AppLvlCompComponent._component_media.template, # type: ignore[attr-defined]
(
'\n"
),
)
self.assertEqual(
AppLvlCompComponent._component_media.template_file, # type: ignore[attr-defined]
"app_lvl_comp/app_lvl_comp.html",
)
self.assertEqual(
AppLvlCompComponent._component_media.js, # type: ignore[attr-defined]
'console.log("JS file");\n',
)
self.assertEqual(
AppLvlCompComponent._component_media.js_file, # type: ignore[attr-defined]
"app_lvl_comp/app_lvl_comp.js",
)
def test_html_variable(self):
class VariableHTMLComponent(Component):
def get_template(self, context):
return Template("{{ variable }}
")
comp = VariableHTMLComponent("variable_html_component")
context = Context({"variable": "Dynamic Content"})
self.assertHTMLEqual(
comp.render(context),
'Dynamic Content
',
)
def test_html_variable_filtered(self):
class FilteredComponent(Component):
template: types.django_html = """
Var1: {{ var1 }}
Var2 (uppercased): {{ var2|upper }}
"""
def get_context_data(self, var1=None, var2=None):
return {
"var1": var1,
"var2": var2,
}
rendered = FilteredComponent.render(kwargs={"var1": "test1", "var2": "test2"})
self.assertHTMLEqual(
rendered,
"""
Var1: test1
Var2 (uppercased): TEST2
""",
)
class ComponentMediaTests(BaseTestCase):
def test_empty_media(self):
class SimpleComponent(Component):
template: types.django_html = """
{% load component_tags %}
{% component_js_dependencies %}
{% component_css_dependencies %}
Variable: {{ variable }}
"""
class Media:
pass
rendered = SimpleComponent.render()
self.assertEqual(rendered.count("