summaryrefslogtreecommitdiff
path: root/tests/regressiontests
diff options
context:
space:
mode:
Diffstat (limited to 'tests/regressiontests')
-rw-r--r--tests/regressiontests/cache/tests.py30
-rw-r--r--tests/regressiontests/datastructures/tests.py12
-rw-r--r--tests/regressiontests/forms/error_messages.py360
-rw-r--r--tests/regressiontests/forms/models.py4
-rw-r--r--tests/regressiontests/forms/widgets.py54
-rw-r--r--tests/regressiontests/templates/filters.py6
-rw-r--r--tests/regressiontests/templates/tests.py6
7 files changed, 471 insertions, 1 deletions
diff --git a/tests/regressiontests/cache/tests.py b/tests/regressiontests/cache/tests.py
index 3879da7703..e94ea33139 100644
--- a/tests/regressiontests/cache/tests.py
+++ b/tests/regressiontests/cache/tests.py
@@ -3,9 +3,12 @@
# Unit tests for cache framework
# Uses whatever cache backend is set in the test settings file.
-from django.core.cache import cache
import time, unittest
+from django.core.cache import cache
+from django.utils.cache import patch_vary_headers
+from django.http import HttpResponse
+
# functions/classes for complex data type tests
def f():
return 42
@@ -87,5 +90,30 @@ class Cache(unittest.TestCase):
cache.set(key, value)
self.assertEqual(cache.get(key), value)
+
+class CacheUtils(unittest.TestCase):
+ """TestCase for django.utils.cache functions."""
+
+ def test_patch_vary_headers(self):
+ headers = (
+ # Initial vary, new headers, resulting vary.
+ (None, ('Accept-Encoding',), 'Accept-Encoding'),
+ ('Accept-Encoding', ('accept-encoding',), 'Accept-Encoding'),
+ ('Accept-Encoding', ('ACCEPT-ENCODING',), 'Accept-Encoding'),
+ ('Cookie', ('Accept-Encoding',), 'Cookie, Accept-Encoding'),
+ ('Cookie, Accept-Encoding', ('Accept-Encoding',), 'Cookie, Accept-Encoding'),
+ ('Cookie, Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'),
+ (None, ('Accept-Encoding', 'COOKIE'), 'Accept-Encoding, COOKIE'),
+ ('Cookie, Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'),
+ ('Cookie , Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'),
+ )
+ for initial_vary, newheaders, resulting_vary in headers:
+ response = HttpResponse()
+ if initial_vary is not None:
+ response['Vary'] = initial_vary
+ patch_vary_headers(response, newheaders)
+ self.assertEqual(response['Vary'], resulting_vary)
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/regressiontests/datastructures/tests.py b/tests/regressiontests/datastructures/tests.py
index d1e21e673c..3b0ccde257 100644
--- a/tests/regressiontests/datastructures/tests.py
+++ b/tests/regressiontests/datastructures/tests.py
@@ -25,11 +25,23 @@
>>> d = MultiValueDict({'name': ['Adrian', 'Simon'], 'position': ['Developer']})
>>> d['name']
'Simon'
+>>> d.get('name')
+'Simon'
>>> d.getlist('name')
['Adrian', 'Simon']
+>>> d['lastname']
+Traceback (most recent call last):
+...
+MultiValueDictKeyError: "Key 'lastname' not found in <MultiValueDict: {'position': ['Developer'], 'name': ['Adrian', 'Simon']}>"
+>>> d.get('lastname')
+
>>> d.get('lastname', 'nonexistent')
'nonexistent'
+>>> d.getlist('lastname')
+[]
>>> d.setlist('lastname', ['Holovaty', 'Willison'])
+>>> d.getlist('lastname')
+['Holovaty', 'Willison']
### SortedDict #################################################################
diff --git a/tests/regressiontests/forms/error_messages.py b/tests/regressiontests/forms/error_messages.py
new file mode 100644
index 0000000000..9f972f5b90
--- /dev/null
+++ b/tests/regressiontests/forms/error_messages.py
@@ -0,0 +1,360 @@
+# -*- coding: utf-8 -*-
+tests = r"""
+>>> from django.newforms import *
+
+# CharField ###################################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s'
+>>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s'
+>>> f = CharField(min_length=5, max_length=10, error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('1234')
+Traceback (most recent call last):
+...
+ValidationError: [u'LENGTH 4, MIN LENGTH 5']
+>>> f.clean('12345678901')
+Traceback (most recent call last):
+...
+ValidationError: [u'LENGTH 11, MAX LENGTH 10']
+
+# IntegerField ################################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid'] = 'INVALID'
+>>> e['min_value'] = 'MIN VALUE IS %s'
+>>> e['max_value'] = 'MAX VALUE IS %s'
+>>> f = IntegerField(min_value=5, max_value=10, error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('abc')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID']
+>>> f.clean('4')
+Traceback (most recent call last):
+...
+ValidationError: [u'MIN VALUE IS 5']
+>>> f.clean('11')
+Traceback (most recent call last):
+...
+ValidationError: [u'MAX VALUE IS 10']
+
+# FloatField ##################################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid'] = 'INVALID'
+>>> e['min_value'] = 'MIN VALUE IS %s'
+>>> e['max_value'] = 'MAX VALUE IS %s'
+>>> f = FloatField(min_value=5, max_value=10, error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('abc')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID']
+>>> f.clean('4')
+Traceback (most recent call last):
+...
+ValidationError: [u'MIN VALUE IS 5']
+>>> f.clean('11')
+Traceback (most recent call last):
+...
+ValidationError: [u'MAX VALUE IS 10']
+
+# DecimalField ################################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid'] = 'INVALID'
+>>> e['min_value'] = 'MIN VALUE IS %s'
+>>> e['max_value'] = 'MAX VALUE IS %s'
+>>> e['max_digits'] = 'MAX DIGITS IS %s'
+>>> e['max_decimal_places'] = 'MAX DP IS %s'
+>>> e['max_whole_digits'] = 'MAX DIGITS BEFORE DP IS %s'
+>>> f = DecimalField(min_value=5, max_value=10, error_messages=e)
+>>> f2 = DecimalField(max_digits=4, decimal_places=2, error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('abc')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID']
+>>> f.clean('4')
+Traceback (most recent call last):
+...
+ValidationError: [u'MIN VALUE IS 5']
+>>> f.clean('11')
+Traceback (most recent call last):
+...
+ValidationError: [u'MAX VALUE IS 10']
+>>> f2.clean('123.45')
+Traceback (most recent call last):
+...
+ValidationError: [u'MAX DIGITS IS 4']
+>>> f2.clean('1.234')
+Traceback (most recent call last):
+...
+ValidationError: [u'MAX DP IS 2']
+>>> f2.clean('123.4')
+Traceback (most recent call last):
+...
+ValidationError: [u'MAX DIGITS BEFORE DP IS 2']
+
+# DateField ###################################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid'] = 'INVALID'
+>>> f = DateField(error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('abc')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID']
+
+# TimeField ###################################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid'] = 'INVALID'
+>>> f = TimeField(error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('abc')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID']
+
+# DateTimeField ###############################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid'] = 'INVALID'
+>>> f = DateTimeField(error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('abc')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID']
+
+# RegexField ##################################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid'] = 'INVALID'
+>>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s'
+>>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s'
+>>> f = RegexField(r'^\d+$', min_length=5, max_length=10, error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('abcde')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID']
+>>> f.clean('1234')
+Traceback (most recent call last):
+...
+ValidationError: [u'LENGTH 4, MIN LENGTH 5']
+>>> f.clean('12345678901')
+Traceback (most recent call last):
+...
+ValidationError: [u'LENGTH 11, MAX LENGTH 10']
+
+# EmailField ##################################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid'] = 'INVALID'
+>>> e['min_length'] = 'LENGTH %(length)s, MIN LENGTH %(min)s'
+>>> e['max_length'] = 'LENGTH %(length)s, MAX LENGTH %(max)s'
+>>> f = EmailField(min_length=8, max_length=10, error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('abcdefgh')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID']
+>>> f.clean('a@b.com')
+Traceback (most recent call last):
+...
+ValidationError: [u'LENGTH 7, MIN LENGTH 8']
+>>> f.clean('aye@bee.com')
+Traceback (most recent call last):
+...
+ValidationError: [u'LENGTH 11, MAX LENGTH 10']
+
+# FileField ##################################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid'] = 'INVALID'
+>>> e['missing'] = 'MISSING'
+>>> e['empty'] = 'EMPTY FILE'
+>>> f = FileField(error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('abc')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID']
+>>> f.clean({})
+Traceback (most recent call last):
+...
+ValidationError: [u'MISSING']
+>>> f.clean({'filename': 'name', 'content':''})
+Traceback (most recent call last):
+...
+ValidationError: [u'EMPTY FILE']
+
+# URLField ##################################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid'] = 'INVALID'
+>>> e['invalid_link'] = 'INVALID LINK'
+>>> f = URLField(verify_exists=True, error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('abc.c')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID']
+>>> f.clean('http://www.jfoiwjfoi23jfoijoaijfoiwjofiwjefewl.com')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID LINK']
+
+# BooleanField ################################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> f = BooleanField(error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+
+# ChoiceField #################################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid_choice'] = '%(value)s IS INVALID CHOICE'
+>>> f = ChoiceField(choices=[('a', 'aye')], error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('b')
+Traceback (most recent call last):
+...
+ValidationError: [u'b IS INVALID CHOICE']
+
+# MultipleChoiceField #########################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid_choice'] = '%(value)s IS INVALID CHOICE'
+>>> e['invalid_list'] = 'NOT A LIST'
+>>> f = MultipleChoiceField(choices=[('a', 'aye')], error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('b')
+Traceback (most recent call last):
+...
+ValidationError: [u'NOT A LIST']
+>>> f.clean(['b'])
+Traceback (most recent call last):
+...
+ValidationError: [u'b IS INVALID CHOICE']
+
+# SplitDateTimeField ##########################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid_date'] = 'INVALID DATE'
+>>> e['invalid_time'] = 'INVALID TIME'
+>>> f = SplitDateTimeField(error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean(['a', 'b'])
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID DATE', u'INVALID TIME']
+
+# IPAddressField ##############################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid'] = 'INVALID IP ADDRESS'
+>>> f = IPAddressField(error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('127.0.0')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID IP ADDRESS']
+
+###############################################################################
+
+# Create choices for the model choice field tests below.
+
+>>> from regressiontests.forms.models import ChoiceModel
+>>> ChoiceModel.objects.create(pk=1, name='a')
+<ChoiceModel: ChoiceModel object>
+>>> ChoiceModel.objects.create(pk=2, name='b')
+<ChoiceModel: ChoiceModel object>
+>>> ChoiceModel.objects.create(pk=3, name='c')
+<ChoiceModel: ChoiceModel object>
+
+# ModelChoiceField ############################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid_choice'] = 'INVALID CHOICE'
+>>> f = ModelChoiceField(queryset=ChoiceModel.objects.all(), error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('4')
+Traceback (most recent call last):
+...
+ValidationError: [u'INVALID CHOICE']
+
+# ModelMultipleChoiceField ####################################################
+
+>>> e = {'required': 'REQUIRED'}
+>>> e['invalid_choice'] = '%s IS INVALID CHOICE'
+>>> e['list'] = 'NOT A LIST OF VALUES'
+>>> f = ModelMultipleChoiceField(queryset=ChoiceModel.objects.all(), error_messages=e)
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'REQUIRED']
+>>> f.clean('3')
+Traceback (most recent call last):
+...
+ValidationError: [u'NOT A LIST OF VALUES']
+>>> f.clean(['4'])
+Traceback (most recent call last):
+...
+ValidationError: [u'4 IS INVALID CHOICE']
+"""
diff --git a/tests/regressiontests/forms/models.py b/tests/regressiontests/forms/models.py
index 1a6f566b6b..c7ce128560 100644
--- a/tests/regressiontests/forms/models.py
+++ b/tests/regressiontests/forms/models.py
@@ -10,6 +10,10 @@ class Defaults(models.Model):
def_date = models.DateField(default = datetime.date(1980, 1, 1))
value = models.IntegerField(default=42)
+class ChoiceModel(models.Model):
+ """For ModelChoiceField and ModelMultipleChoiceField tests."""
+ name = models.CharField(max_length=10)
+
__test__ = {'API_TESTS': """
>>> from django.newforms import form_for_model, form_for_instance
diff --git a/tests/regressiontests/forms/widgets.py b/tests/regressiontests/forms/widgets.py
index cb1d084631..ea8cf135aa 100644
--- a/tests/regressiontests/forms/widgets.py
+++ b/tests/regressiontests/forms/widgets.py
@@ -2,6 +2,7 @@
tests = r"""
>>> from django.newforms import *
>>> from django.newforms.widgets import RadioFieldRenderer
+>>> from django.utils.safestring import mark_safe
>>> import datetime
>>> import time
>>> import re
@@ -205,6 +206,8 @@ u'<textarea rows="10" cols="40" name="msg"></textarea>'
u'<textarea rows="10" cols="40" name="msg">value</textarea>'
>>> w.render('msg', 'some "quoted" & ampersanded value')
u'<textarea rows="10" cols="40" name="msg">some &quot;quoted&quot; &amp; ampersanded value</textarea>'
+>>> w.render('msg', mark_safe('pre &quot;quoted&quot; value'))
+u'<textarea rows="10" cols="40" name="msg">pre &quot;quoted&quot; value</textarea>'
>>> w.render('msg', 'value', attrs={'class': 'pretty', 'rows': 20})
u'<textarea class="pretty" rows="20" cols="40" name="msg">value</textarea>'
@@ -375,6 +378,17 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
<option value="5">5</option>
</select>
+# Choices are escaped correctly
+>>> print w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you &gt; me'))))
+<select name="escape">
+<option value="1">1</option>
+<option value="2">2</option>
+<option value="3">3</option>
+<option value="bad">you &amp; me</option>
+<option value="good">you &gt; me</option>
+</select>
+
+# Unicode choices are correctly rendered as HTML
>>> w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
u'<select name="email">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
@@ -538,6 +552,17 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
<option value="5">5</option>
</select>
+# Choices are escaped correctly
+>>> print w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you &gt; me'))))
+<select multiple="multiple" name="escape">
+<option value="1">1</option>
+<option value="2">2</option>
+<option value="3">3</option>
+<option value="bad">you &amp; me</option>
+<option value="good">you &gt; me</option>
+</select>
+
+# Unicode choices are correctly rendered as HTML
>>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
u'<select multiple="multiple" name="nums">\n<option value="1">1</option>\n<option value="2">2</option>\n<option value="3">3</option>\n<option value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" selected="selected">\u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</option>\n<option value="\u0107\u017e\u0161\u0111">abc\u0107\u017e\u0161\u0111</option>\n</select>'
@@ -663,6 +688,16 @@ You can create your own custom renderers for RadioSelect to use.
<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br />
<label><input type="radio" name="beatle" value="R" /> Ringo</label>
+Or you can use custom RadioSelect fields that use your custom renderer.
+>>> class CustomRadioSelect(RadioSelect):
+... renderer = MyRenderer
+>>> w = CustomRadioSelect()
+>>> print w.render('beatle', 'G', choices=(('J', 'John'), ('P', 'Paul'), ('G', 'George'), ('R', 'Ringo')))
+<label><input type="radio" name="beatle" value="J" /> John</label><br />
+<label><input type="radio" name="beatle" value="P" /> Paul</label><br />
+<label><input checked="checked" type="radio" name="beatle" value="G" /> George</label><br />
+<label><input type="radio" name="beatle" value="R" /> Ringo</label>
+
A RadioFieldRenderer object also allows index access to individual RadioInput
objects.
>>> w = RadioSelect()
@@ -682,6 +717,14 @@ Traceback (most recent call last):
...
IndexError: list index out of range
+# Choices are escaped correctly
+>>> w = RadioSelect()
+>>> print w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you &gt; me'))))
+<ul>
+<li><label><input type="radio" name="escape" value="bad" /> you &amp; me</label></li>
+<li><label><input type="radio" name="escape" value="good" /> you &gt; me</label></li>
+</ul>
+
# Unicode choices are correctly rendered as HTML
>>> w = RadioSelect()
>>> unicode(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')]))
@@ -811,6 +854,17 @@ If 'choices' is passed to both the constructor and render(), then they'll both b
<li><label><input type="checkbox" name="nums" value="5" /> 5</label></li>
</ul>
+# Choices are escaped correctly
+>>> print w.render('escape', None, choices=(('bad', 'you & me'), ('good', mark_safe('you &gt; me'))))
+<ul>
+<li><label><input type="checkbox" name="escape" value="1" /> 1</label></li>
+<li><label><input type="checkbox" name="escape" value="2" /> 2</label></li>
+<li><label><input type="checkbox" name="escape" value="3" /> 3</label></li>
+<li><label><input type="checkbox" name="escape" value="bad" /> you &amp; me</label></li>
+<li><label><input type="checkbox" name="escape" value="good" /> you &gt; me</label></li>
+</ul>
+
+# Unicode choices are correctly rendered as HTML
>>> w.render('nums', ['ŠĐĆŽćžšđ'], choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])
u'<ul>\n<li><label><input type="checkbox" name="nums" value="1" /> 1</label></li>\n<li><label><input type="checkbox" name="nums" value="2" /> 2</label></li>\n<li><label><input type="checkbox" name="nums" value="3" /> 3</label></li>\n<li><label><input checked="checked" type="checkbox" name="nums" value="\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111" /> \u0160\u0110abc\u0106\u017d\u0107\u017e\u0161\u0111</label></li>\n<li><label><input type="checkbox" name="nums" value="\u0107\u017e\u0161\u0111" /> abc\u0107\u017e\u0161\u0111</label></li>\n</ul>'
diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py
index 27b24cb169..2a06703948 100644
--- a/tests/regressiontests/templates/filters.py
+++ b/tests/regressiontests/templates/filters.py
@@ -198,6 +198,12 @@ def get_filter_tests():
'filter-phone2numeric01': ('{{ a|phone2numeric }} {{ b|phone2numeric }}', {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>") }, "&lt;1-800-2255-63&gt; <1-800-2255-63>"),
'filter-phone2numeric02': ('{% autoescape off %}{{ a|phone2numeric }} {{ b|phone2numeric }}{% endautoescape %}', {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>") }, "<1-800-2255-63> <1-800-2255-63>"),
+ # Ensure iriencode keeps safe strings:
+ 'filter-iriencode01': ('{{ url|iriencode }}', {'url': '?test=1&me=2'}, '?test=1&amp;me=2'),
+ 'filter-iriencode02': ('{% autoescape off %}{{ url|iriencode }}{% endautoescape %}', {'url': '?test=1&me=2'}, '?test=1&me=2'),
+ 'filter-iriencode03': ('{{ url|iriencode }}', {'url': mark_safe('?test=1&me=2')}, '?test=1&me=2'),
+ 'filter-iriencode04': ('{% autoescape off %}{{ url|iriencode }}{% endautoescape %}', {'url': mark_safe('?test=1&me=2')}, '?test=1&me=2'),
+
# Chaining a bunch of safeness-preserving filters should not alter
# the safe status either way.
'chaining01': ('{{ a|capfirst|center:"7" }}.{{ b|capfirst|center:"7" }}', {"a": "a < b", "b": mark_safe("a < b")}, " A &lt; b . A < b "),
diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py
index 5c3a0a9081..f3c131dd91 100644
--- a/tests/regressiontests/templates/tests.py
+++ b/tests/regressiontests/templates/tests.py
@@ -268,6 +268,12 @@ class Templates(unittest.TestCase):
# 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\""),
+
# 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"),