import os
import sys
from pathlib import Path
from textwrap import dedent
from django.forms.widgets import Media
from django.template import Context, Template
from django.templatetags.static import static
from django.utils.html import format_html, html_safe
from django.utils.safestring import mark_safe
from pytest_django.asserts import assertHTMLEqual, assertInHTML
from django_components import Component, autodiscover, registry, render_dependencies, types
from django_components.testing import djc_test
from .testutils import setup_test_config
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.
@djc_test
class TestMainMedia:
def test_html_js_css_inlined(self):
class TestComponent(Component):
template = dedent("""
{% load component_tags %}
{% component_js_dependencies %}
{% component_css_dependencies %}
Content
""")
css = ".html-css-only { color: blue; }"
js = "console.log('HTML and JS only');"
assert TestComponent.css == ".html-css-only { color: blue; }"
assert TestComponent.js == "console.log('HTML and JS only');"
rendered = TestComponent.render()
assertInHTML(
'Content
',
rendered,
)
assertInHTML(
"",
rendered,
)
assertInHTML(
"",
rendered,
)
# Check that the HTML / JS / CSS can be accessed on the component class
assert TestComponent.template == dedent("""
{% load component_tags %}
{% component_js_dependencies %}
{% component_css_dependencies %}
Content
""")
assert TestComponent.css == ".html-css-only { color: blue; }"
assert TestComponent.js == "console.log('HTML and JS only');"
@djc_test(
django_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)
assert ".html-css-only {\n color: blue;\n}" in TestComponent.css # type: ignore[operator]
assert 'console.log("JS file");' in TestComponent.js # type: ignore[operator]
rendered_raw = Template(
"""
{% load component_tags %}
{% component_js_dependencies %}
{% component_css_dependencies %}
{% component "test" variable="test" / %}
"""
).render(Context())
rendered = render_dependencies(rendered_raw)
assertInHTML(
"""
""",
rendered,
)
assertInHTML(
"",
rendered,
)
assertInHTML(
'',
rendered,
)
# Check that the HTML / JS / CSS can be accessed on the component class
assert TestComponent.template == (
'\n"
)
assert TestComponent.css == ".html-css-only {\n color: blue;\n}\n"
assert TestComponent.js == 'console.log("JS file");\n'
@djc_test(
django_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)
assert "Variable: {{ variable }}" in TestComponent.template # type: ignore[operator]
assert ".html-css-only {\n color: blue;\n}" in TestComponent.css # type: ignore[operator]
assert 'console.log("HTML and JS only");' in TestComponent.js # type: ignore[operator]
rendered_raw = Template(
"""
{% load component_tags %}
{% component_js_dependencies %}
{% component_css_dependencies %}
{% component "test" variable="test" / %}
"""
).render(Context())
rendered = render_dependencies(rendered_raw)
assert 'Variable: test' in rendered
assertInHTML(
"",
rendered,
)
assertInHTML(
'',
rendered,
)
# Check that the HTML / JS / CSS can be accessed on the component class
assert TestComponent.template == "Variable: {{ variable }}\n"
assert TestComponent.css == (
"/* Used in `MainMediaTest` tests in `test_component_media.py` */\n"
".html-css-only {\n"
" color: blue;\n"
"}"
)
assert TestComponent.js == (
"/* Used in `MainMediaTest` tests in `test_component_media.py` */\n"
'console.log("HTML and JS only");\n'
)
@djc_test(
django_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.
assert AppLvlCompComponent._component_media.css is None # type: ignore[attr-defined]
assert AppLvlCompComponent._component_media.css_file == "app_lvl_comp.css" # type: ignore[attr-defined]
# Access the property to load the CSS
_ = TestComponent.css
assert AppLvlCompComponent._component_media.css == (".html-css-only {\n" " color: blue;\n" "}\n") # type: ignore[attr-defined]
assert AppLvlCompComponent._component_media.css_file == "app_lvl_comp/app_lvl_comp.css" # type: ignore[attr-defined]
# Also check JS and HTML while we're at it
assert AppLvlCompComponent._component_media.template == ( # type: ignore[attr-defined]
'\n"
)
assert AppLvlCompComponent._component_media.template_file == "app_lvl_comp/app_lvl_comp.html" # type: ignore[attr-defined]
assert AppLvlCompComponent._component_media.js == 'console.log("JS file");\n' # type: ignore[attr-defined]
assert AppLvlCompComponent._component_media.js_file == "app_lvl_comp/app_lvl_comp.js" # type: ignore[attr-defined]
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"})
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"})
assertHTMLEqual(
rendered,
"""
Var1: test1
Var2 (uppercased): TEST2
""",
)
@djc_test
class TestComponentMedia:
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()
assert rendered.count("