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' " {% csrf_token %}\n" ' \n' ' \n' "
\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' " {% csrf_token %}\n" ' \n' ' \n' "
\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("', rendered) assertInHTML('', rendered) assertInHTML('', rendered) def test_css_js_as_string(self): class SimpleComponent(Component): template = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = "path/to/style.css" js = "path/to/script.js" rendered = SimpleComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) def test_css_as_dict(self): class SimpleComponent(Component): template = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = { "all": "path/to/style.css", "print": ["path/to/style2.css"], "screen": "path/to/style3.css", } js = ["path/to/script.js"] rendered = SimpleComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) def test_media_custom_render_js(self): class MyMedia(Media): def render_js(self): tags: list[str] = [] for path in self._js: # type: ignore[attr-defined] abs_path = self.absolute_path(path) # type: ignore[attr-defined] tags.append(f'') return tags class SimpleComponent(Component): template = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ media_class = MyMedia class Media: js = ["path/to/script.js", "path/to/script2.js"] rendered = SimpleComponent.render() assert '' in rendered assert '' in rendered def test_media_custom_render_css(self): class MyMedia(Media): def render_css(self): tags: list[str] = [] media = sorted(self._css) # type: ignore[attr-defined] for medium in media: for path in self._css[medium]: # type: ignore[attr-defined] tags.append(f'') return tags class SimpleComponent(Component): template = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ media_class = MyMedia class Media: css = { "all": "path/to/style.css", "print": ["path/to/style2.css"], "screen": "path/to/style3.css", } rendered = SimpleComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) @djc_test class TestMediaPathAsObject: def test_safestring(self): """ Test that media work with paths defined as instances of classes that define the `__html__` method. See https://docs.djangoproject.com/en/5.0/topics/forms/media/#paths-as-objects """ # NOTE: @html_safe adds __html__ method from __str__ @html_safe class JSTag: def __init__(self, path: str) -> None: self.path = path def __str__(self): return f'' @html_safe class CSSTag: def __init__(self, path: str) -> None: self.path = path def __str__(self): return f'' # Format as mentioned in https://github.com/django-components/django-components/issues/522#issuecomment-2173577094 @html_safe class PathObj: def __init__(self, static_path: str) -> None: self.static_path = static_path def __str__(self): return format_html('', static(self.static_path)) class SimpleComponent(Component): template = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = { "all": [ CSSTag("path/to/style.css"), # Formatted by CSSTag mark_safe(''), # Literal ], "print": [ CSSTag("path/to/style3.css"), # Formatted by CSSTag ], "screen": "path/to/style4.css", # Formatted by Media.render_css } js = [ JSTag("path/to/script.js"), # Formatted by JSTag mark_safe(''), # Literal PathObj("path/to/script3.js"), # Literal "path/to/script4.js", # Formatted by Media.render_js ] rendered = SimpleComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) def test_pathlike(self): """ Test that media work with paths defined as instances of classes that define the `__fspath__` method. """ class MyPath(os.PathLike): def __init__(self, path: str) -> None: self.path = path def __fspath__(self): return self.path class SimpleComponent(Component): template = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = { "all": [ MyPath("path/to/style.css"), Path("path/to/style2.css"), ], "print": [ MyPath("path/to/style3.css"), ], "screen": "path/to/style4.css", } js = [ MyPath("path/to/script.js"), Path("path/to/script2.js"), "path/to/script3.js", ] rendered = SimpleComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) def test_str(self): """ Test that media work with paths defined as instances of classes that subclass 'str'. """ class MyStr(str): pass class SimpleComponent(Component): template = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = { "all": [ MyStr("path/to/style.css"), "path/to/style2.css", ], "print": [ MyStr("path/to/style3.css"), ], "screen": "path/to/style4.css", } js = [ MyStr("path/to/script.js"), "path/to/script2.js", ] rendered = SimpleComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) def test_bytes(self): """ Test that media work with paths defined as instances of classes that subclass 'bytes'. """ class MyBytes(bytes): pass class SimpleComponent(Component): template = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = { "all": [ MyBytes(b"path/to/style.css"), b"path/to/style2.css", ], "print": [ MyBytes(b"path/to/style3.css"), ], "screen": b"path/to/style4.css", } js = [ MyBytes(b"path/to/script.js"), "path/to/script2.js", ] rendered = SimpleComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) def test_function(self): class SimpleComponent(Component): template = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = [ lambda: mark_safe(''), # Literal lambda: Path("calendar/style1.css"), lambda: "calendar/style2.css", lambda: b"calendar/style3.css", ] js = [ lambda: mark_safe(''), # Literal lambda: Path("calendar/script1.js"), lambda: "calendar/script2.js", lambda: b"calendar/script3.js", ] rendered = SimpleComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) @djc_test( django_settings={ "STATIC_URL": "static/", } ) def test_works_with_static(self): """Test that all the different ways of defining media files works with Django's staticfiles""" class SimpleComponent(Component): template = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = [ mark_safe(f''), # Literal Path("calendar/style1.css"), "calendar/style2.css", b"calendar/style3.css", lambda: "calendar/style4.css", ] js = [ mark_safe(f''), # Literal Path("calendar/script1.js"), "calendar/script2.js", b"calendar/script3.js", lambda: "calendar/script4.js", ] rendered = SimpleComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) @djc_test class TestMediaStaticfiles: # For context see https://github.com/django-components/django-components/issues/522 @djc_test( django_settings={ # Configure static files. The dummy files are set up in the `./static_root` dir. # The URL should have path prefix /static/. # NOTE: We don't need STATICFILES_DIRS, because we don't run collectstatic # See https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-STATICFILES_DIRS "STATIC_URL": "static/", "STATIC_ROOT": os.path.join(Path(__file__).resolve().parent, "static_root"), # `django.contrib.staticfiles` MUST be installed for staticfiles resolution to work. "INSTALLED_APPS": [ "django.contrib.staticfiles", "django_components", ], } ) def test_default_static_files_storage(self): """Test integration with Django's staticfiles app""" class MyMedia(Media): def render_js(self): tags: list[str] = [] for path in self._js: # type: ignore[attr-defined] abs_path = self.absolute_path(path) # type: ignore[attr-defined] tags.append(f'') return tags class SimpleComponent(Component): template = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ media_class = MyMedia class Media: css = "calendar/style.css" js = "calendar/script.js" rendered = SimpleComponent.render() # NOTE: Since we're using the default storage class for staticfiles, the files should # be searched as specified above (e.g. `calendar/script.js`) inside `static_root` dir. assertInHTML('', rendered) assertInHTML('', rendered) # For context see https://github.com/django-components/django-components/issues/522 @djc_test( django_settings={ # Configure static files. The dummy files are set up in the `./static_root` dir. # The URL should have path prefix /static/. # NOTE: We don't need STATICFILES_DIRS, because we don't run collectstatic # See https://docs.djangoproject.com/en/5.0/ref/settings/#std-setting-STATICFILES_DIRS "STATIC_URL": "static/", "STATIC_ROOT": os.path.join(Path(__file__).resolve().parent, "static_root"), # NOTE: STATICFILES_STORAGE is deprecated since 5.1, use STORAGES instead # See https://docs.djangoproject.com/en/5.0/ref/settings/#staticfiles-storage "STORAGES": { # This was NOT changed "default": { "BACKEND": "django.core.files.storage.FileSystemStorage", }, # This WAS changed so that static files are looked up by the `staticfiles.json` "staticfiles": { "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage", }, }, # `django.contrib.staticfiles` MUST be installed for staticfiles resolution to work. "INSTALLED_APPS": [ "django.contrib.staticfiles", "django_components", ], } ) def test_manifest_static_files_storage(self): """Test integration with Django's staticfiles app and ManifestStaticFilesStorage""" class MyMedia(Media): def render_js(self): tags: list[str] = [] for path in self._js: # type: ignore[attr-defined] abs_path = self.absolute_path(path) # type: ignore[attr-defined] tags.append(f'') return tags class SimpleComponent(Component): template = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ media_class = MyMedia class Media: css = "calendar/style.css" js = "calendar/script.js" rendered = SimpleComponent.render() # NOTE: Since we're using ManifestStaticFilesStorage, we expect the rendered media to link # to the files as defined in staticfiles.json assertInHTML( '', rendered ) assertInHTML('', rendered) @djc_test class TestMediaRelativePath: class ParentComponent(Component): template: types.django_html = """ {% load component_tags %}

Parent content

{% component name="variable_display" shadowing_variable='override' new_variable='unique_val' %} {% endcomponent %}
{% slot 'content' %}

Slot content

{% component name="variable_display" shadowing_variable='slot_default_override' new_variable='slot_default_unique' %} {% endcomponent %} {% endslot %}
""" # noqa def get_context_data(self): return {"shadowing_variable": "NOT SHADOWED"} class VariableDisplay(Component): template: types.django_html = """ {% load component_tags %}

Shadowing variable = {{ shadowing_variable }}

Uniquely named variable = {{ unique_variable }}

""" 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 # Settings required for autodiscover to work @djc_test( django_settings={ "BASE_DIR": Path(__file__).resolve().parent, "STATICFILES_DIRS": [ Path(__file__).resolve().parent / "components", ], } ) def test_component_with_relative_media_paths(self): registry.register(name="parent_component", component=self.ParentComponent) registry.register(name="variable_display", component=self.VariableDisplay) # 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 autodiscover(map_module=lambda p: f"tests.{p}" if p.startswith("components") else p) # Make sure that only relevant components are registered: comps_to_remove = [ comp_name for comp_name in registry.all() if comp_name not in ["relative_file_component", "parent_component", "variable_display"] ] for comp_name in comps_to_remove: registry.unregister(comp_name) template_str: types.django_html = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} {% component name='relative_file_component' variable=variable / %} """ template = Template(template_str) rendered = render_dependencies(template.render(Context({"variable": "test"}))) assertInHTML('', rendered) assertInHTML( """
""", rendered, ) assertInHTML('', rendered) # Settings required for autodiscover to work @djc_test( django_settings={ "BASE_DIR": Path(__file__).resolve().parent, "STATICFILES_DIRS": [ Path(__file__).resolve().parent / "components", ], } ) def test_component_with_relative_media_paths_as_subcomponent(self): registry.register(name="parent_component", component=self.ParentComponent) registry.register(name="variable_display", component=self.VariableDisplay) # 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 autodiscover(map_module=lambda p: f"tests.{p}" if p.startswith("components") else p) registry.unregister("relative_file_pathobj_component") template_str: types.django_html = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} {% component 'parent_component' %} {% fill 'content' %} {% component name='relative_file_component' variable='hello' %} {% endcomponent %} {% endfill %} {% endcomponent %} """ template = Template(template_str) rendered = template.render(Context({})) assertInHTML('', rendered) # Settings required for autodiscover to work @djc_test( django_settings={ "BASE_DIR": Path(__file__).resolve().parent, "STATICFILES_DIRS": [ Path(__file__).resolve().parent / "components", ], } ) def test_component_with_relative_media_does_not_trigger_safestring_path_at__new__(self): """ Test that, for the __html__ objects are not coerced into string throughout the class creation. This is important to allow to call `collectstatic` command. Because some users use `static` inside the `__html__` or `__str__` methods. So if we "render" the safestring using str() during component class creation (__new__), then we force to call `static`. And if this happens during `collectstatic` run, then this triggers an error, because `static` is called before the static files exist. https://github.com/django-components/django-components/issues/522#issuecomment-2173577094 """ registry.register(name="parent_component", component=self.ParentComponent) registry.register(name="variable_display", component=self.VariableDisplay) # Ensure that the module is executed again after import in autodiscovery if "tests.components.relative_file_pathobj.relative_file_pathobj" in sys.modules: del sys.modules["tests.components.relative_file_pathobj.relative_file_pathobj"] # Fix the paths, since the "components" dir is nested autodiscover(map_module=lambda p: f"tests.{p}" if p.startswith("components") else p) # Mark the PathObj instances of 'relative_file_pathobj_component' so they won't raise # error if PathObj.__str__ is triggered. CompCls = registry.get("relative_file_pathobj_component") CompCls.Media.js[0].throw_on_calling_str = False # type: ignore CompCls.Media.css["all"][0].throw_on_calling_str = False # type: ignore rendered = CompCls.render(kwargs={"variable": "abc"}) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) @djc_test class TestSubclassingMedia: def test_media_in_child_and_parent(self): class ParentComponent(Component): template: types.django_html = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = "parent.css" js = "parent.js" class ChildComponent(ParentComponent): class Media: css = "child.css" js = "child.js" rendered = ChildComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) def test_media_in_child_and_grandparent(self): class GrandParentComponent(Component): template: types.django_html = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = "grandparent.css" js = "grandparent.js" class ParentComponent(GrandParentComponent): Media = None class ChildComponent(ParentComponent): class Media: css = "child.css" js = "child.js" rendered = ChildComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) def test_media_in_parent_and_grandparent(self): class GrandParentComponent(Component): template: types.django_html = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = "grandparent.css" js = "grandparent.js" class ParentComponent(GrandParentComponent): class Media: css = "parent.css" js = "parent.js" class ChildComponent(ParentComponent): pass rendered = ChildComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) def test_media_in_multiple_bases(self): class GrandParent1Component(Component): class Media: css = "grandparent1.css" js = "grandparent1.js" class GrandParent2Component(Component): pass # NOTE: The bases don't even have to be Component classes, # as long as they have the nested `Media` class. class GrandParent3Component: # NOTE: When we don't subclass `Component`, we have to correctly format the `Media` class class Media: css = {"all": ["grandparent3.css"]} js = ["grandparent3.js"] class GrandParent4Component: pass class Parent1Component(GrandParent1Component, GrandParent2Component): class Media: css = "parent1.css" js = "parent1.js" class Parent2Component(GrandParent3Component, GrandParent4Component): Media = None class ChildComponent(Parent1Component, Parent2Component): template: types.django_html = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = "child.css" js = "child.js" rendered = ChildComponent.render() assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) def test_extend_false_in_child(self): class Parent1Component(Component): template: types.django_html = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = "parent1.css" js = "parent1.js" class Parent2Component(Component): class Media: css = "parent2.css" js = "parent2.js" class ChildComponent(Parent1Component, Parent2Component): class Media: extend = False css = "child.css" js = "child.js" rendered = ChildComponent.render() assert "parent1.css" not in rendered assert "parent2.css" not in rendered assertInHTML('', rendered) assert "parent1.js" not in rendered assert "parent2.js" not in rendered assertInHTML('', rendered) def test_extend_false_in_parent(self): class GrandParentComponent(Component): class Media: css = "grandparent.css" js = "grandparent.js" class Parent1Component(Component): template: types.django_html = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = "parent1.css" js = "parent1.js" class Parent2Component(GrandParentComponent): class Media: extend = False css = "parent2.css" js = "parent2.js" class ChildComponent(Parent1Component, Parent2Component): class Media: css = "child.css" js = "child.js" rendered = ChildComponent.render() assert "grandparent.css" not in rendered assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assert "grandparent.js" not in rendered assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) def test_extend_list_in_child(self): class Parent1Component(Component): template: types.django_html = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = "parent1.css" js = "parent1.js" class Parent2Component(Component): class Media: css = "parent2.css" js = "parent2.js" class Other1Component(Component): class Media: css = "other1.css" js = "other1.js" class Other2Component: class Media: css = {"all": ["other2.css"]} js = ["other2.js"] class ChildComponent(Parent1Component, Parent2Component): class Media: extend = [Other1Component, Other2Component] css = "child.css" js = "child.js" rendered = ChildComponent.render() assert "parent1.css" not in rendered assert "parent2.css" not in rendered assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assert "parent1.js" not in rendered assert "parent2.js" not in rendered assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) def test_extend_list_in_parent(self): class Other1Component(Component): class Media: css = "other1.css" js = "other1.js" class Other2Component: class Media: css = {"all": ["other2.css"]} js = ["other2.js"] class GrandParentComponent(Component): class Media: css = "grandparent.css" js = "grandparent.js" class Parent1Component(Component): template: types.django_html = """ {% load component_tags %} {% component_js_dependencies %} {% component_css_dependencies %} """ class Media: css = "parent1.css" js = "parent1.js" class Parent2Component(GrandParentComponent): class Media: extend = [Other1Component, Other2Component] css = "parent2.css" js = "parent2.js" class ChildComponent(Parent1Component, Parent2Component): class Media: css = "child.css" js = "child.js" rendered = ChildComponent.render() assert "grandparent.css" not in rendered assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assert "grandparent.js" not in rendered assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered) assertInHTML('', rendered)