mirror of
https://github.com/wrabit/django-cotton.git
synced 2025-07-24 18:03:46 +00:00
wip
This commit is contained in:
parent
514be20faf
commit
f1638be9c4
52 changed files with 986 additions and 928 deletions
|
@ -1,3 +0,0 @@
|
|||
<c-parent>
|
||||
<c-child>d</c-child>
|
||||
</c-parent>
|
|
@ -1,8 +1,8 @@
|
|||
<c-benchmarks.partials.main>
|
||||
I'm default
|
||||
<c-slot name="top">
|
||||
I'm top
|
||||
</c-slot>
|
||||
I'm default
|
||||
<c-slot name="bottom">
|
||||
I'm bottom
|
||||
</c-slot>
|
|
@ -1 +0,0 @@
|
|||
<div class="i-am-child"></div>
|
|
@ -1,3 +0,0 @@
|
|||
<div>
|
||||
{{ name }}
|
||||
</div>
|
|
@ -1,3 +0,0 @@
|
|||
<div class="i-am-parent">
|
||||
{{ slot }}
|
||||
</div>
|
|
@ -1,12 +0,0 @@
|
|||
<c-vars var1="sds" prop_with_default="1" />
|
||||
|
||||
<div>
|
||||
{{ testy }}
|
||||
<p>var1: '{{ var1 }}'</p>
|
||||
<p>attr1: '{{ attr1 }}'</p>
|
||||
<p>empty_var: '{{ empty_var }}'</p>
|
||||
<p>var_with_default: '{{ var_with_default }}'</p>
|
||||
<p>slot: '{{ slot }}'</p>
|
||||
<p>named_slot: '{{ named_slot }}'</p>
|
||||
<p>attrs: '{{ attrs }}'</p>
|
||||
</div>
|
|
@ -1,3 +0,0 @@
|
|||
<c-parent>
|
||||
<c-forms.input name="test" style="width: 100%" silica:model="first_name"/>
|
||||
</c-parent>
|
|
@ -1,3 +0,0 @@
|
|||
{% for i in '123456789' %}
|
||||
<c-parent />
|
||||
{% endfor %}
|
|
@ -1,7 +0,0 @@
|
|||
{% for item in items %}
|
||||
<c-named-slot-component>
|
||||
<c-slot name="name">
|
||||
item name: {{ item.name }}
|
||||
</c-slot>
|
||||
</c-named-slot-component>
|
||||
{% endfor %}
|
|
@ -1 +0,0 @@
|
|||
<c-parent></c-parent>
|
|
@ -1,4 +0,0 @@
|
|||
{% load static %}
|
||||
|
||||
|
||||
<c-parent/>
|
|
@ -1 +1 @@
|
|||
{% for d in data %}<c-test :name="'Taylor'" class="mt-4" />{% endfor %}
|
||||
{% for d in data %}<c-benchmarks.cotton-include />{% endfor %}
|
|
@ -1 +1 @@
|
|||
{% for d in data %}<h2>{{ d }}</h2>{% endfor %}
|
||||
{% for d in data %}<h2>{% include 'benchmarks/native_include.html' %}</h2>{% endfor %}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
<c-vars-test-component var1="im a var" attr1="im an attr">
|
||||
default slot
|
||||
</c-vars-test-component>
|
|
@ -19,8 +19,18 @@ settings.configure(
|
|||
{
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": ["example_project/templates"],
|
||||
"APP_DIRS": False,
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"loaders": [
|
||||
(
|
||||
"django.template.loaders.cached.Loader",
|
||||
[
|
||||
"django_cotton.loader.CottonLoader",
|
||||
"django.template.loaders.filesystem.Loader",
|
||||
"django.template.loaders.app_directories.Loader",
|
||||
],
|
||||
)
|
||||
],
|
||||
"context_processors": [
|
||||
"django.template.context_processors.debug",
|
||||
"django.template.context_processors.request",
|
||||
|
@ -37,38 +47,59 @@ settings.configure(
|
|||
django.setup()
|
||||
|
||||
|
||||
def template_bench(template_name, iterations=500):
|
||||
def template_bench(template_name, iterations=1000):
|
||||
start_time = time.time()
|
||||
for _ in range(iterations):
|
||||
render_to_string(template_name)
|
||||
end_time = time.time()
|
||||
return end_time - start_time, render_to_string(template_name)
|
||||
duration = round((end_time - start_time) * 1000, 2)
|
||||
|
||||
return duration, render_to_string(template_name)
|
||||
|
||||
|
||||
def template_bench_alt(template_name, iterations=500):
|
||||
def template_bench_alt(template_name, iterations=1000):
|
||||
data = list(range(1, iterations))
|
||||
start_time = time.time()
|
||||
render_to_string(template_name, context={"data": data})
|
||||
end_time = time.time()
|
||||
return end_time - start_time, render_to_string(template_name)
|
||||
duration = round((end_time - start_time) * 1000, 2)
|
||||
|
||||
return duration, render_to_string(template_name)
|
||||
|
||||
|
||||
# warm caches
|
||||
template_bench_alt("simple_native.html", iterations=1)
|
||||
template_bench_alt("simple_cotton.html", iterations=1)
|
||||
|
||||
simple_native, _ = template_bench_alt("simple_native.html")
|
||||
simple_cotton, _ = template_bench_alt("simple_cotton.html")
|
||||
|
||||
print(f"Native Django Template: {simple_native} seconds")
|
||||
print(f"Cotton Template: {simple_cotton} seconds")
|
||||
print("---")
|
||||
print(f"Native Django {{% for %}} loop: {simple_native} ms")
|
||||
print(f"Cotton {{% for %}} loop: {simple_cotton} ms")
|
||||
|
||||
# warm caches
|
||||
template_bench("benchmarks/native_include.html", iterations=1)
|
||||
template_bench("cotton/benchmarks/cotton_include.html", iterations=1)
|
||||
|
||||
time_native_include, _ = template_bench("benchmarks/native_include.html")
|
||||
time_cotton_include, _ = template_bench("cotton/benchmarks/cotton_include.html")
|
||||
|
||||
print(f"Native {{% include %}}: {time_native_include} seconds")
|
||||
print(f"Cotton for include:: {time_cotton_include} seconds")
|
||||
print("---")
|
||||
print(f"Native {{% include %}}: {time_native_include} ms")
|
||||
print(f"Cotton for include: {time_cotton_include} ms")
|
||||
|
||||
# warm caches
|
||||
template_bench("benchmarks/native_extends.html", iterations=1)
|
||||
template_bench("cotton/benchmarks/cotton_compiled.html", iterations=1)
|
||||
template_bench("cotton/benchmarks/cotton_extends_equivalent.html", iterations=1)
|
||||
|
||||
time_native_extends, _ = template_bench("benchmarks/native_extends.html")
|
||||
time_compiled_cotton, _ = template_bench("cotton/benchmarks/cotton_compiled.html")
|
||||
time_cotton, _ = template_bench("cotton/benchmarks/cotton.html")
|
||||
time_cotton, _ = template_bench("cotton/benchmarks/cotton_extends_equivalent.html")
|
||||
|
||||
print(f"Native {{% block %}} and {{% extends %}}: {time_native_extends} seconds")
|
||||
print(f"Uncompiled Cotton Template: {time_cotton} seconds")
|
||||
print(f"Compiled Cotton Template: {time_compiled_cotton} seconds")
|
||||
|
||||
print("---")
|
||||
print(f"Native {{% block %}} and {{% extends %}}: {time_native_extends} ms")
|
||||
print(f"Compiled Cotton Template: {time_compiled_cotton} ms")
|
||||
print(f"Uncompiled Cotton Template: {time_cotton} ms")
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
My template path was not specified in settings!
|
|
@ -1 +0,0 @@
|
|||
My template was not specified in settings!
|
|
@ -1 +0,0 @@
|
|||
<c-unspecified-component />
|
|
@ -1,3 +0,0 @@
|
|||
<c-merges-attributes class="extra-class" silica:model="test" another="test">
|
||||
ss
|
||||
</c-merges-attributes>
|
|
@ -1,3 +0,0 @@
|
|||
<c-receives-attributes attribute_1="hello" and-another="woo1" thirdForLuck="yes">
|
||||
ss
|
||||
</c-receives-attributes>
|
|
@ -1,3 +0,0 @@
|
|||
<c-parent>
|
||||
<c-child>d</c-child>
|
||||
</c-parent>
|
|
@ -1 +0,0 @@
|
|||
<div class="i-am-child"></div>
|
|
@ -1,11 +0,0 @@
|
|||
<div>
|
||||
Header:
|
||||
{{ header }}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
Content:
|
||||
{{ slot }}
|
||||
</div>
|
||||
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
{% if none is None %}
|
||||
<p>none is None</p>
|
||||
{% endif %}
|
||||
|
||||
{% if number == 1 %}
|
||||
<p>number is 1</p>
|
||||
{% endif %}
|
||||
|
||||
{% if boolean_true is True %}
|
||||
<p>boolean_true is True</p>
|
||||
{% endif %}
|
||||
|
||||
{% if boolean_false is False %}
|
||||
<p>boolean_false is False</p>
|
||||
{% endif %}
|
||||
|
||||
{% if dict.key == 'value' %}
|
||||
<p>dict.key is 'value'</p>
|
||||
{% endif %}
|
||||
|
||||
{% if list.0 == 1 %}
|
||||
<p>list.0 is 1</p>
|
||||
{% endif %}
|
||||
|
||||
{% if listdict.0.key == 'value' %}
|
||||
<p>listdict.0.key is 'value'</p>
|
||||
{% endif %}
|
|
@ -1,29 +0,0 @@
|
|||
<c-vars :none="None" :number="1" :boolean_true="True" :boolean_false="False" :dict="{'key': 'value'}" :list="[1, 2, 3]" :listdict="[{'key': 'value'}]" />
|
||||
|
||||
{% if none is None %}
|
||||
<p>none is None</p>
|
||||
{% endif %}
|
||||
|
||||
{% if number == 1 %}
|
||||
<p>number is 1</p>
|
||||
{% endif %}
|
||||
|
||||
{% if boolean_true is True %}
|
||||
<p>boolean_true is True</p>
|
||||
{% endif %}
|
||||
|
||||
{% if boolean_false is False %}
|
||||
<p>boolean_false is False</p>
|
||||
{% endif %}
|
||||
|
||||
{% if dict.key == 'value' %}
|
||||
<p>dict.key is 'value'</p>
|
||||
{% endif %}
|
||||
|
||||
{% if list.0 == 1 %}
|
||||
<p>list.0 is 1</p>
|
||||
{% endif %}
|
||||
|
||||
{% if listdict.0.key == 'value' %}
|
||||
<p>listdict.0.key is 'value'</p>
|
||||
{% endif %}
|
|
@ -1,3 +0,0 @@
|
|||
<div {{ attrs_dict|merge:'class:form-group another-class-with:colon' }}>
|
||||
|
||||
</div>
|
|
@ -1,3 +0,0 @@
|
|||
<div>
|
||||
{{ name }}
|
||||
</div>
|
|
@ -1,4 +0,0 @@
|
|||
Attribute 1 says: '{{ attr1 }}'
|
||||
Attribute 2 says: '{{ attr2 }}'
|
||||
Attribute 3 says: '{{ attr3 }}'
|
||||
attrs tag is: '{{ attrs }}'
|
|
@ -1,3 +0,0 @@
|
|||
<div class="i-am-parent">
|
||||
{{slot}}
|
||||
</div>
|
|
@ -1,3 +0,0 @@
|
|||
<div {{ attrs }}>
|
||||
|
||||
</div>
|
|
@ -1,13 +0,0 @@
|
|||
<c-vars var1 default_var="default var" />
|
||||
|
||||
<p>slot: '{{ slot }}'</p>
|
||||
|
||||
<p>attr1: '{{ attr1 }}'</p>
|
||||
<p>attr2: '{{ attr2 }}'</p>
|
||||
|
||||
<p>var1: '{{ var1 }}'</p>
|
||||
<p>default_var: '{{ default_var }}'</p>
|
||||
|
||||
<p>named_slot: '{{ named_slot }}'</p>
|
||||
|
||||
<p>attrs: '{{ attrs }}'</p>
|
|
@ -1 +0,0 @@
|
|||
<div class="{% if 1 < 2 %} some-class {% endif %}">Hello, World!</div>
|
|
@ -1,9 +0,0 @@
|
|||
<c-eval-attributes-test-component
|
||||
:none="None"
|
||||
:number="1"
|
||||
:boolean_true="True"
|
||||
:boolean_false="False"
|
||||
:dict="{'key': 'value'}"
|
||||
:list="[1, 2, 3]"
|
||||
:listdict="[{'key': 'value'}]"
|
||||
/>
|
|
@ -1 +0,0 @@
|
|||
<c-eval-vars-test-component />
|
|
@ -1,3 +0,0 @@
|
|||
<c-parent>
|
||||
<c-forms.input name="test" style="width: 100%" silica:model="first_name"/>
|
||||
</c-parent>
|
|
@ -1,7 +0,0 @@
|
|||
{% for item in items %}
|
||||
<c-named-slot-component>
|
||||
<c-slot name="name">
|
||||
item name: {{ item.name }}
|
||||
</c-slot>
|
||||
</c-named-slot-component>
|
||||
{% endfor %}
|
|
@ -1,12 +0,0 @@
|
|||
<c-vars test="world" name="Will"></c-vars>
|
||||
|
||||
<!-- create different types of native tag examples -->
|
||||
|
||||
<c-native-tags-in-attributes
|
||||
attr1="Hello {{ name }}"
|
||||
attr2="{{ test|default:"none" }}"
|
||||
attr3="{% if 1 == 1 %}cowabonga!{% endif %}"
|
||||
normal="normal"
|
||||
>
|
||||
<c-slot name="named">test</c-slot>
|
||||
</c-native-tags-in-attributes>
|
|
@ -1 +0,0 @@
|
|||
<c-parent></c-parent>
|
|
@ -1,4 +0,0 @@
|
|||
{% load static %}
|
||||
|
||||
|
||||
<c-parent/>
|
|
@ -1,5 +0,0 @@
|
|||
<c-test-component var1="string with space" attr1="I have spaces">
|
||||
<c-slot name="named_slot">
|
||||
named_slot with spaces
|
||||
</c-slot>
|
||||
</c-test-component>
|
|
@ -1,2 +0,0 @@
|
|||
<c-test-component attr1="variable" :attr2="variable">
|
||||
</c-test-component>
|
|
@ -83,7 +83,19 @@ class CottonComponentNode(Node):
|
|||
|
||||
template_path = self._generate_component_template_path(attrs)
|
||||
|
||||
return get_template(template_path).render(ctx)
|
||||
# Use render_context for caching the template
|
||||
cache = context.render_context.get(self)
|
||||
if cache is None:
|
||||
cache = context.render_context[self] = {}
|
||||
|
||||
tpl = cache.get(template_path)
|
||||
if tpl is None:
|
||||
tpl = get_template(template_path)
|
||||
cache[template_path] = tpl
|
||||
|
||||
return tpl.render(ctx)
|
||||
|
||||
# return get_template(template_path).render(ctx)
|
||||
|
||||
def _build_attrs(self, context):
|
||||
"""
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.urls import path
|
||||
from django.test import override_settings
|
||||
from django.views.generic import TemplateView
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class DynamicURLModule:
|
||||
def __init__(self):
|
||||
self.urlpatterns = []
|
||||
|
||||
def __call__(self):
|
||||
return self.urlpatterns
|
||||
|
||||
|
||||
class CottonInlineTestCase(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
# Set tmp dir and register a url module for our tmp files
|
||||
cls.temp_dir = tempfile.mkdtemp()
|
||||
cls.url_module = DynamicURLModule()
|
||||
cls.url_module_name = f"dynamic_urls_{cls.__name__}"
|
||||
sys.modules[cls.url_module_name] = cls.url_module
|
||||
|
||||
# Register our temp directory as a TEMPLATES path
|
||||
cls.new_templates_setting = settings.TEMPLATES.copy()
|
||||
cls.new_templates_setting[0]["DIRS"] = [cls.temp_dir] + cls.new_templates_setting[0]["DIRS"]
|
||||
|
||||
# Apply the setting
|
||||
cls.templates_override = override_settings(TEMPLATES=cls.new_templates_setting)
|
||||
cls.templates_override.enable()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""Remove temporary directory and clean up modules"""
|
||||
cls.templates_override.disable()
|
||||
shutil.rmtree(cls.temp_dir, ignore_errors=True)
|
||||
del sys.modules[cls.url_module_name]
|
||||
super().tearDownClass()
|
||||
|
||||
def tearDown(self):
|
||||
"""Clear cache between tests so that we can use the same file names for simplicity"""
|
||||
cache.clear()
|
||||
|
||||
def create_template(self, name, content, url=None, context={}):
|
||||
"""Create a template file in the temporary directory and return the path"""
|
||||
path = os.path.join(self.temp_dir, name)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
if url:
|
||||
# Create a dynamic class-based view
|
||||
class DynamicTemplateView(TemplateView):
|
||||
template_name = name
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
dynamic_context = super().get_context_data(**kwargs)
|
||||
dynamic_context.update(context)
|
||||
return dynamic_context
|
||||
|
||||
self.register_path(url, DynamicTemplateView.as_view(template_name=name))
|
||||
|
||||
return path
|
||||
|
||||
def make_view(self, template_name):
|
||||
"""Make a view that renders the given template"""
|
||||
return TemplateView.as_view(template_name=template_name)
|
||||
|
||||
def register_path(self, url, view):
|
||||
"""Register a URL pattern and returns path"""
|
||||
url_pattern = path(url, view)
|
||||
self.url_module.urlpatterns.append(url_pattern)
|
||||
return url_pattern
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.url_module.urlpatterns = []
|
||||
|
||||
def get_url_conf(self):
|
||||
return self.url_module_name
|
485
django_cotton/tests/test_attributes.py
Normal file
485
django_cotton/tests/test_attributes.py
Normal file
|
@ -0,0 +1,485 @@
|
|||
from django_cotton.tests.utils import CottonInlineTestCase
|
||||
from django_cotton.tests.utils import get_compiled
|
||||
|
||||
|
||||
class AttributeHandlingTests(CottonInlineTestCase):
|
||||
def test_dynamic_attributes_on_components(self):
|
||||
self.create_template(
|
||||
"eval_attributes_on_component_view.html",
|
||||
"""
|
||||
<c-dynamic-attributes-component
|
||||
:none="None"
|
||||
:number="1"
|
||||
:boolean_true="True"
|
||||
:boolean_false="False"
|
||||
:dict="{'key': 'value'}"
|
||||
:list="[1, 2, 3]"
|
||||
:listdict="[{'key': 'value'}]"
|
||||
:variable="variable"
|
||||
:template_string_lit="{'dummy': {{ dummy }}}"
|
||||
/>
|
||||
""",
|
||||
"view/",
|
||||
context={"variable": 111, "dummy": 222},
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"cotton/dynamic_attributes_component.html",
|
||||
"""
|
||||
{% if none is None %}
|
||||
<p>none is None</p>
|
||||
{% endif %}
|
||||
|
||||
{% if number == 1 %}
|
||||
<p>number is 1</p>
|
||||
{% endif %}
|
||||
|
||||
{% if boolean_true is True %}
|
||||
<p>boolean_true is True</p>
|
||||
{% endif %}
|
||||
|
||||
{% if boolean_false is False %}
|
||||
<p>boolean_false is False</p>
|
||||
{% endif %}
|
||||
|
||||
{% if dict.key == 'value' %}
|
||||
<p>dict.key is 'value'</p>
|
||||
{% endif %}
|
||||
|
||||
{% if list.0 == 1 %}
|
||||
<p>list.0 is 1</p>
|
||||
{% endif %}
|
||||
|
||||
{% if listdict.0.key == 'value' %}
|
||||
<p>listdict.0.key is 'value'</p>
|
||||
{% endif %}
|
||||
|
||||
{% if variable == 111 %}
|
||||
<p>variable is 111</p>
|
||||
{% endif %}
|
||||
|
||||
{% if template_string_lit.dummy == 222 %}
|
||||
<p>template_string_lit.dummy is 222</p>
|
||||
{% endif %}
|
||||
""",
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, "none is None")
|
||||
self.assertContains(response, "number is 1")
|
||||
self.assertContains(response, "boolean_true is True")
|
||||
self.assertContains(response, "boolean_false is False")
|
||||
self.assertContains(response, "list.0 is 1")
|
||||
self.assertContains(response, "dict.key is 'value'")
|
||||
self.assertContains(response, "listdict.0.key is 'value'")
|
||||
self.assertContains(response, "variable is 111")
|
||||
self.assertContains(response, "template_string_lit.dummy is 222")
|
||||
|
||||
def test_dynamic_attributes_in_cvars(self):
|
||||
self.create_template(
|
||||
"eval_attributes_in_cvars_view.html",
|
||||
"""
|
||||
<c-dynamic-attributes-cvars />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"cotton/dynamic_attributes_cvars.html",
|
||||
"""
|
||||
<c-vars
|
||||
:none="None"
|
||||
:number="1"
|
||||
:boolean_true="True"
|
||||
:boolean_false="False"
|
||||
:dict="{'key': 'value'}"
|
||||
:list="[1, 2, 3]"
|
||||
:listdict="[{'key': 'value'}]"
|
||||
:variable="111"
|
||||
/>
|
||||
|
||||
{% if none is None %}
|
||||
<p>none is None</p>
|
||||
{% endif %}
|
||||
|
||||
{% if number == 1 %}
|
||||
<p>number is 1</p>
|
||||
{% endif %}
|
||||
|
||||
{% if boolean_true is True %}
|
||||
<p>boolean_true is True</p>
|
||||
{% endif %}
|
||||
|
||||
{% if boolean_false is False %}
|
||||
<p>boolean_false is False</p>
|
||||
{% endif %}
|
||||
|
||||
{% if dict.key == 'value' %}
|
||||
<p>dict.key is 'value'</p>
|
||||
{% endif %}
|
||||
|
||||
{% if list.0 == 1 %}
|
||||
<p>list.0 is 1</p>
|
||||
{% endif %}
|
||||
|
||||
{% if listdict.0.key == 'value' %}
|
||||
<p>listdict.0.key is 'value'</p>
|
||||
{% endif %}
|
||||
|
||||
{% if variable == 111 %}
|
||||
<p>variable is 111</p>
|
||||
{% endif %}
|
||||
""",
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, "none is None")
|
||||
self.assertContains(response, "number is 1")
|
||||
self.assertContains(response, "boolean_true is True")
|
||||
self.assertContains(response, "boolean_false is False")
|
||||
self.assertContains(response, "list.0 is 1")
|
||||
self.assertContains(response, "dict.key is 'value'")
|
||||
self.assertContains(response, "listdict.0.key is 'value'")
|
||||
self.assertContains(response, "variable is 111")
|
||||
|
||||
def test_we_can_govern_whole_attributes_in_html_elements(self):
|
||||
self.create_template(
|
||||
"cotton/attribute_govern.html",
|
||||
"""
|
||||
<div {% if something %} class="first" {% else %} class="second" {% endif %}><div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"attribute_govern_view.html",
|
||||
"""
|
||||
<c-attribute-govern :something="False" />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, 'class="second"')
|
||||
self.assertNotContains(response, 'class="first"')
|
||||
|
||||
def test_attribute_names_on_component_containing_hyphens_are_converted_to_underscores(
|
||||
self,
|
||||
):
|
||||
self.create_template(
|
||||
"cotton/hyphens.html",
|
||||
"""
|
||||
<div x-data="{{ x_data }}" x-init="{{ x_init }}"></div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"hyphens_view.html",
|
||||
"""
|
||||
<c-hyphens x-data="{}" x-init="do_something()" />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, 'x-data="{}" x-init="do_something()"')
|
||||
|
||||
def test_attribute_names_on_cvars_containing_hyphens_are_converted_to_underscores(
|
||||
self,
|
||||
):
|
||||
self.create_template(
|
||||
"cotton/cvar_hyphens.html",
|
||||
"""
|
||||
<c-vars x-data="{}" x-init="do_something()" />
|
||||
|
||||
<div x-data="{{ x_data }}" x-init="{{ x_init }}"></div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"cvar_hyphens_view.html",
|
||||
"""
|
||||
<c-cvar-hyphens />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, 'x-data="{}" x-init="do_something()"')
|
||||
|
||||
def test_equals_in_attribute_values(self):
|
||||
self.create_template(
|
||||
"cotton/equals.html",
|
||||
"""
|
||||
<div {{ attrs }}><div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"equals_view.html",
|
||||
"""
|
||||
<c-equals
|
||||
@click="this=test"
|
||||
/>
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, '@click="this=test"')
|
||||
|
||||
def test_spaces_are_maintained_around_expressions_inside_attributes(self):
|
||||
self.create_template(
|
||||
"maintain_spaces_in_attributes_view.html",
|
||||
"""
|
||||
<div some_attribute_{{ id }}_something="true"></div>
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, "some_attribute__something")
|
||||
|
||||
def test_boolean_attributes(self):
|
||||
self.create_template(
|
||||
"cotton/boolean_attribute.html",
|
||||
"""
|
||||
{% if is_something is True %}
|
||||
It's True
|
||||
{% endif %}
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"boolean_attribute_view.html",
|
||||
"""
|
||||
<c-boolean-attribute is_something />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, "It's True")
|
||||
|
||||
def test_attributes_without_colons_are_not_evaluated(self):
|
||||
self.create_template(
|
||||
"cotton/static_attrs.html",
|
||||
"""
|
||||
{% if something == "1,234" %}
|
||||
All good
|
||||
{% endif %}
|
||||
|
||||
{% if something == "(1, 234)" %}
|
||||
"1,234" was evaluated as a tuple
|
||||
{% endif %}
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"static_attrs_view.html",
|
||||
"""
|
||||
<c-static-attrs something="{{ something }}" />
|
||||
""",
|
||||
"view/",
|
||||
context={"something": "1,234"},
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, "All good")
|
||||
|
||||
def test_unprocessable_dynamic_attributes_fallback_to_cvars_defaults(self):
|
||||
self.create_template(
|
||||
"cotton/unprocessable_dynamic_attribute.html",
|
||||
"""
|
||||
<c-vars color="gray" />
|
||||
{{ color }}
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"unprocessable_dynamic_attribute_view.html",
|
||||
"""
|
||||
<c-unprocessable-dynamic-attribute :color="button.color" />
|
||||
""",
|
||||
"view/",
|
||||
context={},
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertTrue("gray" in response.content.decode())
|
||||
|
||||
def test_attribute_merging(self):
|
||||
self.create_template(
|
||||
"cotton/merges_attributes.html",
|
||||
"""
|
||||
<div {{ attrs_dict|merge:'class:form-group another-class-with:colon' }}></div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"attribute_merging_view.html",
|
||||
"""
|
||||
<c-merges-attributes class="extra-class" silica:model="test" another="test">ss</c-merges-attributes>
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, 'class="form-group another-class-with:colon extra-class"')
|
||||
|
||||
def test_attributes_can_contain_django_native_tags(self):
|
||||
self.create_template(
|
||||
"native_tags_in_attributes_view.html",
|
||||
"""
|
||||
<c-native-tags-in-attributes
|
||||
attr1="Hello {{ name }}"
|
||||
attr2="{{ test|default:"none" }}"
|
||||
attr3="{% if 1 == 1 %}cowabonga!{% endif %}"
|
||||
>
|
||||
<c-slot name="named">test</c-slot>
|
||||
</c-native-tags-in-attributes>
|
||||
""",
|
||||
"view/",
|
||||
context={"name": "Will", "test": "world"},
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"cotton/native_tags_in_attributes.html",
|
||||
"""
|
||||
Attribute 1 says: '{{ attr1 }}'
|
||||
Attribute 2 says: '{{ attr2 }}'
|
||||
Attribute 3 says: '{{ attr3 }}'
|
||||
attrs tag is: '{{ attrs }}'
|
||||
""",
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, "Attribute 1 says: 'Hello Will'")
|
||||
self.assertContains(response, "Attribute 2 says: 'world'")
|
||||
self.assertContains(response, "Attribute 3 says: 'cowabonga!'")
|
||||
|
||||
self.assertContains(
|
||||
response,
|
||||
"""attrs tag is: 'attr1="Hello Will" attr2="world" attr3="cowabonga!"'""",
|
||||
)
|
||||
|
||||
def test_strings_with_spaces_can_be_passed(self):
|
||||
self.create_template(
|
||||
"string_with_spaces_view.html",
|
||||
"""
|
||||
<c-string-test var1="string with space" attr1="I have spaces">
|
||||
<c-slot name="named_slot">
|
||||
named_slot with spaces
|
||||
</c-slot>
|
||||
</c-string-test>
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"cotton/string_test.html",
|
||||
"""
|
||||
<c-vars var1 default_var="default var" />
|
||||
|
||||
slot: '{{ slot }}'
|
||||
attr1: '{{ attr1 }}'
|
||||
attr2: '{{ attr2 }}'
|
||||
var1: '{{ var1 }}'
|
||||
default_var: '{{ default_var }}'
|
||||
named_slot: '{{ named_slot }}'
|
||||
attrs: '{{ attrs }}'
|
||||
""",
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, "attr1: 'I have spaces'")
|
||||
self.assertContains(response, "var1: 'string with space'")
|
||||
self.assertContains(response, "default_var: 'default var'")
|
||||
self.assertContains(response, "named_slot: '")
|
||||
self.assertContains(response, "named_slot with spaces")
|
||||
self.assertContains(response, """attrs: 'attr1="I have spaces"'""")
|
||||
|
||||
def test_attrs_do_not_contain_cvars(self):
|
||||
self.create_template(
|
||||
"cvars_test_view.html",
|
||||
"""
|
||||
<c-cvars-test-component var1="im a var" attr1="im an attr">
|
||||
default slot
|
||||
</c-cvars-test-component>
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"cotton/cvars_test_component.html",
|
||||
"""
|
||||
<c-vars var1="sds" prop_with_default="1" />
|
||||
|
||||
<div>
|
||||
{{ testy }}
|
||||
<p>var1: '{{ var1 }}'</p>
|
||||
<p>attr1: '{{ attr1 }}'</p>
|
||||
<p>empty_var: '{{ empty_var }}'</p>
|
||||
<p>var_with_default: '{{ var_with_default }}'</p>
|
||||
<p>slot: '{{ slot }}'</p>
|
||||
<p>named_slot: '{{ named_slot }}'</p>
|
||||
<p>attrs: '{{ attrs }}'</p>
|
||||
</div>
|
||||
""",
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, "attr1: 'im an attr'")
|
||||
self.assertContains(response, "var1: 'im a var'")
|
||||
self.assertContains(response, """attrs: 'attr1="im an attr"'""")
|
||||
|
||||
def test_attribute_passing(self):
|
||||
self.create_template(
|
||||
"attribute_passing_view.html",
|
||||
"""
|
||||
<c-attribute-passing attribute_1="hello" and-another="woo1" thirdForLuck="yes" />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
self.create_template("cotton/attribute_passing.html", """<div {{ attrs }}></div>""")
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(
|
||||
response, '<div attribute_1="hello" and-another="woo1" thirdforluck="yes">'
|
||||
)
|
||||
|
||||
def test_loader_preserves_duplicate_attributes(self):
|
||||
compiled = get_compiled("""<a href="#" class="test" class="test2">hello</a>""")
|
||||
|
||||
self.assertEquals(
|
||||
compiled,
|
||||
"""<a href="#" class="test" class="test2">hello</a>""",
|
||||
)
|
104
django_cotton/tests/test_basic.py
Normal file
104
django_cotton/tests/test_basic.py
Normal file
|
@ -0,0 +1,104 @@
|
|||
from django_cotton.tests.utils import CottonInlineTestCase
|
||||
|
||||
|
||||
class BasicComponentTests(CottonInlineTestCase):
|
||||
def test_component_is_rendered(self):
|
||||
self.create_template(
|
||||
"cotton/render.html",
|
||||
"""<div class="i-am-component">{{ slot }}</div>""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"view.html",
|
||||
"""<c-render>Hello, World!</c-render>""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, '<div class="i-am-component">')
|
||||
self.assertContains(response, "Hello, World!")
|
||||
|
||||
def test_nested_rendering(self):
|
||||
self.create_template(
|
||||
"cotton/parent.html",
|
||||
"""
|
||||
<div class="i-am-parent">
|
||||
{{ slot }}
|
||||
</div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"cotton/child.html",
|
||||
"""
|
||||
<div class="i-am-child"></div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"cotton/nested_render_view.html",
|
||||
"""
|
||||
<c-parent>
|
||||
<c-child>d</c-child>
|
||||
</c-parent>
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, '<div class="i-am-parent">')
|
||||
self.assertContains(response, '<div class="i-am-child">')
|
||||
|
||||
def test_cotton_directory_can_be_configured(self):
|
||||
custom_dir = "components"
|
||||
|
||||
self.create_template(
|
||||
f"{custom_dir}/custom_directory.html",
|
||||
"""<div class="i-am-component">{{ slot }}</div>""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"custom_directory_view.html",
|
||||
"""<c-custom-directory>Hello, World!</c-custom-directory>""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.url_conf(), COTTON_DIR=custom_dir):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, '<div class="i-am-component">')
|
||||
self.assertContains(response, "Hello, World!")
|
||||
|
||||
def test_self_closing_is_rendered(self):
|
||||
self.create_template("cotton/self_closing.html", """I self closed!""")
|
||||
self.create_template(
|
||||
"self_closing_view.html",
|
||||
"""
|
||||
1: <c-self-closing/>
|
||||
2: <c-self-closing />
|
||||
3: <c-self-closing />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, "1: I self closed!")
|
||||
self.assertContains(response, "2: I self closed!")
|
||||
self.assertContains(response, "3: I self closed!")
|
||||
|
||||
def test_loader_scans_all_app_directories(self):
|
||||
self.create_template(
|
||||
"app_outside_of_dirs_view.html", """<c-app-outside-of-dirs />""", "view/"
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(
|
||||
response,
|
||||
"""My template path was not specified in settings!""",
|
||||
)
|
|
@ -1,543 +0,0 @@
|
|||
from django.test import TestCase
|
||||
|
||||
from django_cotton.tests.inline_test_case import CottonInlineTestCase
|
||||
from django_cotton.tests.utils import get_compiled, get_rendered
|
||||
|
||||
|
||||
class InlineTestCase(CottonInlineTestCase):
|
||||
def test_component_is_rendered(self):
|
||||
self.create_template(
|
||||
"cotton/render.html",
|
||||
"""<div class="i-am-component">{{ slot }}</div>""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"view.html",
|
||||
"""<c-render>Hello, World!</c-render>""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, '<div class="i-am-component">')
|
||||
self.assertContains(response, "Hello, World!")
|
||||
|
||||
def test_new_lines_in_attributes_are_preserved(self):
|
||||
self.create_template(
|
||||
"cotton/preserved.html",
|
||||
"""<div {{ attrs }}>{{ slot }}</div>""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"preserved_view.html",
|
||||
"""
|
||||
<c-preserved x-data="{
|
||||
attr1: 'im an attr',
|
||||
var1: 'im a var',
|
||||
method() {
|
||||
return 'im a method';
|
||||
}
|
||||
}" />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertTrue(
|
||||
"""{
|
||||
attr1: 'im an attr',
|
||||
var1: 'im a var',
|
||||
method() {
|
||||
return 'im a method';
|
||||
}
|
||||
}"""
|
||||
in response.content.decode()
|
||||
)
|
||||
|
||||
def test_attributes_that_end_or_start_with_quotes_are_preserved(self):
|
||||
self.create_template(
|
||||
"cotton/preserve_quotes.html",
|
||||
"""
|
||||
<div {{ attrs }}><div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"preserve_quotes_view.html",
|
||||
"""
|
||||
<c-preserve-quotes something="var ? 'this' : 'that'" />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, '''"var ? 'this' : 'that'"''')
|
||||
|
||||
def test_we_can_govern_whole_attributes_in_html_elements(self):
|
||||
self.create_template(
|
||||
"cotton/attribute_govern.html",
|
||||
"""
|
||||
<div {% if something %} class="first" {% else %} class="second" {% endif %}><div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"attribute_govern_view.html",
|
||||
"""
|
||||
<c-attribute-govern :something="False" />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, 'class="second"')
|
||||
self.assertNotContains(response, 'class="first"')
|
||||
|
||||
def test_attribute_names_on_component_containing_hyphens_are_converted_to_underscores(
|
||||
self,
|
||||
):
|
||||
self.create_template(
|
||||
"cotton/hyphens.html",
|
||||
"""
|
||||
<div x-data="{{ x_data }}" x-init="{{ x_init }}"></div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"hyphens_view.html",
|
||||
"""
|
||||
<c-hyphens x-data="{}" x-init="do_something()" />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, 'x-data="{}" x-init="do_something()"')
|
||||
|
||||
def test_attribute_names_on_cvars_containing_hyphens_are_converted_to_underscores(
|
||||
self,
|
||||
):
|
||||
self.create_template(
|
||||
"cotton/cvar_hyphens.html",
|
||||
"""
|
||||
<c-vars x-data="{}" x-init="do_something()" />
|
||||
|
||||
<div x-data="{{ x_data }}" x-init="{{ x_init }}"></div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"cvar_hyphens_view.html",
|
||||
"""
|
||||
<c-cvar-hyphens />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, 'x-data="{}" x-init="do_something()"')
|
||||
|
||||
def test_cotton_directory_can_be_configured(self):
|
||||
custom_dir = "components"
|
||||
|
||||
self.create_template(
|
||||
f"{custom_dir}/custom_directory.html",
|
||||
"""<div class="i-am-component">{{ slot }}</div>""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"custom_directory_view.html",
|
||||
"""<c-custom-directory>Hello, World!</c-custom-directory>""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf(), COTTON_DIR=custom_dir):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, '<div class="i-am-component">')
|
||||
self.assertContains(response, "Hello, World!")
|
||||
|
||||
def test_equals_in_attribute_values(self):
|
||||
self.create_template(
|
||||
"cotton/equals.html",
|
||||
"""
|
||||
<div {{ attrs }}><div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"equals_view.html",
|
||||
"""
|
||||
<c-equals
|
||||
@click="this=test"
|
||||
/>
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, '@click="this=test"')
|
||||
|
||||
def test_dynamic_components_via_string(self):
|
||||
self.create_template(
|
||||
"cotton/dynamic_component.html",
|
||||
"""
|
||||
<div>I am dynamic<div>
|
||||
""",
|
||||
)
|
||||
|
||||
html = """
|
||||
<c-component is="dynamic-component" />
|
||||
"""
|
||||
|
||||
rendered = get_rendered(html, {"is": "dynamic-component"})
|
||||
|
||||
self.assertTrue("I am dynamic" in rendered)
|
||||
|
||||
def test_dynamic_components_via_variable(self):
|
||||
self.create_template(
|
||||
"cotton/dynamic_component.html",
|
||||
"""
|
||||
<div>I am dynamic<div>
|
||||
""",
|
||||
)
|
||||
|
||||
html = """
|
||||
<c-component :is="is" />
|
||||
"""
|
||||
|
||||
rendered = get_rendered(html, {"is": "dynamic-component"})
|
||||
|
||||
self.assertTrue("I am dynamic" in rendered)
|
||||
|
||||
def test_dynamic_components_via_expression_attribute(self):
|
||||
self.create_template(
|
||||
"cotton/dynamic_component_expression.html",
|
||||
"""
|
||||
<div>I am dynamic component from expression<div>
|
||||
""",
|
||||
)
|
||||
|
||||
html = """
|
||||
<c-component is="dynamic-{{ is }}" />
|
||||
"""
|
||||
|
||||
rendered = get_rendered(html, {"is": "component-expression"})
|
||||
|
||||
self.assertTrue("I am dynamic component from expression" in rendered)
|
||||
|
||||
def test_dynamic_components_in_subfolders(self):
|
||||
self.create_template(
|
||||
"cotton/subfolder/dynamic_component_expression.html",
|
||||
"""
|
||||
<div>I am dynamic component from expression<div>
|
||||
""",
|
||||
)
|
||||
|
||||
html = """
|
||||
<c-component is="subfolder.{{ is }}" />
|
||||
"""
|
||||
|
||||
rendered = get_rendered(html, {"is": "dynamic-component-expression"})
|
||||
|
||||
self.assertTrue("I am dynamic component from expression" in rendered)
|
||||
|
||||
def test_spaces_are_maintained_around_expressions_inside_attributes(self):
|
||||
self.create_template(
|
||||
"maintain_spaces_in_attributes_view.html",
|
||||
"""
|
||||
<div some_attribute_{{ id }}_something="true"></div>
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, "some_attribute__something")
|
||||
|
||||
def test_dynamic_attributes_are_also_template_parsed(self):
|
||||
self.create_template(
|
||||
"cotton/dynamic_attribute_template_parsing.html",
|
||||
"""
|
||||
{% for image in images %}
|
||||
{{ forloop.counter }}: {{ image }}
|
||||
{% endfor %}
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"dynamic_attributes_parsing_view.html",
|
||||
"""
|
||||
<c-dynamic-attribute-template-parsing :images="['{{ image1 }}', '{{ image2 }}']" />
|
||||
""",
|
||||
"view/",
|
||||
context={"image1": "1.jpg", "image2": "2.jpg"},
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, "1: 1.jpg")
|
||||
self.assertContains(response, "2: 2.jpg")
|
||||
|
||||
def test_boolean_attributes(self):
|
||||
self.create_template(
|
||||
"cotton/boolean_attribute.html",
|
||||
"""
|
||||
{% if is_something is True %}
|
||||
It's True
|
||||
{% endif %}
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"boolean_attribute_view.html",
|
||||
"""
|
||||
<c-boolean-attribute is_something />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, "It's True")
|
||||
|
||||
def test_attributes_without_colons_are_not_evaluated(self):
|
||||
self.create_template(
|
||||
"cotton/static_attrs.html",
|
||||
"""
|
||||
{% if something == "1,234" %}
|
||||
All good
|
||||
{% endif %}
|
||||
|
||||
{% if something == "(1, 234)" %}
|
||||
"1,234" was evaluated as a tuple
|
||||
{% endif %}
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"static_attrs_view.html",
|
||||
"""
|
||||
<c-static-attrs something="{{ something }}" />
|
||||
""",
|
||||
"view/",
|
||||
context={"something": "1,234"},
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, "All good")
|
||||
|
||||
def test_unprocessable_dynamic_attributes_fallback_to_cvars_defaults(self):
|
||||
self.create_template(
|
||||
"cotton/unprocessable_dynamic_attribute.html",
|
||||
"""
|
||||
<c-vars color="gray" />
|
||||
{{ color }}
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"unprocessable_dynamic_attribute_view.html",
|
||||
"""
|
||||
<c-unprocessable-dynamic-attribute :color="button.color" />
|
||||
""",
|
||||
"view/",
|
||||
context={},
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.get_url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertTrue("gray" in response.content.decode())
|
||||
|
||||
|
||||
class CottonTestCase(TestCase):
|
||||
def test_parent_component_is_rendered(self):
|
||||
response = self.client.get("/parent")
|
||||
self.assertContains(response, '<div class="i-am-parent">')
|
||||
|
||||
def test_child_is_rendered(self):
|
||||
response = self.client.get("/child")
|
||||
self.assertContains(response, '<div class="i-am-parent">')
|
||||
self.assertContains(response, '<div class="i-am-child">')
|
||||
|
||||
def test_self_closing_is_rendered(self):
|
||||
response = self.client.get("/self-closing")
|
||||
self.assertContains(response, '<div class="i-am-parent">')
|
||||
|
||||
def test_named_slots_correctly_display_in_loop(self):
|
||||
response = self.client.get("/named-slot-in-loop")
|
||||
self.assertContains(response, "item name: Item 1")
|
||||
self.assertContains(response, "item name: Item 2")
|
||||
self.assertContains(response, "item name: Item 3")
|
||||
|
||||
def test_attribute_passing(self):
|
||||
response = self.client.get("/attribute-passing")
|
||||
self.assertContains(
|
||||
response, '<div attribute_1="hello" and-another="woo1" thirdforluck="yes">'
|
||||
)
|
||||
|
||||
def test_attribute_merging(self):
|
||||
response = self.client.get("/attribute-merging")
|
||||
self.assertContains(response, 'class="form-group another-class-with:colon extra-class"')
|
||||
|
||||
def test_django_syntax_decoding(self):
|
||||
response = self.client.get("/django-syntax-decoding")
|
||||
self.assertContains(response, "some-class")
|
||||
|
||||
def test_vars_are_converted_to_vars_frame_tags(self):
|
||||
compiled = get_compiled(
|
||||
"""
|
||||
<c-vars var1="string with space" />
|
||||
|
||||
content
|
||||
"""
|
||||
)
|
||||
|
||||
self.assertEquals(
|
||||
compiled,
|
||||
"""{% cotton_vars_frame var1=var1|default:"string with space" %}content{% endcotton_vars_frame %}""",
|
||||
)
|
||||
|
||||
def test_loader_preserves_duplicate_attributes(self):
|
||||
compiled = get_compiled("""<a href="#" class="test" class="test2">hello</a>""")
|
||||
|
||||
self.assertEquals(
|
||||
compiled,
|
||||
"""<a href="#" class="test" class="test2">hello</a>""",
|
||||
)
|
||||
|
||||
def test_attrs_do_not_contain_vars(self):
|
||||
response = self.client.get("/vars-test")
|
||||
self.assertContains(response, "attr1: 'im an attr'")
|
||||
self.assertContains(response, "var1: 'im a var'")
|
||||
self.assertContains(response, """attrs: 'attr1="im an attr"'""")
|
||||
|
||||
def test_strings_with_spaces_can_be_passed(self):
|
||||
response = self.client.get("/string-with-spaces")
|
||||
self.assertContains(response, "attr1: 'I have spaces'")
|
||||
self.assertContains(response, "var1: 'string with space'")
|
||||
self.assertContains(response, "default_var: 'default var'")
|
||||
self.assertContains(response, "named_slot: '")
|
||||
self.assertContains(response, "named_slot with spaces")
|
||||
self.assertContains(response, """attrs: 'attr1="I have spaces"'""")
|
||||
|
||||
def test_named_slots_dont_bleed_into_sibling_components(self):
|
||||
html = """
|
||||
<c-test-component>
|
||||
component1
|
||||
<c-slot name="named_slot">named slot 1</c-slot>
|
||||
</c-test-component>
|
||||
<c-test-component>
|
||||
component2
|
||||
</c-test-component>
|
||||
"""
|
||||
|
||||
rendered = get_rendered(html)
|
||||
|
||||
self.assertTrue("named_slot: 'named slot 1'" in rendered)
|
||||
self.assertTrue("named_slot: ''" in rendered)
|
||||
|
||||
def test_template_variables_are_not_parsed(self):
|
||||
html = """
|
||||
<c-test-component attr1="variable" :attr2="variable">
|
||||
<c-slot name="named_slot">
|
||||
<a href="#" silica:click.prevent="variable = 'lineage'">test</a>
|
||||
</c-slot>
|
||||
</c-test-component>
|
||||
"""
|
||||
|
||||
rendered = get_rendered(html, {"variable": 1})
|
||||
|
||||
self.assertTrue("attr1: 'variable'" in rendered)
|
||||
self.assertTrue("attr2: '1'" in rendered)
|
||||
|
||||
def test_component_attributes_can_converted_to_python_types(self):
|
||||
response = self.client.get("/test/eval-attributes")
|
||||
|
||||
self.assertContains(response, "none is None")
|
||||
self.assertContains(response, "number is 1")
|
||||
self.assertContains(response, "boolean_true is True")
|
||||
self.assertContains(response, "boolean_false is False")
|
||||
self.assertContains(response, "list.0 is 1")
|
||||
self.assertContains(response, "dict.key is 'value'")
|
||||
self.assertContains(response, "listdict.0.key is 'value'")
|
||||
|
||||
def test_cvars_can_be_converted_to_python_types(self):
|
||||
response = self.client.get("/test/eval-vars")
|
||||
|
||||
self.assertContains(response, "none is None")
|
||||
self.assertContains(response, "number is 1")
|
||||
self.assertContains(response, "boolean_true is True")
|
||||
self.assertContains(response, "boolean_false is False")
|
||||
self.assertContains(response, "list.0 is 1")
|
||||
self.assertContains(response, "dict.key is 'value'")
|
||||
self.assertContains(response, "listdict.0.key is 'value'")
|
||||
|
||||
def test_attributes_can_contain_django_native_tags(self):
|
||||
response = self.client.get("/test/native-tags-in-attributes")
|
||||
|
||||
self.assertContains(response, "Attribute 1 says: 'Hello Will'")
|
||||
self.assertContains(response, "Attribute 2 says: 'world'")
|
||||
self.assertContains(response, "Attribute 3 says: 'cowabonga!'")
|
||||
|
||||
self.assertContains(
|
||||
response,
|
||||
"""attrs tag is: 'normal="normal" attr1="Hello Will" attr2="world" attr3="cowabonga!"'""",
|
||||
)
|
||||
|
||||
def test_loader_scans_all_app_directories(self):
|
||||
response = self.client.get("/test/unspecified-app-directory-template")
|
||||
|
||||
self.assertContains(
|
||||
response,
|
||||
"""My template was not specified in settings!""",
|
||||
)
|
||||
|
||||
def test_expression_tags_close_to_tag_elements_doesnt_corrupt_the_tag(self):
|
||||
html = """
|
||||
<div{% if 1 = 1 %} attr1="variable" {% endif %}></div>
|
||||
"""
|
||||
|
||||
rendered = get_compiled(html)
|
||||
|
||||
self.assertFalse("</div{% if 1 = 1 %}>" in rendered, "Tag corrupted")
|
||||
self.assertTrue("</div>" in rendered, "</div> not found in rendered string")
|
||||
|
||||
def test_conditionals_evaluation_inside_elements(self):
|
||||
html = """
|
||||
<c-test-component>
|
||||
<select>
|
||||
<option value="1" {% if my_obj.selection == 1 %}selected{% endif %}>Value 1</option>
|
||||
<option value="2" {% if my_obj.selection == 2 %}selected{% endif %}>Value 2</option>
|
||||
</select>
|
||||
</c-test-component>
|
||||
"""
|
||||
|
||||
rendered = get_rendered(html, {"my_obj": {"selection": 1}})
|
||||
|
||||
self.assertTrue('<option value="1" selected>Value 1</option>' in rendered)
|
||||
self.assertTrue('<option value="2" selected>Value 2</option>' not in rendered)
|
68
django_cotton/tests/test_dynamic_components.py
Normal file
68
django_cotton/tests/test_dynamic_components.py
Normal file
|
@ -0,0 +1,68 @@
|
|||
from django_cotton.tests.utils import CottonInlineTestCase
|
||||
from django_cotton.tests.utils import get_rendered
|
||||
|
||||
|
||||
class DynamicComponentTests(CottonInlineTestCase):
|
||||
def test_dynamic_components_via_string(self):
|
||||
self.create_template(
|
||||
"cotton/dynamic_component_via_string.html",
|
||||
"""
|
||||
<div>I am dynamic<div>
|
||||
""",
|
||||
)
|
||||
|
||||
html = """
|
||||
<c-component is="dynamic-component-via-string" />
|
||||
"""
|
||||
|
||||
rendered = get_rendered(html)
|
||||
|
||||
self.assertTrue("I am dynamic" in rendered)
|
||||
|
||||
def test_dynamic_components_via_variable(self):
|
||||
self.create_template(
|
||||
"cotton/dynamic_component_via_variable.html",
|
||||
"""
|
||||
<div>I am dynamic<div>
|
||||
""",
|
||||
)
|
||||
|
||||
html = """
|
||||
<c-component :is="is" />
|
||||
"""
|
||||
|
||||
rendered = get_rendered(html, {"is": "dynamic-component-via-variable"})
|
||||
|
||||
self.assertTrue("I am dynamic" in rendered)
|
||||
|
||||
def test_dynamic_components_via_expression(self):
|
||||
self.create_template(
|
||||
"cotton/dynamic_component_expression.html",
|
||||
"""
|
||||
<div>I am dynamic component from expression<div>
|
||||
""",
|
||||
)
|
||||
|
||||
html = """
|
||||
<c-component is="dynamic-{{ is }}" />
|
||||
"""
|
||||
|
||||
rendered = get_rendered(html, {"is": "component-expression"})
|
||||
|
||||
self.assertTrue("I am dynamic component from expression" in rendered)
|
||||
|
||||
def test_dynamic_components_in_subfolders(self):
|
||||
self.create_template(
|
||||
"cotton/subfolder/dynamic_component_expression.html",
|
||||
"""
|
||||
<div>I am dynamic component from expression<div>
|
||||
""",
|
||||
)
|
||||
|
||||
html = """
|
||||
<c-component is="subfolder.{{ is }}" />
|
||||
"""
|
||||
|
||||
rendered = get_rendered(html, {"is": "dynamic-component-expression"})
|
||||
|
||||
self.assertTrue("I am dynamic component from expression" in rendered)
|
77
django_cotton/tests/test_slots.py
Normal file
77
django_cotton/tests/test_slots.py
Normal file
|
@ -0,0 +1,77 @@
|
|||
from django_cotton.tests.utils import CottonInlineTestCase
|
||||
from django_cotton.tests.utils import get_compiled
|
||||
|
||||
|
||||
class SlotAndContentTests(CottonInlineTestCase):
|
||||
def test_named_slots_correctly_display_in_loop(self):
|
||||
self.create_template(
|
||||
"named_slot_in_loop_view.html",
|
||||
"""
|
||||
{% for item in items %}
|
||||
<c-named-slot-component>
|
||||
<c-slot name="name">
|
||||
item name: {{ item.name }}
|
||||
</c-slot>
|
||||
</c-named-slot-component>
|
||||
{% endfor %}
|
||||
""",
|
||||
"view/",
|
||||
context={
|
||||
"items": [
|
||||
{"name": "Item 1"},
|
||||
{"name": "Item 2"},
|
||||
{"name": "Item 3"},
|
||||
]
|
||||
},
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"cotton/named_slot_component.html",
|
||||
""",
|
||||
<div>
|
||||
{{ name }}
|
||||
</div>
|
||||
""",
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, "item name: Item 1")
|
||||
self.assertContains(response, "item name: Item 2")
|
||||
self.assertContains(response, "item name: Item 3")
|
||||
|
||||
def test_named_slots_dont_bleed_into_sibling_components(self):
|
||||
self.create_template(
|
||||
"slot_bleed_view.html",
|
||||
"""
|
||||
<c-slot-bleed id="1">
|
||||
<c-slot name="named_slot">named slot 1</c-slot>
|
||||
</c-slot-bleed>
|
||||
<c-slot-bleed id="2"></c-slot-bleed>
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"cotton/slot_bleed.html", """named_slot {{ id }}: '{{ named_slot }}'</p>"""
|
||||
)
|
||||
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertTrue("named_slot 1: 'named slot 1'" in response.content.decode())
|
||||
self.assertTrue("named_slot 2: ''" in response.content.decode())
|
||||
|
||||
def test_vars_are_converted_to_vars_frame_tags(self):
|
||||
compiled = get_compiled(
|
||||
"""
|
||||
<c-vars var1="string with space" />
|
||||
|
||||
content
|
||||
"""
|
||||
)
|
||||
|
||||
self.assertEquals(
|
||||
compiled,
|
||||
"""{% cotton_vars_frame var1=var1|default:"string with space" %}content{% endcotton_vars_frame %}""",
|
||||
)
|
91
django_cotton/tests/test_template_rendering.py
Normal file
91
django_cotton/tests/test_template_rendering.py
Normal file
|
@ -0,0 +1,91 @@
|
|||
from django_cotton.tests.utils import CottonInlineTestCase
|
||||
from django_cotton.tests.utils import get_compiled
|
||||
|
||||
|
||||
class TemplateRenderingTests(CottonInlineTestCase):
|
||||
def test_new_lines_in_attributes_are_preserved(self):
|
||||
self.create_template(
|
||||
"cotton/preserved.html",
|
||||
"""<div {{ attrs }}>{{ slot }}</div>""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"preserved_view.html",
|
||||
"""
|
||||
<c-preserved x-data="{
|
||||
attr1: 'im an attr',
|
||||
var1: 'im a var',
|
||||
method() {
|
||||
return 'im a method';
|
||||
}
|
||||
}" />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertTrue(
|
||||
"""{
|
||||
attr1: 'im an attr',
|
||||
var1: 'im a var',
|
||||
method() {
|
||||
return 'im a method';
|
||||
}
|
||||
}"""
|
||||
in response.content.decode()
|
||||
)
|
||||
|
||||
def test_attributes_that_end_or_start_with_quotes_are_preserved(self):
|
||||
self.create_template(
|
||||
"cotton/preserve_quotes.html",
|
||||
"""
|
||||
<div {{ attrs }}><div>
|
||||
""",
|
||||
)
|
||||
|
||||
self.create_template(
|
||||
"preserve_quotes_view.html",
|
||||
"""
|
||||
<c-preserve-quotes something="var ? 'this' : 'that'" />
|
||||
""",
|
||||
"view/",
|
||||
)
|
||||
|
||||
# Override URLconf
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
|
||||
self.assertContains(response, '''"var ? 'this' : 'that'"''')
|
||||
|
||||
def test_expression_tags_close_to_tag_elements_doesnt_corrupt_the_tag(self):
|
||||
html = """
|
||||
<div{% if 1 = 1 %} attr1="variable" {% endif %}></div>
|
||||
"""
|
||||
|
||||
rendered = get_compiled(html)
|
||||
|
||||
self.assertFalse("</div{% if 1 = 1 %}>" in rendered, "Tag corrupted")
|
||||
self.assertTrue("</div>" in rendered, "</div> not found in rendered string")
|
||||
|
||||
def test_conditionals_evaluation_inside_tags(self):
|
||||
self.create_template("cotton/conditionals_in_tags.html", """<div>{{ slot }}</div>""")
|
||||
self.create_template(
|
||||
"conditionals_in_tags_view.html",
|
||||
"""
|
||||
<c-conditionals-in-tags>
|
||||
<select>
|
||||
<option value="1" {% if my_obj.selection == 1 %}selected{% endif %}>Value 1</option>
|
||||
<option value="2" {% if my_obj.selection == 2 %}selected{% endif %}>Value 2</option>
|
||||
</select>
|
||||
</c-conditionals-in-tags>
|
||||
""",
|
||||
"view/",
|
||||
context={"my_obj": {"selection": 1}},
|
||||
)
|
||||
with self.settings(ROOT_URLCONF=self.url_conf()):
|
||||
response = self.client.get("/view/")
|
||||
self.assertContains(response, '<option value="1" selected>Value 1</option>')
|
||||
self.assertNotContains(response, '<option value="2" selected>Value 2</option>')
|
|
@ -1,8 +1,106 @@
|
|||
from django.template import Context, Template
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from django.urls import path
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from django.core.cache import cache
|
||||
from django.test import override_settings
|
||||
from django.template import Context, Template
|
||||
from django.views.generic import TemplateView
|
||||
from django_cotton.cotton_loader import Loader as CottonLoader
|
||||
|
||||
|
||||
class DynamicURLModule:
|
||||
def __init__(self):
|
||||
self.urlpatterns = []
|
||||
|
||||
def __call__(self):
|
||||
return self.urlpatterns
|
||||
|
||||
|
||||
class FileAlreadyExistsError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CottonInlineTestCase(TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
|
||||
# Set tmp dir and register a url module for our tmp files
|
||||
cls.temp_dir = tempfile.mkdtemp()
|
||||
cls.url_module = DynamicURLModule()
|
||||
cls.url_module_name = f"dynamic_urls_{cls.__name__}"
|
||||
sys.modules[cls.url_module_name] = cls.url_module
|
||||
|
||||
# Register our temp directory as a TEMPLATES path
|
||||
cls.new_templates_setting = settings.TEMPLATES.copy()
|
||||
cls.new_templates_setting[0]["DIRS"] = [cls.temp_dir] + cls.new_templates_setting[0]["DIRS"]
|
||||
|
||||
# Apply the setting
|
||||
cls.templates_override = override_settings(TEMPLATES=cls.new_templates_setting)
|
||||
cls.templates_override.enable()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""Remove temporary directory and clean up modules"""
|
||||
cls.templates_override.disable()
|
||||
shutil.rmtree(cls.temp_dir, ignore_errors=True)
|
||||
del sys.modules[cls.url_module_name]
|
||||
super().tearDownClass()
|
||||
|
||||
def tearDown(self):
|
||||
"""Clear cache between tests so that we can use the same file names for simplicity"""
|
||||
cache.clear()
|
||||
|
||||
def create_template(self, name, content, url=None, context={}):
|
||||
"""Create a template file in the temporary directory and return the path"""
|
||||
path = os.path.join(self.temp_dir, name)
|
||||
|
||||
if os.path.exists(path):
|
||||
raise FileAlreadyExistsError(
|
||||
f"A file named '{name}' already exists in the temporary directory."
|
||||
)
|
||||
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
if url:
|
||||
# Create a dynamic class-based view
|
||||
class DynamicTemplateView(TemplateView):
|
||||
template_name = name
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
dynamic_context = super().get_context_data(**kwargs)
|
||||
dynamic_context.update(context)
|
||||
return dynamic_context
|
||||
|
||||
self.register_path(url, DynamicTemplateView.as_view(template_name=name))
|
||||
|
||||
return path
|
||||
|
||||
def make_view(self, template_name):
|
||||
"""Make a view that renders the given template"""
|
||||
return TemplateView.as_view(template_name=template_name)
|
||||
|
||||
def register_path(self, url, view):
|
||||
"""Register a URL pattern and returns path"""
|
||||
url_pattern = path(url, view)
|
||||
self.url_module.urlpatterns.append(url_pattern)
|
||||
return url_pattern
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.url_module.urlpatterns = []
|
||||
|
||||
def url_conf(self):
|
||||
return self.url_module_name
|
||||
|
||||
|
||||
def get_compiled(template_string):
|
||||
return CottonLoader(engine=None).cotton_compiler.process(template_string, "test_key")
|
||||
|
||||
|
|
|
@ -1,58 +1,13 @@
|
|||
from . import views
|
||||
from django.urls import path
|
||||
from django.contrib import admin
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
app_name = "django_cotton"
|
||||
|
||||
|
||||
class NamedSlotInLoop(TemplateView):
|
||||
template_name = "named_slot_in_loop.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return {
|
||||
"items": [
|
||||
{"name": "Item 1"},
|
||||
{"name": "Item 2"},
|
||||
{"name": "Item 3"},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("admin/", admin.site.urls),
|
||||
path("", TemplateView.as_view(template_name="index.html")),
|
||||
path("parent", TemplateView.as_view(template_name="parent_test.html")),
|
||||
path("child", TemplateView.as_view(template_name="child_test.html")),
|
||||
path(
|
||||
"self-closing",
|
||||
TemplateView.as_view(template_name="self_closing_test.html"),
|
||||
),
|
||||
path("include", TemplateView.as_view(template_name="cotton_include.html")),
|
||||
path("playground", TemplateView.as_view(template_name="playground.html")),
|
||||
path("tag", TemplateView.as_view(template_name="tag.html")),
|
||||
path("named-slot-in-loop", NamedSlotInLoop.as_view()),
|
||||
path("test/compiled-cotton", views.compiled_cotton_test_view),
|
||||
path("test/cotton", views.cotton_test_view),
|
||||
path("test/native-extends", views.native_extends_test_view),
|
||||
path("test/native-include", views.native_include_test_view),
|
||||
path("attribute-merging", views.attribute_merging_test_view),
|
||||
path("attribute-passing", views.attribute_passing_test_view),
|
||||
path("django-syntax-decoding", views.django_syntax_decoding_test_view),
|
||||
path(
|
||||
"string-with-spaces",
|
||||
TemplateView.as_view(template_name="string_with_spaces.html"),
|
||||
),
|
||||
path("vars-test", TemplateView.as_view(template_name="vars_test.html")),
|
||||
path("variable-parsing", views.variable_parsing_test_view),
|
||||
path("test/eval-vars", views.eval_vars_test_view),
|
||||
path("test/eval-attributes", views.eval_attributes_test_view),
|
||||
path(
|
||||
"test/native-tags-in-attributes",
|
||||
TemplateView.as_view(template_name="native_tags_in_attributes_view.html"),
|
||||
),
|
||||
path(
|
||||
"test/unspecified-app-directory-template",
|
||||
TemplateView.as_view(template_name="unspecified_view.html"),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,50 +1,14 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# benchmark tests
|
||||
|
||||
|
||||
# benchmarks
|
||||
def compiled_cotton_test_view(request):
|
||||
return render(request, "compiled_cotton_test.html")
|
||||
|
||||
|
||||
def cotton_test_view(request):
|
||||
return render(request, "cotton_test.html")
|
||||
|
||||
|
||||
def native_extends_test_view(request):
|
||||
return render(request, "native_extends_test.html")
|
||||
|
||||
|
||||
def native_include_test_view(request):
|
||||
return render(request, "native_include_test.html")
|
||||
|
||||
|
||||
# Django tests
|
||||
|
||||
|
||||
def attribute_merging_test_view(request):
|
||||
return render(request, "attribute_merging_test.html")
|
||||
|
||||
|
||||
def attribute_passing_test_view(request):
|
||||
return render(request, "attribute_passing_test.html")
|
||||
|
||||
|
||||
def django_syntax_decoding_test_view(request):
|
||||
return render(request, "django_syntax_decoding_test.html")
|
||||
|
||||
|
||||
def variable_parsing_test_view(request):
|
||||
return render(request, "variable_parsing_test.html", {"variable": "some-class"})
|
||||
|
||||
|
||||
def valueless_attributes_test_view(request):
|
||||
return render(request, "valueless_attributes_test_view.html")
|
||||
|
||||
|
||||
def eval_vars_test_view(request):
|
||||
return render(request, "eval_vars_test_view.html")
|
||||
|
||||
|
||||
def eval_attributes_test_view(request):
|
||||
return render(request, "eval_attributes_test_view.html")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue