From b872134bfc14f6322bd1e4b0a08bf5bfd2c43a52 Mon Sep 17 00:00:00 2001 From: Preston Timmons Date: Tue, 11 Nov 2014 19:32:44 -0600 Subject: Fixed #23768 -- Rewrote template tests as unit tests. --- tests/template_tests/syntax_tests/test_basic.py | 316 ++++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 tests/template_tests/syntax_tests/test_basic.py (limited to 'tests/template_tests/syntax_tests/test_basic.py') 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') -- cgit v1.3