diff --git a/AUTHORS b/AUTHORS index 6bd44281c7..bf10e80afd 100644 --- a/AUTHORS +++ b/AUTHORS @@ -554,6 +554,7 @@ answer newbie questions, and generally made Django that much better: polpak@yahoo.com pradeep.gowda@gmail.com Preston Holmes + Preston Timmons Rachel Willmer Radek Švarz Rajesh Dhawan diff --git a/tests/template_tests/filters.py b/tests/template_tests/filters.py index 3668f91c23..97396fbc69 100644 --- a/tests/template_tests/filters.py +++ b/tests/template_tests/filters.py @@ -11,24 +11,12 @@ from __future__ import unicode_literals from datetime import date, datetime, time, timedelta from django.test.utils import str_prefix, TZ_SUPPORT -from django.utils.encoding import python_2_unicode_compatible from django.utils.safestring import mark_safe from django.utils import timezone -# These two classes are used to test auto-escaping of __unicode__ output. +from .syntax_tests.utils import SafeClass, UnsafeClass -@python_2_unicode_compatible -class UnsafeClass: - def __str__(self): - return 'you & me' - - -@python_2_unicode_compatible -class SafeClass: - def __str__(self): - return mark_safe('you > me') - # RESULT SYNTAX -- # 'template_name': ('template contents', 'context dict', # 'expected string output' or Exception class) diff --git a/tests/template_tests/syntax_tests/__init__.py b/tests/template_tests/syntax_tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/template_tests/syntax_tests/test_autoescape.py b/tests/template_tests/syntax_tests/test_autoescape.py new file mode 100644 index 0000000000..6457fe31df --- /dev/null +++ b/tests/template_tests/syntax_tests/test_autoescape.py @@ -0,0 +1,121 @@ +from django.template.base import TemplateSyntaxError +from django.test import TestCase +from django.utils.safestring import mark_safe + +from .utils import render, setup, SafeClass, UnsafeClass + + +class AutoescapeTagTests(TestCase): + + @setup({'autoescape-tag01': '{% autoescape off %}hello{% endautoescape %}'}) + def test_autoescape_tag01(self): + output = render('autoescape-tag01') + self.assertEqual(output, 'hello') + + @setup({'autoescape-tag02': '{% autoescape off %}{{ first }}{% endautoescape %}'}) + def test_autoescape_tag02(self): + output = render('autoescape-tag02', {'first': 'hello'}) + self.assertEqual(output, 'hello') + + @setup({'autoescape-tag03': '{% autoescape on %}{{ first }}{% endautoescape %}'}) + def test_autoescape_tag03(self): + output = render('autoescape-tag03', {'first': 'hello'}) + self.assertEqual(output, '<b>hello</b>') + + # Autoescape disabling and enabling nest in a predictable way. + @setup({'autoescape-tag04': '{% autoescape off %}' + '{{ first }} {% autoescape on %}{{ first }}{% endautoescape %}{% endautoescape %}'}) + def test_autoescape_tag04(self): + output = render('autoescape-tag04', {'first': ''}) + self.assertEqual(output, ' <a>') + + @setup({'autoescape-tag05': '{% autoescape on %}{{ first }}{% endautoescape %}'}) + def test_autoescape_tag05(self): + output = render('autoescape-tag05', {'first': 'first'}) + self.assertEqual(output, '<b>first</b>') + + # Strings (ASCII or unicode) already marked as "safe" are not + # auto-escaped + @setup({'autoescape-tag06': '{{ first }}'}) + def test_autoescape_tag06(self): + output = render('autoescape-tag06', {'first': mark_safe('first')}) + self.assertEqual(output, 'first') + + @setup({'autoescape-tag07': '{% autoescape on %}{{ first }}{% endautoescape %}'}) + def test_autoescape_tag07(self): + output = render('autoescape-tag07', {'first': mark_safe('Apple')}) + self.assertEqual(output, 'Apple') + + @setup({'autoescape-tag08': r'{% autoescape on %}' + r'{{ var|default_if_none:" endquote\" hah" }}{% endautoescape %}'}) + def test_autoescape_tag08(self): + """ + Literal string arguments to filters, if used in the result, are safe. + """ + output = render('autoescape-tag08', {"var": None}) + self.assertEqual(output, ' endquote" hah') + + # Objects which return safe strings as their __str__ method + # won't get double-escaped. + @setup({'autoescape-tag09': r'{{ unsafe }}'}) + def test_autoescape_tag09(self): + output = render('autoescape-tag09', {'unsafe': UnsafeClass()}) + self.assertEqual(output, 'you & me') + + @setup({'autoescape-tag10': r'{{ safe }}'}) + def test_autoescape_tag10(self): + output = render('autoescape-tag10', {'safe': SafeClass()}) + self.assertEqual(output, 'you > me') + + @setup({'autoescape-filtertag01': '{{ first }}{% filter safe %}{{ first }} x'}) + + @setup({'autoescape-ifequal01': '{% ifequal var "this & that" %}yes{% endifequal %}'}) + def test_autoescape_ifequal01(self): + """ + ifequal compares unescaped vales. + """ + output = render('autoescape-ifequal01', {'var': 'this & that'}) + self.assertEqual(output, 'yes') + + # Arguments to filters are 'safe' and manipulate their input unescaped. + @setup({'autoescape-filters01': '{{ var|cut:"&" }}'}) + def test_autoescape_filters01(self): + output = render('autoescape-filters01', {'var': 'this & that'}) + self.assertEqual(output, 'this that') + + @setup({'autoescape-filters02': '{{ var|join:" & " }}'}) + def test_autoescape_filters02(self): + output = render('autoescape-filters02', {'var': ('Tom', 'Dick', 'Harry')}) + self.assertEqual(output, 'Tom & Dick & Harry') + + @setup({'autoescape-literals01': '{{ "this & that" }}'}) + def test_autoescape_literals01(self): + """ + Literal strings are safe. + """ + output = render('autoescape-literals01') + self.assertEqual(output, 'this & that') + + @setup({'autoescape-stringiterations01': '{% for l in var %}{{ l }},{% endfor %}'}) + def test_autoescape_stringiterations01(self): + """ + Iterating over strings outputs safe characters. + """ + output = render('autoescape-stringiterations01', {'var': 'K&R'}) + self.assertEqual(output, 'K,&,R,') + + @setup({'autoescape-lookup01': '{{ var.key }}'}) + def test_autoescape_lookup01(self): + """ + Escape requirement survives lookup. + """ + output = render('autoescape-lookup01', {'var': {'key': 'this & that'}}) + self.assertEqual(output, 'this & that') diff --git a/tests/template_tests/syntax_tests/test_basic.py b/tests/template_tests/syntax_tests/test_basic.py new file mode 100644 index 0000000000..8e92c48e6a --- /dev/null +++ b/tests/template_tests/syntax_tests/test_basic.py @@ -0,0 +1,316 @@ +from django.conf import settings +from django.template.base import Context, TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase + +from .utils import render, setup, SilentGetItemClass, SilentAttrClass, SomeClass + + +basic_templates = { + 'basic-syntax01': 'something cool', + 'basic-syntax02': '{{ headline }}', + 'basic-syntax03': '{{ first }} --- {{ second }}', +} + + +class BasicSyntaxTests(TestCase): + + @setup(basic_templates) + def test_basic_syntax01(self): + """ + Plain text should go through the template parser untouched. + """ + output = render('basic-syntax01') + self.assertEqual(output, "something cool") + + @setup(basic_templates) + def test_basic_syntax02(self): + """ + Variables should be replaced with their value in the current + context + """ + output = render('basic-syntax02', {'headline': 'Success'}) + self.assertEqual(output, 'Success') + + @setup(basic_templates) + def test_basic_syntax03(self): + """ + More than one replacement variable is allowed in a template + """ + output = render('basic-syntax03', {"first": 1, "second": 2}) + self.assertEqual(output, '1 --- 2') + + @setup({'basic-syntax04': 'as{{ missing }}df'}) + def test_basic_syntax04(self): + """ + Fail silently when a variable is not found in the current context + """ + output = render('basic-syntax04') + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'asINVALIDdf') + else: + self.assertEqual(output, 'asdf') + + @setup({'basic-syntax06': '{{ multi word variable }}'}) + def test_basic_syntax06(self): + """ + A variable may not contain more than one word + """ + with self.assertRaises(TemplateSyntaxError): + get_template('basic-syntax06') + + @setup({'basic-syntax07': '{{ }}'}) + def test_basic_syntax07(self): + """ + Raise TemplateSyntaxError for empty variable tags. + """ + with self.assertRaises(TemplateSyntaxError): + get_template('basic-syntax07') + + @setup({'basic-syntax08': '{{ }}'}) + def test_basic_syntax08(self): + """ + Raise TemplateSyntaxError for empty variable tags. + """ + with self.assertRaises(TemplateSyntaxError): + get_template('basic-syntax08') + + @setup({'basic-syntax09': '{{ var.method }}'}) + def test_basic_syntax09(self): + """ + Attribute syntax allows a template to call an object's attribute + """ + output = render('basic-syntax09', {'var': SomeClass()}) + self.assertEqual(output, 'SomeClass.method') + + @setup({'basic-syntax10': '{{ var.otherclass.method }}'}) + def test_basic_syntax10(self): + """ + Multiple levels of attribute access are allowed. + """ + output = render('basic-syntax10', {'var': SomeClass()}) + self.assertEqual(output, 'OtherClass.method') + + @setup({'basic-syntax11': '{{ var.blech }}'}) + def test_basic_syntax11(self): + """ + Fail silently when a variable's attribute isn't found. + """ + output = render('basic-syntax11', {'var': SomeClass()}) + + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + @setup({'basic-syntax12': '{{ var.__dict__ }}'}) + def test_basic_syntax12(self): + """ + Raise TemplateSyntaxError when trying to access a variable + beginning with an underscore. + """ + with self.assertRaises(TemplateSyntaxError): + get_template('basic-syntax12') + + # Raise TemplateSyntaxError when trying to access a variable + # containing an illegal character. + @setup({'basic-syntax13': "{{ va>r }}"}) + def test_basic_syntax13(self): + with self.assertRaises(TemplateSyntaxError): + get_template('basic-syntax13') + + @setup({'basic-syntax14': "{{ (var.r) }}"}) + def test_basic_syntax14(self): + with self.assertRaises(TemplateSyntaxError): + get_template('basic-syntax14') + + @setup({'basic-syntax15': "{{ sp%am }}"}) + def test_basic_syntax15(self): + with self.assertRaises(TemplateSyntaxError): + get_template('basic-syntax15') + + @setup({'basic-syntax16': "{{ eggs! }}"}) + def test_basic_syntax16(self): + with self.assertRaises(TemplateSyntaxError): + get_template('basic-syntax16') + + @setup({'basic-syntax17': "{{ moo? }}"}) + def test_basic_syntax17(self): + with self.assertRaises(TemplateSyntaxError): + get_template('basic-syntax17') + + @setup({'basic-syntax18': "{{ foo.bar }}"}) + def test_basic_syntax18(self): + """ + Attribute syntax allows a template to call a dictionary key's + value. + """ + output = render('basic-syntax18', {"foo": {"bar": "baz"}}) + self.assertEqual(output, "baz") + + @setup({'basic-syntax19': "{{ foo.spam }}"}) + def test_basic_syntax19(self): + """ + Fail silently when a variable's dictionary key isn't found. + """ + output = render('basic-syntax19', {"foo": {"bar": "baz"}}) + + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + @setup({'basic-syntax20': "{{ var.method2 }}"}) + def test_basic_syntax20(self): + """ + Fail silently when accessing a non-simple method + """ + output = render('basic-syntax20', {'var': SomeClass()}) + + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + @setup({'basic-syntax20b': "{{ var.method5 }}"}) + def test_basic_syntax20b(self): + """ + Don't silence a TypeError if it was raised inside a callable. + """ + template = get_template('basic-syntax20b') + + with self.assertRaises(TypeError): + template.render(Context({'var': SomeClass()})) + + # Don't get confused when parsing something that is almost, but not + # quite, a template tag. + @setup({'basic-syntax21': "a {{ moo %} b"}) + def test_basic_syntax21(self): + output = render('basic-syntax21') + self.assertEqual(output, "a {{ moo %} b") + + @setup({'basic-syntax22': "{{ moo #}"}) + def test_basic_syntax22(self): + output = render('basic-syntax22') + self.assertEqual(output, "{{ moo #}") + + @setup({'basic-syntax23': "{{ moo #} {{ cow }}"}) + def test_basic_syntax23(self): + """ + Treat "moo #} {{ cow" as the variable. Not ideal, but costly to work + around, so this triggers an error. + """ + with self.assertRaises(TemplateSyntaxError): + get_template('basic-syntax23') + + @setup({'basic-syntax24': "{{ moo\n }}"}) + def test_basic_syntax24(self): + """ + Embedded newlines make it not-a-tag. + """ + output = render('basic-syntax24') + self.assertEqual(output, "{{ moo\n }}") + + # Literal strings are permitted inside variables, mostly for i18n + # purposes. + @setup({'basic-syntax25': '{{ "fred" }}'}) + def test_basic_syntax25(self): + output = render('basic-syntax25') + self.assertEqual(output, "fred") + + @setup({'basic-syntax26': r'{{ "\"fred\"" }}'}) + def test_basic_syntax26(self): + output = render('basic-syntax26') + self.assertEqual(output, "\"fred\"") + + @setup({'basic-syntax27': r'{{ _("\"fred\"") }}'}) + def test_basic_syntax27(self): + output = render('basic-syntax27') + self.assertEqual(output, "\"fred\"") + + # #12554 -- Make sure a silent_variable_failure Exception is + # suppressed on dictionary and attribute lookup. + @setup({'basic-syntax28': "{{ a.b }}"}) + def test_basic_syntax28(self): + output = render('basic-syntax28', {'a': SilentGetItemClass()}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + @setup({'basic-syntax29': "{{ a.b }}"}) + def test_basic_syntax29(self): + output = render('basic-syntax29', {'a': SilentAttrClass()}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + # Something that starts like a number but has an extra lookup works + # as a lookup. + @setup({'basic-syntax30': "{{ 1.2.3 }}"}) + def test_basic_syntax30(self): + output = render( + 'basic-syntax30', + {"1": {"2": {"3": "d"}}} + ) + self.assertEqual(output, 'd') + + @setup({'basic-syntax31': "{{ 1.2.3 }}"}) + def test_basic_syntax31(self): + output = render( + 'basic-syntax31', + {"1": {"2": ("a", "b", "c", "d")}}, + ) + self.assertEqual(output, 'd') + + @setup({'basic-syntax32': "{{ 1.2.3 }}"}) + def test_basic_syntax32(self): + output = render( + 'basic-syntax32', + {"1": (("x", "x", "x", "x"), ("y", "y", "y", "y"), ("a", "b", "c", "d"))}, + ) + self.assertEqual(output, 'd') + + @setup({'basic-syntax33': "{{ 1.2.3 }}"}) + def test_basic_syntax33(self): + output = render( + 'basic-syntax33', + {"1": ("xxxx", "yyyy", "abcd")}, + ) + self.assertEqual(output, 'd') + + @setup({'basic-syntax34': "{{ 1.2.3 }}"}) + def test_basic_syntax34(self): + output = render( + 'basic-syntax34', + {"1": ({"x": "x"}, {"y": "y"}, {"z": "z", "3": "d"})} + ) + self.assertEqual(output, 'd') + + # Numbers are numbers even if their digits are in the context. + @setup({'basic-syntax35': "{{ 1 }}"}) + def test_basic_syntax35(self): + output = render('basic-syntax35', {"1": "abc"}) + self.assertEqual(output, '1') + + @setup({'basic-syntax36': "{{ 1.2 }}"}) + def test_basic_syntax36(self): + output = render('basic-syntax36', {"1": "abc"}) + self.assertEqual(output, '1.2') + + @setup({'basic-syntax37': '{{ callable }}'}) + def test_basic_syntax37(self): + """ + Call methods in the top level of the context. + """ + output = render('basic-syntax37', {"callable": lambda: "foo bar"}) + self.assertEqual(output, 'foo bar') + + @setup({'basic-syntax38': '{{ var.callable }}'}) + def test_basic_syntax38(self): + """ + Call methods returned from dictionary lookups. + """ + output = render('basic-syntax38', {"var": {"callable": lambda: "foo bar"}}) + self.assertEqual(output, 'foo bar') diff --git a/tests/template_tests/syntax_tests/test_builtins.py b/tests/template_tests/syntax_tests/test_builtins.py new file mode 100644 index 0000000000..5771c0d429 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_builtins.py @@ -0,0 +1,18 @@ +from django.test import TestCase + +from .utils import render, setup + + +class BuiltinsTests(TestCase): + + @setup({'builtins01': '{{ True }}'}) + def test_builtins01(self): + self.assertEqual(render('builtins01'), 'True') + + @setup({'builtins02': '{{ False }}'}) + def test_builtins02(self): + self.assertEqual(render('builtins02'), 'False') + + @setup({'builtins03': '{{ None }}'}) + def test_builtins03(self): + self.assertEqual(render('builtins03'), 'None') diff --git a/tests/template_tests/syntax_tests/test_cache.py b/tests/template_tests/syntax_tests/test_cache.py new file mode 100644 index 0000000000..72b7fc2aa7 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_cache.py @@ -0,0 +1,118 @@ +from django.core.cache import cache +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase + +from .utils import render, setup + + +class CacheTagTests(TestCase): + + def tearDown(self): + cache.clear() + + @setup({'cache03': '{% load cache %}{% cache 2 test %}cache03{% endcache %}'}) + def test_cache03(self): + output = render('cache03') + self.assertEqual(output, 'cache03') + + @setup({ + 'cache03': '{% load cache %}{% cache 2 test %}cache03{% endcache %}', + 'cache04': '{% load cache %}{% cache 2 test %}cache04{% endcache %}', + }) + def test_cache04(self): + render('cache03') + output = render('cache04') + self.assertEqual(output, 'cache03') + + @setup({'cache05': '{% load cache %}{% cache 2 test foo %}cache05{% endcache %}'}) + def test_cache05(self): + output = render('cache05', {'foo': 1}) + self.assertEqual(output, 'cache05') + + @setup({'cache06': '{% load cache %}{% cache 2 test foo %}cache06{% endcache %}'}) + def test_cache06(self): + output = render('cache06', {'foo': 2}) + self.assertEqual(output, 'cache06') + + @setup({ + 'cache05': '{% load cache %}{% cache 2 test foo %}cache05{% endcache %}', + 'cache07': '{% load cache %}{% cache 2 test foo %}cache07{% endcache %}', + }) + def test_cache07(self): + context = {'foo': 1} + render('cache05', context) + output = render('cache07', context) + self.assertEqual(output, 'cache05') + + @setup({ + 'cache06': '{% load cache %}{% cache 2 test foo %}cache06{% endcache %}', + 'cache08': '{% load cache %}{% cache time test foo %}cache08{% endcache %}', + }) + def test_cache08(self): + """ + Allow first argument to be a variable. + """ + context = {'foo': 2, 'time': 2} + render('cache06', context) + output = render('cache08', context) + self.assertEqual(output, 'cache06') + + # Raise exception if we don't have at least 2 args, first one integer. + @setup({'cache11': '{% load cache %}{% cache %}{% endcache %}'}) + def test_cache11(self): + with self.assertRaises(TemplateSyntaxError): + get_template('cache11') + + @setup({'cache12': '{% load cache %}{% cache 1 %}{% endcache %}'}) + def test_cache12(self): + with self.assertRaises(TemplateSyntaxError): + get_template('cache12') + + @setup({'cache13': '{% load cache %}{% cache foo bar %}{% endcache %}'}) + def test_cache13(self): + with self.assertRaises(TemplateSyntaxError): + render('cache13') + + @setup({'cache14': '{% load cache %}{% cache foo bar %}{% endcache %}'}) + def test_cache14(self): + with self.assertRaises(TemplateSyntaxError): + render('cache14', {'foo': 'fail'}) + + @setup({'cache15': '{% load cache %}{% cache foo bar %}{% endcache %}'}) + def test_cache15(self): + with self.assertRaises(TemplateSyntaxError): + render('cache15', {'foo': []}) + + @setup({'cache16': '{% load cache %}{% cache 1 foo bar %}{% endcache %}'}) + def test_cache16(self): + """ + Regression test for #7460. + """ + output = render('cache16', {'foo': 'foo', 'bar': 'with spaces'}) + self.assertEqual(output, '') + + @setup({'cache17': '{% load cache %}{% cache 10 long_cache_key poem %}Some Content{% endcache %}'}) + def test_cache17(self): + """ + Regression test for #11270. + """ + output = render('cache17', {'poem': 'Oh freddled gruntbuggly/' + 'Thy micturations are to me/' + 'As plurdled gabbleblotchits/' + 'On a lurgid bee/' + 'That mordiously hath bitled out/' + 'Its earted jurtles/' + 'Into a rancid festering/' + 'Or else I shall rend thee in the gobberwarts' + 'with my blurglecruncheon/' + 'See if I dont.'}) + self.assertEqual(output, 'Some Content') + + @setup({'cache18': '{% load cache custom %}{% cache 2|noop:"x y" cache18 %}cache18{% endcache %}'}) + def test_cache18(self): + """ + Test whitespace in filter arguments + """ + output = render('cache18') + self.assertEqual(output, 'cache18') diff --git a/tests/template_tests/syntax_tests/test_comment.py b/tests/template_tests/syntax_tests/test_comment.py new file mode 100644 index 0000000000..50d26e7cf1 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_comment.py @@ -0,0 +1,92 @@ +from django.test import TestCase + +from .utils import render, setup + + +class CommentSyntaxTests(TestCase): + + @setup({'comment-syntax01': '{# this is hidden #}hello'}) + def test_comment_syntax01(self): + output = render('comment-syntax01') + self.assertEqual(output, 'hello') + + @setup({'comment-syntax02': '{# this is hidden #}hello{# foo #}'}) + def test_comment_syntax02(self): + output = render('comment-syntax02') + self.assertEqual(output, 'hello') + + @setup({'comment-syntax03': 'foo{# {% if %} #}'}) + def test_comment_syntax03(self): + output = render('comment-syntax03') + self.assertEqual(output, 'foo') + + @setup({'comment-syntax04': 'foo{# {% endblock %} #}'}) + def test_comment_syntax04(self): + output = render('comment-syntax04') + self.assertEqual(output, 'foo') + + @setup({'comment-syntax05': 'foo{# {% somerandomtag %} #}'}) + def test_comment_syntax05(self): + output = render('comment-syntax05') + self.assertEqual(output, 'foo') + + @setup({'comment-syntax06': 'foo{# {% #}'}) + def test_comment_syntax06(self): + output = render('comment-syntax06') + self.assertEqual(output, 'foo') + + @setup({'comment-syntax07': 'foo{# %} #}'}) + def test_comment_syntax07(self): + output = render('comment-syntax07') + self.assertEqual(output, 'foo') + + @setup({'comment-syntax08': 'foo{# %} #}bar'}) + def test_comment_syntax08(self): + output = render('comment-syntax08') + self.assertEqual(output, 'foobar') + + @setup({'comment-syntax09': 'foo{# {{ #}'}) + def test_comment_syntax09(self): + output = render('comment-syntax09') + self.assertEqual(output, 'foo') + + @setup({'comment-syntax10': 'foo{# }} #}'}) + def test_comment_syntax10(self): + output = render('comment-syntax10') + self.assertEqual(output, 'foo') + + @setup({'comment-syntax11': 'foo{# { #}'}) + def test_comment_syntax11(self): + output = render('comment-syntax11') + self.assertEqual(output, 'foo') + + @setup({'comment-syntax12': 'foo{# } #}'}) + def test_comment_syntax12(self): + output = render('comment-syntax12') + self.assertEqual(output, 'foo') + + @setup({'comment-tag01': '{% comment %}this is hidden{% endcomment %}hello'}) + def test_comment_tag01(self): + output = render('comment-tag01') + self.assertEqual(output, 'hello') + + @setup({'comment-tag02': '{% comment %}this is hidden{% endcomment %}' + 'hello{% comment %}foo{% endcomment %}'}) + def test_comment_tag02(self): + output = render('comment-tag02') + self.assertEqual(output, 'hello') + + @setup({'comment-tag03': 'foo{% comment %} {% if %} {% endcomment %}'}) + def test_comment_tag03(self): + output = render('comment-tag03') + self.assertEqual(output, 'foo') + + @setup({'comment-tag04': 'foo{% comment %} {% endblock %} {% endcomment %}'}) + def test_comment_tag04(self): + output = render('comment-tag04') + self.assertEqual(output, 'foo') + + @setup({'comment-tag05': 'foo{% comment %} {% somerandomtag %} {% endcomment %}'}) + def test_comment_tag05(self): + output = render('comment-tag05') + self.assertEqual(output, 'foo') diff --git a/tests/template_tests/syntax_tests/test_cycle.py b/tests/template_tests/syntax_tests/test_cycle.py new file mode 100644 index 0000000000..cbe579c4b9 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_cycle.py @@ -0,0 +1,165 @@ +import warnings + +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase +from django.utils.deprecation import RemovedInDjango20Warning + +from .utils import render, setup + + +class CycleTagTests(TestCase): + + @setup({'cycle01': '{% cycle a %}'}) + def test_cycle01(self): + with self.assertRaises(TemplateSyntaxError): + get_template('cycle01') + + @setup({'cycle02': '{% cycle a,b,c as abc %}{% cycle abc %}'}) + def test_cycle02(self): + output = render('cycle02') + self.assertEqual(output, 'ab') + + @setup({'cycle03': '{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}'}) + def test_cycle03(self): + output = render('cycle03') + self.assertEqual(output, 'abc') + + @setup({'cycle04': '{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}'}) + def test_cycle04(self): + output = render('cycle04') + self.assertEqual(output, 'abca') + + @setup({'cycle05': '{% cycle %}'}) + def test_cycle05(self): + with self.assertRaises(TemplateSyntaxError): + get_template('cycle05') + + @setup({'cycle06': '{% cycle a %}'}) + def test_cycle06(self): + with self.assertRaises(TemplateSyntaxError): + get_template('cycle06') + + @setup({'cycle07': '{% cycle a,b,c as foo %}{% cycle bar %}'}) + def test_cycle07(self): + with self.assertRaises(TemplateSyntaxError): + get_template('cycle07') + + @setup({'cycle08': '{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo %}{{ foo }}'}) + def test_cycle08(self): + output = render('cycle08') + self.assertEqual(output, 'abbbcc') + + @setup({'cycle09': '{% for i in test %}{% cycle a,b %}{{ i }},{% endfor %}'}) + def test_cycle09(self): + output = render('cycle09', {'test': list(range(5))}) + self.assertEqual(output, 'a0,b1,a2,b3,a4,') + + @setup({'cycle10': "{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}"}) + def test_cycle10(self): + output = render('cycle10') + self.assertEqual(output, 'ab') + + @setup({'cycle11': "{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}"}) + def test_cycle11(self): + output = render('cycle11') + self.assertEqual(output, 'abc') + + @setup({'cycle12': "{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}"}) + def test_cycle12(self): + output = render('cycle12') + self.assertEqual(output, 'abca') + + @setup({'cycle13': "{% for i in test %}{% cycle 'a' 'b' %}{{ i }},{% endfor %}"}) + def test_cycle13(self): + output = render('cycle13', {'test': list(range(5))}) + self.assertEqual(output, 'a0,b1,a2,b3,a4,') + + @setup({'cycle14': '{% cycle one two as foo %}{% cycle foo %}'}) + def test_cycle14(self): + output = render('cycle14', {'one': '1', 'two': '2'}) + self.assertEqual(output, '12') + + @setup({'cycle15': '{% for i in test %}{% cycle aye bee %}{{ i }},{% endfor %}'}) + def test_cycle15(self): + output = render('cycle15', {'test': list(range(5)), 'aye': 'a', 'bee': 'b'}) + self.assertEqual(output, 'a0,b1,a2,b3,a4,') + + @setup({'cycle16': '{% cycle one|lower two as foo %}{% cycle foo %}'}) + def test_cycle16(self): + output = render('cycle16', {'one': 'A', 'two': '2'}) + self.assertEqual(output, 'a2') + + @setup({'cycle17': "{% cycle 'a' 'b' 'c' as abc silent %}" + "{% cycle abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}"}) + def test_cycle17(self): + output = render('cycle17') + self.assertEqual(output, '') + + @setup({'cycle18': "{% cycle 'a' 'b' 'c' as foo invalid_flag %}"}) + def test_cycle18(self): + with self.assertRaises(TemplateSyntaxError): + get_template('cycle18') + + @setup({'cycle19': "{% cycle 'a' 'b' as silent %}{% cycle silent %}"}) + def test_cycle19(self): + output = render('cycle19') + self.assertEqual(output, 'ab') + + @setup({'cycle20': '{% cycle one two as foo %} & {% cycle foo %}'}) + def test_cycle20(self): + output = render('cycle20', {'two': 'C & D', 'one': 'A & B'}) + self.assertEqual(output, 'A & B & C & D') + + @setup({'cycle21': '{% filter force_escape %}' + '{% cycle one two as foo %} & {% cycle foo %}{% endfilter %}'}) + def test_cycle21(self): + output = render('cycle21', {'two': 'C & D', 'one': 'A & B'}) + self.assertEqual(output, 'A &amp; B & C &amp; D') + + @setup({'cycle22': "{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{{ x }}{% endfor %}"}) + def test_cycle22(self): + output = render('cycle22', {'values': [1, 2, 3, 4]}) + self.assertEqual(output, '1234') + + @setup({'cycle23': "{% for x in values %}" + "{% cycle 'a' 'b' 'c' as abc silent %}{{ abc }}{{ x }}{% endfor %}"}) + def test_cycle23(self): + output = render('cycle23', {'values': [1, 2, 3, 4]}) + self.assertEqual(output, 'a1b2c3a4') + + @setup({ + 'cycle24': "{% for x in values %}" + "{% cycle 'a' 'b' 'c' as abc silent %}{% include 'included-cycle' %}{% endfor %}", + 'included-cycle': '{{ abc }}', + }) + def test_cycle24(self): + output = render('cycle24', {'values': [1, 2, 3, 4]}) + self.assertEqual(output, 'abca') + + @setup({'cycle25': '{% cycle a as abc %}'}) + def test_cycle25(self): + output = render('cycle25', {'a': '<'}) + self.assertEqual(output, '<') + + @setup({'cycle26': '{% load cycle from future %}{% cycle a b as ab %}{% cycle ab %}'}) + def test_cycle26(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('cycle26', {'a': '<', 'b': '>'}) + self.assertEqual(output, '<>') + + @setup({'cycle27': '{% load cycle from future %}' + '{% autoescape off %}{% cycle a b as ab %}{% cycle ab %}{% endautoescape %}'}) + def test_cycle27(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('cycle27', {'a': '<', 'b': '>'}) + self.assertEqual(output, '<>') + + @setup({'cycle28': '{% load cycle from future %}{% cycle a|safe b as ab %}{% cycle ab %}'}) + def test_cycle28(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('cycle28', {'a': '<', 'b': '>'}) + self.assertEqual(output, '<>') diff --git a/tests/template_tests/syntax_tests/test_exceptions.py b/tests/template_tests/syntax_tests/test_exceptions.py new file mode 100644 index 0000000000..923ca4a205 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_exceptions.py @@ -0,0 +1,61 @@ +from django.conf import settings +from django.template.base import TemplateDoesNotExist, TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase + +from .test_extends import inheritance_templates +from .utils import render, setup + + +class ExceptionsTests(TestCase): + + @setup({'exception01': "{% extends 'nonexistent' %}"}) + def test_exception01(self): + """ + Raise exception for invalid template name + """ + with self.assertRaises(TemplateDoesNotExist): + render('exception01') + + @setup({'exception02': '{% extends nonexistent %}'}) + def test_exception02(self): + """ + Raise exception for invalid variable template name + """ + if settings.TEMPLATE_STRING_IF_INVALID: + with self.assertRaises(TemplateDoesNotExist): + render('exception02') + else: + with self.assertRaises(TemplateSyntaxError): + render('exception02') + + @setup( + {'exception03': "{% extends 'inheritance01' %}" + "{% block first %}2{% endblock %}{% extends 'inheritance16' %}"}, + inheritance_templates, + ) + def test_exception03(self): + """ + Raise exception for extra {% extends %} tags + """ + with self.assertRaises(TemplateSyntaxError): + get_template('exception03') + + @setup( + {'exception04': "{% extends 'inheritance17' %}{% block first %}{% echo 400 %}5678{% endblock %}"}, + inheritance_templates, + ) + def test_exception04(self): + """ + Raise exception for custom tags used in child with {% load %} tag in parent, not in child + """ + with self.assertRaises(TemplateSyntaxError): + get_template('exception04') + + @setup({'exception05': '{% block first %}{{ block.super }}{% endblock %}'}) + def test_exception05(self): + """ + Raise exception for block.super used in base template + """ + with self.assertRaises(TemplateSyntaxError): + render('exception05') diff --git a/tests/template_tests/syntax_tests/test_extends.py b/tests/template_tests/syntax_tests/test_extends.py new file mode 100644 index 0000000000..7abfb382a0 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_extends.py @@ -0,0 +1,399 @@ +from django.template.base import Template +from django.test import TestCase + +from .utils import render, setup + + +inheritance_templates = { + 'inheritance01': "1{% block first %}&{% endblock %}3{% block second %}_{% endblock %}", + 'inheritance02': "{% extends 'inheritance01' %}" + "{% block first %}2{% endblock %}{% block second %}4{% endblock %}", + 'inheritance03': "{% extends 'inheritance02' %}", + 'inheritance04': "{% extends 'inheritance01' %}", + 'inheritance05': "{% extends 'inheritance02' %}", + 'inheritance06': "{% extends foo %}", + 'inheritance07': "{% extends 'inheritance01' %}{% block second %}5{% endblock %}", + 'inheritance08': "{% extends 'inheritance02' %}{% block second %}5{% endblock %}", + 'inheritance09': "{% extends 'inheritance04' %}", + 'inheritance10': "{% extends 'inheritance04' %} ", + 'inheritance11': "{% extends 'inheritance04' %}" + "{% block first %}2{% endblock %}{% block second %}4{% endblock %}", + 'inheritance12': "{% extends 'inheritance07' %}{% block first %}2{% endblock %}", + 'inheritance13': "{% extends 'inheritance02' %}" + "{% block first %}a{% endblock %}{% block second %}b{% endblock %}", + 'inheritance14': "{% extends 'inheritance01' %}{% block newblock %}NO DISPLAY{% endblock %}", + 'inheritance15': "{% extends 'inheritance01' %}" + "{% block first %}2{% block inner %}inner{% endblock %}{% endblock %}", + 'inheritance16': "{% extends 'inheritance15' %}{% block inner %}out{% endblock %}", + 'inheritance17': "{% load testtags %}{% block first %}1234{% endblock %}", + 'inheritance18': "{% load testtags %}{% echo this that theother %}5678", + 'inheritance19': "{% extends 'inheritance01' %}" + "{% block first %}{% load testtags %}{% echo 400 %}5678{% endblock %}", + 'inheritance20': "{% extends 'inheritance01' %}{% block first %}{{ block.super }}a{% endblock %}", + 'inheritance21': "{% extends 'inheritance02' %}{% block first %}{{ block.super }}a{% endblock %}", + 'inheritance22': "{% extends 'inheritance04' %}{% block first %}{{ block.super }}a{% endblock %}", + 'inheritance23': "{% extends 'inheritance20' %}{% block first %}{{ block.super }}b{% endblock %}", + 'inheritance24': "{% extends context_template %}" + "{% block first %}2{% endblock %}{% block second %}4{% endblock %}", + 'inheritance25': "{% extends context_template.1 %}" + "{% block first %}2{% endblock %}{% block second %}4{% endblock %}", + 'inheritance26': "no tags", + 'inheritance27': "{% extends 'inheritance26' %}", + 'inheritance 28': "{% block first %}!{% endblock %}", + 'inheritance29': "{% extends 'inheritance 28' %}", + 'inheritance30': "1{% if optional %}{% block opt %}2{% endblock %}{% endif %}3", + 'inheritance31': "{% extends 'inheritance30' %}{% block opt %}two{% endblock %}", + 'inheritance32': "{% extends 'inheritance30' %}{% block opt %}two{% endblock %}", + 'inheritance33': "1{% ifequal optional 1 %}{% block opt %}2{% endblock %}{% endifequal %}3", + 'inheritance34': "{% extends 'inheritance33' %}{% block opt %}two{% endblock %}", + 'inheritance35': "{% extends 'inheritance33' %}{% block opt %}two{% endblock %}", + 'inheritance36': "{% for n in numbers %}_{% block opt %}{{ n }}{% endblock %}{% endfor %}_", + 'inheritance37': "{% extends 'inheritance36' %}{% block opt %}X{% endblock %}", + 'inheritance38': "{% extends 'inheritance36' %}{% block opt %}X{% endblock %}", + 'inheritance39': "{% extends 'inheritance30' %}{% block opt %}new{{ block.super }}{% endblock %}", + 'inheritance40': "{% extends 'inheritance33' %}{% block opt %}new{{ block.super }}{% endblock %}", + 'inheritance41': "{% extends 'inheritance36' %}{% block opt %}new{{ block.super }}{% endblock %}", + 'inheritance42': "{% extends 'inheritance02'|cut:' ' %}", +} + + +class InheritanceTests(TestCase): + + @setup(inheritance_templates) + def test_inheritance01(self): + """ + Standard template with no inheritance + """ + output = render('inheritance01') + self.assertEqual(output, '1&3_') + + @setup(inheritance_templates) + def test_inheritance02(self): + """ + Standard two-level inheritance + """ + output = render('inheritance02') + self.assertEqual(output, '1234') + + @setup(inheritance_templates) + def test_inheritance03(self): + """ + Three-level with no redefinitions on third level + """ + output = render('inheritance03') + self.assertEqual(output, '1234') + + @setup(inheritance_templates) + def test_inheritance04(self): + """ + Two-level with no redefinitions on second level + """ + output = render('inheritance04') + self.assertEqual(output, '1&3_') + + @setup(inheritance_templates) + def test_inheritance05(self): + """ + Two-level with double quotes instead of single quotes + """ + output = render('inheritance05') + self.assertEqual(output, '1234') + + @setup(inheritance_templates) + def test_inheritance06(self): + """ + Three-level with variable parent-template name + """ + output = render('inheritance06', {'foo': 'inheritance02'}) + self.assertEqual(output, '1234') + + @setup(inheritance_templates) + def test_inheritance07(self): + """ + Two-level with one block defined, one block not defined + """ + output = render('inheritance07') + self.assertEqual(output, '1&35') + + @setup(inheritance_templates) + def test_inheritance08(self): + """ + Three-level with one block defined on this level, two blocks + defined next level + """ + output = render('inheritance08') + self.assertEqual(output, '1235') + + @setup(inheritance_templates) + def test_inheritance09(self): + """ + Three-level with second and third levels blank + """ + output = render('inheritance09') + self.assertEqual(output, '1&3_') + + @setup(inheritance_templates) + def test_inheritance10(self): + """ + Three-level with space NOT in a block -- should be ignored + """ + output = render('inheritance10') + self.assertEqual(output, '1&3_') + + @setup(inheritance_templates) + def test_inheritance11(self): + """ + Three-level with both blocks defined on this level, but none on + second level + """ + output = render('inheritance11') + self.assertEqual(output, '1234') + + @setup(inheritance_templates) + def test_inheritance12(self): + """ + Three-level with this level providing one and second level + providing the other + """ + output = render('inheritance12') + self.assertEqual(output, '1235') + + @setup(inheritance_templates) + def test_inheritance13(self): + """ + Three-level with this level overriding second level + """ + output = render('inheritance13') + self.assertEqual(output, '1a3b') + + @setup(inheritance_templates) + def test_inheritance14(self): + """ + A block defined only in a child template shouldn't be displayed + """ + output = render('inheritance14') + self.assertEqual(output, '1&3_') + + @setup(inheritance_templates) + def test_inheritance15(self): + """ + A block within another block + """ + output = render('inheritance15') + self.assertEqual(output, '12inner3_') + + @setup(inheritance_templates) + def test_inheritance16(self): + """ + A block within another block (level 2) + """ + output = render('inheritance16') + self.assertEqual(output, '12out3_') + + @setup(inheritance_templates) + def test_inheritance17(self): + """ + {% load %} tag (parent -- setup for exception04) + """ + output = render('inheritance17') + self.assertEqual(output, '1234') + + @setup(inheritance_templates) + def test_inheritance18(self): + """ + {% load %} tag (standard usage, without inheritance) + """ + output = render('inheritance18') + self.assertEqual(output, 'this that theother5678') + + @setup(inheritance_templates) + def test_inheritance19(self): + """ + {% load %} tag (within a child template) + """ + output = render('inheritance19') + self.assertEqual(output, '140056783_') + + @setup(inheritance_templates) + def test_inheritance20(self): + """ + Two-level inheritance with {{ block.super }} + """ + output = render('inheritance20') + self.assertEqual(output, '1&a3_') + + @setup(inheritance_templates) + def test_inheritance21(self): + """ + Three-level inheritance with {{ block.super }} from parent + """ + output = render('inheritance21') + self.assertEqual(output, '12a34') + + @setup(inheritance_templates) + def test_inheritance22(self): + """ + Three-level inheritance with {{ block.super }} from grandparent + """ + output = render('inheritance22') + self.assertEqual(output, '1&a3_') + + @setup(inheritance_templates) + def test_inheritance23(self): + """ + Three-level inheritance with {{ block.super }} from parent and + grandparent + """ + output = render('inheritance23') + self.assertEqual(output, '1&ab3_') + + @setup(inheritance_templates) + def test_inheritance24(self): + """ + Inheritance from local context without use of template loader + """ + output = render('inheritance24', { + 'context_template': Template("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}") + }) + self.assertEqual(output, '1234') + + @setup(inheritance_templates) + def test_inheritance25(self): + """ + Inheritance from local context with variable parent template + """ + output = render('inheritance25', { + 'context_template': [ + Template("Wrong"), + Template("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}"), + ], + }) + self.assertEqual(output, '1234') + + @setup(inheritance_templates) + def test_inheritance26(self): + """ + Set up a base template to extend + """ + output = render('inheritance26') + self.assertEqual(output, 'no tags') + + @setup(inheritance_templates) + def test_inheritance27(self): + """ + Inheritance from a template that doesn't have any blocks + """ + output = render('inheritance27') + self.assertEqual(output, 'no tags') + + @setup(inheritance_templates) + def test_inheritance_28(self): + """ + Set up a base template with a space in it. + """ + output = render('inheritance 28') + self.assertEqual(output, '!') + + @setup(inheritance_templates) + def test_inheritance29(self): + """ + Inheritance from a template with a space in its name should work. + """ + output = render('inheritance29') + self.assertEqual(output, '!') + + @setup(inheritance_templates) + def test_inheritance30(self): + """ + Base template, putting block in a conditional {% if %} tag + """ + output = render('inheritance30', {'optional': True}) + self.assertEqual(output, '123') + + # Inherit from a template with block wrapped in an {% if %} tag + # (in parent), still gets overridden + @setup(inheritance_templates) + def test_inheritance31(self): + output = render('inheritance31', {'optional': True}) + self.assertEqual(output, '1two3') + + @setup(inheritance_templates) + def test_inheritance32(self): + output = render('inheritance32') + self.assertEqual(output, '13') + + @setup(inheritance_templates) + def test_inheritance33(self): + """ + Base template, putting block in a conditional {% ifequal %} tag + """ + output = render('inheritance33', {'optional': 1}) + self.assertEqual(output, '123') + + @setup(inheritance_templates) + def test_inheritance34(self): + """ + Inherit from a template with block wrapped in an {% ifequal %} tag + (in parent), still gets overridden + """ + output = render('inheritance34', {'optional': 1}) + self.assertEqual(output, '1two3') + + @setup(inheritance_templates) + def test_inheritance35(self): + """ + Inherit from a template with block wrapped in an {% ifequal %} tag + (in parent), still gets overridden + """ + output = render('inheritance35', {'optional': 2}) + self.assertEqual(output, '13') + + @setup(inheritance_templates) + def test_inheritance36(self): + """ + Base template, putting block in a {% for %} tag + """ + output = render('inheritance36', {'numbers': '123'}) + self.assertEqual(output, '_1_2_3_') + + @setup(inheritance_templates) + def test_inheritance37(self): + """ + Inherit from a template with block wrapped in an {% for %} tag + (in parent), still gets overridden + """ + output = render('inheritance37', {'numbers': '123'}) + self.assertEqual(output, '_X_X_X_') + + @setup(inheritance_templates) + def test_inheritance38(self): + """ + Inherit from a template with block wrapped in an {% for %} tag + (in parent), still gets overridden + """ + output = render('inheritance38') + self.assertEqual(output, '_') + + # The super block will still be found. + @setup(inheritance_templates) + def test_inheritance39(self): + output = render('inheritance39', {'optional': True}) + self.assertEqual(output, '1new23') + + @setup(inheritance_templates) + def test_inheritance40(self): + output = render('inheritance40', {'optional': 1}) + self.assertEqual(output, '1new23') + + @setup(inheritance_templates) + def test_inheritance41(self): + output = render('inheritance41', {'numbers': '123'}) + self.assertEqual(output, '_new1_new2_new3_') + + @setup(inheritance_templates) + def test_inheritance42(self): + """ + Expression starting and ending with a quote + """ + output = render('inheritance42') + self.assertEqual(output, '1234') diff --git a/tests/template_tests/syntax_tests/test_filter_syntax.py b/tests/template_tests/syntax_tests/test_filter_syntax.py new file mode 100644 index 0000000000..4e0b33575c --- /dev/null +++ b/tests/template_tests/syntax_tests/test_filter_syntax.py @@ -0,0 +1,236 @@ +# coding: utf-8 +from __future__ import unicode_literals +import warnings + +from django.conf import settings +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase +from django.utils.deprecation import RemovedInDjango20Warning + +from .utils import render, setup, SomeClass, SomeOtherException, UTF8Class + + +class FilterSyntaxTests(TestCase): + + @setup({'filter-syntax01': '{{ var|upper }}'}) + def test_filter_syntax01(self): + """ + Basic filter usage + """ + output = render('filter-syntax01', {"var": "Django is the greatest!"}) + self.assertEqual(output, "DJANGO IS THE GREATEST!") + + @setup({'filter-syntax02': '{{ var|upper|lower }}'}) + def test_filter_syntax02(self): + """ + Chained filters + """ + output = render('filter-syntax02', {"var": "Django is the greatest!"}) + self.assertEqual(output, "django is the greatest!") + + @setup({'filter-syntax03': '{{ var |upper }}'}) + def test_filter_syntax03(self): + """ + Allow spaces before the filter pipe + """ + output = render('filter-syntax03', {'var': 'Django is the greatest!'}) + self.assertEqual(output, 'DJANGO IS THE GREATEST!') + + @setup({'filter-syntax04': '{{ var| upper }}'}) + def test_filter_syntax04(self): + """ + Allow spaces after the filter pipe + """ + output = render('filter-syntax04', {'var': 'Django is the greatest!'}) + self.assertEqual(output, 'DJANGO IS THE GREATEST!') + + @setup({'filter-syntax05': '{{ var|does_not_exist }}'}) + def test_filter_syntax05(self): + """ + Raise TemplateSyntaxError for a nonexistent filter + """ + with self.assertRaises(TemplateSyntaxError): + get_template('filter-syntax05') + + @setup({'filter-syntax06': '{{ var|fil(ter) }}'}) + def test_filter_syntax06(self): + """ + Raise TemplateSyntaxError when trying to access a filter containing + an illegal character + """ + with self.assertRaises(TemplateSyntaxError): + get_template('filter-syntax06') + + @setup({'filter-syntax07': "{% nothing_to_see_here %}"}) + def test_filter_syntax07(self): + """ + Raise TemplateSyntaxError for invalid block tags + """ + with self.assertRaises(TemplateSyntaxError): + get_template('filter-syntax07') + + @setup({'filter-syntax08': "{% %}"}) + def test_filter_syntax08(self): + """ + Raise TemplateSyntaxError for empty block tags + """ + with self.assertRaises(TemplateSyntaxError): + get_template('filter-syntax08') + + @setup({'filter-syntax09': '{{ var|removetags:"b i"|upper|lower }}'}) + def test_filter_syntax09(self): + """ + Chained filters, with an argument to the first one + """ + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('filter-syntax09', {'var': 'Yes'}) + self.assertEqual(output, 'yes') + + @setup({'filter-syntax10': r'{{ var|default_if_none:" endquote\" hah" }}'}) + def test_filter_syntax10(self): + """ + Literal string as argument is always "safe" from auto-escaping. + """ + output = render('filter-syntax10', {"var": None}) + self.assertEqual(output, ' endquote" hah') + + @setup({'filter-syntax11': r'{{ var|default_if_none:var2 }}'}) + def test_filter_syntax11(self): + """ + Variable as argument + """ + output = render('filter-syntax11', {"var": None, "var2": "happy"}) + self.assertEqual(output, 'happy') + + @setup({'filter-syntax12': r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}'}) + def test_filter_syntax12(self): + """ + Default argument testing + """ + output = render('filter-syntax12', {"var": True}) + self.assertEqual(output, 'yup yes') + + @setup({'filter-syntax13': r'1{{ var.method3 }}2'}) + def test_filter_syntax13(self): + """ + Fail silently for methods that raise an exception with a + `silent_variable_failure` attribute + """ + output = render('filter-syntax13', {"var": SomeClass()}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, "1INVALID2") + else: + self.assertEqual(output, "12") + + @setup({'filter-syntax14': r'1{{ var.method4 }}2'}) + def test_filter_syntax14(self): + """ + In methods that raise an exception without a + `silent_variable_attribute` set to True, the exception propagates + """ + with self.assertRaises(SomeOtherException): + render('filter-syntax14', {"var": SomeClass()}) + + @setup({'filter-syntax15': r'{{ var|default_if_none:"foo\bar" }}'}) + def test_filter_syntax15(self): + """ + Escaped backslash in argument + """ + output = render('filter-syntax15', {"var": None}) + self.assertEqual(output, r'foo\bar') + + @setup({'filter-syntax16': r'{{ var|default_if_none:"foo\now" }}'}) + def test_filter_syntax16(self): + """ + Escaped backslash using known escape char + """ + output = render('filter-syntax16', {"var": None}) + self.assertEqual(output, r'foo\now') + + @setup({'filter-syntax17': r'{{ var|join:"" }}'}) + def test_filter_syntax17(self): + """ + Empty strings can be passed as arguments to filters + """ + output = render('filter-syntax17', {'var': ['a', 'b', 'c']}) + self.assertEqual(output, 'abc') + + @setup({'filter-syntax18': r'{{ var }}'}) + def test_filter_syntax18(self): + """ + Make sure that any unicode strings are converted to bytestrings + in the final output. + """ + output = render('filter-syntax18', {'var': UTF8Class()}) + self.assertEqual(output, '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111') + + @setup({'filter-syntax19': '{{ var|truncatewords:1 }}'}) + def test_filter_syntax19(self): + """ + Numbers as filter arguments should work + """ + output = render('filter-syntax19', {"var": "hello world"}) + self.assertEqual(output, "hello ...") + + @setup({'filter-syntax20': '{{ ""|default_if_none:"was none" }}'}) + def test_filter_syntax20(self): + """ + Filters should accept empty string constants + """ + output = render('filter-syntax20') + self.assertEqual(output, "") + + @setup({'filter-syntax21': r'1{{ var.silent_fail_key }}2'}) + def test_filter_syntax21(self): + """ + Fail silently for non-callable attribute and dict lookups which + raise an exception with a "silent_variable_failure" attribute + """ + output = render('filter-syntax21', {"var": SomeClass()}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, "1INVALID2") + else: + self.assertEqual(output, "12") + + @setup({'filter-syntax22': r'1{{ var.silent_fail_attribute }}2'}) + def test_filter_syntax22(self): + """ + Fail silently for non-callable attribute and dict lookups which + raise an exception with a `silent_variable_failure` attribute + """ + output = render('filter-syntax22', {"var": SomeClass()}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, "1INVALID2") + else: + self.assertEqual(output, "12") + + @setup({'filter-syntax23': r'1{{ var.noisy_fail_key }}2'}) + def test_filter_syntax23(self): + """ + In attribute and dict lookups that raise an unexpected exception + without a `silent_variable_attribute` set to True, the exception + propagates + """ + with self.assertRaises(SomeOtherException): + render('filter-syntax23', {"var": SomeClass()}) + + @setup({'filter-syntax24': r'1{{ var.noisy_fail_attribute }}2'}) + def test_filter_syntax24(self): + """ + In attribute and dict lookups that raise an unexpected exception + without a `silent_variable_attribute` set to True, the exception + propagates + """ + with self.assertRaises(SomeOtherException): + render('filter-syntax24', {"var": SomeClass()}) + + @setup({'filter-syntax25': '{{ var.attribute_error_attribute }}'}) + def test_filter_syntax25(self): + """ + #16383 - Attribute errors from an @property value should be + reraised. + """ + with self.assertRaises(AttributeError): + render('filter-syntax25', {'var': SomeClass()}) diff --git a/tests/template_tests/syntax_tests/test_filter_tag.py b/tests/template_tests/syntax_tests/test_filter_tag.py new file mode 100644 index 0000000000..51671fc5ac --- /dev/null +++ b/tests/template_tests/syntax_tests/test_filter_tag.py @@ -0,0 +1,48 @@ +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase + +from .utils import render, setup + + +class FilterTagTests(TestCase): + + @setup({'filter01': '{% filter upper %}{% endfilter %}'}) + def test_filter01(self): + output = render('filter01') + self.assertEqual(output, '') + + @setup({'filter02': '{% filter upper %}django{% endfilter %}'}) + def test_filter02(self): + output = render('filter02') + self.assertEqual(output, 'DJANGO') + + @setup({'filter03': '{% filter upper|lower %}django{% endfilter %}'}) + def test_filter03(self): + output = render('filter03') + self.assertEqual(output, 'django') + + @setup({'filter04': '{% filter cut:remove %}djangospam{% endfilter %}'}) + def test_filter04(self): + output = render('filter04', {'remove': 'spam'}) + self.assertEqual(output, 'django') + + @setup({'filter05': '{% filter safe %}fail{% endfilter %}'}) + def test_filter05(self): + with self.assertRaises(TemplateSyntaxError): + get_template('filter05') + + @setup({'filter05bis': '{% filter upper|safe %}fail{% endfilter %}'}) + def test_filter05bis(self): + with self.assertRaises(TemplateSyntaxError): + get_template('filter05bis') + + @setup({'filter06': '{% filter escape %}fail{% endfilter %}'}) + def test_filter06(self): + with self.assertRaises(TemplateSyntaxError): + get_template('filter06') + + @setup({'filter06bis': '{% filter upper|escape %}fail{% endfilter %}'}) + def test_filter06bis(self): + with self.assertRaises(TemplateSyntaxError): + get_template('filter06bis') diff --git a/tests/template_tests/syntax_tests/test_firstof.py b/tests/template_tests/syntax_tests/test_firstof.py new file mode 100644 index 0000000000..23216416ac --- /dev/null +++ b/tests/template_tests/syntax_tests/test_firstof.py @@ -0,0 +1,90 @@ +import warnings + +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase +from django.utils.deprecation import RemovedInDjango20Warning + +from .utils import render, setup + + +class FirstOfTagTests(TestCase): + + @setup({'firstof01': '{% firstof a b c %}'}) + def test_firstof01(self): + output = render('firstof01', {'a': 0, 'c': 0, 'b': 0}) + self.assertEqual(output, '') + + @setup({'firstof02': '{% firstof a b c %}'}) + def test_firstof02(self): + output = render('firstof02', {'a': 1, 'c': 0, 'b': 0}) + self.assertEqual(output, '1') + + @setup({'firstof03': '{% firstof a b c %}'}) + def test_firstof03(self): + output = render('firstof03', {'a': 0, 'c': 0, 'b': 2}) + self.assertEqual(output, '2') + + @setup({'firstof04': '{% firstof a b c %}'}) + def test_firstof04(self): + output = render('firstof04', {'a': 0, 'c': 3, 'b': 0}) + self.assertEqual(output, '3') + + @setup({'firstof05': '{% firstof a b c %}'}) + def test_firstof05(self): + output = render('firstof05', {'a': 1, 'c': 3, 'b': 2}) + self.assertEqual(output, '1') + + @setup({'firstof06': '{% firstof a b c %}'}) + def test_firstof06(self): + output = render('firstof06', {'c': 3, 'b': 0}) + self.assertEqual(output, '3') + + @setup({'firstof07': '{% firstof a b "c" %}'}) + def test_firstof07(self): + output = render('firstof07', {'a': 0}) + self.assertEqual(output, 'c') + + @setup({'firstof08': '{% firstof a b "c and d" %}'}) + def test_firstof08(self): + output = render('firstof08', {'a': 0, 'b': 0}) + self.assertEqual(output, 'c and d') + + @setup({'firstof09': '{% firstof %}'}) + def test_firstof09(self): + with self.assertRaises(TemplateSyntaxError): + get_template('firstof09') + + @setup({'firstof10': '{% firstof a %}'}) + def test_firstof10(self): + output = render('firstof10', {'a': '<'}) + self.assertEqual(output, '<') + + @setup({'firstof11': '{% load firstof from future %}{% firstof a b %}'}) + def test_firstof11(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('firstof11', {'a': '<', 'b': '>'}) + self.assertEqual(output, '<') + + @setup({'firstof12': '{% load firstof from future %}{% firstof a b %}'}) + def test_firstof12(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('firstof12', {'a': '', 'b': '>'}) + self.assertEqual(output, '>') + + @setup({'firstof13': '{% load firstof from future %}' + '{% autoescape off %}{% firstof a %}{% endautoescape %}'}) + def test_firstof13(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('firstof13', {'a': '<'}) + self.assertEqual(output, '<') + + @setup({'firstof14': '{% load firstof from future %}{% firstof a|safe b %}'}) + def test_firstof14(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('firstof14', {'a': '<'}) + self.assertEqual(output, '<') diff --git a/tests/template_tests/syntax_tests/test_for.py b/tests/template_tests/syntax_tests/test_for.py new file mode 100644 index 0000000000..94b9b95cca --- /dev/null +++ b/tests/template_tests/syntax_tests/test_for.py @@ -0,0 +1,179 @@ +import warnings + +from django.conf import settings +from django.template.base import TemplateSyntaxError +from django.test import TestCase +from django.utils.deprecation import RemovedInDjango20Warning + +from .utils import render, setup + + +class ForTagTests(TestCase): + + @setup({'for-tag01': '{% for val in values %}{{ val }}{% endfor %}'}) + def test_for_tag01(self): + output = render('for-tag01', {'values': [1, 2, 3]}) + self.assertEqual(output, '123') + + @setup({'for-tag02': '{% for val in values reversed %}{{ val }}{% endfor %}'}) + def test_for_tag02(self): + output = render('for-tag02', {'values': [1, 2, 3]}) + self.assertEqual(output, '321') + + @setup({'for-tag-vars01': '{% for val in values %}{{ forloop.counter }}{% endfor %}'}) + def test_for_tag_vars01(self): + output = render('for-tag-vars01', {'values': [6, 6, 6]}) + self.assertEqual(output, '123') + + @setup({'for-tag-vars02': '{% for val in values %}{{ forloop.counter0 }}{% endfor %}'}) + def test_for_tag_vars02(self): + output = render('for-tag-vars02', {'values': [6, 6, 6]}) + self.assertEqual(output, '012') + + @setup({'for-tag-vars03': '{% for val in values %}{{ forloop.revcounter }}{% endfor %}'}) + def test_for_tag_vars03(self): + output = render('for-tag-vars03', {'values': [6, 6, 6]}) + self.assertEqual(output, '321') + + @setup({'for-tag-vars04': '{% for val in values %}{{ forloop.revcounter0 }}{% endfor %}'}) + def test_for_tag_vars04(self): + output = render('for-tag-vars04', {'values': [6, 6, 6]}) + self.assertEqual(output, '210') + + @setup({'for-tag-vars05': '{% for val in values %}' + '{% if forloop.first %}f{% else %}x{% endif %}{% endfor %}'}) + def test_for_tag_vars05(self): + output = render('for-tag-vars05', {'values': [6, 6, 6]}) + self.assertEqual(output, 'fxx') + + @setup({'for-tag-vars06': '{% for val in values %}' + '{% if forloop.last %}l{% else %}x{% endif %}{% endfor %}'}) + def test_for_tag_vars06(self): + output = render('for-tag-vars06', {'values': [6, 6, 6]}) + self.assertEqual(output, 'xxl') + + @setup({'for-tag-unpack01': '{% for key,value in items %}{{ key }}:{{ value }}/{% endfor %}'}) + def test_for_tag_unpack01(self): + output = render('for-tag-unpack01', {'items': (('one', 1), ('two', 2))}) + self.assertEqual(output, 'one:1/two:2/') + + @setup({'for-tag-unpack03': '{% for key, value in items %}{{ key }}:{{ value }}/{% endfor %}'}) + def test_for_tag_unpack03(self): + output = render('for-tag-unpack03', {'items': (('one', 1), ('two', 2))}) + self.assertEqual(output, 'one:1/two:2/') + + @setup({'for-tag-unpack04': '{% for key , value in items %}{{ key }}:{{ value }}/{% endfor %}'}) + def test_for_tag_unpack04(self): + output = render('for-tag-unpack04', {'items': (('one', 1), ('two', 2))}) + self.assertEqual(output, 'one:1/two:2/') + + @setup({'for-tag-unpack05': '{% for key ,value in items %}{{ key }}:{{ value }}/{% endfor %}'}) + def test_for_tag_unpack05(self): + output = render('for-tag-unpack05', {'items': (('one', 1), ('two', 2))}) + self.assertEqual(output, 'one:1/two:2/') + + @setup({'for-tag-unpack06': '{% for key value in items %}{{ key }}:{{ value }}/{% endfor %}'}) + def test_for_tag_unpack06(self): + with self.assertRaises(TemplateSyntaxError): + render('for-tag-unpack06', {'items': (('one', 1), ('two', 2))}) + + @setup({'for-tag-unpack07': '{% for key,,value in items %}{{ key }}:{{ value }}/{% endfor %}'}) + def test_for_tag_unpack07(self): + with self.assertRaises(TemplateSyntaxError): + render('for-tag-unpack07', {'items': (('one', 1), ('two', 2))}) + + @setup({'for-tag-unpack08': '{% for key,value, in items %}{{ key }}:{{ value }}/{% endfor %}'}) + def test_for_tag_unpack08(self): + with self.assertRaises(TemplateSyntaxError): + render('for-tag-unpack08', {'items': (('one', 1), ('two', 2))}) + + @setup({'for-tag-unpack09': '{% for val in items %}{{ val.0 }}:{{ val.1 }}/{% endfor %}'}) + def test_for_tag_unpack09(self): + """ + Ensure that a single loopvar doesn't truncate the list in val. + """ + output = render('for-tag-unpack09', {'items': (('one', 1), ('two', 2))}) + self.assertEqual(output, 'one:1/two:2/') + + @setup({'for-tag-unpack13': '{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}'}) + def test_for_tag_unpack13(self): + output = render('for-tag-unpack13', {'items': (('one', 1, 'carrot'), ('two', 2, 'cheese'))}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'one:1,carrot/two:2,cheese/') + else: + self.assertEqual(output, 'one:1,carrot/two:2,cheese/') + + @setup({'for-tag-empty01': '{% for val in values %}{{ val }}{% empty %}empty text{% endfor %}'}) + def test_for_tag_empty01(self): + output = render('for-tag-empty01', {'values': [1, 2, 3]}) + self.assertEqual(output, '123') + + @setup({'for-tag-empty02': '{% for val in values %}{{ val }}{% empty %}values array empty{% endfor %}'}) + def test_for_tag_empty02(self): + output = render('for-tag-empty02', {'values': []}) + self.assertEqual(output, 'values array empty') + + @setup({'for-tag-empty03': '{% for val in values %}' + '{{ val }}{% empty %}values array not found{% endfor %}'}) + def test_for_tag_empty03(self): + output = render('for-tag-empty03') + self.assertEqual(output, 'values array not found') + + @setup({'for-tag-filter-ws': "{% load custom %}{% for x in s|noop:'x y' %}{{ x }}{% endfor %}"}) + def test_for_tag_filter_ws(self): + """ + #19882 + """ + output = render('for-tag-filter-ws', {'s': 'abc'}) + self.assertEqual(output, 'abc') + + # These tests raise deprecation warnings and will raise an exception + # in Django 2.0. The existing behavior is silent truncation if the + # length of loopvars differs to the length of each set of items. + @setup({'for-tag-unpack10': '{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}'}) + def test_for_tag_unpack10(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render( + 'for-tag-unpack10', + {'items': (('one', 1, 'carrot'), ('two', 2, 'orange'))}, + ) + self.assertEqual(output, 'one:1/two:2/') + + @setup({'for-tag-unpack11': '{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}'}) + def test_for_tag_unpack11(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render( + 'for-tag-unpack11', + {'items': (('one', 1), ('two', 2))}, + ) + + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'one:1,INVALID/two:2,INVALID/') + else: + self.assertEqual(output, 'one:1,/two:2,/') + + @setup({'for-tag-unpack12': '{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}'}) + def test_for_tag_unpack12(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render( + 'for-tag-unpack12', + {'items': (('one', 1, 'carrot'), ('two', 2))} + ) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'one:1,carrot/two:2,INVALID/') + else: + self.assertEqual(output, 'one:1,carrot/two:2,/') + + @setup({'for-tag-unpack14': '{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}'}) + def test_for_tag_unpack14(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('for-tag-unpack14', {'items': (1, 2)}) + + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID:INVALID/INVALID:INVALID/') + else: + self.assertEqual(output, ':/:/') diff --git a/tests/template_tests/syntax_tests/test_i18n.py b/tests/template_tests/syntax_tests/test_i18n.py new file mode 100644 index 0000000000..9a2581fc88 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_i18n.py @@ -0,0 +1,399 @@ +# coding: utf-8 +from __future__ import unicode_literals + +from django.conf import settings +from django.test import TestCase +from django.utils.safestring import mark_safe + +from .utils import render, setup + + +class I18nTagTests(TestCase): + + @setup({'i18n01': '{% load i18n %}{% trans \'xxxyyyxxx\' %}'}) + def test_i18n01(self): + """ + simple translation of a string delimited by ' + """ + output = render('i18n01') + self.assertEqual(output, 'xxxyyyxxx') + + @setup({'i18n02': '{% load i18n %}{% trans "xxxyyyxxx" %}'}) + def test_i18n02(self): + """ + simple translation of a string delimited by " + """ + output = render('i18n02') + self.assertEqual(output, 'xxxyyyxxx') + + @setup({'i18n03': '{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}'}) + def test_i18n03(self): + """ + simple translation of a variable + """ + output = render('i18n03', {'anton': b'\xc3\x85'}) + self.assertEqual(output, 'Å') + + @setup({'i18n04': '{% load i18n %}{% blocktrans with berta=anton|lower %}{{ berta }}{% endblocktrans %}'}) + def test_i18n04(self): + """ + simple translation of a variable and filter + """ + output = render('i18n04', {'anton': b'\xc3\x85'}) + self.assertEqual(output, 'å') + + @setup({'legacyi18n04': '{% load i18n %}' + '{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}'}) + def test_legacyi18n04(self): + """ + simple translation of a variable and filter + """ + output = render('legacyi18n04', {'anton': b'\xc3\x85'}) + self.assertEqual(output, 'å') + + @setup({'i18n05': '{% load i18n %}{% blocktrans %}xxx{{ anton }}xxx{% endblocktrans %}'}) + def test_i18n05(self): + """ + simple translation of a string with interpolation + """ + output = render('i18n05', {'anton': 'yyy'}) + self.assertEqual(output, 'xxxyyyxxx') + + @setup({'i18n06': '{% load i18n %}{% trans "Page not found" %}'}) + def test_i18n06(self): + """ + simple translation of a string to german + """ + output = render('i18n06', {'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, 'Seite nicht gefunden') + + @setup({'i18n07': '{% load i18n %}' + '{% blocktrans count counter=number %}singular{% plural %}' + '{{ counter }} plural{% endblocktrans %}'}) + def test_i18n07(self): + """ + translation of singular form + """ + output = render('i18n07', {'number': 1}) + self.assertEqual(output, 'singular') + + @setup({'legacyi18n07': '{% load i18n %}' + '{% blocktrans count number as counter %}singular{% plural %}' + '{{ counter }} plural{% endblocktrans %}'}) + def test_legacyi18n07(self): + """ + translation of singular form + """ + output = render('legacyi18n07', {'number': 1}) + self.assertEqual(output, 'singular') + + @setup({'i18n08': '{% load i18n %}' + '{% blocktrans count number as counter %}singular{% plural %}' + '{{ counter }} plural{% endblocktrans %}'}) + def test_i18n08(self): + """ + translation of plural form + """ + output = render('i18n08', {'number': 2}) + self.assertEqual(output, '2 plural') + + @setup({'legacyi18n08': '{% load i18n %}' + '{% blocktrans count counter=number %}singular{% plural %}' + '{{ counter }} plural{% endblocktrans %}'}) + def test_legacyi18n08(self): + """ + translation of plural form + """ + output = render('legacyi18n08', {'number': 2}) + self.assertEqual(output, '2 plural') + + @setup({'i18n09': '{% load i18n %}{% trans "Page not found" noop %}'}) + def test_i18n09(self): + """ + simple non-translation (only marking) of a string to german + """ + output = render('i18n09', {'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, 'Page not found') + + @setup({'i18n10': '{{ bool|yesno:_("yes,no,maybe") }}'}) + def test_i18n10(self): + """ + translation of a variable with a translated filter + """ + output = render('i18n10', {'bool': True, 'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, 'Ja') + + @setup({'i18n11': '{{ bool|yesno:"ja,nein" }}'}) + def test_i18n11(self): + """ + translation of a variable with a non-translated filter + """ + output = render('i18n11', {'bool': True}) + self.assertEqual(output, 'ja') + + @setup({'i18n12': '{% load i18n %}' + '{% get_available_languages as langs %}{% for lang in langs %}' + '{% ifequal lang.0 "de" %}{{ lang.0 }}{% endifequal %}{% endfor %}'}) + def test_i18n12(self): + """ + usage of the get_available_languages tag + """ + output = render('i18n12') + self.assertEqual(output, 'de') + + @setup({'i18n13': '{{ _("Password") }}'}) + def test_i18n13(self): + """ + translation of constant strings + """ + output = render('i18n13', {'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, 'Passwort') + + @setup({'i18n14': '{% cycle "foo" _("Password") _(\'Password\') as c %} {% cycle c %} {% cycle c %}'}) + def test_i18n14(self): + """ + translation of constant strings + """ + output = render('i18n14', {'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, 'foo Passwort Passwort') + + @setup({'i18n15': '{{ absent|default:_("Password") }}'}) + def test_i18n15(self): + """ + translation of constant strings + """ + output = render('i18n15', {'absent': '', 'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, 'Passwort') + + @setup({'i18n16': '{{ _("<") }}'}) + def test_i18n16(self): + """ + translation of constant strings + """ + output = render('i18n16', {'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, '<') + + @setup({'i18n17': '{% load i18n %}' + '{% blocktrans with berta=anton|escape %}{{ berta }}{% endblocktrans %}'}) + def test_i18n17(self): + """ + Escaping inside blocktrans and trans works as if it was directly in the template. + """ + output = render('i18n17', {'anton': 'α & β'}) + self.assertEqual(output, 'α & β') + + @setup({'i18n18': '{% load i18n %}' + '{% blocktrans with berta=anton|force_escape %}{{ berta }}{% endblocktrans %}'}) + def test_i18n18(self): + output = render('i18n18', {'anton': 'α & β'}) + self.assertEqual(output, 'α & β') + + @setup({'i18n19': '{% load i18n %}{% blocktrans %}{{ andrew }}{% endblocktrans %}'}) + def test_i18n19(self): + output = render('i18n19', {'andrew': 'a & b'}) + self.assertEqual(output, 'a & b') + + @setup({'i18n20': '{% load i18n %}{% trans andrew %}'}) + def test_i18n20(self): + output = render('i18n20', {'andrew': 'a & b'}) + self.assertEqual(output, 'a & b') + + @setup({'i18n21': '{% load i18n %}{% blocktrans %}{{ andrew }}{% endblocktrans %}'}) + def test_i18n21(self): + output = render('i18n21', {'andrew': mark_safe('a & b')}) + self.assertEqual(output, 'a & b') + + @setup({'i18n22': '{% load i18n %}{% trans andrew %}'}) + def test_i18n22(self): + output = render('i18n22', {'andrew': mark_safe('a & b')}) + self.assertEqual(output, 'a & b') + + @setup({'legacyi18n17': '{% load i18n %}' + '{% blocktrans with anton|escape as berta %}{{ berta }}{% endblocktrans %}'}) + def test_legacyi18n17(self): + output = render('legacyi18n17', {'anton': 'α & β'}) + self.assertEqual(output, 'α & β') + + @setup({'legacyi18n18': '{% load i18n %}' + '{% blocktrans with anton|force_escape as berta %}' + '{{ berta }}{% endblocktrans %}'}) + def test_legacyi18n18(self): + output = render('legacyi18n18', {'anton': 'α & β'}) + self.assertEqual(output, 'α & β') + + @setup({'i18n23': '{% load i18n %}{% trans "Page not found"|capfirst|slice:"6:" %}'}) + def test_i18n23(self): + """ + #5972 - Use filters with the {% trans %} tag + """ + output = render('i18n23', {'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, 'nicht gefunden') + + @setup({'i18n24': '{% load i18n %}{% trans \'Page not found\'|upper %}'}) + def test_i18n24(self): + output = render('i18n24', {'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, 'SEITE NICHT GEFUNDEN') + + @setup({'i18n25': '{% load i18n %}{% trans somevar|upper %}'}) + def test_i18n25(self): + output = render('i18n25', {'somevar': 'Page not found', 'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, 'SEITE NICHT GEFUNDEN') + + @setup({'i18n26': '{% load i18n %}' + '{% blocktrans with extra_field=myextra_field count counter=number %}' + 'singular {{ extra_field }}{% plural %}plural{% endblocktrans %}'}) + def test_i18n26(self): + """ + translation of plural form with extra field in singular form (#13568) + """ + output = render('i18n26', {'myextra_field': 'test', 'number': 1}) + self.assertEqual(output, 'singular test') + + @setup({'legacyi18n26': '{% load i18n %}' + '{% blocktrans with myextra_field as extra_field count number as counter %}' + 'singular {{ extra_field }}{% plural %}plural{% endblocktrans %}'}) + def test_legacyi18n26(self): + output = render('legacyi18n26', {'myextra_field': 'test', 'number': 1}) + self.assertEqual(output, 'singular test') + + @setup({'i18n27': '{% load i18n %}{% blocktrans count counter=number %}' + '{{ counter }} result{% plural %}{{ counter }} results' + '{% endblocktrans %}'}) + def test_i18n27(self): + """ + translation of singular form in russian (#14126) + """ + output = render('i18n27', {'number': 1, 'LANGUAGE_CODE': 'ru'}) + self.assertEqual(output, '1 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442') + + @setup({'legacyi18n27': '{% load i18n %}' + '{% blocktrans count number as counter %}{{ counter }} result' + '{% plural %}{{ counter }} results{% endblocktrans %}'}) + def test_legacyi18n27(self): + output = render('legacyi18n27', {'number': 1, 'LANGUAGE_CODE': 'ru'}) + self.assertEqual(output, '1 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442') + + @setup({'i18n28': '{% load i18n %}' + '{% blocktrans with a=anton b=berta %}{{ a }} + {{ b }}{% endblocktrans %}'}) + def test_i18n28(self): + """ + simple translation of multiple variables + """ + output = render('i18n28', {'anton': 'α', 'berta': 'β'}) + self.assertEqual(output, 'α + β') + + @setup({'legacyi18n28': '{% load i18n %}' + '{% blocktrans with anton as a and berta as b %}' + '{{ a }} + {{ b }}{% endblocktrans %}'}) + def test_legacyi18n28(self): + output = render('legacyi18n28', {'anton': 'α', 'berta': 'β'}) + self.assertEqual(output, 'α + β') + + # retrieving language information + @setup({'i18n28_2': '{% load i18n %}' + '{% get_language_info for "de" as l %}' + '{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}'}) + def test_i18n28_2(self): + output = render('i18n28_2') + self.assertEqual(output, 'de: German/Deutsch bidi=False') + + @setup({'i18n29': '{% load i18n %}' + '{% get_language_info for LANGUAGE_CODE as l %}' + '{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}'}) + def test_i18n29(self): + output = render('i18n29', {'LANGUAGE_CODE': 'fi'}) + self.assertEqual(output, 'fi: Finnish/suomi bidi=False') + + @setup({'i18n30': '{% load i18n %}' + '{% get_language_info_list for langcodes as langs %}' + '{% for l in langs %}{{ l.code }}: {{ l.name }}/' + '{{ l.name_local }} bidi={{ l.bidi }}; {% endfor %}'}) + def test_i18n30(self): + output = render('i18n30', {'langcodes': ['it', 'no']}) + self.assertEqual(output, 'it: Italian/italiano bidi=False; no: Norwegian/norsk bidi=False; ') + + @setup({'i18n31': '{% load i18n %}' + '{% get_language_info_list for langcodes as langs %}' + '{% for l in langs %}{{ l.code }}: {{ l.name }}/' + '{{ l.name_local }} bidi={{ l.bidi }}; {% endfor %}'}) + def test_i18n31(self): + output = render('i18n31', {'langcodes': (('sl', 'Slovenian'), ('fa', 'Persian'))}) + self.assertEqual( + output, + 'sl: Slovenian/Sloven\u0161\u010dina bidi=False; ' + 'fa: Persian/\u0641\u0627\u0631\u0633\u06cc bidi=True; ' + ) + + @setup({'i18n32': '{% load i18n %}{{ "hu"|language_name }} ' + '{{ "hu"|language_name_local }} {{ "hu"|language_bidi }}'}) + def test_i18n32(self): + output = render('i18n32') + self.assertEqual(output, 'Hungarian Magyar False') + + @setup({'i18n33': '{% load i18n %}' + '{{ langcode|language_name }} {{ langcode|language_name_local }} ' + '{{ langcode|language_bidi }}'}) + def test_i18n33(self): + output = render('i18n33', {'langcode': 'nl'}) + self.assertEqual(output, 'Dutch Nederlands False') + + # blocktrans handling of variables which are not in the context. + # this should work as if blocktrans was not there (#19915) + @setup({'i18n34': '{% load i18n %}{% blocktrans %}{{ missing }}{% endblocktrans %}'}) + def test_i18n34(self): + output = render('i18n34') + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + @setup({'i18n34_2': '{% load i18n %}{% blocktrans with a=\'α\' %}{{ missing }}{% endblocktrans %}'}) + def test_i18n34_2(self): + output = render('i18n34_2') + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + @setup({'i18n34_3': '{% load i18n %}{% blocktrans with a=anton %}{{ missing }}{% endblocktrans %}'}) + def test_i18n34_3(self): + output = render('i18n34_3', {'anton': '\xce\xb1'}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + # trans tag with as var + @setup({'i18n35': '{% load i18n %}{% trans "Page not found" as page_not_found %}{{ page_not_found }}'}) + def test_i18n35(self): + output = render('i18n35', {'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, 'Seite nicht gefunden') + + @setup({'i18n36': '{% load i18n %}' + '{% trans "Page not found" noop as page_not_found %}{{ page_not_found }}'}) + def test_i18n36(self): + output = render('i18n36', {'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, 'Page not found') + + @setup({'i18n37': '{% load i18n %}' + '{% trans "Page not found" as page_not_found %}' + '{% blocktrans %}Error: {{ page_not_found }}{% endblocktrans %}'}) + def test_i18n37(self): + output = render('i18n37', {'LANGUAGE_CODE': 'de'}) + self.assertEqual(output, 'Error: Seite nicht gefunden') + + # Test whitespace in filter arguments + @setup({'i18n38': '{% load i18n custom %}' + '{% get_language_info for "de"|noop:"x y" as l %}' + '{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}'}) + def test_i18n38(self): + output = render('i18n38') + self.assertEqual(output, 'de: German/Deutsch bidi=False') + + @setup({'i18n38_2': '{% load i18n custom %}' + '{% get_language_info_list for langcodes|noop:"x y" as langs %}' + '{% for l in langs %}{{ l.code }}: {{ l.name }}/' + '{{ l.name_local }} bidi={{ l.bidi }}; {% endfor %}'}) + def test_i18n38_2(self): + output = render('i18n38_2', {'langcodes': ['it', 'no']}) + self.assertEqual(output, 'it: Italian/italiano bidi=False; no: Norwegian/norsk bidi=False; ') diff --git a/tests/template_tests/syntax_tests/test_if.py b/tests/template_tests/syntax_tests/test_if.py new file mode 100644 index 0000000000..0a8da7e120 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_if.py @@ -0,0 +1,524 @@ +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase + +from .utils import render, setup, TestObj + + +class IfTagTests(TestCase): + + @setup({'if-tag01': '{% if foo %}yes{% else %}no{% endif %}'}) + def test_if_tag01(self): + output = render('if-tag01', {'foo': True}) + self.assertEqual(output, 'yes') + + @setup({'if-tag02': '{% if foo %}yes{% else %}no{% endif %}'}) + def test_if_tag02(self): + output = render('if-tag02', {'foo': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag03': '{% if foo %}yes{% else %}no{% endif %}'}) + def test_if_tag03(self): + output = render('if-tag03') + self.assertEqual(output, 'no') + + @setup({'if-tag04': '{% if foo %}foo{% elif bar %}bar{% endif %}'}) + def test_if_tag04(self): + output = render('if-tag04', {'foo': True}) + self.assertEqual(output, 'foo') + + @setup({'if-tag05': '{% if foo %}foo{% elif bar %}bar{% endif %}'}) + def test_if_tag05(self): + output = render('if-tag05', {'bar': True}) + self.assertEqual(output, 'bar') + + @setup({'if-tag06': '{% if foo %}foo{% elif bar %}bar{% endif %}'}) + def test_if_tag06(self): + output = render('if-tag06') + self.assertEqual(output, '') + + @setup({'if-tag07': '{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}'}) + def test_if_tag07(self): + output = render('if-tag07', {'foo': True}) + self.assertEqual(output, 'foo') + + @setup({'if-tag08': '{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}'}) + def test_if_tag08(self): + output = render('if-tag08', {'bar': True}) + self.assertEqual(output, 'bar') + + @setup({'if-tag09': '{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}'}) + def test_if_tag09(self): + output = render('if-tag09') + self.assertEqual(output, 'nothing') + + @setup({'if-tag10': '{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}'}) + def test_if_tag10(self): + output = render('if-tag10', {'foo': True}) + self.assertEqual(output, 'foo') + + @setup({'if-tag11': '{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}'}) + def test_if_tag11(self): + output = render('if-tag11', {'bar': True}) + self.assertEqual(output, 'bar') + + @setup({'if-tag12': '{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}'}) + def test_if_tag12(self): + output = render('if-tag12', {'baz': True}) + self.assertEqual(output, 'baz') + + @setup({'if-tag13': '{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}'}) + def test_if_tag13(self): + output = render('if-tag13') + self.assertEqual(output, 'nothing') + + # Filters + @setup({'if-tag-filter01': '{% if foo|length == 5 %}yes{% else %}no{% endif %}'}) + def test_if_tag_filter01(self): + output = render('if-tag-filter01', {'foo': 'abcde'}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-filter02': '{% if foo|upper == \'ABC\' %}yes{% else %}no{% endif %}'}) + def test_if_tag_filter02(self): + output = render('if-tag-filter02') + self.assertEqual(output, 'no') + + # Equality + @setup({'if-tag-eq01': '{% if foo == bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_eq01(self): + output = render('if-tag-eq01') + self.assertEqual(output, 'yes') + + @setup({'if-tag-eq02': '{% if foo == bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_eq02(self): + output = render('if-tag-eq02', {'foo': 1}) + self.assertEqual(output, 'no') + + @setup({'if-tag-eq03': '{% if foo == bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_eq03(self): + output = render('if-tag-eq03', {'foo': 1, 'bar': 1}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-eq04': '{% if foo == bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_eq04(self): + output = render('if-tag-eq04', {'foo': 1, 'bar': 2}) + self.assertEqual(output, 'no') + + @setup({'if-tag-eq05': '{% if foo == \'\' %}yes{% else %}no{% endif %}'}) + def test_if_tag_eq05(self): + output = render('if-tag-eq05') + self.assertEqual(output, 'no') + + # Comparison + @setup({'if-tag-gt-01': '{% if 2 > 1 %}yes{% else %}no{% endif %}'}) + def test_if_tag_gt_01(self): + output = render('if-tag-gt-01') + self.assertEqual(output, 'yes') + + @setup({'if-tag-gt-02': '{% if 1 > 1 %}yes{% else %}no{% endif %}'}) + def test_if_tag_gt_02(self): + output = render('if-tag-gt-02') + self.assertEqual(output, 'no') + + @setup({'if-tag-gte-01': '{% if 1 >= 1 %}yes{% else %}no{% endif %}'}) + def test_if_tag_gte_01(self): + output = render('if-tag-gte-01') + self.assertEqual(output, 'yes') + + @setup({'if-tag-gte-02': '{% if 1 >= 2 %}yes{% else %}no{% endif %}'}) + def test_if_tag_gte_02(self): + output = render('if-tag-gte-02') + self.assertEqual(output, 'no') + + @setup({'if-tag-lt-01': '{% if 1 < 2 %}yes{% else %}no{% endif %}'}) + def test_if_tag_lt_01(self): + output = render('if-tag-lt-01') + self.assertEqual(output, 'yes') + + @setup({'if-tag-lt-02': '{% if 1 < 1 %}yes{% else %}no{% endif %}'}) + def test_if_tag_lt_02(self): + output = render('if-tag-lt-02') + self.assertEqual(output, 'no') + + @setup({'if-tag-lte-01': '{% if 1 <= 1 %}yes{% else %}no{% endif %}'}) + def test_if_tag_lte_01(self): + output = render('if-tag-lte-01') + self.assertEqual(output, 'yes') + + @setup({'if-tag-lte-02': '{% if 2 <= 1 %}yes{% else %}no{% endif %}'}) + def test_if_tag_lte_02(self): + output = render('if-tag-lte-02') + self.assertEqual(output, 'no') + + # Contains + @setup({'if-tag-in-01': '{% if 1 in x %}yes{% else %}no{% endif %}'}) + def test_if_tag_in_01(self): + output = render('if-tag-in-01', {'x': [1]}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-in-02': '{% if 2 in x %}yes{% else %}no{% endif %}'}) + def test_if_tag_in_02(self): + output = render('if-tag-in-02', {'x': [1]}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not-in-01': '{% if 1 not in x %}yes{% else %}no{% endif %}'}) + def test_if_tag_not_in_01(self): + output = render('if-tag-not-in-01', {'x': [1]}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not-in-02': '{% if 2 not in x %}yes{% else %}no{% endif %}'}) + def test_if_tag_not_in_02(self): + output = render('if-tag-not-in-02', {'x': [1]}) + self.assertEqual(output, 'yes') + + # AND + @setup({'if-tag-and01': '{% if foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_and01(self): + output = render('if-tag-and01', {'foo': True, 'bar': True}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-and02': '{% if foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_and02(self): + output = render('if-tag-and02', {'foo': True, 'bar': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag-and03': '{% if foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_and03(self): + output = render('if-tag-and03', {'foo': False, 'bar': True}) + self.assertEqual(output, 'no') + + @setup({'if-tag-and04': '{% if foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_and04(self): + output = render('if-tag-and04', {'foo': False, 'bar': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag-and05': '{% if foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_and05(self): + output = render('if-tag-and05', {'foo': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag-and06': '{% if foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_and06(self): + output = render('if-tag-and06', {'bar': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag-and07': '{% if foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_and07(self): + output = render('if-tag-and07', {'foo': True}) + self.assertEqual(output, 'no') + + @setup({'if-tag-and08': '{% if foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_and08(self): + output = render('if-tag-and08', {'bar': True}) + self.assertEqual(output, 'no') + + # OR + @setup({'if-tag-or01': '{% if foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_or01(self): + output = render('if-tag-or01', {'foo': True, 'bar': True}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-or02': '{% if foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_or02(self): + output = render('if-tag-or02', {'foo': True, 'bar': False}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-or03': '{% if foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_or03(self): + output = render('if-tag-or03', {'foo': False, 'bar': True}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-or04': '{% if foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_or04(self): + output = render('if-tag-or04', {'foo': False, 'bar': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag-or05': '{% if foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_or05(self): + output = render('if-tag-or05', {'foo': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag-or06': '{% if foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_or06(self): + output = render('if-tag-or06', {'bar': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag-or07': '{% if foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_or07(self): + output = render('if-tag-or07', {'foo': True}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-or08': '{% if foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_or08(self): + output = render('if-tag-or08', {'bar': True}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-or09': '{% if foo or bar or baz %}yes{% else %}no{% endif %}'}) + def test_if_tag_or09(self): + """ + multiple ORs + """ + output = render('if-tag-or09', {'baz': True}) + self.assertEqual(output, 'yes') + + # NOT + @setup({'if-tag-not01': '{% if not foo %}no{% else %}yes{% endif %}'}) + def test_if_tag_not01(self): + output = render('if-tag-not01', {'foo': True}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-not02': '{% if not not foo %}no{% else %}yes{% endif %}'}) + def test_if_tag_not02(self): + output = render('if-tag-not02', {'foo': True}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not06': '{% if foo and not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not06(self): + output = render('if-tag-not06') + self.assertEqual(output, 'no') + + @setup({'if-tag-not07': '{% if foo and not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not07(self): + output = render('if-tag-not07', {'foo': True, 'bar': True}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not08': '{% if foo and not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not08(self): + output = render('if-tag-not08', {'foo': True, 'bar': False}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-not09': '{% if foo and not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not09(self): + output = render('if-tag-not09', {'foo': False, 'bar': True}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not10': '{% if foo and not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not10(self): + output = render('if-tag-not10', {'foo': False, 'bar': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not11': '{% if not foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not11(self): + output = render('if-tag-not11') + self.assertEqual(output, 'no') + + @setup({'if-tag-not12': '{% if not foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not12(self): + output = render('if-tag-not12', {'foo': True, 'bar': True}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not13': '{% if not foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not13(self): + output = render('if-tag-not13', {'foo': True, 'bar': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not14': '{% if not foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not14(self): + output = render('if-tag-not14', {'foo': False, 'bar': True}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-not15': '{% if not foo and bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not15(self): + output = render('if-tag-not15', {'foo': False, 'bar': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not16': '{% if foo or not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not16(self): + output = render('if-tag-not16') + self.assertEqual(output, 'yes') + + @setup({'if-tag-not17': '{% if foo or not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not17(self): + output = render('if-tag-not17', {'foo': True, 'bar': True}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-not18': '{% if foo or not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not18(self): + output = render('if-tag-not18', {'foo': True, 'bar': False}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-not19': '{% if foo or not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not19(self): + output = render('if-tag-not19', {'foo': False, 'bar': True}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not20': '{% if foo or not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not20(self): + output = render('if-tag-not20', {'foo': False, 'bar': False}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-not21': '{% if not foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not21(self): + output = render('if-tag-not21') + self.assertEqual(output, 'yes') + + @setup({'if-tag-not22': '{% if not foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not22(self): + output = render('if-tag-not22', {'foo': True, 'bar': True}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-not23': '{% if not foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not23(self): + output = render('if-tag-not23', {'foo': True, 'bar': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not24': '{% if not foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not24(self): + output = render('if-tag-not24', {'foo': False, 'bar': True}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-not25': '{% if not foo or bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not25(self): + output = render('if-tag-not25', {'foo': False, 'bar': False}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-not26': '{% if not foo and not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not26(self): + output = render('if-tag-not26') + self.assertEqual(output, 'yes') + + @setup({'if-tag-not27': '{% if not foo and not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not27(self): + output = render('if-tag-not27', {'foo': True, 'bar': True}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not28': '{% if not foo and not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not28(self): + output = render('if-tag-not28', {'foo': True, 'bar': False}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not29': '{% if not foo and not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not29(self): + output = render('if-tag-not29', {'foo': False, 'bar': True}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not30': '{% if not foo and not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not30(self): + output = render('if-tag-not30', {'foo': False, 'bar': False}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-not31': '{% if not foo or not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not31(self): + output = render('if-tag-not31') + self.assertEqual(output, 'yes') + + @setup({'if-tag-not32': '{% if not foo or not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not32(self): + output = render('if-tag-not32', {'foo': True, 'bar': True}) + self.assertEqual(output, 'no') + + @setup({'if-tag-not33': '{% if not foo or not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not33(self): + output = render('if-tag-not33', {'foo': True, 'bar': False}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-not34': '{% if not foo or not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not34(self): + output = render('if-tag-not34', {'foo': False, 'bar': True}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-not35': '{% if not foo or not bar %}yes{% else %}no{% endif %}'}) + def test_if_tag_not35(self): + output = render('if-tag-not35', {'foo': False, 'bar': False}) + self.assertEqual(output, 'yes') + + # Various syntax errors + @setup({'if-tag-error01': '{% if %}yes{% endif %}'}) + def test_if_tag_error01(self): + with self.assertRaises(TemplateSyntaxError): + get_template('if-tag-error01') + + @setup({'if-tag-error02': '{% if foo and %}yes{% else %}no{% endif %}'}) + def test_if_tag_error02(self): + with self.assertRaises(TemplateSyntaxError): + render('if-tag-error02', {'foo': True}) + + @setup({'if-tag-error03': '{% if foo or %}yes{% else %}no{% endif %}'}) + def test_if_tag_error03(self): + with self.assertRaises(TemplateSyntaxError): + render('if-tag-error03', {'foo': True}) + + @setup({'if-tag-error04': '{% if not foo and %}yes{% else %}no{% endif %}'}) + def test_if_tag_error04(self): + with self.assertRaises(TemplateSyntaxError): + render('if-tag-error04', {'foo': True}) + + @setup({'if-tag-error05': '{% if not foo or %}yes{% else %}no{% endif %}'}) + def test_if_tag_error05(self): + with self.assertRaises(TemplateSyntaxError): + render('if-tag-error05', {'foo': True}) + + @setup({'if-tag-error06': '{% if abc def %}yes{% endif %}'}) + def test_if_tag_error06(self): + with self.assertRaises(TemplateSyntaxError): + get_template('if-tag-error06') + + @setup({'if-tag-error07': '{% if not %}yes{% endif %}'}) + def test_if_tag_error07(self): + with self.assertRaises(TemplateSyntaxError): + get_template('if-tag-error07') + + @setup({'if-tag-error08': '{% if and %}yes{% endif %}'}) + def test_if_tag_error08(self): + with self.assertRaises(TemplateSyntaxError): + get_template('if-tag-error08') + + @setup({'if-tag-error09': '{% if or %}yes{% endif %}'}) + def test_if_tag_error09(self): + with self.assertRaises(TemplateSyntaxError): + get_template('if-tag-error09') + + @setup({'if-tag-error10': '{% if == %}yes{% endif %}'}) + def test_if_tag_error10(self): + with self.assertRaises(TemplateSyntaxError): + get_template('if-tag-error10') + + @setup({'if-tag-error11': '{% if 1 == %}yes{% endif %}'}) + def test_if_tag_error11(self): + with self.assertRaises(TemplateSyntaxError): + get_template('if-tag-error11') + + @setup({'if-tag-error12': '{% if a not b %}yes{% endif %}'}) + def test_if_tag_error12(self): + with self.assertRaises(TemplateSyntaxError): + get_template('if-tag-error12') + + @setup({'if-tag-shortcircuit01': '{% if x.is_true or x.is_bad %}yes{% else %}no{% endif %}'}) + def test_if_tag_shortcircuit01(self): + """ + If evaluations are shortcircuited where possible + """ + output = render('if-tag-shortcircuit01', {'x': TestObj()}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-shortcircuit02': '{% if x.is_false and x.is_bad %}yes{% else %}no{% endif %}'}) + def test_if_tag_shortcircuit02(self): + """ + The is_bad() function should not be evaluated. If it is, an + exception is raised. + """ + output = render('if-tag-shortcircuit02', {'x': TestObj()}) + self.assertEqual(output, 'no') + + @setup({'if-tag-badarg01': '{% if x|default_if_none:y %}yes{% endif %}'}) + def test_if_tag_badarg01(self): + """ + Non-existent args + """ + output = render('if-tag-badarg01') + self.assertEqual(output, '') + + @setup({'if-tag-badarg02': '{% if x|default_if_none:y %}yes{% endif %}'}) + def test_if_tag_badarg02(self): + output = render('if-tag-badarg02', {'y': 0}) + self.assertEqual(output, '') + + @setup({'if-tag-badarg03': '{% if x|default_if_none:y %}yes{% endif %}'}) + def test_if_tag_badarg03(self): + output = render('if-tag-badarg03', {'y': 1}) + self.assertEqual(output, 'yes') + + @setup({'if-tag-badarg04': '{% if x|default_if_none:y %}yes{% else %}no{% endif %}'}) + def test_if_tag_badarg04(self): + output = render('if-tag-badarg04') + self.assertEqual(output, 'no') diff --git a/tests/template_tests/syntax_tests/test_if_changed.py b/tests/template_tests/syntax_tests/test_if_changed.py new file mode 100644 index 0000000000..446712dfd3 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_if_changed.py @@ -0,0 +1,154 @@ +from django.test import TestCase + +from .utils import render, setup + + +class IfChangedTagTests(TestCase): + + @setup({'ifchanged01': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}'}) + def test_ifchanged01(self): + output = render('ifchanged01', {'num': (1, 2, 3)}) + self.assertEqual(output, '123') + + @setup({'ifchanged02': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}'}) + def test_ifchanged02(self): + output = render('ifchanged02', {'num': (1, 1, 3)}) + self.assertEqual(output, '13') + + @setup({'ifchanged03': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}'}) + def test_ifchanged03(self): + output = render('ifchanged03', {'num': (1, 1, 1)}) + self.assertEqual(output, '1') + + @setup({'ifchanged04': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}' + '{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}' + '{% endfor %}{% endfor %}'}) + def test_ifchanged04(self): + output = render('ifchanged04', {'num': (1, 2, 3), 'numx': (2, 2, 2)}) + self.assertEqual(output, '122232') + + @setup({'ifchanged05': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}' + '{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}' + '{% endfor %}{% endfor %}'}) + def test_ifchanged05(self): + output = render('ifchanged05', {'num': (1, 1, 1), 'numx': (1, 2, 3)}) + self.assertEqual(output, '1123123123') + + @setup({'ifchanged06': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}' + '{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}' + '{% endfor %}{% endfor %}'}) + def test_ifchanged06(self): + output = render('ifchanged06', {'num': (1, 1, 1), 'numx': (2, 2, 2)}) + self.assertEqual(output, '1222') + + @setup({'ifchanged07': '{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}' + '{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}' + '{% for y in numy %}{% ifchanged %}{{ y }}{% endifchanged %}' + '{% endfor %}{% endfor %}{% endfor %}'}) + def test_ifchanged07(self): + output = render('ifchanged07', {'num': (1, 1, 1), 'numx': (2, 2, 2), 'numy': (3, 3, 3)}) + self.assertEqual(output, '1233323332333') + + @setup({'ifchanged08': '{% for data in datalist %}{% for c,d in data %}' + '{% if c %}{% ifchanged %}{{ d }}{% endifchanged %}' + '{% endif %}{% endfor %}{% endfor %}'}) + def test_ifchanged08(self): + output = render('ifchanged08', {'datalist': [ + [(1, 'a'), (1, 'a'), (0, 'b'), (1, 'c')], + [(0, 'a'), (1, 'c'), (1, 'd'), (1, 'd'), (0, 'e')] + ]}) + self.assertEqual(output, 'accd') + + @setup({'ifchanged-param01': '{% for n in num %}{% ifchanged n %}..{% endifchanged %}' + '{{ n }}{% endfor %}'}) + def test_ifchanged_param01(self): + """ + Test one parameter given to ifchanged. + """ + output = render('ifchanged-param01', {'num': (1, 2, 3)}) + self.assertEqual(output, '..1..2..3') + + @setup({'ifchanged-param02': '{% for n in num %}{% for x in numx %}{% ifchanged n %}..{% endifchanged %}' + '{{ x }}{% endfor %}{% endfor %}'}) + def test_ifchanged_param02(self): + output = render('ifchanged-param02', {'num': (1, 2, 3), 'numx': (5, 6, 7)}) + self.assertEqual(output, '..567..567..567') + + @setup({'ifchanged-param03': '{% for n in num %}{{ n }}{% for x in numx %}' + '{% ifchanged x n %}{{ x }}{% endifchanged %}' + '{% endfor %}{% endfor %}'}) + def test_ifchanged_param03(self): + """ + Test multiple parameters to ifchanged. + """ + output = render('ifchanged-param03', {'num': (1, 1, 2), 'numx': (5, 6, 6)}) + self.assertEqual(output, '156156256') + + @setup({'ifchanged-param04': '{% for d in days %}{% ifchanged %}{{ d.day }}{% endifchanged %}' + '{% for h in d.hours %}{% ifchanged d h %}{{ h }}{% endifchanged %}' + '{% endfor %}{% endfor %}'}) + def test_ifchanged_param04(self): + """ + Test a date+hour like construct, where the hour of the last day is + the same but the date had changed, so print the hour anyway. + """ + output = render( + 'ifchanged-param04', + {'days': [{'hours': [1, 2, 3], 'day': 1}, {'hours': [3], 'day': 2}]}, + ) + self.assertEqual(output, '112323') + + @setup({'ifchanged-param05': '{% for d in days %}{% ifchanged d.day %}{{ d.day }}{% endifchanged %}' + '{% for h in d.hours %}{% ifchanged d.day h %}{{ h }}{% endifchanged %}' + '{% endfor %}{% endfor %}'}) + def test_ifchanged_param05(self): + """ + Logically the same as above, just written with explicit ifchanged + for the day. + """ + output = render( + 'ifchanged-param05', + {'days': [{'hours': [1, 2, 3], 'day': 1}, {'hours': [3], 'day': 2}]}, + ) + self.assertEqual(output, '112323') + + @setup({'ifchanged-else01': '{% for id in ids %}{{ id }}' + '{% ifchanged id %}-first{% else %}-other{% endifchanged %}' + ',{% endfor %}'}) + def test_ifchanged_else01(self): + """ + Test the else clause of ifchanged. + """ + output = render('ifchanged-else01', {'ids': [1, 1, 2, 2, 2, 3]}) + self.assertEqual(output, '1-first,1-other,2-first,2-other,2-other,3-first,') + + @setup({'ifchanged-else02': '{% for id in ids %}{{ id }}-' + '{% ifchanged id %}{% cycle red,blue %}{% else %}grey{% endifchanged %}' + ',{% endfor %}'}) + def test_ifchanged_else02(self): + output = render('ifchanged-else02', {'ids': [1, 1, 2, 2, 2, 3]}) + self.assertEqual(output, '1-red,1-grey,2-blue,2-grey,2-grey,3-red,') + + @setup({'ifchanged-else03': '{% for id in ids %}{{ id }}' + '{% ifchanged id %}-{% cycle red,blue %}{% else %}{% endifchanged %}' + ',{% endfor %}'}) + def test_ifchanged_else03(self): + output = render('ifchanged-else03', {'ids': [1, 1, 2, 2, 2, 3]}) + self.assertEqual(output, '1-red,1,2-blue,2,2,3-red,') + + @setup({'ifchanged-else04': '{% for id in ids %}' + '{% ifchanged %}***{{ id }}*{% else %}...{% endifchanged %}' + '{{ forloop.counter }}{% endfor %}'}) + def test_ifchanged_else04(self): + output = render('ifchanged-else04', {'ids': [1, 1, 2, 2, 2, 3, 4]}) + self.assertEqual(output, '***1*1...2***2*3...4...5***3*6***4*7') + + @setup({'ifchanged-filter-ws': '{% load custom %}{% for n in num %}' + '{% ifchanged n|noop:"x y" %}..{% endifchanged %}{{ n }}' + '{% endfor %}'}) + def test_ifchanged_filter_ws(self): + """ + Test whitespace in filter arguments + """ + output = render('ifchanged-filter-ws', {'num': (1, 2, 3)}) + self.assertEqual(output, '..1..2..3') diff --git a/tests/template_tests/syntax_tests/test_if_equal.py b/tests/template_tests/syntax_tests/test_if_equal.py new file mode 100644 index 0000000000..a4aba43f96 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_if_equal.py @@ -0,0 +1,217 @@ +from django.test import TestCase + +from .utils import render, setup + + +class IfEqualTagTests(TestCase): + + @setup({'ifequal01': '{% ifequal a b %}yes{% endifequal %}'}) + def test_ifequal01(self): + output = render('ifequal01', {'a': 1, 'b': 2}) + self.assertEqual(output, '') + + @setup({'ifequal02': '{% ifequal a b %}yes{% endifequal %}'}) + def test_ifequal02(self): + output = render('ifequal02', {'a': 1, 'b': 1}) + self.assertEqual(output, 'yes') + + @setup({'ifequal03': '{% ifequal a b %}yes{% else %}no{% endifequal %}'}) + def test_ifequal03(self): + output = render('ifequal03', {'a': 1, 'b': 2}) + self.assertEqual(output, 'no') + + @setup({'ifequal04': '{% ifequal a b %}yes{% else %}no{% endifequal %}'}) + def test_ifequal04(self): + output = render('ifequal04', {'a': 1, 'b': 1}) + self.assertEqual(output, 'yes') + + @setup({'ifequal05': '{% ifequal a \'test\' %}yes{% else %}no{% endifequal %}'}) + def test_ifequal05(self): + output = render('ifequal05', {'a': 'test'}) + self.assertEqual(output, 'yes') + + @setup({'ifequal06': '{% ifequal a \'test\' %}yes{% else %}no{% endifequal %}'}) + def test_ifequal06(self): + output = render('ifequal06', {'a': 'no'}) + self.assertEqual(output, 'no') + + @setup({'ifequal07': '{% ifequal a "test" %}yes{% else %}no{% endifequal %}'}) + def test_ifequal07(self): + output = render('ifequal07', {'a': 'test'}) + self.assertEqual(output, 'yes') + + @setup({'ifequal08': '{% ifequal a "test" %}yes{% else %}no{% endifequal %}'}) + def test_ifequal08(self): + output = render('ifequal08', {'a': 'no'}) + self.assertEqual(output, 'no') + + @setup({'ifequal09': '{% ifequal a "test" %}yes{% else %}no{% endifequal %}'}) + def test_ifequal09(self): + output = render('ifequal09') + self.assertEqual(output, 'no') + + @setup({'ifequal10': '{% ifequal a b %}yes{% else %}no{% endifequal %}'}) + def test_ifequal10(self): + output = render('ifequal10') + self.assertEqual(output, 'yes') + + # SMART SPLITTING + @setup({'ifequal-split01': '{% ifequal a "test man" %}yes{% else %}no{% endifequal %}'}) + def test_ifequal_split01(self): + output = render('ifequal-split01') + self.assertEqual(output, 'no') + + @setup({'ifequal-split02': '{% ifequal a "test man" %}yes{% else %}no{% endifequal %}'}) + def test_ifequal_split02(self): + output = render('ifequal-split02', {'a': 'foo'}) + self.assertEqual(output, 'no') + + @setup({'ifequal-split03': '{% ifequal a "test man" %}yes{% else %}no{% endifequal %}'}) + def test_ifequal_split03(self): + output = render('ifequal-split03', {'a': 'test man'}) + self.assertEqual(output, 'yes') + + @setup({'ifequal-split04': '{% ifequal a \'test man\' %}yes{% else %}no{% endifequal %}'}) + def test_ifequal_split04(self): + output = render('ifequal-split04', {'a': 'test man'}) + self.assertEqual(output, 'yes') + + @setup({'ifequal-split05': '{% ifequal a \'i "love" you\' %}yes{% else %}no{% endifequal %}'}) + def test_ifequal_split05(self): + output = render('ifequal-split05', {'a': ''}) + self.assertEqual(output, 'no') + + @setup({'ifequal-split06': '{% ifequal a \'i "love" you\' %}yes{% else %}no{% endifequal %}'}) + def test_ifequal_split06(self): + output = render('ifequal-split06', {'a': 'i "love" you'}) + self.assertEqual(output, 'yes') + + @setup({'ifequal-split07': '{% ifequal a \'i "love" you\' %}yes{% else %}no{% endifequal %}'}) + def test_ifequal_split07(self): + output = render('ifequal-split07', {'a': 'i love you'}) + self.assertEqual(output, 'no') + + @setup({'ifequal-split08': r"{% ifequal a 'I\'m happy' %}yes{% else %}no{% endifequal %}"}) + def test_ifequal_split08(self): + output = render('ifequal-split08', {'a': "I'm happy"}) + self.assertEqual(output, 'yes') + + @setup({'ifequal-split09': r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}"}) + def test_ifequal_split09(self): + output = render('ifequal-split09', {'a': 'slash\man'}) + self.assertEqual(output, 'yes') + + @setup({'ifequal-split10': r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}"}) + def test_ifequal_split10(self): + output = render('ifequal-split10', {'a': 'slashman'}) + self.assertEqual(output, 'no') + + # NUMERIC RESOLUTION + @setup({'ifequal-numeric01': '{% ifequal x 5 %}yes{% endifequal %}'}) + def test_ifequal_numeric01(self): + output = render('ifequal-numeric01', {'x': '5'}) + self.assertEqual(output, '') + + @setup({'ifequal-numeric02': '{% ifequal x 5 %}yes{% endifequal %}'}) + def test_ifequal_numeric02(self): + output = render('ifequal-numeric02', {'x': 5}) + self.assertEqual(output, 'yes') + + @setup({'ifequal-numeric03': '{% ifequal x 5.2 %}yes{% endifequal %}'}) + def test_ifequal_numeric03(self): + output = render('ifequal-numeric03', {'x': 5}) + self.assertEqual(output, '') + + @setup({'ifequal-numeric04': '{% ifequal x 5.2 %}yes{% endifequal %}'}) + def test_ifequal_numeric04(self): + output = render('ifequal-numeric04', {'x': 5.2}) + self.assertEqual(output, 'yes') + + @setup({'ifequal-numeric05': '{% ifequal x 0.2 %}yes{% endifequal %}'}) + def test_ifequal_numeric05(self): + output = render('ifequal-numeric05', {'x': 0.2}) + self.assertEqual(output, 'yes') + + @setup({'ifequal-numeric06': '{% ifequal x .2 %}yes{% endifequal %}'}) + def test_ifequal_numeric06(self): + output = render('ifequal-numeric06', {'x': 0.2}) + self.assertEqual(output, 'yes') + + @setup({'ifequal-numeric07': '{% ifequal x 2. %}yes{% endifequal %}'}) + def test_ifequal_numeric07(self): + output = render('ifequal-numeric07', {'x': 2}) + self.assertEqual(output, '') + + @setup({'ifequal-numeric08': '{% ifequal x "5" %}yes{% endifequal %}'}) + def test_ifequal_numeric08(self): + output = render('ifequal-numeric08', {'x': 5}) + self.assertEqual(output, '') + + @setup({'ifequal-numeric09': '{% ifequal x "5" %}yes{% endifequal %}'}) + def test_ifequal_numeric09(self): + output = render('ifequal-numeric09', {'x': '5'}) + self.assertEqual(output, 'yes') + + @setup({'ifequal-numeric10': '{% ifequal x -5 %}yes{% endifequal %}'}) + def test_ifequal_numeric10(self): + output = render('ifequal-numeric10', {'x': -5}) + self.assertEqual(output, 'yes') + + @setup({'ifequal-numeric11': '{% ifequal x -5.2 %}yes{% endifequal %}'}) + def test_ifequal_numeric11(self): + output = render('ifequal-numeric11', {'x': -5.2}) + self.assertEqual(output, 'yes') + + @setup({'ifequal-numeric12': '{% ifequal x +5 %}yes{% endifequal %}'}) + def test_ifequal_numeric12(self): + output = render('ifequal-numeric12', {'x': 5}) + self.assertEqual(output, 'yes') + + # FILTER EXPRESSIONS AS ARGUMENTS + @setup({'ifequal-filter01': '{% ifequal a|upper "A" %}x{% endifequal %}'}) + def test_ifequal_filter01(self): + output = render('ifequal-filter01', {'a': 'a'}) + self.assertEqual(output, 'x') + + @setup({'ifequal-filter02': '{% ifequal "A" a|upper %}x{% endifequal %}'}) + def test_ifequal_filter02(self): + output = render('ifequal-filter02', {'a': 'a'}) + self.assertEqual(output, 'x') + + @setup({'ifequal-filter03': '{% ifequal a|upper b|upper %}x{% endifequal %}'}) + def test_ifequal_filter03(self): + output = render('ifequal-filter03', {'a': 'x', 'b': 'X'}) + self.assertEqual(output, 'x') + + @setup({'ifequal-filter04': '{% ifequal x|slice:"1" "a" %}x{% endifequal %}'}) + def test_ifequal_filter04(self): + output = render('ifequal-filter04', {'x': 'aaa'}) + self.assertEqual(output, 'x') + + @setup({'ifequal-filter05': '{% ifequal x|slice:"1"|upper "A" %}x{% endifequal %}'}) + def test_ifequal_filter05(self): + output = render('ifequal-filter05', {'x': 'aaa'}) + self.assertEqual(output, 'x') + + +class IfNotEqualTagTests(TestCase): + + @setup({'ifnotequal01': '{% ifnotequal a b %}yes{% endifnotequal %}'}) + def test_ifnotequal01(self): + output = render('ifnotequal01', {'a': 1, 'b': 2}) + self.assertEqual(output, 'yes') + + @setup({'ifnotequal02': '{% ifnotequal a b %}yes{% endifnotequal %}'}) + def test_ifnotequal02(self): + output = render('ifnotequal02', {'a': 1, 'b': 1}) + self.assertEqual(output, '') + + @setup({'ifnotequal03': '{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}'}) + def test_ifnotequal03(self): + output = render('ifnotequal03', {'a': 1, 'b': 2}) + self.assertEqual(output, 'yes') + + @setup({'ifnotequal04': '{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}'}) + def test_ifnotequal04(self): + output = render('ifnotequal04', {'a': 1, 'b': 1}) + self.assertEqual(output, 'no') diff --git a/tests/template_tests/syntax_tests/test_include.py b/tests/template_tests/syntax_tests/test_include.py new file mode 100644 index 0000000000..8aef0da35e --- /dev/null +++ b/tests/template_tests/syntax_tests/test_include.py @@ -0,0 +1,204 @@ +from django.conf import settings +from django.template.base import Context, TemplateDoesNotExist, TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase + +from .test_basic import basic_templates +from .utils import render, setup + + +include_fail_templates = { + 'include-fail1': '{% load bad_tag %}{% badtag %}', + 'include-fail2': '{% load broken_tag %}', +} + + +class IncludeTagTests(TestCase): + + @setup({'include01': '{% include "basic-syntax01" %}'}, basic_templates) + def test_include01(self): + output = render('include01') + self.assertEqual(output, 'something cool') + + @setup({'include02': '{% include "basic-syntax02" %}'}, basic_templates) + def test_include02(self): + output = render('include02', {'headline': 'Included'}) + self.assertEqual(output, 'Included') + + @setup({'include03': '{% include template_name %}'}, basic_templates) + def test_include03(self): + output = render( + 'include03', + {'template_name': 'basic-syntax02', 'headline': 'Included'}, + ) + self.assertEqual(output, 'Included') + + @setup({'include04': 'a{% include "nonexistent" %}b'}) + def test_include04(self): + template = get_template('include04') + + if settings.TEMPLATE_DEBUG: + with self.assertRaises(TemplateDoesNotExist): + template.render(Context({})) + else: + output = template.render(Context({})) + self.assertEqual(output, "ab") + + @setup({ + 'include 05': 'template with a space', + 'include06': '{% include "include 05"%}', + }) + def test_include06(self): + output = render('include06') + self.assertEqual(output, "template with a space") + + @setup({'include07': '{% include "basic-syntax02" with headline="Inline" %}'}, basic_templates) + def test_include07(self): + output = render('include07', {'headline': 'Included'}) + self.assertEqual(output, 'Inline') + + @setup({'include08': '{% include headline with headline="Dynamic" %}'}, basic_templates) + def test_include08(self): + output = render('include08', {'headline': 'basic-syntax02'}) + self.assertEqual(output, 'Dynamic') + + @setup( + {'include09': '{{ first }}--' + '{% include "basic-syntax03" with first=second|lower|upper second=first|upper %}' + '--{{ second }}'}, + basic_templates, + ) + def test_include09(self): + output = render('include09', {'first': 'Ul', 'second': 'lU'}) + self.assertEqual(output, 'Ul--LU --- UL--lU') + + @setup({'include10': '{% include "basic-syntax03" only %}'}, basic_templates) + def test_include10(self): + output = render('include10', {'first': '1'}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID --- INVALID') + else: + self.assertEqual(output, ' --- ') + + @setup({'include11': '{% include "basic-syntax03" only with second=2 %}'}, basic_templates) + def test_include11(self): + output = render('include11', {'first': '1'}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID --- 2') + else: + self.assertEqual(output, ' --- 2') + + @setup({'include12': '{% include "basic-syntax03" with first=1 only %}'}, basic_templates) + def test_include12(self): + output = render('include12', {'second': '2'}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, '1 --- INVALID') + else: + self.assertEqual(output, '1 --- ') + + @setup( + {'include13': '{% autoescape off %}{% include "basic-syntax03" %}{% endautoescape %}'}, + basic_templates, + ) + def test_include13(self): + output = render('include13', {'first': '&'}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, '& --- INVALID') + else: + self.assertEqual(output, '& --- ') + + @setup( + {'include14': '{% autoescape off %}' + '{% include "basic-syntax03" with first=var1 only %}' + '{% endautoescape %}'}, + basic_templates, + ) + def test_include14(self): + output = render('include14', {'var1': '&'}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, '& --- INVALID') + else: + self.assertEqual(output, '& --- ') + + # Include syntax errors + @setup({'include-error01': '{% include "basic-syntax01" with %}'}) + def test_include_error01(self): + with self.assertRaises(TemplateSyntaxError): + get_template('include-error01') + + @setup({'include-error02': '{% include "basic-syntax01" with "no key" %}'}) + def test_include_error02(self): + with self.assertRaises(TemplateSyntaxError): + get_template('include-error02') + + @setup({'include-error03': '{% include "basic-syntax01" with dotted.arg="error" %}'}) + def test_include_error03(self): + with self.assertRaises(TemplateSyntaxError): + get_template('include-error03') + + @setup({'include-error04': '{% include "basic-syntax01" something_random %}'}) + def test_include_error04(self): + with self.assertRaises(TemplateSyntaxError): + get_template('include-error04') + + @setup({'include-error05': '{% include "basic-syntax01" foo="duplicate" foo="key" %}'}) + def test_include_error05(self): + with self.assertRaises(TemplateSyntaxError): + get_template('include-error05') + + @setup({'include-error06': '{% include "basic-syntax01" only only %}'}) + def test_include_error06(self): + with self.assertRaises(TemplateSyntaxError): + get_template('include-error06') + + @setup(include_fail_templates) + def test_include_fail1(self): + with self.assertRaises(RuntimeError): + get_template('include-fail1') + + @setup(include_fail_templates) + def test_include_fail2(self): + with self.assertRaises(TemplateSyntaxError): + get_template('include-fail2') + + @setup({'include-error07': '{% include "include-fail1" %}'}, include_fail_templates) + def test_include_error07(self): + t = get_template('include-error07') + + if settings.TEMPLATE_DEBUG: + with self.assertRaises(RuntimeError): + t.render(Context()) + else: + self.assertEqual(t.render(Context()), '') + + @setup({'include-error08': '{% include "include-fail2" %}'}, include_fail_templates) + def test_include_error08(self): + t = get_template('include-error08') + + if settings.TEMPLATE_DEBUG: + with self.assertRaises(TemplateSyntaxError): + t.render(Context()) + else: + self.assertEqual(t.render(Context()), '') + + @setup({'include-error09': '{% include failed_include %}'}, include_fail_templates) + def test_include_error09(self): + c = Context({'failed_include': 'include-fail1'}) + t = get_template('include-error09') + + if settings.TEMPLATE_DEBUG: + with self.assertRaises(RuntimeError): + t.render(c) + else: + self.assertEqual(t.render(c), '') + + @setup({'include-error10': '{% include failed_include %}'}, include_fail_templates) + def test_include_error10(self): + c = Context({'failed_include': 'include-fail2'}) + t = get_template('include-error10') + + if settings.TEMPLATE_DEBUG: + with self.assertRaises(TemplateSyntaxError): + t.render(c) + else: + self.assertEqual(t.render(c), '') diff --git a/tests/template_tests/syntax_tests/test_invalid_string.py b/tests/template_tests/syntax_tests/test_invalid_string.py new file mode 100644 index 0000000000..5aadbdbcfa --- /dev/null +++ b/tests/template_tests/syntax_tests/test_invalid_string.py @@ -0,0 +1,62 @@ +from django.conf import settings +from django.test import TestCase + +from .utils import render, setup + + +class InvalidStringTests(TestCase): + + @setup({'invalidstr01': '{{ var|default:"Foo" }}'}) + def test_invalidstr01(self): + output = render('invalidstr01') + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, 'Foo') + + @setup({'invalidstr02': '{{ var|default_if_none:"Foo" }}'}) + def test_invalidstr02(self): + output = render('invalidstr02') + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + @setup({'invalidstr03': '{% for v in var %}({{ v }}){% endfor %}'}) + def test_invalidstr03(self): + output = render('invalidstr03') + self.assertEqual(output, '') + + @setup({'invalidstr04': '{% if var %}Yes{% else %}No{% endif %}'}) + def test_invalidstr04(self): + output = render('invalidstr04') + self.assertEqual(output, 'No') + + @setup({'invalidstr04_2': '{% if var|default:"Foo" %}Yes{% else %}No{% endif %}'}) + def test_invalidstr04_2(self): + output = render('invalidstr04_2') + self.assertEqual(output, 'Yes') + + @setup({'invalidstr05': '{{ var }}'}) + def test_invalidstr05(self): + output = render('invalidstr05') + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + @setup({'invalidstr06': '{{ var.prop }}'}) + def test_invalidstr06(self): + output = render('invalidstr06') + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + @setup({'invalidstr07': '{% load i18n %}{% blocktrans %}{{ var }}{% endblocktrans %}'}) + def test_invalidstr07(self): + output = render('invalidstr07') + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') diff --git a/tests/template_tests/syntax_tests/test_list_index.py b/tests/template_tests/syntax_tests/test_list_index.py new file mode 100644 index 0000000000..4fc7b34d1a --- /dev/null +++ b/tests/template_tests/syntax_tests/test_list_index.py @@ -0,0 +1,76 @@ +from django.conf import settings +from django.test import TestCase + +from .utils import render, setup + + +class ListIndexTests(TestCase): + + @setup({'list-index01': '{{ var.1 }}'}) + def test_list_index01(self): + """ + List-index syntax allows a template to access a certain item of a + subscriptable object. + """ + output = render('list-index01', {'var': ['first item', 'second item']}) + self.assertEqual(output, 'second item') + + @setup({'list-index02': '{{ var.5 }}'}) + def test_list_index02(self): + """ + Fail silently when the list index is out of range. + """ + output = render('list-index02', {'var': ['first item', 'second item']}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + @setup({'list-index03': '{{ var.1 }}'}) + def test_list_index03(self): + """ + Fail silently when the list index is out of range. + """ + output = render('list-index03', {'var': None}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + @setup({'list-index04': '{{ var.1 }}'}) + def test_list_index04(self): + """ + Fail silently when variable is a dict without the specified key. + """ + output = render('list-index04', {'var': {}}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') + + @setup({'list-index05': '{{ var.1 }}'}) + def test_list_index05(self): + """ + Dictionary lookup wins out when dict's key is a string. + """ + output = render('list-index05', {'var': {'1': "hello"}}) + self.assertEqual(output, 'hello') + + @setup({'list-index06': '{{ var.1 }}'}) + def test_list_index06(self): + """ + But list-index lookup wins out when dict's key is an int, which + behind the scenes is really a dictionary lookup (for a dict) + after converting the key to an int. + """ + output = render('list-index06', {"var": {1: "hello"}}) + self.assertEqual(output, 'hello') + + @setup({'list-index07': '{{ var.1 }}'}) + def test_list_index07(self): + """ + Dictionary lookup wins out when there is a string and int version + of the key. + """ + output = render('list-index07', {"var": {'1': "hello", 1: "world"}}) + self.assertEqual(output, 'hello') diff --git a/tests/template_tests/syntax_tests/test_load.py b/tests/template_tests/syntax_tests/test_load.py new file mode 100644 index 0000000000..b00b6b25ef --- /dev/null +++ b/tests/template_tests/syntax_tests/test_load.py @@ -0,0 +1,72 @@ +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase + +from .utils import render, setup + + +class LoadTagTests(TestCase): + + @setup({'load01': '{% load testtags subpackage.echo %}{% echo test %} {% echo2 "test" %}'}) + def test_load01(self): + output = render('load01') + self.assertEqual(output, 'test test') + + @setup({'load02': '{% load subpackage.echo %}{% echo2 "test" %}'}) + def test_load02(self): + output = render('load02') + self.assertEqual(output, 'test') + + # {% load %} tag, importing individual tags + @setup({'load03': '{% load echo from testtags %}{% echo this that theother %}'}) + def test_load03(self): + output = render('load03') + self.assertEqual(output, 'this that theother') + + @setup({'load04': '{% load echo other_echo from testtags %}' + '{% echo this that theother %} {% other_echo and another thing %}'}) + def test_load04(self): + output = render('load04') + self.assertEqual(output, 'this that theother and another thing') + + @setup({'load05': '{% load echo upper from testtags %}' + '{% echo this that theother %} {{ statement|upper }}'}) + def test_load05(self): + output = render('load05', {'statement': 'not shouting'}) + self.assertEqual(output, 'this that theother NOT SHOUTING') + + @setup({'load06': '{% load echo2 from subpackage.echo %}{% echo2 "test" %}'}) + def test_load06(self): + output = render('load06') + self.assertEqual(output, 'test') + + # {% load %} tag errors + @setup({'load07': '{% load echo other_echo bad_tag from testtags %}'}) + def test_load07(self): + with self.assertRaises(TemplateSyntaxError): + get_template('load07') + + @setup({'load08': '{% load echo other_echo bad_tag from %}'}) + def test_load08(self): + with self.assertRaises(TemplateSyntaxError): + get_template('load08') + + @setup({'load09': '{% load from testtags %}'}) + def test_load09(self): + with self.assertRaises(TemplateSyntaxError): + get_template('load09') + + @setup({'load10': '{% load echo from bad_library %}'}) + def test_load10(self): + with self.assertRaises(TemplateSyntaxError): + get_template('load10') + + @setup({'load11': '{% load subpackage.echo_invalid %}'}) + def test_load11(self): + with self.assertRaises(TemplateSyntaxError): + get_template('load11') + + @setup({'load12': '{% load subpackage.missing %}'}) + def test_load12(self): + with self.assertRaises(TemplateSyntaxError): + get_template('load12') diff --git a/tests/template_tests/syntax_tests/test_lorem.py b/tests/template_tests/syntax_tests/test_lorem.py new file mode 100644 index 0000000000..9deba40e52 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_lorem.py @@ -0,0 +1,11 @@ +from django.test import TestCase + +from .utils import render, setup + + +class LoremTagTests(TestCase): + + @setup({'lorem1': '{% lorem 3 w %}'}) + def test_lorem1(self): + output = render('lorem1') + self.assertEqual(output, 'lorem ipsum dolor') diff --git a/tests/template_tests/syntax_tests/test_multiline.py b/tests/template_tests/syntax_tests/test_multiline.py new file mode 100644 index 0000000000..af4c623dd1 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_multiline.py @@ -0,0 +1,21 @@ +from django.test import TestCase + +from .utils import render, setup + + +multiline_string = """ +Hello, +boys. +How +are +you +gentlemen. +""" + + +class MultilineTests(TestCase): + + @setup({'multiline01': multiline_string}) + def test_multiline01(self): + output = render('multiline01') + self.assertEqual(output, multiline_string) diff --git a/tests/template_tests/syntax_tests/test_named_endblock.py b/tests/template_tests/syntax_tests/test_named_endblock.py new file mode 100644 index 0000000000..d12c14d503 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_named_endblock.py @@ -0,0 +1,54 @@ +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase + +from .utils import render, setup + + +class NamedEndblockTests(TestCase): + + @setup({'namedendblocks01': '1{% block first %}_{% block second %}' + '2{% endblock second %}_{% endblock first %}3'}) + def test_namedendblocks01(self): + output = render('namedendblocks01') + self.assertEqual(output, '1_2_3') + + # Unbalanced blocks + @setup({'namedendblocks02': '1{% block first %}_{% block second %}' + '2{% endblock first %}_{% endblock second %}3'}) + def test_namedendblocks02(self): + with self.assertRaises(TemplateSyntaxError): + get_template('namedendblocks02') + + @setup({'namedendblocks03': '1{% block first %}_{% block second %}' + '2{% endblock %}_{% endblock second %}3'}) + def test_namedendblocks03(self): + with self.assertRaises(TemplateSyntaxError): + get_template('namedendblocks03') + + @setup({'namedendblocks04': '1{% block first %}_{% block second %}' + '2{% endblock second %}_{% endblock third %}3'}) + def test_namedendblocks04(self): + with self.assertRaises(TemplateSyntaxError): + get_template('namedendblocks04') + + @setup({'namedendblocks05': '1{% block first %}_{% block second %}2{% endblock first %}'}) + def test_namedendblocks05(self): + with self.assertRaises(TemplateSyntaxError): + get_template('namedendblocks05') + + # Mixed named and unnamed endblocks + @setup({'namedendblocks06': '1{% block first %}_{% block second %}' + '2{% endblock %}_{% endblock first %}3'}) + def test_namedendblocks06(self): + """ + Mixed named and unnamed endblocks + """ + output = render('namedendblocks06') + self.assertEqual(output, '1_2_3') + + @setup({'namedendblocks07': '1{% block first %}_{% block second %}' + '2{% endblock second %}_{% endblock %}3'}) + def test_namedendblocks07(self): + output = render('namedendblocks07') + self.assertEqual(output, '1_2_3') diff --git a/tests/template_tests/syntax_tests/test_now.py b/tests/template_tests/syntax_tests/test_now.py new file mode 100644 index 0000000000..fecf30fc8c --- /dev/null +++ b/tests/template_tests/syntax_tests/test_now.py @@ -0,0 +1,61 @@ +from datetime import datetime + +from django.test import TestCase +from django.utils.formats import date_format + +from .utils import render, setup + + +class NowTagTests(TestCase): + + @setup({'now01': '{% now "j n Y" %}'}) + def test_now01(self): + """ + Simple case + """ + output = render('now01') + self.assertEqual(output, "%d %d %d" % ( + datetime.now().day, datetime.now().month, datetime.now().year, + )) + + # Check parsing of locale strings + @setup({'now02': '{% now "DATE_FORMAT" %}'}) + def test_now02(self): + output = render('now02') + self.assertEqual(output, date_format(datetime.now())) + + @setup({'now03': '{% now \'j n Y\' %}'}) + def test_now03(self): + """ + #15092 - Also accept simple quotes + """ + output = render('now03') + self.assertEqual(output, "%d %d %d" % ( + datetime.now().day, datetime.now().month, datetime.now().year, + )) + + @setup({'now04': '{% now \'DATE_FORMAT\' %}'}) + def test_now04(self): + output = render('now04') + self.assertEqual(output, date_format(datetime.now())) + + @setup({'now05': '{% now \'j "n" Y\'%}'}) + def test_now05(self): + output = render('now05') + self.assertEqual(output, '%d "%d" %d' % ( + datetime.now().day, datetime.now().month, datetime.now().year, + )) + + @setup({'now06': '{% now "j \'n\' Y"%}'}) + def test_now06(self): + output = render('now06') + self.assertEqual(output, "%d '%d' %d" % ( + datetime.now().day, datetime.now().month, datetime.now().year, + )) + + @setup({'now07': '{% now "j n Y" as N %}-{{N}}-'}) + def test_now07(self): + output = render('now07') + self.assertEqual(output, '-%d %d %d-' % ( + datetime.now().day, datetime.now().month, datetime.now().year, + )) diff --git a/tests/template_tests/syntax_tests/test_numpy.py b/tests/template_tests/syntax_tests/test_numpy.py new file mode 100644 index 0000000000..de799c9108 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_numpy.py @@ -0,0 +1,41 @@ +from unittest import skipIf + +from django.conf import settings +from django.test import TestCase + +from .utils import render, setup + +try: + import numpy +except ImportError: + numpy = False + + +@skipIf(numpy is False, "Numpy must be installed to run these tests.") +class NumpyTests(TestCase): + + @setup({'numpy-array-index01': '{{ var.1 }}'}) + def test_numpy_array_index01(self): + """ + Numpy's array-index syntax allows a template to access a certain + item of a subscriptable object. + """ + output = render( + 'numpy-array-index01', + {'var': numpy.array(["first item", "second item"])}, + ) + self.assertEqual(output, 'second item') + + @setup({'numpy-array-index02': '{{ var.5 }}'}) + def test_numpy_array_index02(self): + """ + Fail silently when the array index is out of range. + """ + output = render( + 'numpy-array-index02', + {'var': numpy.array(["first item", "second item"])}, + ) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID') + else: + self.assertEqual(output, '') diff --git a/tests/template_tests/syntax_tests/test_regroup.py b/tests/template_tests/syntax_tests/test_regroup.py new file mode 100644 index 0000000000..c9fb9b691c --- /dev/null +++ b/tests/template_tests/syntax_tests/test_regroup.py @@ -0,0 +1,103 @@ +from datetime import date + +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase + +from .utils import render, setup + + +class RegroupTagTests(TestCase): + + @setup({'regroup01': '' + '{% regroup data by bar as grouped %}' + '{% for group in grouped %}' + '{{ group.grouper }}:' + '{% for item in group.list %}' + '{{ item.foo }}' + '{% endfor %},' + '{% endfor %}'}) + def test_regroup01(self): + output = render('regroup01', { + 'data': [{'foo': 'c', 'bar': 1}, + {'foo': 'd', 'bar': 1}, + {'foo': 'a', 'bar': 2}, + {'foo': 'b', 'bar': 2}, + {'foo': 'x', 'bar': 3}], + }) + self.assertEqual(output, '1:cd,2:ab,3:x,') + + @setup({'regroup02': '' + '{% regroup data by bar as grouped %}' + '{% for group in grouped %}' + '{{ group.grouper }}:' + '{% for item in group.list %}' + '{{ item.foo }}' + '{% endfor %}' + '{% endfor %}'}) + def test_regroup02(self): + """ + Test for silent failure when target variable isn't found + """ + output = render('regroup02', {}) + self.assertEqual(output, '') + + @setup({'regroup03': '' + '{% regroup data by at|date:"m" as grouped %}' + '{% for group in grouped %}' + '{{ group.grouper }}:' + '{% for item in group.list %}' + '{{ item.at|date:"d" }}' + '{% endfor %},' + '{% endfor %}'}) + def test_regroup03(self): + """ + Regression tests for #17675 + The date template filter has expects_localtime = True + """ + output = render('regroup03', { + 'data': [{'at': date(2012, 2, 14)}, + {'at': date(2012, 2, 28)}, + {'at': date(2012, 7, 4)}], + }) + self.assertEqual(output, '02:1428,07:04,') + + @setup({'regroup04': '' + '{% regroup data by bar|join:"" as grouped %}' + '{% for group in grouped %}' + '{{ group.grouper }}:' + '{% for item in group.list %}' + '{{ item.foo|first }}' + '{% endfor %},' + '{% endfor %}'}) + def test_regroup04(self): + """ + The join template filter has needs_autoescape = True + """ + output = render('regroup04', { + 'data': [{'foo': 'x', 'bar': ['ab', 'c']}, + {'foo': 'y', 'bar': ['a', 'bc']}, + {'foo': 'z', 'bar': ['a', 'd']}], + }) + self.assertEqual(output, 'abc:xy,ad:z,') + + # Test syntax errors + @setup({'regroup05': '{% regroup data by bar as %}'}) + def test_regroup05(self): + with self.assertRaises(TemplateSyntaxError): + get_template('regroup05') + + @setup({'regroup06': '{% regroup data by bar thisaintright grouped %}'}) + def test_regroup06(self): + with self.assertRaises(TemplateSyntaxError): + get_template('regroup06') + + @setup({'regroup07': '{% regroup data thisaintright bar as grouped %}'}) + def test_regroup07(self): + with self.assertRaises(TemplateSyntaxError): + get_template('regroup07') + + @setup({'regroup08': '{% regroup data by bar as grouped toomanyargs %}'}) + def test_regroup08(self): + with self.assertRaises(TemplateSyntaxError): + get_template('regroup08') diff --git a/tests/template_tests/syntax_tests/test_setup.py b/tests/template_tests/syntax_tests/test_setup.py new file mode 100644 index 0000000000..ca91093078 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_setup.py @@ -0,0 +1,29 @@ +from django.conf import settings +from django.test import TestCase + +from .utils import setup + + +class SetupTests(TestCase): + + def test_setup(self): + """ + Let's just make sure setup runs cases in the right order. + """ + cases = [] + + @setup({}) + def method(self): + cases.append([ + settings.TEMPLATE_STRING_IF_INVALID, + settings.TEMPLATE_DEBUG, + ]) + + method(self) + + self.assertEqual(cases[0], ['', False]) + self.assertEqual(cases[1], ['', False]) + self.assertEqual(cases[2], ['INVALID', False]) + self.assertEqual(cases[3], ['INVALID', False]) + self.assertEqual(cases[4], ['', True]) + self.assertEqual(cases[5], ['', True]) diff --git a/tests/template_tests/syntax_tests/test_simple_tag.py b/tests/template_tests/syntax_tests/test_simple_tag.py new file mode 100644 index 0000000000..e6296ea26d --- /dev/null +++ b/tests/template_tests/syntax_tests/test_simple_tag.py @@ -0,0 +1,23 @@ +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase + +from .utils import render, setup + + +class SimpleTagTests(TestCase): + + @setup({'simpletag-renamed01': '{% load custom %}{% minusone 7 %}'}) + def test_simpletag_renamed01(self): + output = render('simpletag-renamed01') + self.assertEqual(output, '6') + + @setup({'simpletag-renamed02': '{% load custom %}{% minustwo 7 %}'}) + def test_simpletag_renamed02(self): + output = render('simpletag-renamed02') + self.assertEqual(output, '5') + + @setup({'simpletag-renamed03': '{% load custom %}{% minustwo_overridden_name 7 %}'}) + def test_simpletag_renamed03(self): + with self.assertRaises(TemplateSyntaxError): + get_template('simpletag-renamed03') diff --git a/tests/template_tests/syntax_tests/test_spaceless.py b/tests/template_tests/syntax_tests/test_spaceless.py new file mode 100644 index 0000000000..0c73a58bc5 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_spaceless.py @@ -0,0 +1,38 @@ +from django.test import TestCase + +from .utils import render, setup + + +class SpacelessTagTests(TestCase): + + @setup({'spaceless01': "{% spaceless %} text {% endspaceless %}"}) + def test_spaceless01(self): + output = render('spaceless01') + self.assertEqual(output, " text ") + + @setup({'spaceless02': "{% spaceless %} \n text \n {% endspaceless %}"}) + def test_spaceless02(self): + output = render('spaceless02') + self.assertEqual(output, " text ") + + @setup({'spaceless03': "{% spaceless %}text{% endspaceless %}"}) + def test_spaceless03(self): + output = render('spaceless03') + self.assertEqual(output, "text") + + @setup({'spaceless04': "{% spaceless %} {{ text }} {% endspaceless %}"}) + def test_spaceless04(self): + output = render('spaceless04', {'text': 'This & that'}) + self.assertEqual(output, "This & that") + + @setup({'spaceless05': "{% autoescape off %}{% spaceless %}" + " {{ text }} {% endspaceless %}" + "{% endautoescape %}"}) + def test_spaceless05(self): + output = render('spaceless05', {'text': 'This & that'}) + self.assertEqual(output, "This & that") + + @setup({'spaceless06': "{% spaceless %} {{ text|safe }} {% endspaceless %}"}) + def test_spaceless06(self): + output = render('spaceless06', {'text': 'This & that'}) + self.assertEqual(output, "This & that") diff --git a/tests/template_tests/syntax_tests/test_ssi.py b/tests/template_tests/syntax_tests/test_ssi.py new file mode 100644 index 0000000000..33db059c92 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_ssi.py @@ -0,0 +1,88 @@ +import os +import warnings + +from django.test import override_settings, TestCase +from django.utils._os import upath +from django.utils.deprecation import RemovedInDjango19Warning + +from .utils import render, setup + + +cwd = os.path.dirname(os.path.abspath(upath(__file__))) +root = os.path.abspath(os.path.join(cwd, "..")) + + +@override_settings(ALLOWED_INCLUDE_ROOTS=(root)) +class SsiTagTests(TestCase): + + # Test normal behavior + @setup({'ssi01': '{%% ssi "%s" %%}' % os.path.join( + root, 'templates', 'ssi_include.html', + )}) + def test_ssi01(self): + output = render('ssi01') + self.assertEqual(output, 'This is for testing an ssi include. {{ test }}\n') + + @setup({'ssi02': '{%% ssi "%s" %%}' % os.path.join( + root, 'not_here', + )}) + def test_ssi02(self): + output = render('ssi02') + self.assertEqual(output, ''), + + @setup({'ssi03': "{%% ssi '%s' %%}" % os.path.join( + root, 'not_here', + )}) + def test_ssi03(self): + output = render('ssi03') + self.assertEqual(output, ''), + + # Test passing as a variable + @setup({'ssi04': '{% load ssi from future %}{% ssi ssi_file %}'}) + def test_ssi04(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango19Warning) + output = render('ssi04', { + 'ssi_file': os.path.join(root, 'templates', 'ssi_include.html') + }) + self.assertEqual(output, 'This is for testing an ssi include. {{ test }}\n') + + @setup({'ssi05': '{% load ssi from future %}{% ssi ssi_file %}'}) + def test_ssi05(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango19Warning) + output = render('ssi05', {'ssi_file': 'no_file'}) + self.assertEqual(output, '') + + # Test parsed output + @setup({'ssi06': '{%% ssi "%s" parsed %%}' % os.path.join( + root, 'templates', 'ssi_include.html', + )}) + def test_ssi06(self): + output = render('ssi06', {'test': 'Look ma! It parsed!'}) + self.assertEqual(output, 'This is for testing an ssi include. ' + 'Look ma! It parsed!\n') + + @setup({'ssi07': '{%% ssi "%s" parsed %%}' % os.path.join( + root, 'not_here', + )}) + def test_ssi07(self): + output = render('ssi07', {'test': 'Look ma! It parsed!'}) + self.assertEqual(output, '') + + # Test space in file name + @setup({'ssi08': '{%% ssi "%s" %%}' % os.path.join( + root, 'templates', 'ssi include with spaces.html', + )}) + def test_ssi08(self): + output = render('ssi08') + self.assertEqual(output, 'This is for testing an ssi include ' + 'with spaces in its name. {{ test }}\n') + + @setup({'ssi09': '{%% ssi "%s" parsed %%}' % os.path.join( + root, 'templates', 'ssi include with spaces.html', + )}) + def test_ssi09(self): + output = render('ssi09', {'test': 'Look ma! It parsed!'}) + self.assertEqual(output, 'This is for testing an ssi include ' + 'with spaces in its name. Look ma! It parsed!\n') diff --git a/tests/template_tests/syntax_tests/test_static.py b/tests/template_tests/syntax_tests/test_static.py new file mode 100644 index 0000000000..8b39a1465f --- /dev/null +++ b/tests/template_tests/syntax_tests/test_static.py @@ -0,0 +1,51 @@ +from django.conf import settings +from django.test import override_settings, TestCase +from django.utils.six.moves.urllib.parse import urljoin + +from .utils import render, setup + + +@override_settings(MEDIA_URL="/media/", STATIC_URL="/static/") +class StaticTagTests(TestCase): + + @setup({'static-prefixtag01': '{% load static %}{% get_static_prefix %}'}) + def test_static_prefixtag01(self): + output = render('static-prefixtag01') + self.assertEqual(output, settings.STATIC_URL) + + @setup({'static-prefixtag02': '{% load static %}' + '{% get_static_prefix as static_prefix %}{{ static_prefix }}'}) + def test_static_prefixtag02(self): + output = render('static-prefixtag02') + self.assertEqual(output, settings.STATIC_URL) + + @setup({'static-prefixtag03': '{% load static %}{% get_media_prefix %}'}) + def test_static_prefixtag03(self): + output = render('static-prefixtag03') + self.assertEqual(output, settings.MEDIA_URL) + + @setup({'static-prefixtag04': '{% load static %}' + '{% get_media_prefix as media_prefix %}{{ media_prefix }}'}) + def test_static_prefixtag04(self): + output = render('static-prefixtag04') + self.assertEqual(output, settings.MEDIA_URL) + + @setup({'static-statictag01': '{% load static %}{% static "admin/base.css" %}'}) + def test_static_statictag01(self): + output = render('static-statictag01') + self.assertEqual(output, urljoin(settings.STATIC_URL, 'admin/base.css')) + + @setup({'static-statictag02': '{% load static %}{% static base_css %}'}) + def test_static_statictag02(self): + output = render('static-statictag02', {'base_css': 'admin/base.css'}) + self.assertEqual(output, urljoin(settings.STATIC_URL, 'admin/base.css')) + + @setup({'static-statictag03': '{% load static %}{% static "admin/base.css" as foo %}{{ foo }}'}) + def test_static_statictag03(self): + output = render('static-statictag03') + self.assertEqual(output, urljoin(settings.STATIC_URL, 'admin/base.css')) + + @setup({'static-statictag04': '{% load static %}{% static base_css as foo %}{{ foo }}'}) + def test_static_statictag04(self): + output = render('static-statictag04', {'base_css': 'admin/base.css'}) + self.assertEqual(output, urljoin(settings.STATIC_URL, 'admin/base.css')) diff --git a/tests/template_tests/syntax_tests/test_template_tag.py b/tests/template_tests/syntax_tests/test_template_tag.py new file mode 100644 index 0000000000..aa56a971f9 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_template_tag.py @@ -0,0 +1,68 @@ +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase + +from .utils import render, setup + + +class TemplateTagTests(TestCase): + + @setup({'templatetag01': '{% templatetag openblock %}'}) + def test_templatetag01(self): + output = render('templatetag01') + self.assertEqual(output, '{%') + + @setup({'templatetag02': '{% templatetag closeblock %}'}) + def test_templatetag02(self): + output = render('templatetag02') + self.assertEqual(output, '%}') + + @setup({'templatetag03': '{% templatetag openvariable %}'}) + def test_templatetag03(self): + output = render('templatetag03') + self.assertEqual(output, '{{') + + @setup({'templatetag04': '{% templatetag closevariable %}'}) + def test_templatetag04(self): + output = render('templatetag04') + self.assertEqual(output, '}}') + + @setup({'templatetag05': '{% templatetag %}'}) + def test_templatetag05(self): + with self.assertRaises(TemplateSyntaxError): + get_template('templatetag05') + + @setup({'templatetag06': '{% templatetag foo %}'}) + def test_templatetag06(self): + with self.assertRaises(TemplateSyntaxError): + get_template('templatetag06') + + @setup({'templatetag07': '{% templatetag openbrace %}'}) + def test_templatetag07(self): + output = render('templatetag07') + self.assertEqual(output, '{') + + @setup({'templatetag08': '{% templatetag closebrace %}'}) + def test_templatetag08(self): + output = render('templatetag08') + self.assertEqual(output, '}') + + @setup({'templatetag09': '{% templatetag openbrace %}{% templatetag openbrace %}'}) + def test_templatetag09(self): + output = render('templatetag09') + self.assertEqual(output, '{{') + + @setup({'templatetag10': '{% templatetag closebrace %}{% templatetag closebrace %}'}) + def test_templatetag10(self): + output = render('templatetag10') + self.assertEqual(output, '}}') + + @setup({'templatetag11': '{% templatetag opencomment %}'}) + def test_templatetag11(self): + output = render('templatetag11') + self.assertEqual(output, '{#') + + @setup({'templatetag12': '{% templatetag closecomment %}'}) + def test_templatetag12(self): + output = render('templatetag12') + self.assertEqual(output, '#}') diff --git a/tests/template_tests/syntax_tests/test_url.py b/tests/template_tests/syntax_tests/test_url.py new file mode 100644 index 0000000000..7881bc58ed --- /dev/null +++ b/tests/template_tests/syntax_tests/test_url.py @@ -0,0 +1,267 @@ +# coding: utf-8 +import warnings + +from django.core.urlresolvers import NoReverseMatch +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import override_settings, TestCase +from django.utils.deprecation import RemovedInDjango20Warning + +from .utils import render, setup + + +@override_settings(ROOT_URLCONF='template_tests.urls') +class UrlTagTests(TestCase): + + # Successes + @setup({'url01': '{% url "template_tests.views.client" client.id %}'}) + def test_url01(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url01', {'client': {'id': 1}}) + self.assertEqual(output, '/client/1/') + + @setup({'url02': '{% url "template_tests.views.client_action" id=client.id action="update" %}'}) + def test_url02(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url02', {'client': {'id': 1}}) + self.assertEqual(output, '/client/1/update/') + + @setup({'url02a': '{% url "template_tests.views.client_action" client.id "update" %}'}) + def test_url02a(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url02a', {'client': {'id': 1}}) + self.assertEqual(output, '/client/1/update/') + + @setup({'url02b': "{% url 'template_tests.views.client_action' id=client.id action='update' %}"}) + def test_url02b(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url02b', {'client': {'id': 1}}) + self.assertEqual(output, '/client/1/update/') + + @setup({'url02c': "{% url 'template_tests.views.client_action' client.id 'update' %}"}) + def test_url02c(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url02c', {'client': {'id': 1}}) + self.assertEqual(output, '/client/1/update/') + + @setup({'url03': '{% url "template_tests.views.index" %}'}) + def test_url03(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url03') + self.assertEqual(output, '/') + + @setup({'url04': '{% url "named.client" client.id %}'}) + def test_url04(self): + output = render('url04', {'client': {'id': 1}}) + self.assertEqual(output, '/named-client/1/') + + @setup({'url05': '{% url "метка_оператора" v %}'}) + def test_url05(self): + output = render('url05', {'v': 'Ω'}) + self.assertEqual(output, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/') + + @setup({'url06': '{% url "метка_оператора_2" tag=v %}'}) + def test_url06(self): + output = render('url06', {'v': 'Ω'}) + self.assertEqual(output, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/') + + @setup({'url07': '{% url "template_tests.views.client2" tag=v %}'}) + def test_url07(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url07', {'v': 'Ω'}) + self.assertEqual(output, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/') + + @setup({'url08': '{% url "метка_оператора" v %}'}) + def test_url08(self): + output = render('url08', {'v': 'Ω'}) + self.assertEqual(output, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/') + + @setup({'url09': '{% url "метка_оператора_2" tag=v %}'}) + def test_url09(self): + output = render('url09', {'v': 'Ω'}) + self.assertEqual(output, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/') + + @setup({'url10': '{% url "template_tests.views.client_action" id=client.id action="two words" %}'}) + def test_url10(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url10', {'client': {'id': 1}}) + self.assertEqual(output, '/client/1/two%20words/') + + @setup({'url11': '{% url "template_tests.views.client_action" id=client.id action="==" %}'}) + def test_url11(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url11', {'client': {'id': 1}}) + self.assertEqual(output, '/client/1/==/') + + @setup({'url12': '{% url "template_tests.views.client_action" ' + 'id=client.id action="!$&\'()*+,;=~:@," %}'}) + def test_url12(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url12', {'client': {'id': 1}}) + self.assertEqual(output, '/client/1/!$&\'()*+,;=~:@,/') + + @setup({'url13': '{% url "template_tests.views.client_action" ' + 'id=client.id action=arg|join:"-" %}'}) + def test_url13(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url13', {'client': {'id': 1}, 'arg': ['a', 'b']}) + self.assertEqual(output, '/client/1/a-b/') + + @setup({'url14': '{% url "template_tests.views.client_action" client.id arg|join:"-" %}'}) + def test_url14(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url14', {'client': {'id': 1}, 'arg': ['a', 'b']}) + self.assertEqual(output, '/client/1/a-b/') + + @setup({'url15': '{% url "template_tests.views.client_action" 12 "test" %}'}) + def test_url15(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url15') + self.assertEqual(output, '/client/12/test/') + + @setup({'url18': '{% url "template_tests.views.client" "1,2" %}'}) + def test_url18(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url18') + self.assertEqual(output, '/client/1,2/') + + @setup({'url19': '{% url named_url client.id %}'}) + def test_url19(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url19', {'client': {'id': 1}, 'named_url': 'template_tests.views.client'}) + self.assertEqual(output, '/client/1/') + + @setup({'url20': '{% url url_name_in_var client.id %}'}) + def test_url20(self): + output = render('url20', {'client': {'id': 1}, 'url_name_in_var': 'named.client'}) + self.assertEqual(output, '/named-client/1/') + + # Failures + @setup({'url-fail01': '{% url %}'}) + def test_url_fail01(self): + with self.assertRaises(TemplateSyntaxError): + get_template('url-fail01') + + @setup({'url-fail02': '{% url "no_such_view" %}'}) + def test_url_fail02(self): + with self.assertRaises(NoReverseMatch): + render('url-fail02') + + @setup({'url-fail03': '{% url "template_tests.views.client" %}'}) + def test_url_fail03(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + with self.assertRaises(NoReverseMatch): + render('url-fail03') + + @setup({'url-fail04': '{% url "view" id, %}'}) + def test_url_fail04(self): + with self.assertRaises(TemplateSyntaxError): + get_template('url-fail04') + + @setup({'url-fail05': '{% url "view" id= %}'}) + def test_url_fail05(self): + with self.assertRaises(TemplateSyntaxError): + get_template('url-fail05') + + @setup({'url-fail06': '{% url "view" a.id=id %}'}) + def test_url_fail06(self): + with self.assertRaises(TemplateSyntaxError): + get_template('url-fail06') + + @setup({'url-fail07': '{% url "view" a.id!id %}'}) + def test_url_fail07(self): + with self.assertRaises(TemplateSyntaxError): + get_template('url-fail07') + + @setup({'url-fail08': '{% url "view" id="unterminatedstring %}'}) + def test_url_fail08(self): + with self.assertRaises(TemplateSyntaxError): + get_template('url-fail08') + + @setup({'url-fail09': '{% url "view" id=", %}'}) + def test_url_fail09(self): + with self.assertRaises(TemplateSyntaxError): + get_template('url-fail09') + + @setup({'url-fail11': '{% url named_url %}'}) + def test_url_fail11(self): + with self.assertRaises(NoReverseMatch): + render('url-fail11') + + @setup({'url-fail12': '{% url named_url %}'}) + def test_url_fail12(self): + with self.assertRaises(NoReverseMatch): + render('url-fail12', {'named_url': 'no_such_view'}) + + @setup({'url-fail13': '{% url named_url %}'}) + def test_url_fail13(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + with self.assertRaises(NoReverseMatch): + render('url-fail13', {'named_url': 'template_tests.views.client'}) + + @setup({'url-fail14': '{% url named_url id, %}'}) + def test_url_fail14(self): + with self.assertRaises(TemplateSyntaxError): + render('url-fail14', {'named_url': 'view'}) + + @setup({'url-fail15': '{% url named_url id= %}'}) + def test_url_fail15(self): + with self.assertRaises(TemplateSyntaxError): + render('url-fail15', {'named_url': 'view'}) + + @setup({'url-fail16': '{% url named_url a.id=id %}'}) + def test_url_fail16(self): + with self.assertRaises(TemplateSyntaxError): + render('url-fail16', {'named_url': 'view'}) + + @setup({'url-fail17': '{% url named_url a.id!id %}'}) + def test_url_fail17(self): + with self.assertRaises(TemplateSyntaxError): + render('url-fail17', {'named_url': 'view'}) + + @setup({'url-fail18': '{% url named_url id="unterminatedstring %}'}) + def test_url_fail18(self): + with self.assertRaises(TemplateSyntaxError): + render('url-fail18', {'named_url': 'view'}) + + @setup({'url-fail19': '{% url named_url id=", %}'}) + def test_url_fail19(self): + with self.assertRaises(TemplateSyntaxError): + render('url-fail19', {'named_url': 'view'}) + + # {% url ... as var %} + @setup({'url-asvar01': '{% url "template_tests.views.index" as url %}'}) + def test_url_asvar01(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url-asvar01') + self.assertEqual(output, '') + + @setup({'url-asvar02': '{% url "template_tests.views.index" as url %}{{ url }}'}) + def test_url_asvar02(self): + with warnings.catch_warnings(): + warnings.simplefilter("ignore", RemovedInDjango20Warning) + output = render('url-asvar02') + self.assertEqual(output, '/') + + @setup({'url-asvar03': '{% url "no_such_view" as url %}{{ url }}'}) + def test_url_asvar03(self): + output = render('url-asvar03') + self.assertEqual(output, '') diff --git a/tests/template_tests/syntax_tests/test_verbatim.py b/tests/template_tests/syntax_tests/test_verbatim.py new file mode 100644 index 0000000000..333c68a8c8 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_verbatim.py @@ -0,0 +1,39 @@ +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase + +from .utils import render, setup + + +class VerbatimTagTests(TestCase): + + @setup({'verbatim-tag01': '{% verbatim %}{{bare }}{% endverbatim %}'}) + def test_verbatim_tag01(self): + output = render('verbatim-tag01') + self.assertEqual(output, '{{bare }}') + + @setup({'verbatim-tag02': '{% verbatim %}{% endif %}{% endverbatim %}'}) + def test_verbatim_tag02(self): + output = render('verbatim-tag02') + self.assertEqual(output, '{% endif %}') + + @setup({'verbatim-tag03': '{% verbatim %}It\'s the {% verbatim %} tag{% endverbatim %}'}) + def test_verbatim_tag03(self): + output = render('verbatim-tag03') + self.assertEqual(output, 'It\'s the {% verbatim %} tag') + + @setup({'verbatim-tag04': '{% verbatim %}{% verbatim %}{% endverbatim %}{% endverbatim %}'}) + def test_verbatim_tag04(self): + with self.assertRaises(TemplateSyntaxError): + get_template('verbatim-tag04') + + @setup({'verbatim-tag05': '{% verbatim %}{% endverbatim %}{% verbatim %}{% endverbatim %}'}) + def test_verbatim_tag05(self): + output = render('verbatim-tag05') + self.assertEqual(output, '') + + @setup({'verbatim-tag06': '{% verbatim special %}' + 'Don\'t {% endverbatim %} just yet{% endverbatim special %}'}) + def test_verbatim_tag06(self): + output = render('verbatim-tag06') + self.assertEqual(output, 'Don\'t {% endverbatim %} just yet') diff --git a/tests/template_tests/syntax_tests/test_width_ratio.py b/tests/template_tests/syntax_tests/test_width_ratio.py new file mode 100644 index 0000000000..05f9e9ac3d --- /dev/null +++ b/tests/template_tests/syntax_tests/test_width_ratio.py @@ -0,0 +1,146 @@ +from django.template.base import TemplateSyntaxError +from django.template.loader import get_template +from django.test import TestCase +from django.utils import six + +from .utils import render, setup + + +class WidthRatioTagTests(TestCase): + + @setup({'widthratio01': '{% widthratio a b 0 %}'}) + def test_widthratio01(self): + output = render('widthratio01', {'a': 50, 'b': 100}) + self.assertEqual(output, '0') + + @setup({'widthratio02': '{% widthratio a b 100 %}'}) + def test_widthratio02(self): + output = render('widthratio02', {'a': 0, 'b': 0}) + self.assertEqual(output, '0') + + @setup({'widthratio03': '{% widthratio a b 100 %}'}) + def test_widthratio03(self): + output = render('widthratio03', {'a': 0, 'b': 100}) + self.assertEqual(output, '0') + + @setup({'widthratio04': '{% widthratio a b 100 %}'}) + def test_widthratio04(self): + output = render('widthratio04', {'a': 50, 'b': 100}) + self.assertEqual(output, '50') + + @setup({'widthratio05': '{% widthratio a b 100 %}'}) + def test_widthratio05(self): + output = render('widthratio05', {'a': 100, 'b': 100}) + self.assertEqual(output, '100') + + @setup({'widthratio06': '{% widthratio a b 100 %}'}) + def test_widthratio06(self): + """ + 62.5 should round to 63 on Python 2 and 62 on Python 3 + See http://docs.python.org/py3k/whatsnew/3.0.html + """ + output = render('widthratio06', {'a': 50, 'b': 80}) + self.assertEqual(output, '62' if six.PY3 else '63') + + @setup({'widthratio07': '{% widthratio a b 100 %}'}) + def test_widthratio07(self): + """ + 71.4 should round to 71 + """ + output = render('widthratio07', {'a': 50, 'b': 70}) + self.assertEqual(output, '71') + + # Raise exception if we don't have 3 args, last one an integer + @setup({'widthratio08': '{% widthratio %}'}) + def test_widthratio08(self): + with self.assertRaises(TemplateSyntaxError): + get_template('widthratio08') + + @setup({'widthratio09': '{% widthratio a b %}'}) + def test_widthratio09(self): + with self.assertRaises(TemplateSyntaxError): + render('widthratio09', {'a': 50, 'b': 100}) + + @setup({'widthratio10': '{% widthratio a b 100.0 %}'}) + def test_widthratio10(self): + output = render('widthratio10', {'a': 50, 'b': 100}) + self.assertEqual(output, '50') + + @setup({'widthratio11': '{% widthratio a b c %}'}) + def test_widthratio11(self): + """ + #10043: widthratio should allow max_width to be a variable + """ + output = render('widthratio11', {'a': 50, 'c': 100, 'b': 100}) + self.assertEqual(output, '50') + + # #18739: widthratio should handle None args consistently with + # non-numerics + @setup({'widthratio12a': '{% widthratio a b c %}'}) + def test_widthratio12a(self): + output = render('widthratio12a', {'a': 'a', 'c': 100, 'b': 100}) + self.assertEqual(output, '') + + @setup({'widthratio12b': '{% widthratio a b c %}'}) + def test_widthratio12b(self): + output = render('widthratio12b', {'a': None, 'c': 100, 'b': 100}) + self.assertEqual(output, '') + + @setup({'widthratio13a': '{% widthratio a b c %}'}) + def test_widthratio13a(self): + output = render('widthratio13a', {'a': 0, 'c': 100, 'b': 'b'}) + self.assertEqual(output, '') + + @setup({'widthratio13b': '{% widthratio a b c %}'}) + def test_widthratio13b(self): + output = render('widthratio13b', {'a': 0, 'c': 100, 'b': None}) + self.assertEqual(output, '') + + @setup({'widthratio14a': '{% widthratio a b c %}'}) + def test_widthratio14a(self): + with self.assertRaises(TemplateSyntaxError): + render('widthratio14a', {'a': 0, 'c': 'c', 'b': 100}) + + @setup({'widthratio14b': '{% widthratio a b c %}'}) + def test_widthratio14b(self): + with self.assertRaises(TemplateSyntaxError): + render('widthratio14b', {'a': 0, 'c': None, 'b': 100}) + + @setup({'widthratio15': '{% load custom %}{% widthratio a|noop:"x y" b 0 %}'}) + def test_widthratio15(self): + """ + Test whitespace in filter argument + """ + output = render('widthratio15', {'a': 50, 'b': 100}) + self.assertEqual(output, '0') + + # Widthratio with variable assignment + @setup({'widthratio16': '{% widthratio a b 100 as variable %}-{{ variable }}-'}) + def test_widthratio16(self): + output = render('widthratio16', {'a': 50, 'b': 100}) + self.assertEqual(output, '-50-') + + @setup({'widthratio17': '{% widthratio a b 100 as variable %}-{{ variable }}-'}) + def test_widthratio17(self): + output = render('widthratio17', {'a': 100, 'b': 100}) + self.assertEqual(output, '-100-') + + @setup({'widthratio18': '{% widthratio a b 100 as %}'}) + def test_widthratio18(self): + with self.assertRaises(TemplateSyntaxError): + get_template('widthratio18') + + @setup({'widthratio19': '{% widthratio a b 100 not_as variable %}'}) + def test_widthratio19(self): + with self.assertRaises(TemplateSyntaxError): + get_template('widthratio19') + + @setup({'widthratio20': '{% widthratio a b 100 %}'}) + def test_widthratio20(self): + output = render('widthratio20', {'a': float('inf'), 'b': float('inf')}) + self.assertEqual(output, '') + + @setup({'widthratio21': '{% widthratio a b 100 %}'}) + def test_widthratio21(self): + output = render('widthratio21', {'a': float('inf'), 'b': 2}) + self.assertEqual(output, '') diff --git a/tests/template_tests/syntax_tests/test_with.py b/tests/template_tests/syntax_tests/test_with.py new file mode 100644 index 0000000000..cc0a3344c2 --- /dev/null +++ b/tests/template_tests/syntax_tests/test_with.py @@ -0,0 +1,53 @@ +from django.conf import settings +from django.template.base import TemplateSyntaxError +from django.test import TestCase + +from .utils import render, setup + + +class WithTagTests(TestCase): + + @setup({'with01': '{% with key=dict.key %}{{ key }}{% endwith %}'}) + def test_with01(self): + output = render('with01', {'dict': {'key': 50}}) + self.assertEqual(output, '50') + + @setup({'legacywith01': '{% with dict.key as key %}{{ key }}{% endwith %}'}) + def test_legacywith01(self): + output = render('legacywith01', {'dict': {'key': 50}}) + self.assertEqual(output, '50') + + @setup({'with02': '{{ key }}{% with key=dict.key %}' + '{{ key }}-{{ dict.key }}-{{ key }}' + '{% endwith %}{{ key }}'}) + def test_with02(self): + output = render('with02', {'dict': {'key': 50}}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID50-50-50INVALID') + else: + self.assertEqual(output, '50-50-50') + + @setup({'legacywith02': '{{ key }}{% with dict.key as key %}' + '{{ key }}-{{ dict.key }}-{{ key }}' + '{% endwith %}{{ key }}'}) + def test_legacywith02(self): + output = render('legacywith02', {'dict': {'key': 50}}) + if settings.TEMPLATE_STRING_IF_INVALID: + self.assertEqual(output, 'INVALID50-50-50INVALID') + else: + self.assertEqual(output, '50-50-50') + + @setup({'with03': '{% with a=alpha b=beta %}{{ a }}{{ b }}{% endwith %}'}) + def test_with03(self): + output = render('with03', {'alpha': 'A', 'beta': 'B'}) + self.assertEqual(output, 'AB') + + @setup({'with-error01': '{% with dict.key xx key %}{{ key }}{% endwith %}'}) + def test_with_error01(self): + with self.assertRaises(TemplateSyntaxError): + render('with-error01', {'dict': {'key': 50}}) + + @setup({'with-error02': '{% with dict.key as %}{{ key }}{% endwith %}'}) + def test_with_error02(self): + with self.assertRaises(TemplateSyntaxError): + render('with-error02', {'dict': {'key': 50}}) diff --git a/tests/template_tests/syntax_tests/utils.py b/tests/template_tests/syntax_tests/utils.py new file mode 100644 index 0000000000..5445f5b8c4 --- /dev/null +++ b/tests/template_tests/syntax_tests/utils.py @@ -0,0 +1,205 @@ +# coding: utf-8 + +from __future__ import unicode_literals + +import functools + +from django import template +from django.template import Library +from django.template.base import Context +from django.template.engine import Engine +from django.template.loader import get_template +from django.test.utils import override_settings +from django.utils import translation +from django.utils.encoding import python_2_unicode_compatible +from django.utils.safestring import mark_safe + + +def render(template_name, context=None): + if context is None: + context = {} + + t = get_template(template_name) + with translation.override(context.get('LANGUAGE_CODE', 'en-us')): + return t.render(Context(context)) + + +def setup(templates, *args): + """ + Runs test method multiple times in the following order: + + TEMPLATE_DEBUG CACHED TEMPLATE_STRING_IF_INVALID + -------------- ------ -------------------------- + False False + False True + False False INVALID + False True INVALID + True False + True True + """ + + for arg in args: + templates.update(arg) + + # numerous tests make use of an inclusion tag + # add this in here for simplicity + templates["inclusion.html"] = "{{ result }}" + + def decorator(func): + @register_test_tags + @override_settings(TEMPLATE_LOADERS=[ + ('django.template.loaders.cached.Loader', [ + ('django.template.loaders.locmem.Loader', templates), + ]), + ]) + def inner(self): + loader = Engine.get_default().template_loaders[0] + + func(self) + func(self) + loader.reset() + + with override_settings(TEMPLATE_STRING_IF_INVALID='INVALID'): + func(self) + func(self) + loader.reset() + + with override_settings(TEMPLATE_DEBUG=True): + func(self) + func(self) + loader.reset() + return inner + return decorator + + +# Custom template tag for tests + +register = Library() + + +class EchoNode(template.Node): + def __init__(self, contents): + self.contents = contents + + def render(self, context): + return ' '.join(self.contents) + + +@register.tag +def echo(parser, token): + return EchoNode(token.contents.split()[1:]) +register.tag('other_echo', echo) + + +@register.filter +def upper(value): + return value.upper() + + +def register_test_tags(func): + @functools.wraps(func) + def inner(self): + template.libraries['testtags'] = register + func(self) + del template.libraries['testtags'] + return inner + + +# Helper objects + +class SomeException(Exception): + silent_variable_failure = True + + +class SomeOtherException(Exception): + pass + + +class ShouldNotExecuteException(Exception): + pass + + +class SomeClass: + def __init__(self): + self.otherclass = OtherClass() + + def method(self): + return 'SomeClass.method' + + def method2(self, o): + return o + + def method3(self): + raise SomeException + + def method4(self): + raise SomeOtherException + + def method5(self): + raise TypeError + + def __getitem__(self, key): + if key == 'silent_fail_key': + raise SomeException + elif key == 'noisy_fail_key': + raise SomeOtherException + raise KeyError + + @property + def silent_fail_attribute(self): + raise SomeException + + @property + def noisy_fail_attribute(self): + raise SomeOtherException + + @property + def attribute_error_attribute(self): + raise AttributeError + + +class OtherClass: + def method(self): + return 'OtherClass.method' + + +class TestObj(object): + def is_true(self): + return True + + def is_false(self): + return False + + def is_bad(self): + raise ShouldNotExecuteException() + + +class SilentGetItemClass(object): + def __getitem__(self, key): + raise SomeException + + +class SilentAttrClass(object): + def b(self): + raise SomeException + b = property(b) + + +@python_2_unicode_compatible +class UTF8Class: + "Class whose __str__ returns non-ASCII data on Python 2" + def __str__(self): + return 'ŠĐĆŽćžšđ' + + +# These two classes are used to test auto-escaping of unicode output. +@python_2_unicode_compatible +class UnsafeClass: + def __str__(self): + return 'you & me' + + +@python_2_unicode_compatible +class SafeClass: + def __str__(self): + return mark_safe('you > me') diff --git a/tests/template_tests/tests.py b/tests/template_tests/tests.py index 67ac08601b..43dac24b92 100644 --- a/tests/template_tests/tests.py +++ b/tests/template_tests/tests.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -from datetime import date, datetime import os import sys import traceback @@ -9,7 +8,6 @@ import unittest import warnings from django import template -from django.conf import settings from django.contrib.auth.models import Group from django.core import urlresolvers from django.template import loader, Context, RequestContext, Template, TemplateSyntaxError @@ -18,144 +16,18 @@ from django.template.loaders import app_directories, filesystem from django.test import RequestFactory, TestCase from django.test.utils import override_settings, extend_sys_path from django.utils.deprecation import RemovedInDjango19Warning, RemovedInDjango20Warning -from django.utils.encoding import python_2_unicode_compatible -from django.utils.formats import date_format from django.utils._os import upath -from django.utils.safestring import mark_safe from django.utils import six -from django.utils.six.moves.urllib.parse import urljoin from django.utils import translation -# NumPy installed? -try: - import numpy -except ImportError: - numpy = False - from . import filters - -################################# -# Custom template tag for tests # -################################# - -register = template.Library() - - -class EchoNode(template.Node): - def __init__(self, contents): - self.contents = contents - - def render(self, context): - return " ".join(self.contents) - - -def do_echo(parser, token): - return EchoNode(token.contents.split()[1:]) - - -def do_upper(value): - return value.upper() - -register.tag("echo", do_echo) -register.tag("other_echo", do_echo) -register.filter("upper", do_upper) - -template.libraries['testtags'] = register - -##################################### -# Helper objects for template tests # -##################################### - - -class SomeException(Exception): - silent_variable_failure = True - - -class SomeOtherException(Exception): - pass +from .syntax_tests.utils import register_test_tags, ShouldNotExecuteException class ContextStackException(Exception): pass -class ShouldNotExecuteException(Exception): - pass - - -class SomeClass: - def __init__(self): - self.otherclass = OtherClass() - - def method(self): - return "SomeClass.method" - - def method2(self, o): - return o - - def method3(self): - raise SomeException - - def method4(self): - raise SomeOtherException - - def method5(self): - raise TypeError - - def __getitem__(self, key): - if key == 'silent_fail_key': - raise SomeException - elif key == 'noisy_fail_key': - raise SomeOtherException - raise KeyError - - @property - def silent_fail_attribute(self): - raise SomeException - - @property - def noisy_fail_attribute(self): - raise SomeOtherException - - @property - def attribute_error_attribute(self): - raise AttributeError - - -class OtherClass: - def method(self): - return "OtherClass.method" - - -class TestObj(object): - def is_true(self): - return True - - def is_false(self): - return False - - def is_bad(self): - raise ShouldNotExecuteException() - - -class SilentGetItemClass(object): - def __getitem__(self, key): - raise SomeException - - -class SilentAttrClass(object): - def b(self): - raise SomeException - b = property(b) - - -@python_2_unicode_compatible -class UTF8Class: - "Class whose __str__ returns non-ASCII data on Python 2" - def __str__(self): - return 'ŠĐĆŽćžšđ' - - class TemplateLoaderTests(TestCase): def test_loaders_security(self): @@ -522,23 +394,12 @@ class TemplateRegressionTests(TestCase): # Set ALLOWED_INCLUDE_ROOTS so that ssi works. -@override_settings(MEDIA_URL="/media/", STATIC_URL="/static/", - TEMPLATE_DEBUG=False, ALLOWED_INCLUDE_ROOTS=( - os.path.dirname(os.path.abspath(upath(__file__))),), - ROOT_URLCONF='template_tests.urls', - ) +@override_settings(TEMPLATE_DEBUG=False, ROOT_URLCONF='template_tests.urls') class TemplateTests(TestCase): + @register_test_tags def test_templates(self): - template_tests = self.get_template_tests() - filter_tests = filters.get_filter_tests() - - # Quickly check that we aren't accidentally using a name in both - # template and filter tests. - overlapping_names = [name for name in filter_tests if name in template_tests] - assert not overlapping_names, 'Duplicate test name(s): %s' % ', '.join(overlapping_names) - - template_tests.update(filter_tests) + template_tests = filters.get_filter_tests() templates = dict((name, t[0]) for name, t in six.iteritems(template_tests)) with override_settings(TEMPLATE_LOADERS=[ @@ -639,1219 +500,6 @@ class TemplateTests(TestCase): raise ContextStackException return output - def get_template_tests(self): - # SYNTAX -- - # 'template_name': ('template contents', 'context dict', 'expected string output' or Exception class) - # This import is necessary when tests are run isolated: - from .templatetags import custom # noqa - basedir = os.path.dirname(os.path.abspath(upath(__file__))) - tests = { - ### BASIC SYNTAX ################################################ - - # Plain text should go through the template parser untouched - 'basic-syntax01': ("something cool", {}, "something cool"), - - # Variables should be replaced with their value in the current - # context - 'basic-syntax02': ("{{ headline }}", {'headline': 'Success'}, "Success"), - - # More than one replacement variable is allowed in a template - 'basic-syntax03': ("{{ first }} --- {{ second }}", {"first": 1, "second": 2}, "1 --- 2"), - - # Fail silently when a variable is not found in the current context - 'basic-syntax04': ("as{{ missing }}df", {}, ("asdf", "asINVALIDdf")), - - # A variable may not contain more than one word - 'basic-syntax06': ("{{ multi word variable }}", {}, template.TemplateSyntaxError), - - # Raise TemplateSyntaxError for empty variable tags - 'basic-syntax07': ("{{ }}", {}, template.TemplateSyntaxError), - 'basic-syntax08': ("{{ }}", {}, template.TemplateSyntaxError), - - # Attribute syntax allows a template to call an object's attribute - 'basic-syntax09': ("{{ var.method }}", {"var": SomeClass()}, "SomeClass.method"), - - # Multiple levels of attribute access are allowed - 'basic-syntax10': ("{{ var.otherclass.method }}", {"var": SomeClass()}, "OtherClass.method"), - - # Fail silently when a variable's attribute isn't found - 'basic-syntax11': ("{{ var.blech }}", {"var": SomeClass()}, ("", "INVALID")), - - # Raise TemplateSyntaxError when trying to access a variable beginning with an underscore - 'basic-syntax12': ("{{ var.__dict__ }}", {"var": SomeClass()}, template.TemplateSyntaxError), - - # Raise TemplateSyntaxError when trying to access a variable containing an illegal character - 'basic-syntax13': ("{{ va>r }}", {}, template.TemplateSyntaxError), - 'basic-syntax14': ("{{ (var.r) }}", {}, template.TemplateSyntaxError), - 'basic-syntax15': ("{{ sp%am }}", {}, template.TemplateSyntaxError), - 'basic-syntax16': ("{{ eggs! }}", {}, template.TemplateSyntaxError), - 'basic-syntax17': ("{{ moo? }}", {}, template.TemplateSyntaxError), - - # Attribute syntax allows a template to call a dictionary key's value - 'basic-syntax18': ("{{ foo.bar }}", {"foo": {"bar": "baz"}}, "baz"), - - # Fail silently when a variable's dictionary key isn't found - 'basic-syntax19': ("{{ foo.spam }}", {"foo": {"bar": "baz"}}, ("", "INVALID")), - - # Fail silently when accessing a non-simple method - 'basic-syntax20': ("{{ var.method2 }}", {"var": SomeClass()}, ("", "INVALID")), - - # Don't silence a TypeError if it was raised inside a callable - 'basic-syntax20b': ("{{ var.method5 }}", {"var": SomeClass()}, TypeError), - - # Don't get confused when parsing something that is almost, but not - # quite, a template tag. - 'basic-syntax21': ("a {{ moo %} b", {}, "a {{ moo %} b"), - 'basic-syntax22': ("{{ moo #}", {}, "{{ moo #}"), - - # Will try to treat "moo #} {{ cow" as the variable. Not ideal, but - # costly to work around, so this triggers an error. - 'basic-syntax23': ("{{ moo #} {{ cow }}", {"cow": "cow"}, template.TemplateSyntaxError), - - # Embedded newlines make it not-a-tag. - 'basic-syntax24': ("{{ moo\n }}", {}, "{{ moo\n }}"), - - # Literal strings are permitted inside variables, mostly for i18n - # purposes. - 'basic-syntax25': ('{{ "fred" }}', {}, "fred"), - 'basic-syntax26': (r'{{ "\"fred\"" }}', {}, "\"fred\""), - 'basic-syntax27': (r'{{ _("\"fred\"") }}', {}, "\"fred\""), - - # regression test for ticket #12554 - # make sure a silent_variable_failure Exception is suppressed - # on dictionary and attribute lookup - 'basic-syntax28': ("{{ a.b }}", {'a': SilentGetItemClass()}, ('', 'INVALID')), - 'basic-syntax29': ("{{ a.b }}", {'a': SilentAttrClass()}, ('', 'INVALID')), - - # Something that starts like a number but has an extra lookup works as a lookup. - 'basic-syntax30': ("{{ 1.2.3 }}", {"1": {"2": {"3": "d"}}}, "d"), - 'basic-syntax31': ("{{ 1.2.3 }}", {"1": {"2": ("a", "b", "c", "d")}}, "d"), - 'basic-syntax32': ("{{ 1.2.3 }}", {"1": (("x", "x", "x", "x"), ("y", "y", "y", "y"), ("a", "b", "c", "d"))}, "d"), - 'basic-syntax33': ("{{ 1.2.3 }}", {"1": ("xxxx", "yyyy", "abcd")}, "d"), - 'basic-syntax34': ("{{ 1.2.3 }}", {"1": ({"x": "x"}, {"y": "y"}, {"z": "z", "3": "d"})}, "d"), - - # Numbers are numbers even if their digits are in the context. - 'basic-syntax35': ("{{ 1 }}", {"1": "abc"}, "1"), - 'basic-syntax36': ("{{ 1.2 }}", {"1": "abc"}, "1.2"), - - # Call methods in the top level of the context - 'basic-syntax37': ('{{ callable }}', {"callable": lambda: "foo bar"}, "foo bar"), - - # Call methods returned from dictionary lookups - 'basic-syntax38': ('{{ var.callable }}', {"var": {"callable": lambda: "foo bar"}}, "foo bar"), - - 'builtins01': ('{{ True }}', {}, "True"), - 'builtins02': ('{{ False }}', {}, "False"), - 'builtins03': ('{{ None }}', {}, "None"), - - # List-index syntax allows a template to access a certain item of a subscriptable object. - 'list-index01': ("{{ var.1 }}", {"var": ["first item", "second item"]}, "second item"), - - # Fail silently when the list index is out of range. - 'list-index02': ("{{ var.5 }}", {"var": ["first item", "second item"]}, ("", "INVALID")), - - # Fail silently when the variable is not a subscriptable object. - 'list-index03': ("{{ var.1 }}", {"var": None}, ("", "INVALID")), - - # Fail silently when variable is a dict without the specified key. - 'list-index04': ("{{ var.1 }}", {"var": {}}, ("", "INVALID")), - - # Dictionary lookup wins out when dict's key is a string. - 'list-index05': ("{{ var.1 }}", {"var": {'1': "hello"}}, "hello"), - - # But list-index lookup wins out when dict's key is an int, which - # behind the scenes is really a dictionary lookup (for a dict) - # after converting the key to an int. - 'list-index06': ("{{ var.1 }}", {"var": {1: "hello"}}, "hello"), - - # Dictionary lookup wins out when there is a string and int version of the key. - 'list-index07': ("{{ var.1 }}", {"var": {'1': "hello", 1: "world"}}, "hello"), - - # Basic filter usage - 'filter-syntax01': ("{{ var|upper }}", {"var": "Django is the greatest!"}, "DJANGO IS THE GREATEST!"), - - # Chained filters - 'filter-syntax02': ("{{ var|upper|lower }}", {"var": "Django is the greatest!"}, "django is the greatest!"), - - # Allow spaces before the filter pipe - 'filter-syntax03': ("{{ var |upper }}", {"var": "Django is the greatest!"}, "DJANGO IS THE GREATEST!"), - - # Allow spaces after the filter pipe - 'filter-syntax04': ("{{ var| upper }}", {"var": "Django is the greatest!"}, "DJANGO IS THE GREATEST!"), - - # Raise TemplateSyntaxError for a nonexistent filter - 'filter-syntax05': ("{{ var|does_not_exist }}", {}, template.TemplateSyntaxError), - - # Raise TemplateSyntaxError when trying to access a filter containing an illegal character - 'filter-syntax06': ("{{ var|fil(ter) }}", {}, template.TemplateSyntaxError), - - # Raise TemplateSyntaxError for invalid block tags - 'filter-syntax07': ("{% nothing_to_see_here %}", {}, template.TemplateSyntaxError), - - # Raise TemplateSyntaxError for empty block tags - 'filter-syntax08': ("{% %}", {}, template.TemplateSyntaxError), - - # Chained filters, with an argument to the first one - 'filter-syntax09': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "Yes"}, "yes"), - - # Literal string as argument is always "safe" from auto-escaping.. - 'filter-syntax10': (r'{{ var|default_if_none:" endquote\" hah" }}', - {"var": None}, ' endquote" hah'), - - # Variable as argument - 'filter-syntax11': (r'{{ var|default_if_none:var2 }}', {"var": None, "var2": "happy"}, 'happy'), - - # Default argument testing - 'filter-syntax12': (r'{{ var|yesno:"yup,nup,mup" }} {{ var|yesno }}', {"var": True}, 'yup yes'), - - # Fail silently for methods that raise an exception with a - # "silent_variable_failure" attribute - 'filter-syntax13': (r'1{{ var.method3 }}2', {"var": SomeClass()}, ("12", "1INVALID2")), - - # In methods that raise an exception without a - # "silent_variable_attribute" set to True, the exception propagates - 'filter-syntax14': (r'1{{ var.method4 }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException)), - - # Escaped backslash in argument - 'filter-syntax15': (r'{{ var|default_if_none:"foo\bar" }}', {"var": None}, r'foo\bar'), - - # Escaped backslash using known escape char - 'filter-syntax16': (r'{{ var|default_if_none:"foo\now" }}', {"var": None}, r'foo\now'), - - # Empty strings can be passed as arguments to filters - 'filter-syntax17': (r'{{ var|join:"" }}', {'var': ['a', 'b', 'c']}, 'abc'), - - # Make sure that any unicode strings are converted to bytestrings - # in the final output. - 'filter-syntax18': (r'{{ var }}', {'var': UTF8Class()}, '\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'), - - # Numbers as filter arguments should work - 'filter-syntax19': ('{{ var|truncatewords:1 }}', {"var": "hello world"}, "hello ..."), - - # filters should accept empty string constants - 'filter-syntax20': ('{{ ""|default_if_none:"was none" }}', {}, ""), - - # Fail silently for non-callable attribute and dict lookups which - # raise an exception with a "silent_variable_failure" attribute - 'filter-syntax21': (r'1{{ var.silent_fail_key }}2', {"var": SomeClass()}, ("12", "1INVALID2")), - 'filter-syntax22': (r'1{{ var.silent_fail_attribute }}2', {"var": SomeClass()}, ("12", "1INVALID2")), - - # In attribute and dict lookups that raise an unexpected exception - # without a "silent_variable_attribute" set to True, the exception - # propagates - 'filter-syntax23': (r'1{{ var.noisy_fail_key }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException)), - 'filter-syntax24': (r'1{{ var.noisy_fail_attribute }}2', {"var": SomeClass()}, (SomeOtherException, SomeOtherException)), - - # #16383 - A @property that raises AttributeError should not fail loudly. - 'filter-syntax25': ('{{ var.attribute_error_attribute }}', {"var": SomeClass()}, (AttributeError)), - - ### COMMENT SYNTAX ######################################################## - 'comment-syntax01': ("{# this is hidden #}hello", {}, "hello"), - 'comment-syntax02': ("{# this is hidden #}hello{# foo #}", {}, "hello"), - - # Comments can contain invalid stuff. - 'comment-syntax03': ("foo{# {% if %} #}", {}, "foo"), - 'comment-syntax04': ("foo{# {% endblock %} #}", {}, "foo"), - 'comment-syntax05': ("foo{# {% somerandomtag %} #}", {}, "foo"), - 'comment-syntax06': ("foo{# {% #}", {}, "foo"), - 'comment-syntax07': ("foo{# %} #}", {}, "foo"), - 'comment-syntax08': ("foo{# %} #}bar", {}, "foobar"), - 'comment-syntax09': ("foo{# {{ #}", {}, "foo"), - 'comment-syntax10': ("foo{# }} #}", {}, "foo"), - 'comment-syntax11': ("foo{# { #}", {}, "foo"), - 'comment-syntax12': ("foo{# } #}", {}, "foo"), - - ### COMMENT TAG ########################################################### - 'comment-tag01': ("{% comment %}this is hidden{% endcomment %}hello", {}, "hello"), - 'comment-tag02': ("{% comment %}this is hidden{% endcomment %}hello{% comment %}foo{% endcomment %}", {}, "hello"), - - # Comment tag can contain invalid stuff. - 'comment-tag03': ("foo{% comment %} {% if %} {% endcomment %}", {}, "foo"), - 'comment-tag04': ("foo{% comment %} {% endblock %} {% endcomment %}", {}, "foo"), - 'comment-tag05': ("foo{% comment %} {% somerandomtag %} {% endcomment %}", {}, "foo"), - - ### CYCLE TAG ############################################################# - 'cycle01': ('{% cycle a %}', {}, template.TemplateSyntaxError), - 'cycle02': ('{% cycle a,b,c as abc %}{% cycle abc %}', {}, 'ab'), - 'cycle03': ('{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}', {}, 'abc'), - 'cycle04': ('{% cycle a,b,c as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}', {}, 'abca'), - 'cycle05': ('{% cycle %}', {}, template.TemplateSyntaxError), - 'cycle06': ('{% cycle a %}', {}, template.TemplateSyntaxError), - 'cycle07': ('{% cycle a,b,c as foo %}{% cycle bar %}', {}, template.TemplateSyntaxError), - 'cycle08': ('{% cycle a,b,c as foo %}{% cycle foo %}{{ foo }}{{ foo }}{% cycle foo %}{{ foo }}', {}, 'abbbcc'), - 'cycle09': ("{% for i in test %}{% cycle a,b %}{{ i }},{% endfor %}", {'test': range(5)}, 'a0,b1,a2,b3,a4,'), - 'cycle10': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}", {}, 'ab'), - 'cycle11': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}", {}, 'abc'), - 'cycle12': ("{% cycle 'a' 'b' 'c' as abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}", {}, 'abca'), - 'cycle13': ("{% for i in test %}{% cycle 'a' 'b' %}{{ i }},{% endfor %}", {'test': range(5)}, 'a0,b1,a2,b3,a4,'), - 'cycle14': ("{% cycle one two as foo %}{% cycle foo %}", {'one': '1', 'two': '2'}, '12'), - 'cycle15': ("{% for i in test %}{% cycle aye bee %}{{ i }},{% endfor %}", {'test': range(5), 'aye': 'a', 'bee': 'b'}, 'a0,b1,a2,b3,a4,'), - 'cycle16': ("{% cycle one|lower two as foo %}{% cycle foo %}", {'one': 'A', 'two': '2'}, 'a2'), - 'cycle17': ("{% cycle 'a' 'b' 'c' as abc silent %}{% cycle abc %}{% cycle abc %}{% cycle abc %}{% cycle abc %}", {}, ""), - 'cycle18': ("{% cycle 'a' 'b' 'c' as foo invalid_flag %}", {}, template.TemplateSyntaxError), - 'cycle19': ("{% cycle 'a' 'b' as silent %}{% cycle silent %}", {}, "ab"), - 'cycle20': ("{% cycle one two as foo %} & {% cycle foo %}", {'one': 'A & B', 'two': 'C & D'}, "A & B & C & D"), - 'cycle21': ("{% filter force_escape %}{% cycle one two as foo %} & {% cycle foo %}{% endfilter %}", {'one': 'A & B', 'two': 'C & D'}, "A &amp; B & C &amp; D"), - 'cycle22': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{{ x }}{% endfor %}", {'values': [1, 2, 3, 4]}, "1234"), - 'cycle23': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{{ abc }}{{ x }}{% endfor %}", {'values': [1, 2, 3, 4]}, "a1b2c3a4"), - 'included-cycle': ('{{ abc }}', {'abc': 'xxx'}, 'xxx'), - 'cycle24': ("{% for x in values %}{% cycle 'a' 'b' 'c' as abc silent %}{% include 'included-cycle' %}{% endfor %}", {'values': [1, 2, 3, 4]}, "abca"), - 'cycle25': ('{% cycle a as abc %}', {'a': '<'}, '<'), - - 'cycle26': ('{% load cycle from future %}{% cycle a b as ab %}{% cycle ab %}', {'a': '<', 'b': '>'}, '<>'), - 'cycle27': ('{% load cycle from future %}{% autoescape off %}{% cycle a b as ab %}{% cycle ab %}{% endautoescape %}', {'a': '<', 'b': '>'}, '<>'), - 'cycle28': ('{% load cycle from future %}{% cycle a|safe b as ab %}{% cycle ab %}', {'a': '<', 'b': '>'}, '<>'), - - ### EXCEPTIONS ############################################################ - - # Raise exception for invalid template name - 'exception01': ("{% extends 'nonexistent' %}", {}, (template.TemplateDoesNotExist, template.TemplateDoesNotExist)), - - # Raise exception for invalid template name (in variable) - 'exception02': ("{% extends nonexistent %}", {}, (template.TemplateSyntaxError, template.TemplateDoesNotExist)), - - # Raise exception for extra {% extends %} tags - 'exception03': ("{% extends 'inheritance01' %}{% block first %}2{% endblock %}{% extends 'inheritance16' %}", {}, template.TemplateSyntaxError), - - # Raise exception for custom tags used in child with {% load %} tag in parent, not in child - 'exception04': ("{% extends 'inheritance17' %}{% block first %}{% echo 400 %}5678{% endblock %}", {}, template.TemplateSyntaxError), - - # Raise exception for block.super used in base template - 'exception05': ("{% block first %}{{ block.super }}{% endblock %}", {}, template.TemplateSyntaxError), - - ### FILTER TAG ############################################################ - 'filter01': ('{% filter upper %}{% endfilter %}', {}, ''), - 'filter02': ('{% filter upper %}django{% endfilter %}', {}, 'DJANGO'), - 'filter03': ('{% filter upper|lower %}django{% endfilter %}', {}, 'django'), - 'filter04': ('{% filter cut:remove %}djangospam{% endfilter %}', {'remove': 'spam'}, 'django'), - 'filter05': ('{% filter safe %}fail{% endfilter %}', {}, template.TemplateSyntaxError), - 'filter05bis': ('{% filter upper|safe %}fail{% endfilter %}', {}, template.TemplateSyntaxError), - 'filter06': ('{% filter escape %}fail{% endfilter %}', {}, template.TemplateSyntaxError), - 'filter06bis': ('{% filter upper|escape %}fail{% endfilter %}', {}, template.TemplateSyntaxError), - - ### FIRSTOF TAG ########################################################### - 'firstof01': ('{% firstof a b c %}', {'a': 0, 'b': 0, 'c': 0}, ''), - 'firstof02': ('{% firstof a b c %}', {'a': 1, 'b': 0, 'c': 0}, '1'), - 'firstof03': ('{% firstof a b c %}', {'a': 0, 'b': 2, 'c': 0}, '2'), - 'firstof04': ('{% firstof a b c %}', {'a': 0, 'b': 0, 'c': 3}, '3'), - 'firstof05': ('{% firstof a b c %}', {'a': 1, 'b': 2, 'c': 3}, '1'), - 'firstof06': ('{% firstof a b c %}', {'b': 0, 'c': 3}, '3'), - 'firstof07': ('{% firstof a b "c" %}', {'a': 0}, 'c'), - 'firstof08': ('{% firstof a b "c and d" %}', {'a': 0, 'b': 0}, 'c and d'), - 'firstof09': ('{% firstof %}', {}, template.TemplateSyntaxError), - 'firstof10': ('{% firstof a %}', {'a': '<'}, '<'), - - 'firstof11': ('{% load firstof from future %}{% firstof a b %}', {'a': '<', 'b': '>'}, '<'), - 'firstof12': ('{% load firstof from future %}{% firstof a b %}', {'a': '', 'b': '>'}, '>'), - 'firstof13': ('{% load firstof from future %}{% autoescape off %}{% firstof a %}{% endautoescape %}', {'a': '<'}, '<'), - 'firstof14': ('{% load firstof from future %}{% firstof a|safe b %}', {'a': '<'}, '<'), - - ### FOR TAG ############################################################### - 'for-tag01': ("{% for val in values %}{{ val }}{% endfor %}", {"values": [1, 2, 3]}, "123"), - 'for-tag02': ("{% for val in values reversed %}{{ val }}{% endfor %}", {"values": [1, 2, 3]}, "321"), - 'for-tag-vars01': ("{% for val in values %}{{ forloop.counter }}{% endfor %}", {"values": [6, 6, 6]}, "123"), - 'for-tag-vars02': ("{% for val in values %}{{ forloop.counter0 }}{% endfor %}", {"values": [6, 6, 6]}, "012"), - 'for-tag-vars03': ("{% for val in values %}{{ forloop.revcounter }}{% endfor %}", {"values": [6, 6, 6]}, "321"), - 'for-tag-vars04': ("{% for val in values %}{{ forloop.revcounter0 }}{% endfor %}", {"values": [6, 6, 6]}, "210"), - 'for-tag-vars05': ("{% for val in values %}{% if forloop.first %}f{% else %}x{% endif %}{% endfor %}", {"values": [6, 6, 6]}, "fxx"), - 'for-tag-vars06': ("{% for val in values %}{% if forloop.last %}l{% else %}x{% endif %}{% endfor %}", {"values": [6, 6, 6]}, "xxl"), - 'for-tag-unpack01': ("{% for key,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), - 'for-tag-unpack03': ("{% for key, value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), - 'for-tag-unpack04': ("{% for key , value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), - 'for-tag-unpack05': ("{% for key ,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), - 'for-tag-unpack06': ("{% for key value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError), - 'for-tag-unpack07': ("{% for key,,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError), - 'for-tag-unpack08': ("{% for key,value, in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError), - # Ensure that a single loopvar doesn't truncate the list in val. - 'for-tag-unpack09': ("{% for val in items %}{{ val.0 }}:{{ val.1 }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), - 'for-tag-unpack13': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'cheese'))}, ("one:1,carrot/two:2,cheese/", "one:1,carrot/two:2,cheese/")), - 'for-tag-empty01': ("{% for val in values %}{{ val }}{% empty %}empty text{% endfor %}", {"values": [1, 2, 3]}, "123"), - 'for-tag-empty02': ("{% for val in values %}{{ val }}{% empty %}values array empty{% endfor %}", {"values": []}, "values array empty"), - 'for-tag-empty03': ("{% for val in values %}{{ val }}{% empty %}values array not found{% endfor %}", {}, "values array not found"), - # Ticket 19882 - 'for-tag-filter-ws': ("{% load custom %}{% for x in s|noop:'x y' %}{{ x }}{% endfor %}", {'s': 'abc'}, 'abc'), - - # These tests raise deprecation warnings and will raise an exception - # in Django 2.0. The existing behavior is silent truncation if the - # length of loopvars differs to the length of each set of items. - 'for-tag-unpack10': ("{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'orange'))}, "one:1/two:2/"), - 'for-tag-unpack11': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, ("one:1,/two:2,/", "one:1,INVALID/two:2,INVALID/")), - 'for-tag-unpack12': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2))}, ("one:1,carrot/two:2,/", "one:1,carrot/two:2,INVALID/")), - 'for-tag-unpack14': ("{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}", {"items": (1, 2)}, (":/:/", "INVALID:INVALID/INVALID:INVALID/")), - - ### IF TAG ################################################################ - 'if-tag01': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": True}, "yes"), - 'if-tag02': ("{% if foo %}yes{% else %}no{% endif %}", {"foo": False}, "no"), - 'if-tag03': ("{% if foo %}yes{% else %}no{% endif %}", {}, "no"), - - 'if-tag04': ("{% if foo %}foo{% elif bar %}bar{% endif %}", {'foo': True}, "foo"), - 'if-tag05': ("{% if foo %}foo{% elif bar %}bar{% endif %}", {'bar': True}, "bar"), - 'if-tag06': ("{% if foo %}foo{% elif bar %}bar{% endif %}", {}, ""), - 'if-tag07': ("{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}", {'foo': True}, "foo"), - 'if-tag08': ("{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}", {'bar': True}, "bar"), - 'if-tag09': ("{% if foo %}foo{% elif bar %}bar{% else %}nothing{% endif %}", {}, "nothing"), - 'if-tag10': ("{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}", {'foo': True}, "foo"), - 'if-tag11': ("{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}", {'bar': True}, "bar"), - 'if-tag12': ("{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}", {'baz': True}, "baz"), - 'if-tag13': ("{% if foo %}foo{% elif bar %}bar{% elif baz %}baz{% else %}nothing{% endif %}", {}, "nothing"), - - # Filters - 'if-tag-filter01': ("{% if foo|length == 5 %}yes{% else %}no{% endif %}", {'foo': 'abcde'}, "yes"), - 'if-tag-filter02': ("{% if foo|upper == 'ABC' %}yes{% else %}no{% endif %}", {}, "no"), - - # Equality - 'if-tag-eq01': ("{% if foo == bar %}yes{% else %}no{% endif %}", {}, "yes"), - 'if-tag-eq02': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1}, "no"), - 'if-tag-eq03': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1, 'bar': 1}, "yes"), - 'if-tag-eq04': ("{% if foo == bar %}yes{% else %}no{% endif %}", {'foo': 1, 'bar': 2}, "no"), - 'if-tag-eq05': ("{% if foo == '' %}yes{% else %}no{% endif %}", {}, "no"), - - # Comparison - 'if-tag-gt-01': ("{% if 2 > 1 %}yes{% else %}no{% endif %}", {}, "yes"), - 'if-tag-gt-02': ("{% if 1 > 1 %}yes{% else %}no{% endif %}", {}, "no"), - 'if-tag-gte-01': ("{% if 1 >= 1 %}yes{% else %}no{% endif %}", {}, "yes"), - 'if-tag-gte-02': ("{% if 1 >= 2 %}yes{% else %}no{% endif %}", {}, "no"), - 'if-tag-lt-01': ("{% if 1 < 2 %}yes{% else %}no{% endif %}", {}, "yes"), - 'if-tag-lt-02': ("{% if 1 < 1 %}yes{% else %}no{% endif %}", {}, "no"), - 'if-tag-lte-01': ("{% if 1 <= 1 %}yes{% else %}no{% endif %}", {}, "yes"), - 'if-tag-lte-02': ("{% if 2 <= 1 %}yes{% else %}no{% endif %}", {}, "no"), - - # Contains - 'if-tag-in-01': ("{% if 1 in x %}yes{% else %}no{% endif %}", {'x': [1]}, "yes"), - 'if-tag-in-02': ("{% if 2 in x %}yes{% else %}no{% endif %}", {'x': [1]}, "no"), - 'if-tag-not-in-01': ("{% if 1 not in x %}yes{% else %}no{% endif %}", {'x': [1]}, "no"), - 'if-tag-not-in-02': ("{% if 2 not in x %}yes{% else %}no{% endif %}", {'x': [1]}, "yes"), - - # AND - 'if-tag-and01': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'), - 'if-tag-and02': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'), - 'if-tag-and03': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'), - 'if-tag-and04': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'), - 'if-tag-and05': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': False}, 'no'), - 'if-tag-and06': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'bar': False}, 'no'), - 'if-tag-and07': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'foo': True}, 'no'), - 'if-tag-and08': ("{% if foo and bar %}yes{% else %}no{% endif %}", {'bar': True}, 'no'), - - # OR - 'if-tag-or01': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'), - 'if-tag-or02': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'), - 'if-tag-or03': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'), - 'if-tag-or04': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'), - 'if-tag-or05': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': False}, 'no'), - 'if-tag-or06': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': False}, 'no'), - 'if-tag-or07': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'foo': True}, 'yes'), - 'if-tag-or08': ("{% if foo or bar %}yes{% else %}no{% endif %}", {'bar': True}, 'yes'), - - # multiple ORs - 'if-tag-or09': ("{% if foo or bar or baz %}yes{% else %}no{% endif %}", {'baz': True}, 'yes'), - - # NOT - 'if-tag-not01': ("{% if not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'yes'), - 'if-tag-not02': ("{% if not not foo %}no{% else %}yes{% endif %}", {'foo': True}, 'no'), - # not03 to not05 removed, now TemplateSyntaxErrors - - 'if-tag-not06': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {}, 'no'), - 'if-tag-not07': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'), - 'if-tag-not08': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'), - 'if-tag-not09': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'), - 'if-tag-not10': ("{% if foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'), - - 'if-tag-not11': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {}, 'no'), - 'if-tag-not12': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'), - 'if-tag-not13': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'), - 'if-tag-not14': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'), - 'if-tag-not15': ("{% if not foo and bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'no'), - - 'if-tag-not16': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {}, 'yes'), - 'if-tag-not17': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'), - 'if-tag-not18': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'), - 'if-tag-not19': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'), - 'if-tag-not20': ("{% if foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'), - - 'if-tag-not21': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {}, 'yes'), - 'if-tag-not22': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'yes'), - 'if-tag-not23': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'), - 'if-tag-not24': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'), - 'if-tag-not25': ("{% if not foo or bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'), - - 'if-tag-not26': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {}, 'yes'), - 'if-tag-not27': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'), - 'if-tag-not28': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'no'), - 'if-tag-not29': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'no'), - 'if-tag-not30': ("{% if not foo and not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'), - - 'if-tag-not31': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {}, 'yes'), - 'if-tag-not32': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': True}, 'no'), - 'if-tag-not33': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': True, 'bar': False}, 'yes'), - 'if-tag-not34': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': True}, 'yes'), - 'if-tag-not35': ("{% if not foo or not bar %}yes{% else %}no{% endif %}", {'foo': False, 'bar': False}, 'yes'), - - # Various syntax errors - 'if-tag-error01': ("{% if %}yes{% endif %}", {}, template.TemplateSyntaxError), - 'if-tag-error02': ("{% if foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError), - 'if-tag-error03': ("{% if foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError), - 'if-tag-error04': ("{% if not foo and %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError), - 'if-tag-error05': ("{% if not foo or %}yes{% else %}no{% endif %}", {'foo': True}, template.TemplateSyntaxError), - 'if-tag-error06': ("{% if abc def %}yes{% endif %}", {}, template.TemplateSyntaxError), - 'if-tag-error07': ("{% if not %}yes{% endif %}", {}, template.TemplateSyntaxError), - 'if-tag-error08': ("{% if and %}yes{% endif %}", {}, template.TemplateSyntaxError), - 'if-tag-error09': ("{% if or %}yes{% endif %}", {}, template.TemplateSyntaxError), - 'if-tag-error10': ("{% if == %}yes{% endif %}", {}, template.TemplateSyntaxError), - 'if-tag-error11': ("{% if 1 == %}yes{% endif %}", {}, template.TemplateSyntaxError), - 'if-tag-error12': ("{% if a not b %}yes{% endif %}", {}, template.TemplateSyntaxError), - - # If evaluations are shortcircuited where possible - # If is_bad is invoked, it will raise a ShouldNotExecuteException - 'if-tag-shortcircuit01': ('{% if x.is_true or x.is_bad %}yes{% else %}no{% endif %}', {'x': TestObj()}, "yes"), - 'if-tag-shortcircuit02': ('{% if x.is_false and x.is_bad %}yes{% else %}no{% endif %}', {'x': TestObj()}, "no"), - - # Non-existent args - 'if-tag-badarg01': ("{% if x|default_if_none:y %}yes{% endif %}", {}, ''), - 'if-tag-badarg02': ("{% if x|default_if_none:y %}yes{% endif %}", {'y': 0}, ''), - 'if-tag-badarg03': ("{% if x|default_if_none:y %}yes{% endif %}", {'y': 1}, 'yes'), - 'if-tag-badarg04': ("{% if x|default_if_none:y %}yes{% else %}no{% endif %}", {}, 'no'), - - # Additional, more precise parsing tests are in SmartIfTests - - ### IFCHANGED TAG ######################################################### - 'ifchanged01': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', {'num': (1, 2, 3)}, '123'), - 'ifchanged02': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', {'num': (1, 1, 3)}, '13'), - 'ifchanged03': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% endfor %}', {'num': (1, 1, 1)}, '1'), - 'ifchanged04': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', {'num': (1, 2, 3), 'numx': (2, 2, 2)}, '122232'), - 'ifchanged05': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', {'num': (1, 1, 1), 'numx': (1, 2, 3)}, '1123123123'), - 'ifchanged06': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', {'num': (1, 1, 1), 'numx': (2, 2, 2)}, '1222'), - 'ifchanged07': ('{% for n in num %}{% ifchanged %}{{ n }}{% endifchanged %}{% for x in numx %}{% ifchanged %}{{ x }}{% endifchanged %}{% for y in numy %}{% ifchanged %}{{ y }}{% endifchanged %}{% endfor %}{% endfor %}{% endfor %}', {'num': (1, 1, 1), 'numx': (2, 2, 2), 'numy': (3, 3, 3)}, '1233323332333'), - 'ifchanged08': ('{% for data in datalist %}{% for c,d in data %}{% if c %}{% ifchanged %}{{ d }}{% endifchanged %}{% endif %}{% endfor %}{% endfor %}', {'datalist': [[(1, 'a'), (1, 'a'), (0, 'b'), (1, 'c')], [(0, 'a'), (1, 'c'), (1, 'd'), (1, 'd'), (0, 'e')]]}, 'accd'), - - # Test one parameter given to ifchanged. - 'ifchanged-param01': ('{% for n in num %}{% ifchanged n %}..{% endifchanged %}{{ n }}{% endfor %}', {'num': (1, 2, 3)}, '..1..2..3'), - 'ifchanged-param02': ('{% for n in num %}{% for x in numx %}{% ifchanged n %}..{% endifchanged %}{{ x }}{% endfor %}{% endfor %}', {'num': (1, 2, 3), 'numx': (5, 6, 7)}, '..567..567..567'), - - # Test multiple parameters to ifchanged. - 'ifchanged-param03': ('{% for n in num %}{{ n }}{% for x in numx %}{% ifchanged x n %}{{ x }}{% endifchanged %}{% endfor %}{% endfor %}', {'num': (1, 1, 2), 'numx': (5, 6, 6)}, '156156256'), - - # Test a date+hour like construct, where the hour of the last day - # is the same but the date had changed, so print the hour anyway. - 'ifchanged-param04': ('{% for d in days %}{% ifchanged %}{{ d.day }}{% endifchanged %}{% for h in d.hours %}{% ifchanged d h %}{{ h }}{% endifchanged %}{% endfor %}{% endfor %}', {'days': [{'day': 1, 'hours': [1, 2, 3]}, {'day': 2, 'hours': [3]}]}, '112323'), - - # Logically the same as above, just written with explicit - # ifchanged for the day. - 'ifchanged-param05': ('{% for d in days %}{% ifchanged d.day %}{{ d.day }}{% endifchanged %}{% for h in d.hours %}{% ifchanged d.day h %}{{ h }}{% endifchanged %}{% endfor %}{% endfor %}', {'days': [{'day': 1, 'hours': [1, 2, 3]}, {'day': 2, 'hours': [3]}]}, '112323'), - - # Test the else clause of ifchanged. - 'ifchanged-else01': ('{% for id in ids %}{{ id }}{% ifchanged id %}-first{% else %}-other{% endifchanged %},{% endfor %}', {'ids': [1, 1, 2, 2, 2, 3]}, '1-first,1-other,2-first,2-other,2-other,3-first,'), - - 'ifchanged-else02': ('{% for id in ids %}{{ id }}-{% ifchanged id %}{% cycle red,blue %}{% else %}grey{% endifchanged %},{% endfor %}', {'ids': [1, 1, 2, 2, 2, 3]}, '1-red,1-grey,2-blue,2-grey,2-grey,3-red,'), - 'ifchanged-else03': ('{% for id in ids %}{{ id }}{% ifchanged id %}-{% cycle red,blue %}{% else %}{% endifchanged %},{% endfor %}', {'ids': [1, 1, 2, 2, 2, 3]}, '1-red,1,2-blue,2,2,3-red,'), - - 'ifchanged-else04': ('{% for id in ids %}{% ifchanged %}***{{ id }}*{% else %}...{% endifchanged %}{{ forloop.counter }}{% endfor %}', {'ids': [1, 1, 2, 2, 2, 3, 4]}, '***1*1...2***2*3...4...5***3*6***4*7'), - - # Test whitespace in filter arguments - 'ifchanged-filter-ws': ('{% load custom %}{% for n in num %}{% ifchanged n|noop:"x y" %}..{% endifchanged %}{{ n }}{% endfor %}', {'num': (1, 2, 3)}, '..1..2..3'), - - ### IFEQUAL TAG ########################################################### - 'ifequal01': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 2}, ""), - 'ifequal02': ("{% ifequal a b %}yes{% endifequal %}", {"a": 1, "b": 1}, "yes"), - 'ifequal03': ("{% ifequal a b %}yes{% else %}no{% endifequal %}", {"a": 1, "b": 2}, "no"), - 'ifequal04': ("{% ifequal a b %}yes{% else %}no{% endifequal %}", {"a": 1, "b": 1}, "yes"), - 'ifequal05': ("{% ifequal a 'test' %}yes{% else %}no{% endifequal %}", {"a": "test"}, "yes"), - 'ifequal06': ("{% ifequal a 'test' %}yes{% else %}no{% endifequal %}", {"a": "no"}, "no"), - 'ifequal07': ('{% ifequal a "test" %}yes{% else %}no{% endifequal %}', {"a": "test"}, "yes"), - 'ifequal08': ('{% ifequal a "test" %}yes{% else %}no{% endifequal %}', {"a": "no"}, "no"), - 'ifequal09': ('{% ifequal a "test" %}yes{% else %}no{% endifequal %}', {}, "no"), - 'ifequal10': ('{% ifequal a b %}yes{% else %}no{% endifequal %}', {}, "yes"), - - # SMART SPLITTING - 'ifequal-split01': ('{% ifequal a "test man" %}yes{% else %}no{% endifequal %}', {}, "no"), - 'ifequal-split02': ('{% ifequal a "test man" %}yes{% else %}no{% endifequal %}', {'a': 'foo'}, "no"), - 'ifequal-split03': ('{% ifequal a "test man" %}yes{% else %}no{% endifequal %}', {'a': 'test man'}, "yes"), - 'ifequal-split04': ("{% ifequal a 'test man' %}yes{% else %}no{% endifequal %}", {'a': 'test man'}, "yes"), - 'ifequal-split05': ("{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}", {'a': ''}, "no"), - 'ifequal-split06': ("{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}", {'a': 'i "love" you'}, "yes"), - 'ifequal-split07': ("{% ifequal a 'i \"love\" you' %}yes{% else %}no{% endifequal %}", {'a': 'i love you'}, "no"), - 'ifequal-split08': (r"{% ifequal a 'I\'m happy' %}yes{% else %}no{% endifequal %}", {'a': "I'm happy"}, "yes"), - 'ifequal-split09': (r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}", {'a': r"slash\man"}, "yes"), - 'ifequal-split10': (r"{% ifequal a 'slash\man' %}yes{% else %}no{% endifequal %}", {'a': r"slashman"}, "no"), - - # NUMERIC RESOLUTION - 'ifequal-numeric01': ('{% ifequal x 5 %}yes{% endifequal %}', {'x': '5'}, ''), - 'ifequal-numeric02': ('{% ifequal x 5 %}yes{% endifequal %}', {'x': 5}, 'yes'), - 'ifequal-numeric03': ('{% ifequal x 5.2 %}yes{% endifequal %}', {'x': 5}, ''), - 'ifequal-numeric04': ('{% ifequal x 5.2 %}yes{% endifequal %}', {'x': 5.2}, 'yes'), - 'ifequal-numeric05': ('{% ifequal x 0.2 %}yes{% endifequal %}', {'x': .2}, 'yes'), - 'ifequal-numeric06': ('{% ifequal x .2 %}yes{% endifequal %}', {'x': .2}, 'yes'), - 'ifequal-numeric07': ('{% ifequal x 2. %}yes{% endifequal %}', {'x': 2}, ''), - 'ifequal-numeric08': ('{% ifequal x "5" %}yes{% endifequal %}', {'x': 5}, ''), - 'ifequal-numeric09': ('{% ifequal x "5" %}yes{% endifequal %}', {'x': '5'}, 'yes'), - 'ifequal-numeric10': ('{% ifequal x -5 %}yes{% endifequal %}', {'x': -5}, 'yes'), - 'ifequal-numeric11': ('{% ifequal x -5.2 %}yes{% endifequal %}', {'x': -5.2}, 'yes'), - 'ifequal-numeric12': ('{% ifequal x +5 %}yes{% endifequal %}', {'x': 5}, 'yes'), - - # FILTER EXPRESSIONS AS ARGUMENTS - 'ifequal-filter01': ('{% ifequal a|upper "A" %}x{% endifequal %}', {'a': 'a'}, 'x'), - 'ifequal-filter02': ('{% ifequal "A" a|upper %}x{% endifequal %}', {'a': 'a'}, 'x'), - 'ifequal-filter03': ('{% ifequal a|upper b|upper %}x{% endifequal %}', {'a': 'x', 'b': 'X'}, 'x'), - 'ifequal-filter04': ('{% ifequal x|slice:"1" "a" %}x{% endifequal %}', {'x': 'aaa'}, 'x'), - 'ifequal-filter05': ('{% ifequal x|slice:"1"|upper "A" %}x{% endifequal %}', {'x': 'aaa'}, 'x'), - - ### IFNOTEQUAL TAG ######################################################## - 'ifnotequal01': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 2}, "yes"), - 'ifnotequal02': ("{% ifnotequal a b %}yes{% endifnotequal %}", {"a": 1, "b": 1}, ""), - 'ifnotequal03': ("{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}", {"a": 1, "b": 2}, "yes"), - 'ifnotequal04': ("{% ifnotequal a b %}yes{% else %}no{% endifnotequal %}", {"a": 1, "b": 1}, "no"), - - ## INCLUDE TAG ########################################################### - 'include01': ('{% include "basic-syntax01" %}', {}, "something cool"), - 'include02': ('{% include "basic-syntax02" %}', {'headline': 'Included'}, "Included"), - 'include03': ('{% include template_name %}', {'template_name': 'basic-syntax02', 'headline': 'Included'}, "Included"), - 'include04': ('a{% include "nonexistent" %}b', {}, ("ab", "ab", template.TemplateDoesNotExist)), - 'include 05': ('template with a space', {}, 'template with a space'), - 'include06': ('{% include "include 05"%}', {}, 'template with a space'), - - # extra inline context - 'include07': ('{% include "basic-syntax02" with headline="Inline" %}', {'headline': 'Included'}, 'Inline'), - 'include08': ('{% include headline with headline="Dynamic" %}', {'headline': 'basic-syntax02'}, 'Dynamic'), - 'include09': ('{{ first }}--{% include "basic-syntax03" with first=second|lower|upper second=first|upper %}--{{ second }}', {'first': 'Ul', 'second': 'lU'}, 'Ul--LU --- UL--lU'), - - # isolated context - 'include10': ('{% include "basic-syntax03" only %}', {'first': '1'}, (' --- ', 'INVALID --- INVALID')), - 'include11': ('{% include "basic-syntax03" only with second=2 %}', {'first': '1'}, (' --- 2', 'INVALID --- 2')), - 'include12': ('{% include "basic-syntax03" with first=1 only %}', {'second': '2'}, ('1 --- ', '1 --- INVALID')), - - # autoescape context - 'include13': ('{% autoescape off %}{% include "basic-syntax03" %}{% endautoescape %}', {'first': '&'}, ('& --- ', '& --- INVALID')), - 'include14': ('{% autoescape off %}{% include "basic-syntax03" with first=var1 only %}{% endautoescape %}', {'var1': '&'}, ('& --- ', '& --- INVALID')), - - 'include-error01': ('{% include "basic-syntax01" with %}', {}, template.TemplateSyntaxError), - 'include-error02': ('{% include "basic-syntax01" with "no key" %}', {}, template.TemplateSyntaxError), - 'include-error03': ('{% include "basic-syntax01" with dotted.arg="error" %}', {}, template.TemplateSyntaxError), - 'include-error04': ('{% include "basic-syntax01" something_random %}', {}, template.TemplateSyntaxError), - 'include-error05': ('{% include "basic-syntax01" foo="duplicate" foo="key" %}', {}, template.TemplateSyntaxError), - 'include-error06': ('{% include "basic-syntax01" only only %}', {}, template.TemplateSyntaxError), - - ### INCLUSION ERROR REPORTING ############################################# - 'include-fail1': ('{% load bad_tag %}{% badtag %}', {}, RuntimeError), - 'include-fail2': ('{% load broken_tag %}', {}, template.TemplateSyntaxError), - 'include-error07': ('{% include "include-fail1" %}', {}, ('', '', RuntimeError)), - 'include-error08': ('{% include "include-fail2" %}', {}, ('', '', template.TemplateSyntaxError)), - 'include-error09': ('{% include failed_include %}', {'failed_include': 'include-fail1'}, ('', '', RuntimeError)), - 'include-error10': ('{% include failed_include %}', {'failed_include': 'include-fail2'}, ('', '', template.TemplateSyntaxError)), - - - ### NAMED ENDBLOCKS ####################################################### - - # Basic test - 'namedendblocks01': ("1{% block first %}_{% block second %}2{% endblock second %}_{% endblock first %}3", {}, '1_2_3'), - - # Unbalanced blocks - 'namedendblocks02': ("1{% block first %}_{% block second %}2{% endblock first %}_{% endblock second %}3", {}, template.TemplateSyntaxError), - 'namedendblocks03': ("1{% block first %}_{% block second %}2{% endblock %}_{% endblock second %}3", {}, template.TemplateSyntaxError), - 'namedendblocks04': ("1{% block first %}_{% block second %}2{% endblock second %}_{% endblock third %}3", {}, template.TemplateSyntaxError), - 'namedendblocks05': ("1{% block first %}_{% block second %}2{% endblock first %}", {}, template.TemplateSyntaxError), - - # Mixed named and unnamed endblocks - 'namedendblocks06': ("1{% block first %}_{% block second %}2{% endblock %}_{% endblock first %}3", {}, '1_2_3'), - 'namedendblocks07': ("1{% block first %}_{% block second %}2{% endblock second %}_{% endblock %}3", {}, '1_2_3'), - - ### INHERITANCE ########################################################### - - # Standard template with no inheritance - 'inheritance01': ("1{% block first %}&{% endblock %}3{% block second %}_{% endblock %}", {}, '1&3_'), - - # Standard two-level inheritance - 'inheritance02': ("{% extends 'inheritance01' %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {}, '1234'), - - # Three-level with no redefinitions on third level - 'inheritance03': ("{% extends 'inheritance02' %}", {}, '1234'), - - # Two-level with no redefinitions on second level - 'inheritance04': ("{% extends 'inheritance01' %}", {}, '1&3_'), - - # Two-level with double quotes instead of single quotes - 'inheritance05': ('{% extends "inheritance02" %}', {}, '1234'), - - # Three-level with variable parent-template name - 'inheritance06': ("{% extends foo %}", {'foo': 'inheritance02'}, '1234'), - - # Two-level with one block defined, one block not defined - 'inheritance07': ("{% extends 'inheritance01' %}{% block second %}5{% endblock %}", {}, '1&35'), - - # Three-level with one block defined on this level, two blocks defined next level - 'inheritance08': ("{% extends 'inheritance02' %}{% block second %}5{% endblock %}", {}, '1235'), - - # Three-level with second and third levels blank - 'inheritance09': ("{% extends 'inheritance04' %}", {}, '1&3_'), - - # Three-level with space NOT in a block -- should be ignored - 'inheritance10': ("{% extends 'inheritance04' %} ", {}, '1&3_'), - - # Three-level with both blocks defined on this level, but none on second level - 'inheritance11': ("{% extends 'inheritance04' %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {}, '1234'), - - # Three-level with this level providing one and second level providing the other - 'inheritance12': ("{% extends 'inheritance07' %}{% block first %}2{% endblock %}", {}, '1235'), - - # Three-level with this level overriding second level - 'inheritance13': ("{% extends 'inheritance02' %}{% block first %}a{% endblock %}{% block second %}b{% endblock %}", {}, '1a3b'), - - # A block defined only in a child template shouldn't be displayed - 'inheritance14': ("{% extends 'inheritance01' %}{% block newblock %}NO DISPLAY{% endblock %}", {}, '1&3_'), - - # A block within another block - 'inheritance15': ("{% extends 'inheritance01' %}{% block first %}2{% block inner %}inner{% endblock %}{% endblock %}", {}, '12inner3_'), - - # A block within another block (level 2) - 'inheritance16': ("{% extends 'inheritance15' %}{% block inner %}out{% endblock %}", {}, '12out3_'), - - # {% load %} tag (parent -- setup for exception04) - 'inheritance17': ("{% load testtags %}{% block first %}1234{% endblock %}", {}, '1234'), - - # {% load %} tag (standard usage, without inheritance) - 'inheritance18': ("{% load testtags %}{% echo this that theother %}5678", {}, 'this that theother5678'), - - # {% load %} tag (within a child template) - 'inheritance19': ("{% extends 'inheritance01' %}{% block first %}{% load testtags %}{% echo 400 %}5678{% endblock %}", {}, '140056783_'), - - # Two-level inheritance with {{ block.super }} - 'inheritance20': ("{% extends 'inheritance01' %}{% block first %}{{ block.super }}a{% endblock %}", {}, '1&a3_'), - - # Three-level inheritance with {{ block.super }} from parent - 'inheritance21': ("{% extends 'inheritance02' %}{% block first %}{{ block.super }}a{% endblock %}", {}, '12a34'), - - # Three-level inheritance with {{ block.super }} from grandparent - 'inheritance22': ("{% extends 'inheritance04' %}{% block first %}{{ block.super }}a{% endblock %}", {}, '1&a3_'), - - # Three-level inheritance with {{ block.super }} from parent and grandparent - 'inheritance23': ("{% extends 'inheritance20' %}{% block first %}{{ block.super }}b{% endblock %}", {}, '1&ab3_'), - - # Inheritance from local context without use of template loader - 'inheritance24': ("{% extends context_template %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {'context_template': template.Template("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}")}, '1234'), - - # Inheritance from local context with variable parent template - 'inheritance25': ("{% extends context_template.1 %}{% block first %}2{% endblock %}{% block second %}4{% endblock %}", {'context_template': [template.Template("Wrong"), template.Template("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}")]}, '1234'), - - # Set up a base template to extend - 'inheritance26': ("no tags", {}, 'no tags'), - - # Inheritance from a template that doesn't have any blocks - 'inheritance27': ("{% extends 'inheritance26' %}", {}, 'no tags'), - - # Set up a base template with a space in it. - 'inheritance 28': ("{% block first %}!{% endblock %}", {}, '!'), - - # Inheritance from a template with a space in its name should work. - 'inheritance29': ("{% extends 'inheritance 28' %}", {}, '!'), - - # Base template, putting block in a conditional {% if %} tag - 'inheritance30': ("1{% if optional %}{% block opt %}2{% endblock %}{% endif %}3", {'optional': True}, '123'), - - # Inherit from a template with block wrapped in an {% if %} tag (in parent), still gets overridden - 'inheritance31': ("{% extends 'inheritance30' %}{% block opt %}two{% endblock %}", {'optional': True}, '1two3'), - 'inheritance32': ("{% extends 'inheritance30' %}{% block opt %}two{% endblock %}", {}, '13'), - - # Base template, putting block in a conditional {% ifequal %} tag - 'inheritance33': ("1{% ifequal optional 1 %}{% block opt %}2{% endblock %}{% endifequal %}3", {'optional': 1}, '123'), - - # Inherit from a template with block wrapped in an {% ifequal %} tag (in parent), still gets overridden - 'inheritance34': ("{% extends 'inheritance33' %}{% block opt %}two{% endblock %}", {'optional': 1}, '1two3'), - 'inheritance35': ("{% extends 'inheritance33' %}{% block opt %}two{% endblock %}", {'optional': 2}, '13'), - - # Base template, putting block in a {% for %} tag - 'inheritance36': ("{% for n in numbers %}_{% block opt %}{{ n }}{% endblock %}{% endfor %}_", {'numbers': '123'}, '_1_2_3_'), - - # Inherit from a template with block wrapped in an {% for %} tag (in parent), still gets overridden - 'inheritance37': ("{% extends 'inheritance36' %}{% block opt %}X{% endblock %}", {'numbers': '123'}, '_X_X_X_'), - 'inheritance38': ("{% extends 'inheritance36' %}{% block opt %}X{% endblock %}", {}, '_'), - - # The super block will still be found. - 'inheritance39': ("{% extends 'inheritance30' %}{% block opt %}new{{ block.super }}{% endblock %}", {'optional': True}, '1new23'), - 'inheritance40': ("{% extends 'inheritance33' %}{% block opt %}new{{ block.super }}{% endblock %}", {'optional': 1}, '1new23'), - 'inheritance41': ("{% extends 'inheritance36' %}{% block opt %}new{{ block.super }}{% endblock %}", {'numbers': '123'}, '_new1_new2_new3_'), - - # Expression starting and ending with a quote - 'inheritance42': ("{% extends 'inheritance02'|cut:' ' %}", {}, '1234'), - - ### LOADING TAG LIBRARIES ################################################# - 'load01': ("{% load testtags subpackage.echo %}{% echo test %} {% echo2 \"test\" %}", {}, "test test"), - 'load02': ("{% load subpackage.echo %}{% echo2 \"test\" %}", {}, "test"), - - # {% load %} tag, importing individual tags - 'load03': ("{% load echo from testtags %}{% echo this that theother %}", {}, 'this that theother'), - 'load04': ("{% load echo other_echo from testtags %}{% echo this that theother %} {% other_echo and another thing %}", {}, 'this that theother and another thing'), - 'load05': ("{% load echo upper from testtags %}{% echo this that theother %} {{ statement|upper }}", {'statement': 'not shouting'}, 'this that theother NOT SHOUTING'), - 'load06': ("{% load echo2 from subpackage.echo %}{% echo2 \"test\" %}", {}, "test"), - - # {% load %} tag errors - 'load07': ("{% load echo other_echo bad_tag from testtags %}", {}, template.TemplateSyntaxError), - 'load08': ("{% load echo other_echo bad_tag from %}", {}, template.TemplateSyntaxError), - 'load09': ("{% load from testtags %}", {}, template.TemplateSyntaxError), - 'load10': ("{% load echo from bad_library %}", {}, template.TemplateSyntaxError), - 'load11': ("{% load subpackage.echo_invalid %}", {}, template.TemplateSyntaxError), - 'load12': ("{% load subpackage.missing %}", {}, template.TemplateSyntaxError), - - 'lorem1': ("{% lorem 3 w %}", {}, "lorem ipsum dolor"), - - ### I18N ################################################################## - - # {% spaceless %} tag - 'spaceless01': ("{% spaceless %} text {% endspaceless %}", {}, " text "), - 'spaceless02': ("{% spaceless %} \n text \n {% endspaceless %}", {}, " text "), - 'spaceless03': ("{% spaceless %}text{% endspaceless %}", {}, "text"), - 'spaceless04': ("{% spaceless %} {{ text }} {% endspaceless %}", {'text': 'This & that'}, "This & that"), - 'spaceless05': ("{% autoescape off %}{% spaceless %} {{ text }} {% endspaceless %}{% endautoescape %}", {'text': 'This & that'}, "This & that"), - 'spaceless06': ("{% spaceless %} {{ text|safe }} {% endspaceless %}", {'text': 'This & that'}, "This & that"), - - # simple translation of a string delimited by ' - 'i18n01': ("{% load i18n %}{% trans 'xxxyyyxxx' %}", {}, "xxxyyyxxx"), - - # simple translation of a string delimited by " - 'i18n02': ('{% load i18n %}{% trans "xxxyyyxxx" %}', {}, "xxxyyyxxx"), - - # simple translation of a variable - 'i18n03': ('{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}', {'anton': b'\xc3\x85'}, "Å"), - - # simple translation of a variable and filter - 'i18n04': ('{% load i18n %}{% blocktrans with berta=anton|lower %}{{ berta }}{% endblocktrans %}', {'anton': b'\xc3\x85'}, 'å'), - 'legacyi18n04': ('{% load i18n %}{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}', {'anton': b'\xc3\x85'}, 'å'), - - # simple translation of a string with interpolation - 'i18n05': ('{% load i18n %}{% blocktrans %}xxx{{ anton }}xxx{% endblocktrans %}', {'anton': 'yyy'}, "xxxyyyxxx"), - - # simple translation of a string to german - 'i18n06': ('{% load i18n %}{% trans "Page not found" %}', {'LANGUAGE_CODE': 'de'}, "Seite nicht gefunden"), - - # translation of singular form - 'i18n07': ('{% load i18n %}{% blocktrans count counter=number %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 1}, "singular"), - 'legacyi18n07': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 1}, "singular"), - - # translation of plural form - 'i18n08': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 2}, "2 plural"), - 'legacyi18n08': ('{% load i18n %}{% blocktrans count counter=number %}singular{% plural %}{{ counter }} plural{% endblocktrans %}', {'number': 2}, "2 plural"), - - # simple non-translation (only marking) of a string to german - 'i18n09': ('{% load i18n %}{% trans "Page not found" noop %}', {'LANGUAGE_CODE': 'de'}, "Page not found"), - - # translation of a variable with a translated filter - 'i18n10': ('{{ bool|yesno:_("yes,no,maybe") }}', {'bool': True, 'LANGUAGE_CODE': 'de'}, 'Ja'), - - # translation of a variable with a non-translated filter - 'i18n11': ('{{ bool|yesno:"ja,nein" }}', {'bool': True}, 'ja'), - - # usage of the get_available_languages tag - 'i18n12': ('{% load i18n %}{% get_available_languages as langs %}{% for lang in langs %}{% ifequal lang.0 "de" %}{{ lang.0 }}{% endifequal %}{% endfor %}', {}, 'de'), - - # translation of constant strings - 'i18n13': ('{{ _("Password") }}', {'LANGUAGE_CODE': 'de'}, 'Passwort'), - 'i18n14': ('{% cycle "foo" _("Password") _(\'Password\') as c %} {% cycle c %} {% cycle c %}', {'LANGUAGE_CODE': 'de'}, 'foo Passwort Passwort'), - 'i18n15': ('{{ absent|default:_("Password") }}', {'LANGUAGE_CODE': 'de', 'absent': ""}, 'Passwort'), - 'i18n16': ('{{ _("<") }}', {'LANGUAGE_CODE': 'de'}, '<'), - - # Escaping inside blocktrans and trans works as if it was directly in the - # template. - 'i18n17': ('{% load i18n %}{% blocktrans with berta=anton|escape %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, 'α & β'), - 'i18n18': ('{% load i18n %}{% blocktrans with berta=anton|force_escape %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, 'α & β'), - 'i18n19': ('{% load i18n %}{% blocktrans %}{{ andrew }}{% endblocktrans %}', {'andrew': 'a & b'}, 'a & b'), - 'i18n20': ('{% load i18n %}{% trans andrew %}', {'andrew': 'a & b'}, 'a & b'), - 'i18n21': ('{% load i18n %}{% blocktrans %}{{ andrew }}{% endblocktrans %}', {'andrew': mark_safe('a & b')}, 'a & b'), - 'i18n22': ('{% load i18n %}{% trans andrew %}', {'andrew': mark_safe('a & b')}, 'a & b'), - 'legacyi18n17': ('{% load i18n %}{% blocktrans with anton|escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, 'α & β'), - 'legacyi18n18': ('{% load i18n %}{% blocktrans with anton|force_escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, 'α & β'), - - # Use filters with the {% trans %} tag, #5972 - 'i18n23': ('{% load i18n %}{% trans "Page not found"|capfirst|slice:"6:" %}', {'LANGUAGE_CODE': 'de'}, 'nicht gefunden'), - 'i18n24': ("{% load i18n %}{% trans 'Page not found'|upper %}", {'LANGUAGE_CODE': 'de'}, 'SEITE NICHT GEFUNDEN'), - 'i18n25': ('{% load i18n %}{% trans somevar|upper %}', {'somevar': 'Page not found', 'LANGUAGE_CODE': 'de'}, 'SEITE NICHT GEFUNDEN'), - - # translation of plural form with extra field in singular form (#13568) - 'i18n26': ('{% load i18n %}{% blocktrans with extra_field=myextra_field count counter=number %}singular {{ extra_field }}{% plural %}plural{% endblocktrans %}', {'number': 1, 'myextra_field': 'test'}, "singular test"), - 'legacyi18n26': ('{% load i18n %}{% blocktrans with myextra_field as extra_field count number as counter %}singular {{ extra_field }}{% plural %}plural{% endblocktrans %}', {'number': 1, 'myextra_field': 'test'}, "singular test"), - - # translation of singular form in russian (#14126) - 'i18n27': ('{% load i18n %}{% blocktrans count counter=number %}{{ counter }} result{% plural %}{{ counter }} results{% endblocktrans %}', {'number': 1, 'LANGUAGE_CODE': 'ru'}, '1 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442'), - 'legacyi18n27': ('{% load i18n %}{% blocktrans count number as counter %}{{ counter }} result{% plural %}{{ counter }} results{% endblocktrans %}', {'number': 1, 'LANGUAGE_CODE': 'ru'}, '1 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442'), - - # simple translation of multiple variables - 'i18n28': ('{% load i18n %}{% blocktrans with a=anton b=berta %}{{ a }} + {{ b }}{% endblocktrans %}', {'anton': 'α', 'berta': 'β'}, 'α + β'), - 'legacyi18n28': ('{% load i18n %}{% blocktrans with anton as a and berta as b %}{{ a }} + {{ b }}{% endblocktrans %}', {'anton': 'α', 'berta': 'β'}, 'α + β'), - - # retrieving language information - 'i18n28_2': ('{% load i18n %}{% get_language_info for "de" as l %}{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}', {}, 'de: German/Deutsch bidi=False'), - 'i18n29': ('{% load i18n %}{% get_language_info for LANGUAGE_CODE as l %}{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}', {'LANGUAGE_CODE': 'fi'}, 'fi: Finnish/suomi bidi=False'), - 'i18n30': ('{% load i18n %}{% get_language_info_list for langcodes as langs %}{% for l in langs %}{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}; {% endfor %}', {'langcodes': ['it', 'no']}, 'it: Italian/italiano bidi=False; no: Norwegian/norsk bidi=False; '), - 'i18n31': ('{% load i18n %}{% get_language_info_list for langcodes as langs %}{% for l in langs %}{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}; {% endfor %}', {'langcodes': (('sl', 'Slovenian'), ('fa', 'Persian'))}, 'sl: Slovenian/Sloven\u0161\u010dina bidi=False; fa: Persian/\u0641\u0627\u0631\u0633\u06cc bidi=True; '), - 'i18n32': ('{% load i18n %}{{ "hu"|language_name }} {{ "hu"|language_name_local }} {{ "hu"|language_bidi }}', {}, 'Hungarian Magyar False'), - 'i18n33': ('{% load i18n %}{{ langcode|language_name }} {{ langcode|language_name_local }} {{ langcode|language_bidi }}', {'langcode': 'nl'}, 'Dutch Nederlands False'), - - # blocktrans handling of variables which are not in the context. - # this should work as if blocktrans was not there (bug #19915) - 'i18n34': ('{% load i18n %}{% blocktrans %}{{ missing }}{% endblocktrans %}', {}, ('', 'INVALID')), - 'i18n34_2': ("{% load i18n %}{% blocktrans with a='α' %}{{ missing }}{% endblocktrans %}", {}, ('', 'INVALID')), - 'i18n34_3': ('{% load i18n %}{% blocktrans with a=anton %}{{ missing }}{% endblocktrans %}', {'anton': 'α'}, ('', 'INVALID')), - - # trans tag with as var - 'i18n35': ('{% load i18n %}{% trans "Page not found" as page_not_found %}{{ page_not_found }}', {'LANGUAGE_CODE': 'de'}, "Seite nicht gefunden"), - 'i18n36': ('{% load i18n %}{% trans "Page not found" noop as page_not_found %}{{ page_not_found }}', {'LANGUAGE_CODE': 'de'}, "Page not found"), - 'i18n36': ('{% load i18n %}{% trans "Page not found" as page_not_found noop %}{{ page_not_found }}', {'LANGUAGE_CODE': 'de'}, "Page not found"), - 'i18n37': ('{% load i18n %}{% trans "Page not found" as page_not_found %}{% blocktrans %}Error: {{ page_not_found }}{% endblocktrans %}', {'LANGUAGE_CODE': 'de'}, "Error: Seite nicht gefunden"), - - # Test whitespace in filter arguments - 'i18n38': ('{% load i18n custom %}{% get_language_info for "de"|noop:"x y" as l %}{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}', {}, 'de: German/Deutsch bidi=False'), - 'i18n38_2': ('{% load i18n custom %}{% get_language_info_list for langcodes|noop:"x y" as langs %}{% for l in langs %}{{ l.code }}: {{ l.name }}/{{ l.name_local }} bidi={{ l.bidi }}; {% endfor %}', {'langcodes': ['it', 'no']}, 'it: Italian/italiano bidi=False; no: Norwegian/norsk bidi=False; '), - - ### HANDLING OF TEMPLATE_STRING_IF_INVALID ################################### - - 'invalidstr01': ('{{ var|default:"Foo" }}', {}, ('Foo', 'INVALID')), - 'invalidstr02': ('{{ var|default_if_none:"Foo" }}', {}, ('', 'INVALID')), - 'invalidstr03': ('{% for v in var %}({{ v }}){% endfor %}', {}, ''), - 'invalidstr04': ('{% if var %}Yes{% else %}No{% endif %}', {}, 'No'), - 'invalidstr04_2': ('{% if var|default:"Foo" %}Yes{% else %}No{% endif %}', {}, 'Yes'), - 'invalidstr05': ('{{ var }}', {}, ('', ('INVALID %s', 'var'))), - 'invalidstr06': ('{{ var.prop }}', {'var': {}}, ('', ('INVALID %s', 'var.prop'))), - 'invalidstr07': ('{% load i18n %}{% blocktrans %}{{ var }}{% endblocktrans %}', - {}, ('', ('INVALID %s', 'var'))), - - ### MULTILINE ############################################################# - - 'multiline01': (""" - Hello, - boys. - How - are - you - gentlemen. - """, - {}, - """ - Hello, - boys. - How - are - you - gentlemen. - """), - - ### REGROUP TAG ########################################################### - 'regroup01': ('{% regroup data by bar as grouped %}' - '{% for group in grouped %}' - '{{ group.grouper }}:' - '{% for item in group.list %}' - '{{ item.foo }}' - '{% endfor %},' - '{% endfor %}', - {'data': [{'foo': 'c', 'bar': 1}, - {'foo': 'd', 'bar': 1}, - {'foo': 'a', 'bar': 2}, - {'foo': 'b', 'bar': 2}, - {'foo': 'x', 'bar': 3}]}, - '1:cd,2:ab,3:x,'), - - # Test for silent failure when target variable isn't found - 'regroup02': ('{% regroup data by bar as grouped %}' - '{% for group in grouped %}' - '{{ group.grouper }}:' - '{% for item in group.list %}' - '{{ item.foo }}' - '{% endfor %},' - '{% endfor %}', - {}, ''), - - # Regression tests for #17675 - # The date template filter has expects_localtime = True - 'regroup03': ('{% regroup data by at|date:"m" as grouped %}' - '{% for group in grouped %}' - '{{ group.grouper }}:' - '{% for item in group.list %}' - '{{ item.at|date:"d" }}' - '{% endfor %},' - '{% endfor %}', - {'data': [{'at': date(2012, 2, 14)}, - {'at': date(2012, 2, 28)}, - {'at': date(2012, 7, 4)}]}, - '02:1428,07:04,'), - # The join template filter has needs_autoescape = True - 'regroup04': ('{% regroup data by bar|join:"" as grouped %}' - '{% for group in grouped %}' - '{{ group.grouper }}:' - '{% for item in group.list %}' - '{{ item.foo|first }}' - '{% endfor %},' - '{% endfor %}', - {'data': [{'foo': 'x', 'bar': ['ab', 'c']}, - {'foo': 'y', 'bar': ['a', 'bc']}, - {'foo': 'z', 'bar': ['a', 'd']}]}, - 'abc:xy,ad:z,'), - - # Test syntax - 'regroup05': ('{% regroup data by bar as %}', {}, - template.TemplateSyntaxError), - 'regroup06': ('{% regroup data by bar thisaintright grouped %}', {}, - template.TemplateSyntaxError), - 'regroup07': ('{% regroup data thisaintright bar as grouped %}', {}, - template.TemplateSyntaxError), - 'regroup08': ('{% regroup data by bar as grouped toomanyargs %}', {}, - template.TemplateSyntaxError), - - ### SSI TAG ######################################################## - - # Test normal behavior - 'ssi01': ('{%% ssi "%s" %%}' % os.path.join(basedir, 'templates', 'ssi_include.html'), {}, 'This is for testing an ssi include. {{ test }}\n'), - 'ssi02': ('{%% ssi "%s" %%}' % os.path.join(basedir, 'not_here'), {}, ''), - 'ssi03': ("{%% ssi '%s' %%}" % os.path.join(basedir, 'not_here'), {}, ''), - - # Test passing as a variable - 'ssi04': ('{% load ssi from future %}{% ssi ssi_file %}', {'ssi_file': os.path.join(basedir, 'templates', 'ssi_include.html')}, 'This is for testing an ssi include. {{ test }}\n'), - 'ssi05': ('{% load ssi from future %}{% ssi ssi_file %}', {'ssi_file': 'no_file'}, ''), - - # Test parsed output - 'ssi06': ('{%% ssi "%s" parsed %%}' % os.path.join(basedir, 'templates', 'ssi_include.html'), {'test': 'Look ma! It parsed!'}, 'This is for testing an ssi include. Look ma! It parsed!\n'), - 'ssi07': ('{%% ssi "%s" parsed %%}' % os.path.join(basedir, 'not_here'), {'test': 'Look ma! It parsed!'}, ''), - - # Test space in file name - 'ssi08': ('{%% ssi "%s" %%}' % os.path.join(basedir, 'templates', 'ssi include with spaces.html'), {}, 'This is for testing an ssi include with spaces in its name. {{ test }}\n'), - 'ssi09': ('{%% ssi "%s" parsed %%}' % os.path.join(basedir, 'templates', 'ssi include with spaces.html'), {'test': 'Look ma! It parsed!'}, 'This is for testing an ssi include with spaces in its name. Look ma! It parsed!\n'), - - ### TEMPLATETAG TAG ####################################################### - 'templatetag01': ('{% templatetag openblock %}', {}, '{%'), - 'templatetag02': ('{% templatetag closeblock %}', {}, '%}'), - 'templatetag03': ('{% templatetag openvariable %}', {}, '{{'), - 'templatetag04': ('{% templatetag closevariable %}', {}, '}}'), - 'templatetag05': ('{% templatetag %}', {}, template.TemplateSyntaxError), - 'templatetag06': ('{% templatetag foo %}', {}, template.TemplateSyntaxError), - 'templatetag07': ('{% templatetag openbrace %}', {}, '{'), - 'templatetag08': ('{% templatetag closebrace %}', {}, '}'), - 'templatetag09': ('{% templatetag openbrace %}{% templatetag openbrace %}', {}, '{{'), - 'templatetag10': ('{% templatetag closebrace %}{% templatetag closebrace %}', {}, '}}'), - 'templatetag11': ('{% templatetag opencomment %}', {}, '{#'), - 'templatetag12': ('{% templatetag closecomment %}', {}, '#}'), - - # Simple tags with customized names - 'simpletag-renamed01': ('{% load custom %}{% minusone 7 %}', {}, '6'), - 'simpletag-renamed02': ('{% load custom %}{% minustwo 7 %}', {}, '5'), - 'simpletag-renamed03': ('{% load custom %}{% minustwo_overridden_name 7 %}', {}, template.TemplateSyntaxError), - - ### WIDTHRATIO TAG ######################################################## - 'widthratio01': ('{% widthratio a b 0 %}', {'a': 50, 'b': 100}, '0'), - 'widthratio02': ('{% widthratio a b 100 %}', {'a': 0, 'b': 0}, '0'), - 'widthratio03': ('{% widthratio a b 100 %}', {'a': 0, 'b': 100}, '0'), - 'widthratio04': ('{% widthratio a b 100 %}', {'a': 50, 'b': 100}, '50'), - 'widthratio05': ('{% widthratio a b 100 %}', {'a': 100, 'b': 100}, '100'), - - # 62.5 should round to 63 on Python 2 and 62 on Python 3 - # See http://docs.python.org/py3k/whatsnew/3.0.html - 'widthratio06': ('{% widthratio a b 100 %}', {'a': 50, 'b': 80}, '62' if six.PY3 else '63'), - - # 71.4 should round to 71 - 'widthratio07': ('{% widthratio a b 100 %}', {'a': 50, 'b': 70}, '71'), - - # Raise exception if we don't have 3 args, last one an integer - 'widthratio08': ('{% widthratio %}', {}, template.TemplateSyntaxError), - 'widthratio09': ('{% widthratio a b %}', {'a': 50, 'b': 100}, template.TemplateSyntaxError), - 'widthratio10': ('{% widthratio a b 100.0 %}', {'a': 50, 'b': 100}, '50'), - - # #10043: widthratio should allow max_width to be a variable - 'widthratio11': ('{% widthratio a b c %}', {'a': 50, 'b': 100, 'c': 100}, '50'), - - # #18739: widthratio should handle None args consistently with non-numerics - 'widthratio12a': ('{% widthratio a b c %}', {'a': 'a', 'b': 100, 'c': 100}, ''), - 'widthratio12b': ('{% widthratio a b c %}', {'a': None, 'b': 100, 'c': 100}, ''), - 'widthratio13a': ('{% widthratio a b c %}', {'a': 0, 'b': 'b', 'c': 100}, ''), - 'widthratio13b': ('{% widthratio a b c %}', {'a': 0, 'b': None, 'c': 100}, ''), - 'widthratio14a': ('{% widthratio a b c %}', {'a': 0, 'b': 100, 'c': 'c'}, template.TemplateSyntaxError), - 'widthratio14b': ('{% widthratio a b c %}', {'a': 0, 'b': 100, 'c': None}, template.TemplateSyntaxError), - - # Test whitespace in filter argument - 'widthratio15': ('{% load custom %}{% widthratio a|noop:"x y" b 0 %}', {'a': 50, 'b': 100}, '0'), - - # Widthratio with variable assignment - 'widthratio16': ('{% widthratio a b 100 as variable %}-{{ variable }}-', {'a': 50, 'b': 100}, '-50-'), - 'widthratio17': ('{% widthratio a b 100 as variable %}-{{ variable }}-', {'a': 100, 'b': 100}, '-100-'), - - 'widthratio18': ('{% widthratio a b 100 as %}', {}, template.TemplateSyntaxError), - 'widthratio19': ('{% widthratio a b 100 not_as variable %}', {}, template.TemplateSyntaxError), - 'widthratio20': ('{% widthratio a b 100 %}', {'a': float('inf'), 'b': float('inf')}, ''), - 'widthratio21': ('{% widthratio a b 100 %}', {'a': float('inf'), 'b': 2}, ''), - - ### WITH TAG ######################################################## - 'with01': ('{% with key=dict.key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'), - 'legacywith01': ('{% with dict.key as key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, '50'), - - 'with02': ('{{ key }}{% with key=dict.key %}{{ key }}-{{ dict.key }}-{{ key }}{% endwith %}{{ key }}', {'dict': {'key': 50}}, ('50-50-50', 'INVALID50-50-50INVALID')), - 'legacywith02': ('{{ key }}{% with dict.key as key %}{{ key }}-{{ dict.key }}-{{ key }}{% endwith %}{{ key }}', {'dict': {'key': 50}}, ('50-50-50', 'INVALID50-50-50INVALID')), - - 'with03': ('{% with a=alpha b=beta %}{{ a }}{{ b }}{% endwith %}', {'alpha': 'A', 'beta': 'B'}, 'AB'), - - 'with-error01': ('{% with dict.key xx key %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, template.TemplateSyntaxError), - 'with-error02': ('{% with dict.key as %}{{ key }}{% endwith %}', {'dict': {'key': 50}}, template.TemplateSyntaxError), - - ### NOW TAG ######################################################## - # Simple case - 'now01': ('{% now "j n Y" %}', {}, "%d %d %d" % ( - datetime.now().day, datetime.now().month, datetime.now().year)), - # Check parsing of locale strings - 'now02': ('{% now "DATE_FORMAT" %}', {}, date_format(datetime.now())), - # Also accept simple quotes - #15092 - 'now03': ("{% now 'j n Y' %}", {}, "%d %d %d" % ( - datetime.now().day, datetime.now().month, datetime.now().year)), - 'now04': ("{% now 'DATE_FORMAT' %}", {}, date_format(datetime.now())), - 'now05': ('''{% now 'j "n" Y'%}''', {}, '''%d "%d" %d''' % ( - datetime.now().day, datetime.now().month, datetime.now().year)), - 'now06': ('''{% now "j 'n' Y"%}''', {}, '''%d '%d' %d''' % ( - datetime.now().day, datetime.now().month, datetime.now().year)), - 'now07': ('''{% now "j n Y" as N %}-{{N}}-''', {}, '''-%d %d %d-''' % ( - datetime.now().day, datetime.now().month, datetime.now().year)), - - ### URL TAG ######################################################## - # Successes - 'url01': ('{% url "template_tests.views.client" client.id %}', {'client': {'id': 1}}, '/client/1/'), - 'url02': ('{% url "template_tests.views.client_action" id=client.id action="update" %}', {'client': {'id': 1}}, '/client/1/update/'), - 'url02a': ('{% url "template_tests.views.client_action" client.id "update" %}', {'client': {'id': 1}}, '/client/1/update/'), - 'url02b': ("{% url 'template_tests.views.client_action' id=client.id action='update' %}", {'client': {'id': 1}}, '/client/1/update/'), - 'url02c': ("{% url 'template_tests.views.client_action' client.id 'update' %}", {'client': {'id': 1}}, '/client/1/update/'), - 'url03': ('{% url "template_tests.views.index" %}', {}, '/'), - 'url04': ('{% url "named.client" client.id %}', {'client': {'id': 1}}, '/named-client/1/'), - 'url05': ('{% url "метка_оператора" v %}', {'v': 'Ω'}, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'), - 'url06': ('{% url "метка_оператора_2" tag=v %}', {'v': 'Ω'}, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'), - 'url07': ('{% url "template_tests.views.client2" tag=v %}', {'v': 'Ω'}, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'), - 'url08': ('{% url "метка_оператора" v %}', {'v': 'Ω'}, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'), - 'url09': ('{% url "метка_оператора_2" tag=v %}', {'v': 'Ω'}, '/%D0%AE%D0%BD%D0%B8%D0%BA%D0%BE%D0%B4/%CE%A9/'), - 'url10': ('{% url "template_tests.views.client_action" id=client.id action="two words" %}', {'client': {'id': 1}}, '/client/1/two%20words/'), - 'url11': ('{% url "template_tests.views.client_action" id=client.id action="==" %}', {'client': {'id': 1}}, '/client/1/==/'), - 'url12': ('{% url "template_tests.views.client_action" id=client.id action="!$&\'()*+,;=~:@," %}', {'client': {'id': 1}}, '/client/1/!$&\'()*+,;=~:@,/'), - 'url13': ('{% url "template_tests.views.client_action" id=client.id action=arg|join:"-" %}', {'client': {'id': 1}, 'arg': ['a', 'b']}, '/client/1/a-b/'), - 'url14': ('{% url "template_tests.views.client_action" client.id arg|join:"-" %}', {'client': {'id': 1}, 'arg': ['a', 'b']}, '/client/1/a-b/'), - 'url15': ('{% url "template_tests.views.client_action" 12 "test" %}', {}, '/client/12/test/'), - 'url18': ('{% url "template_tests.views.client" "1,2" %}', {}, '/client/1,2/'), - - 'url19': ('{% url named_url client.id %}', {'named_url': 'template_tests.views.client', 'client': {'id': 1}}, '/client/1/'), - 'url20': ('{% url url_name_in_var client.id %}', {'url_name_in_var': 'named.client', 'client': {'id': 1}}, '/named-client/1/'), - - # Failures - 'url-fail01': ('{% url %}', {}, template.TemplateSyntaxError), - 'url-fail02': ('{% url "no_such_view" %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)), - 'url-fail03': ('{% url "template_tests.views.client" %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)), - 'url-fail04': ('{% url "view" id, %}', {}, template.TemplateSyntaxError), - 'url-fail05': ('{% url "view" id= %}', {}, template.TemplateSyntaxError), - 'url-fail06': ('{% url "view" a.id=id %}', {}, template.TemplateSyntaxError), - 'url-fail07': ('{% url "view" a.id!id %}', {}, template.TemplateSyntaxError), - 'url-fail08': ('{% url "view" id="unterminatedstring %}', {}, template.TemplateSyntaxError), - 'url-fail09': ('{% url "view" id=", %}', {}, template.TemplateSyntaxError), - - 'url-fail11': ('{% url named_url %}', {}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)), - 'url-fail12': ('{% url named_url %}', {'named_url': 'no_such_view'}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)), - 'url-fail13': ('{% url named_url %}', {'named_url': 'template_tests.views.client'}, (urlresolvers.NoReverseMatch, urlresolvers.NoReverseMatch)), - 'url-fail14': ('{% url named_url id, %}', {'named_url': 'view'}, template.TemplateSyntaxError), - 'url-fail15': ('{% url named_url id= %}', {'named_url': 'view'}, template.TemplateSyntaxError), - 'url-fail16': ('{% url named_url a.id=id %}', {'named_url': 'view'}, template.TemplateSyntaxError), - 'url-fail17': ('{% url named_url a.id!id %}', {'named_url': 'view'}, template.TemplateSyntaxError), - 'url-fail18': ('{% url named_url id="unterminatedstring %}', {'named_url': 'view'}, template.TemplateSyntaxError), - 'url-fail19': ('{% url named_url id=", %}', {'named_url': 'view'}, template.TemplateSyntaxError), - - # {% url ... as var %} - 'url-asvar01': ('{% url "template_tests.views.index" as url %}', {}, ''), - 'url-asvar02': ('{% url "template_tests.views.index" as url %}{{ url }}', {}, '/'), - 'url-asvar03': ('{% url "no_such_view" as url %}{{ url }}', {}, ''), - - ### CACHE TAG ###################################################### - 'cache03': ('{% load cache %}{% cache 2 test %}cache03{% endcache %}', {}, 'cache03'), - 'cache04': ('{% load cache %}{% cache 2 test %}cache04{% endcache %}', {}, 'cache03'), - 'cache05': ('{% load cache %}{% cache 2 test foo %}cache05{% endcache %}', {'foo': 1}, 'cache05'), - 'cache06': ('{% load cache %}{% cache 2 test foo %}cache06{% endcache %}', {'foo': 2}, 'cache06'), - 'cache07': ('{% load cache %}{% cache 2 test foo %}cache07{% endcache %}', {'foo': 1}, 'cache05'), - - # Allow first argument to be a variable. - 'cache08': ('{% load cache %}{% cache time test foo %}cache08{% endcache %}', {'foo': 2, 'time': 2}, 'cache06'), - - # Raise exception if we don't have at least 2 args, first one integer. - 'cache11': ('{% load cache %}{% cache %}{% endcache %}', {}, template.TemplateSyntaxError), - 'cache12': ('{% load cache %}{% cache 1 %}{% endcache %}', {}, template.TemplateSyntaxError), - 'cache13': ('{% load cache %}{% cache foo bar %}{% endcache %}', {}, template.TemplateSyntaxError), - 'cache14': ('{% load cache %}{% cache foo bar %}{% endcache %}', {'foo': 'fail'}, template.TemplateSyntaxError), - 'cache15': ('{% load cache %}{% cache foo bar %}{% endcache %}', {'foo': []}, template.TemplateSyntaxError), - - # Regression test for #7460. - 'cache16': ('{% load cache %}{% cache 1 foo bar %}{% endcache %}', {'foo': 'foo', 'bar': 'with spaces'}, ''), - - # Regression test for #11270. - 'cache17': ('{% load cache %}{% cache 10 long_cache_key poem %}Some Content{% endcache %}', {'poem': 'Oh freddled gruntbuggly/Thy micturations are to me/As plurdled gabbleblotchits/On a lurgid bee/That mordiously hath bitled out/Its earted jurtles/Into a rancid festering/Or else I shall rend thee in the gobberwarts with my blurglecruncheon/See if I dont.'}, 'Some Content'), - - # Test whitespace in filter arguments - 'cache18': ('{% load cache custom %}{% cache 2|noop:"x y" cache18 %}cache18{% endcache %}', {}, 'cache18'), - - ### AUTOESCAPE TAG ############################################## - 'autoescape-tag01': ("{% autoescape off %}hello{% endautoescape %}", {}, "hello"), - 'autoescape-tag02': ("{% autoescape off %}{{ first }}{% endautoescape %}", {"first": "hello"}, "hello"), - 'autoescape-tag03': ("{% autoescape on %}{{ first }}{% endautoescape %}", {"first": "hello"}, "<b>hello</b>"), - - # Autoescape disabling and enabling nest in a predictable way. - 'autoescape-tag04': ("{% autoescape off %}{{ first }} {% autoescape on%}{{ first }}{% endautoescape %}{% endautoescape %}", {"first": ""}, " <a>"), - - 'autoescape-tag05': ("{% autoescape on %}{{ first }}{% endautoescape %}", {"first": "first"}, "<b>first</b>"), - - # Strings (ASCII or unicode) already marked as "safe" are not - # auto-escaped - 'autoescape-tag06': ("{{ first }}", {"first": mark_safe("first")}, "first"), - 'autoescape-tag07': ("{% autoescape on %}{{ first }}{% endautoescape %}", {"first": mark_safe("Apple")}, "Apple"), - - # Literal string arguments to filters, if used in the result, are - # safe. - 'autoescape-tag08': (r'{% autoescape on %}{{ var|default_if_none:" endquote\" hah" }}{% endautoescape %}', {"var": None}, ' endquote" hah'), - - # Objects which return safe strings as their __unicode__ method - # won't get double-escaped. - 'autoescape-tag09': (r'{{ unsafe }}', {'unsafe': filters.UnsafeClass()}, 'you & me'), - 'autoescape-tag10': (r'{{ safe }}', {'safe': filters.SafeClass()}, 'you > me'), - - # The "safe" and "escape" filters cannot work due to internal - # implementation details (fortunately, the (no)autoescape block - # tags can be used in those cases) - 'autoescape-filtertag01': ("{{ first }}{% filter safe %}{{ first }} x"}, template.TemplateSyntaxError), - - # ifqeual compares unescaped vales. - 'autoescape-ifequal01': ('{% ifequal var "this & that" %}yes{% endifequal %}', {"var": "this & that"}, "yes"), - - # Arguments to filters are 'safe' and manipulate their input unescaped. - 'autoescape-filters01': ('{{ var|cut:"&" }}', {"var": "this & that"}, "this that"), - 'autoescape-filters02': ('{{ var|join:" & \" }}', {"var": ("Tom", "Dick", "Harry")}, "Tom & Dick & Harry"), - - # Literal strings are safe. - 'autoescape-literals01': ('{{ "this & that" }}', {}, "this & that"), - - # Iterating over strings outputs safe characters. - 'autoescape-stringiterations01': ('{% for l in var %}{{ l }},{% endfor %}', {'var': 'K&R'}, "K,&,R,"), - - # Escape requirement survives lookup. - 'autoescape-lookup01': ('{{ var.key }}', {"var": {"key": "this & that"}}, "this & that"), - - # Static template tags - 'static-prefixtag01': ('{% load static %}{% get_static_prefix %}', {}, settings.STATIC_URL), - 'static-prefixtag02': ('{% load static %}{% get_static_prefix as static_prefix %}{{ static_prefix }}', {}, settings.STATIC_URL), - 'static-prefixtag03': ('{% load static %}{% get_media_prefix %}', {}, settings.MEDIA_URL), - 'static-prefixtag04': ('{% load static %}{% get_media_prefix as media_prefix %}{{ media_prefix }}', {}, settings.MEDIA_URL), - 'static-statictag01': ('{% load static %}{% static "admin/base.css" %}', {}, urljoin(settings.STATIC_URL, 'admin/base.css')), - 'static-statictag02': ('{% load static %}{% static base_css %}', {'base_css': 'admin/base.css'}, urljoin(settings.STATIC_URL, 'admin/base.css')), - 'static-statictag03': ('{% load static %}{% static "admin/base.css" as foo %}{{ foo }}', {}, urljoin(settings.STATIC_URL, 'admin/base.css')), - 'static-statictag04': ('{% load static %}{% static base_css as foo %}{{ foo }}', {'base_css': 'admin/base.css'}, urljoin(settings.STATIC_URL, 'admin/base.css')), - - # Verbatim template tag outputs contents without rendering. - 'verbatim-tag01': ('{% verbatim %}{{bare }}{% endverbatim %}', {}, '{{bare }}'), - 'verbatim-tag02': ('{% verbatim %}{% endif %}{% endverbatim %}', {}, '{% endif %}'), - 'verbatim-tag03': ("{% verbatim %}It's the {% verbatim %} tag{% endverbatim %}", {}, "It's the {% verbatim %} tag"), - 'verbatim-tag04': ('{% verbatim %}{% verbatim %}{% endverbatim %}{% endverbatim %}', {}, template.TemplateSyntaxError), - 'verbatim-tag05': ('{% verbatim %}{% endverbatim %}{% verbatim %}{% endverbatim %}', {}, ''), - 'verbatim-tag06': ("{% verbatim special %}Don't {% endverbatim %} just yet{% endverbatim special %}", {}, "Don't {% endverbatim %} just yet"), - } - - if numpy: - tests.update({ - # Numpy's array-index syntax allows a template to access a certain item of a subscriptable object. - 'numpy-array-index01': ("{{ var.1 }}", {"var": numpy.array(["first item", "second item"])}, "second item"), - - # Fail silently when the array index is out of range. - 'numpy-array-index02': ("{{ var.5 }}", {"var": numpy.array(["first item", "second item"])}, ("", "INVALID")), - }) - - return tests - class TemplateTagLoading(TestCase):