diff options
| author | Jeremy Dunck <jdunck@gmail.com> | 2007-12-01 22:12:44 +0000 |
|---|---|---|
| committer | Jeremy Dunck <jdunck@gmail.com> | 2007-12-01 22:12:44 +0000 |
| commit | 23384af79b2f43f08e8395d0a84a57c2a98df392 (patch) | |
| tree | 77e45f0e0fc6cb9b3380982313f45febf2ebf6f0 /tests/regressiontests | |
| parent | 85ce45bc441e4ace2fba83bc2d2432e22ec9da94 (diff) | |
gis: Merged 6672-6783 vis svnmerge from trunk
git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@6815 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'tests/regressiontests')
| -rw-r--r-- | tests/regressiontests/cache/tests.py | 30 | ||||
| -rw-r--r-- | tests/regressiontests/datastructures/tests.py | 12 | ||||
| -rw-r--r-- | tests/regressiontests/dateformat/tests.py | 4 | ||||
| -rw-r--r-- | tests/regressiontests/defaultfilters/tests.py | 2 | ||||
| -rw-r--r-- | tests/regressiontests/forms/error_messages.py | 45 | ||||
| -rw-r--r-- | tests/regressiontests/forms/fields.py | 14 | ||||
| -rw-r--r-- | tests/regressiontests/forms/localflavor/ca.py | 4 | ||||
| -rw-r--r-- | tests/regressiontests/forms/models.py | 4 | ||||
| -rw-r--r-- | tests/regressiontests/forms/widgets.py | 61 | ||||
| -rw-r--r-- | tests/regressiontests/i18n/tests.py | 14 | ||||
| -rw-r--r-- | tests/regressiontests/templates/filters.py | 44 | ||||
| -rw-r--r-- | tests/regressiontests/templates/tests.py | 59 | ||||
| -rw-r--r-- | tests/regressiontests/templates/unicode.py | 10 | ||||
| -rw-r--r-- | tests/regressiontests/utils/datastructures.py | 51 | ||||
| -rw-r--r-- | tests/regressiontests/utils/tests.py | 13 | ||||
| -rw-r--r-- | tests/regressiontests/utils/timesince.py | 2 | ||||
| -rw-r--r-- | tests/regressiontests/views/tests/static.py | 10 |
17 files changed, 338 insertions, 41 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/dateformat/tests.py b/tests/regressiontests/dateformat/tests.py index 30c9a4e6dd..481e36a7dd 100644 --- a/tests/regressiontests/dateformat/tests.py +++ b/tests/regressiontests/dateformat/tests.py @@ -66,6 +66,9 @@ u'1979 189 CET' >>> format(my_birthday, r'jS o\f F') u'8th of July' + +>>> format(the_future, r'Y') +u'2100' """ from django.utils import dateformat, translation @@ -84,3 +87,4 @@ except AttributeError: my_birthday = datetime.datetime(1979, 7, 8, 22, 00) summertime = datetime.datetime(2005, 10, 30, 1, 00) wintertime = datetime.datetime(2005, 10, 30, 4, 00) +the_future = datetime.datetime(2100, 10, 25, 0, 00) diff --git a/tests/regressiontests/defaultfilters/tests.py b/tests/regressiontests/defaultfilters/tests.py index 26d448900d..bfa03cd6e1 100644 --- a/tests/regressiontests/defaultfilters/tests.py +++ b/tests/regressiontests/defaultfilters/tests.py @@ -37,6 +37,8 @@ u'' u'13.1031' >>> floatformat(u'foo', u'bar') u'' +>>> floatformat(None) +u'' >>> addslashes(u'"double quotes" and \'single quotes\'') u'\\"double quotes\\" and \\\'single quotes\\\'' diff --git a/tests/regressiontests/forms/error_messages.py b/tests/regressiontests/forms/error_messages.py index ff7e110f6f..9f972f5b90 100644 --- a/tests/regressiontests/forms/error_messages.py +++ b/tests/regressiontests/forms/error_messages.py @@ -312,4 +312,49 @@ ValidationError: [u'REQUIRED'] 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/fields.py b/tests/regressiontests/forms/fields.py index 3b93d70338..cff5db6fca 100644 --- a/tests/regressiontests/forms/fields.py +++ b/tests/regressiontests/forms/fields.py @@ -323,6 +323,10 @@ Decimal("3.14") Traceback (most recent call last): ... ValidationError: [u'Enter a number.'] +>>> f.clean(u'łąść') +Traceback (most recent call last): +... +ValidationError: [u'Enter a number.'] >>> f.clean('1.0 ') Decimal("1.0") >>> f.clean(' 1.0') @@ -914,6 +918,11 @@ False >>> f.clean('Django rocks') True +>>> f.clean('True') +True +>>> f.clean('False') +False + >>> f = BooleanField(required=False) >>> f.clean('') False @@ -930,6 +939,11 @@ False >>> f.clean('Django rocks') True +A form's BooleanField with a hidden widget will output the string 'False', so +that should clean to the boolean value False: +>>> f.clean('False') +False + # ChoiceField ################################################################# >>> f = ChoiceField(choices=[('1', '1'), ('2', '2')]) diff --git a/tests/regressiontests/forms/localflavor/ca.py b/tests/regressiontests/forms/localflavor/ca.py index baeb2ad9a8..a13a6de65f 100644 --- a/tests/regressiontests/forms/localflavor/ca.py +++ b/tests/regressiontests/forms/localflavor/ca.py @@ -147,6 +147,10 @@ u'BC' u'NS' >>> f.clean(' manitoba ') u'MB' +>>> f.clean(' new brunswick ') +u'NB' +>>> f.clean('NB') +u'NB' >>> f.clean('T2S 2H7') Traceback (most recent call last): ... 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..0e69602103 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 @@ -128,6 +129,13 @@ u'<input type="hidden" class="fun" value="\u0160\u0110\u0106\u017d\u0107\u017e\u >>> w.render('email', '', attrs={'class': 'special'}) u'<input type="hidden" class="special" name="email" />' +Boolean values are rendered to their string forms ("True" and "False"). +>>> w = HiddenInput() +>>> w.render('get_spam', False) +u'<input type="hidden" name="get_spam" value="False" />' +>>> w.render('get_spam', True) +u'<input type="hidden" name="get_spam" value="True" />' + # MultipleHiddenInput Widget ################################################## >>> w = MultipleHiddenInput() @@ -205,6 +213,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 "quoted" & ampersanded value</textarea>' +>>> w.render('msg', mark_safe('pre "quoted" value')) +u'<textarea rows="10" cols="40" name="msg">pre "quoted" 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 +385,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 > me')))) +<select name="escape"> +<option value="1">1</option> +<option value="2">2</option> +<option value="3">3</option> +<option value="bad">you & me</option> +<option value="good">you > 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 +559,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 > 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 & me</option> +<option value="good">you > 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 +695,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 +724,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 > me')))) +<ul> +<li><label><input type="radio" name="escape" value="bad" /> you & me</label></li> +<li><label><input type="radio" name="escape" value="good" /> you > me</label></li> +</ul> + # Unicode choices are correctly rendered as HTML >>> w = RadioSelect() >>> unicode(w.render('email', 'ŠĐĆŽćžšđ', choices=[('ŠĐĆŽćžšđ', 'ŠĐabcĆŽćžšđ'), ('ćžšđ', 'abcćžšđ')])) @@ -811,6 +861,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 > 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 & me</label></li> +<li><label><input type="checkbox" name="escape" value="good" /> you > 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/i18n/tests.py b/tests/regressiontests/i18n/tests.py index 0a18e8bea5..2ffc62f90d 100644 --- a/tests/regressiontests/i18n/tests.py +++ b/tests/regressiontests/i18n/tests.py @@ -4,7 +4,7 @@ import misc regressions = ur""" Format string interpolation should work with *_lazy objects. ->>> from django.utils.translation import ugettext_lazy, activate, deactivate, gettext_lazy +>>> from django.utils.translation import ugettext, ugettext_lazy, activate, deactivate, gettext_lazy >>> s = ugettext_lazy('Add %(name)s') >>> d = {'name': 'Ringo'} >>> s % d @@ -39,6 +39,18 @@ unicode(string_concat(...)) should not raise a TypeError - #4796 <module 'django.utils.translation' from ...> >>> unicode(django.utils.translation.string_concat("dja", "ngo")) u'django' + +Translating a string requiring no auto-escaping shouldn't change the "safe" +status. + +>>> from django.utils.safestring import mark_safe +>>> s = mark_safe('Password') +>>> type(s) +<class 'django.utils.safestring.SafeString'> +>>> activate('de') +>>> type(ugettext(s)) +<class 'django.utils.safestring.SafeUnicode'> +>>> deactivate() """ __test__ = { diff --git a/tests/regressiontests/templates/filters.py b/tests/regressiontests/templates/filters.py index 5d7129480c..4175bdbe5f 100644 --- a/tests/regressiontests/templates/filters.py +++ b/tests/regressiontests/templates/filters.py @@ -12,6 +12,15 @@ from datetime import datetime, timedelta from django.utils.tzinfo import LocalTimezone from django.utils.safestring import mark_safe +# These two classes are used to test auto-escaping of __unicode__ output. +class UnsafeClass: + def __unicode__(self): + return u'you & me' + +class SafeClass: + def __unicode__(self): + return mark_safe(u'you > me') + # RESULT SYNTAX -- # 'template_name': ('template contents', 'context dict', # 'expected string output' or Exception class) @@ -94,6 +103,11 @@ def get_filter_tests(): 'filter-urlize03': ('{% autoescape off %}{{ a|urlize }}{% endautoescape %}', {"a": mark_safe("a & b")}, 'a & b'), 'filter-urlize04': ('{{ a|urlize }}', {"a": mark_safe("a & b")}, 'a & b'), + # This will lead to a nonsense result, but at least it won't be + # exploitable for XSS purposes when auto-escaping is on. + 'filter-urlize05': ('{% autoescape off %}{{ a|urlize }}{% endautoescape %}', {"a": "<script>alert('foo')</script>"}, "<script>alert('foo')</script>"), + 'filter-urlize06': ('{{ a|urlize }}', {"a": "<script>alert('foo')</script>"}, '<script>alert('foo')</script>'), + 'filter-urlizetrunc01': ('{% autoescape off %}{{ a|urlizetrunc:"8" }} {{ b|urlizetrunc:"8" }}{% endautoescape %}', {"a": "http://example.com/x=&y=", "b": mark_safe("http://example.com?x=&y=")}, u'<a href="http://example.com/x=&y=" rel="nofollow">http:...</a> <a href="http://example.com?x=&y=" rel="nofollow">http:...</a>'), 'filter-urlizetrunc02': ('{{ a|urlizetrunc:"8" }} {{ b|urlizetrunc:"8" }}', {"a": "http://example.com/x=&y=", "b": mark_safe("http://example.com?x=&y=")}, u'<a href="http://example.com/x=&y=" rel="nofollow">http:...</a> <a href="http://example.com?x=&y=" rel="nofollow">http:...</a>'), @@ -177,23 +191,28 @@ def get_filter_tests(): 'filter-unordered_list04': ('{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}', {"a": ["x>", [[mark_safe("<y"), []]]]}, "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>"), 'filter-unordered_list05': ('{% autoescape off %}{{ a|unordered_list }}{% endautoescape %}', {"a": ["x>", [["<y", []]]]}, "\t<li>x>\n\t<ul>\n\t\t<li><y</li>\n\t</ul>\n\t</li>"), - # If the input to "default" filter is marked as safe, then so is the - # output. However, if the default arg is used, auto-escaping kicks in - # (if enabled), because we cannot mark the default as safe. + # Literal string arguments to the default filter are always treated as + # safe strings, regardless of the auto-escaping state. # # Note: we have to use {"a": ""} here, otherwise the invalid template # variable string interferes with the test result. - 'filter-default01': ('{{ a|default:"x<" }}', {"a": ""}, "x<"), + 'filter-default01': ('{{ a|default:"x<" }}', {"a": ""}, "x<"), 'filter-default02': ('{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}', {"a": ""}, "x<"), 'filter-default03': ('{{ a|default:"x<" }}', {"a": mark_safe("x>")}, "x>"), 'filter-default04': ('{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}', {"a": mark_safe("x>")}, "x>"), - 'filter-default_if_none01': ('{{ a|default:"x<" }}', {"a": None}, "x<"), + 'filter-default_if_none01': ('{{ a|default:"x<" }}', {"a": None}, "x<"), 'filter-default_if_none02': ('{% autoescape off %}{{ a|default:"x<" }}{% endautoescape %}', {"a": None}, "x<"), 'filter-phone2numeric01': ('{{ a|phone2numeric }} {{ b|phone2numeric }}', {"a": "<1-800-call-me>", "b": mark_safe("<1-800-call-me>") }, "<1-800-2255-63> <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&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 < b . A < b "), @@ -209,12 +228,19 @@ def get_filter_tests(): # Force to safe, then back (also showing why using force_escape too # early in a chain can lead to unexpected results). - 'chaining07': ('{{ a|force_escape|cut:"b" }}', {"a": "a < b"}, "a < "), - 'chaining08': ('{% autoescape off %}{{ a|force_escape|cut:"b" }}{% endautoescape %}', {"a": "a < b"}, "a < "), - 'chaining09': ('{{ a|cut:"b"|force_escape }}', {"a": "a < b"}, "a < "), - 'chaining10': ('{% autoescape off %}{{ a|cut:"b"|force_escape }}{% endautoescape %}', {"a": "a < b"}, "a < "), + 'chaining07': ('{{ a|force_escape|cut:";" }}', {"a": "a < b"}, "a &lt b"), + 'chaining08': ('{% autoescape off %}{{ a|force_escape|cut:";" }}{% endautoescape %}', {"a": "a < b"}, "a < b"), + 'chaining09': ('{{ a|cut:";"|force_escape }}', {"a": "a < b"}, "a < b"), + 'chaining10': ('{% autoescape off %}{{ a|cut:";"|force_escape }}{% endautoescape %}', {"a": "a < b"}, "a < b"), 'chaining11': ('{{ a|cut:"b"|safe }}', {"a": "a < b"}, "a < "), 'chaining12': ('{% autoescape off %}{{ a|cut:"b"|safe }}{% endautoescape %}', {"a": "a < b"}, "a < "), 'chaining13': ('{{ a|safe|force_escape }}', {"a": "a < b"}, "a < b"), 'chaining14': ('{% autoescape off %}{{ a|safe|force_escape }}{% endautoescape %}', {"a": "a < b"}, "a < b"), + + # Filters decorated with stringfilter still respect is_safe. + 'autoescape-stringfilter01': (r'{{ unsafe|capfirst }}', {'unsafe': UnsafeClass()}, 'You & me'), + 'autoescape-stringfilter02': (r'{% autoescape off %}{{ unsafe|capfirst }}{% endautoescape %}', {'unsafe': UnsafeClass()}, 'You & me'), + 'autoescape-stringfilter03': (r'{{ safe|capfirst }}', {'safe': SafeClass()}, 'You > me'), + 'autoescape-stringfilter04': (r'{% autoescape off %}{{ safe|capfirst }}{% endautoescape %}', {'safe': SafeClass()}, 'You > me'), } + diff --git a/tests/regressiontests/templates/tests.py b/tests/regressiontests/templates/tests.py index d52e8f0abf..cbbd88b06c 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"), @@ -318,9 +324,9 @@ class Templates(unittest.TestCase): # Chained filters, with an argument to the first one 'filter-syntax09': ('{{ var|removetags:"b i"|upper|lower }}', {"var": "<b><i>Yes</i></b>"}, "yes"), - # Escaped string as argument + # Literal string as argument is always "safe" from auto-escaping.. 'filter-syntax10': (r'{{ var|default_if_none:" endquote\" hah" }}', - {"var": None}, ' endquote" hah'), + {"var": None}, ' endquote" hah'), # Variable as argument 'filter-syntax11': (r'{{ var|default_if_none:var2 }}', {"var": None, "var2": "happy"}, 'happy'), @@ -617,7 +623,7 @@ class Templates(unittest.TestCase): ### INHERITANCE ########################################################### # Standard template with no inheritance - 'inheritance01': ("1{% block first %}_{% endblock %}3{% block second %}_{% endblock %}", {}, '1_3_'), + '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'), @@ -626,7 +632,7 @@ class Templates(unittest.TestCase): 'inheritance03': ("{% extends 'inheritance02' %}", {}, '1234'), # Two-level with no redefinitions on second level - 'inheritance04': ("{% extends 'inheritance01' %}", {}, '1_3_'), + 'inheritance04': ("{% extends 'inheritance01' %}", {}, '1&3_'), # Two-level with double quotes instead of single quotes 'inheritance05': ('{% extends "inheritance02" %}', {}, '1234'), @@ -635,16 +641,16 @@ class Templates(unittest.TestCase): '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'), + '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_'), + 'inheritance09': ("{% extends 'inheritance04' %}", {}, '1&3_'), # Three-level with space NOT in a block -- should be ignored - 'inheritance10': ("{% extends 'inheritance04' %} ", {}, '1_3_'), + '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'), @@ -656,7 +662,7 @@ class Templates(unittest.TestCase): '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_'), + '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_'), @@ -674,16 +680,16 @@ class Templates(unittest.TestCase): '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_'), + '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_'), + '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_'), + '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'), @@ -705,10 +711,10 @@ class Templates(unittest.TestCase): 'i18n02': ('{% load i18n %}{% trans "xxxyyyxxx" %}', {}, "xxxyyyxxx"), # simple translation of a variable - 'i18n03': ('{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}', {'anton': 'xxxyyyxxx'}, "xxxyyyxxx"), + 'i18n03': ('{% load i18n %}{% blocktrans %}{{ anton }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u"Å"), # simple translation of a variable and filter - 'i18n04': ('{% load i18n %}{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'XXXYYYXXX'}, "xxxyyyxxx"), + 'i18n04': ('{% load i18n %}{% blocktrans with anton|lower as berta %}{{ berta }}{% endblocktrans %}', {'anton': '\xc3\x85'}, u'å'), # simple translation of a string with interpolation 'i18n05': ('{% load i18n %}{% blocktrans %}xxx{{ anton }}xxx{% endblocktrans %}', {'anton': 'yyy'}, "xxxyyyxxx"), @@ -717,10 +723,10 @@ class Templates(unittest.TestCase): 'i18n06': ('{% load i18n %}{% trans "Page not found" %}', {'LANGUAGE_CODE': 'de'}, "Seite nicht gefunden"), # translation of singular form - 'i18n07': ('{% load i18n %}{% blocktrans count number as counter %}singular{% plural %}plural{% endblocktrans %}', {'number': 1}, "singular"), + 'i18n07': ('{% 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 %}plural{% endblocktrans %}', {'number': 2}, "plural"), + 'i18n08': ('{% load i18n %}{% blocktrans count number as counter %}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"), @@ -734,8 +740,16 @@ class Templates(unittest.TestCase): # 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 a constant string - 'i18n13': ('{{ _("Page not found") }}', {'LANGUAGE_CODE': 'de'}, 'Seite nicht gefunden'), + # 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 works as if it was directly in the + # template. + 'i18n17': ('{% load i18n %}{% blocktrans with anton|escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α & β'), + 'i18n18': ('{% load i18n %}{% blocktrans with anton|force_escape as berta %}{{ berta }}{% endblocktrans %}', {'anton': 'α & β'}, u'α & β'), ### HANDLING OF TEMPLATE_STRING_IF_INVALID ################################### @@ -883,9 +897,14 @@ class Templates(unittest.TestCase): 'autoescape-tag06': ("{{ first }}", {"first": mark_safe("<b>first</b>")}, "<b>first</b>"), 'autoescape-tag07': ("{% autoescape on %}{{ first }}{% endautoescape %}", {"first": mark_safe(u"<b>Apple</b>")}, u"<b>Apple</b>"), - # String arguments to filters, if used in the result, are escaped, - # too. - 'basic-syntax08': (r'{% autoescape on %}{{ var|default_if_none:" endquote\" hah" }}{% endautoescape %}', {"var": None}, ' endquote" hah'), + # 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 diff --git a/tests/regressiontests/templates/unicode.py b/tests/regressiontests/templates/unicode.py index efda11c2da..e5f308d202 100644 --- a/tests/regressiontests/templates/unicode.py +++ b/tests/regressiontests/templates/unicode.py @@ -3,6 +3,7 @@ unicode_tests = ur""" Templates can be created from unicode strings. >>> from django.template import * +>>> from django.utils.safestring import SafeData >>> t1 = Template(u'ŠĐĆŽćžšđ {{ var }}') Templates can also be created from bytestrings. These are assumed by encoded @@ -24,10 +25,13 @@ Contexts can be constructed from unicode or UTF-8 bytestrings. >>> c4 = Context({u'var': '\xc4\x90\xc4\x91'}) Since both templates and all four contexts represent the same thing, they all -render the same (and are returned as unicode objects). +render the same (and are returned as unicode objects and "safe" objects as +well, for auto-escaping purposes). >>> t1.render(c3) == t2.render(c3) True ->>> type(t1.render(c3)) -<type 'unicode'> +>>> isinstance(t1.render(c3), unicode) +True +>>> isinstance(t1.render(c3), SafeData) +True """ diff --git a/tests/regressiontests/utils/datastructures.py b/tests/regressiontests/utils/datastructures.py new file mode 100644 index 0000000000..52bf81a9e0 --- /dev/null +++ b/tests/regressiontests/utils/datastructures.py @@ -0,0 +1,51 @@ +""" +>>> from django.utils.datastructures import SortedDict + +>>> d = SortedDict() +>>> d[7] = 'seven' +>>> d[1] = 'one' +>>> d[9] = 'nine' +>>> d.keys() +[7, 1, 9] +>>> d.values() +['seven', 'one', 'nine'] +>>> d.items() +[(7, 'seven'), (1, 'one'), (9, 'nine')] + +# Overwriting an item keeps it's place. +>>> d[1] = 'ONE' +>>> d.values() +['seven', 'ONE', 'nine'] + +# New items go to the end. +>>> d[0] = 'nil' +>>> d.keys() +[7, 1, 9, 0] + +# Deleting an item, then inserting the same key again will place it at the end. +>>> del d[7] +>>> d.keys() +[1, 9, 0] +>>> d[7] = 'lucky number 7' +>>> d.keys() +[1, 9, 0, 7] + +# Changing the keys won't do anything, it's only a copy of the keys dict. +>>> k = d.keys() +>>> k.remove(9) +>>> d.keys() +[1, 9, 0, 7] + +# Initialising a SortedDict with two keys will just take the first one. A real +# dict will actually take the second value so we will too, but we'll keep the +# ordering from the first key found. +>>> tuples = ((2, 'two'), (1, 'one'), (2, 'second-two')) +>>> d = SortedDict(tuples) +>>> d.keys() +[2, 1] +>>> real_dict = dict(tuples) +>>> real_dict.values() +['one', 'second-two'] +>>> d.values() +['second-two', 'one'] +"""
\ No newline at end of file diff --git a/tests/regressiontests/utils/tests.py b/tests/regressiontests/utils/tests.py index eb3a722888..abcd7212d8 100644 --- a/tests/regressiontests/utils/tests.py +++ b/tests/regressiontests/utils/tests.py @@ -6,7 +6,14 @@ from unittest import TestCase from django.utils import html, checksums -from timesince import timesince_tests +import timesince +import datastructures + +# Extra tests +__test__ = { + 'timesince': timesince, + 'datastructures': datastructures, +} class TestUtilsHtml(TestCase): @@ -142,10 +149,6 @@ class TestUtilsChecksums(TestCase): for value, output in items: self.check_output(f, value, output) -__test__ = { - 'timesince_tests': timesince_tests, -} - if __name__ == "__main__": import doctest doctest.testmod() diff --git a/tests/regressiontests/utils/timesince.py b/tests/regressiontests/utils/timesince.py index 30200be6e9..4f304ec7f7 100644 --- a/tests/regressiontests/utils/timesince.py +++ b/tests/regressiontests/utils/timesince.py @@ -1,4 +1,4 @@ -timesince_tests = """ +""" >>> from datetime import datetime, timedelta >>> from django.utils.timesince import timesince diff --git a/tests/regressiontests/views/tests/static.py b/tests/regressiontests/views/tests/static.py index 0a67cf543e..c731b249e8 100644 --- a/tests/regressiontests/views/tests/static.py +++ b/tests/regressiontests/views/tests/static.py @@ -12,4 +12,12 @@ class StaticTests(TestCase): for filename in media_files: response = self.client.get('/views/site_media/%s' % filename) file = open(path.join(media_dir, filename)) - self.assertEquals(file.read(), response.content)
\ No newline at end of file + self.assertEquals(file.read(), response.content) + + def test_copes_with_empty_path_component(self): + file_name = 'file.txt' + response = self.client.get('/views/site_media//%s' % file_name) + file = open(path.join(media_dir, file_name)) + self.assertEquals(file.read(), response.content) + + |
