Commit b872134b authored by Preston Timmons's avatar Preston Timmons Committed by Tim Graham
Browse files

Fixed #23768 -- Rewrote template tests as unit tests.

parent 4a4ad277
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -554,6 +554,7 @@ answer newbie questions, and generally made Django that much better:
    polpak@yahoo.com
    pradeep.gowda@gmail.com
    Preston Holmes <preston@ptone.com>
    Preston Timmons <prestontimmons@gmail.com>
    Rachel Willmer <http://www.willmer.com/kb/>
    Radek Švarz <http://www.svarz.cz/translate/>
    Rajesh Dhawan <rajesh.dhawan@gmail.com>
+1 −13
Original line number Diff line number Diff line
@@ -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 &gt; me')

# RESULT SYNTAX --
# 'template_name': ('template contents', 'context dict',
#                   'expected string output' or Exception class)
+0 −0

Empty file added.

+121 −0
Original line number Diff line number Diff line
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': '<b>hello</b>'})
        self.assertEqual(output, '<b>hello</b>')

    @setup({'autoescape-tag03': '{% autoescape on %}{{ first }}{% endautoescape %}'})
    def test_autoescape_tag03(self):
        output = render('autoescape-tag03', {'first': '<b>hello</b>'})
        self.assertEqual(output, '&lt;b&gt;hello&lt;/b&gt;')

    # 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': '<a>'})
        self.assertEqual(output, '<a> &lt;a&gt;')

    @setup({'autoescape-tag05': '{% autoescape on %}{{ first }}{% endautoescape %}'})
    def test_autoescape_tag05(self):
        output = render('autoescape-tag05', {'first': '<b>first</b>'})
        self.assertEqual(output, '&lt;b&gt;first&lt;/b&gt;')

    # 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('<b>first</b>')})
        self.assertEqual(output, '<b>first</b>')

    @setup({'autoescape-tag07': '{% autoescape on %}{{ first }}{% endautoescape %}'})
    def test_autoescape_tag07(self):
        output = render('autoescape-tag07', {'first': mark_safe('<b>Apple</b>')})
        self.assertEqual(output, '<b>Apple</b>')

    @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 &amp; me')

    @setup({'autoescape-tag10': r'{{ safe }}'})
    def test_autoescape_tag10(self):
        output = render('autoescape-tag10', {'safe': SafeClass()})
        self.assertEqual(output, 'you &gt; me')

    @setup({'autoescape-filtertag01': '{{ first }}{% filter safe %}{{ first }} x<y{% endfilter %}'})
    def test_autoescape_filtertag01(self):
        """
        The "safe" and "escape" filters cannot work due to internal
        implementation details (fortunately, the (no)autoescape block
        tags can be used in those cases)
        """
        with self.assertRaises(TemplateSyntaxError):
            render('autoescape-filtertag01', {'first': '<a>'})

    @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,&amp;,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 &amp; that')
+316 −0
Original line number Diff line number Diff line
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')
Loading