From 53aef92bf89f2d655a125d94a0e24203e76af1d6 Mon Sep 17 00:00:00 2001 From: Adrian Holovaty Date: Sun, 1 Jan 2006 18:37:33 +0000 Subject: [PATCH] Fixed #1145 -- Added unit tests for default template filters and fixed two bugs in filters. Thanks, Luke Plant git-svn-id: http://code.djangoproject.com/svn/django/trunk@1811 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/template/defaultfilters.py | 12 +- django/utils/timesince.py | 6 +- tests/othertests/defaultfilters.py | 310 +++++++++++++++++++++++++ tests/runtests.py | 2 +- 4 files changed, 325 insertions(+), 5 deletions(-) diff --git a/django/core/template/defaultfilters.py b/django/core/template/defaultfilters.py index de6da35cbb..f273fa0726 100644 --- a/django/core/template/defaultfilters.py +++ b/django/core/template/defaultfilters.py @@ -117,7 +117,8 @@ def urlize(value): def urlizetrunc(value, limit): """ - Converts URLs into clickable links, truncating URLs to the given character limit + Converts URLs into clickable links, truncating URLs to the given character limit, + and adding 'rel=nofollow' attribute to discourage spamming. Argument: Length to truncate URLs to. """ @@ -254,7 +255,14 @@ def slice_(value, arg): for an introduction. """ try: - return value[slice(*[x and int(x) or None for x in arg.split(':')])] + bits = [] + for x in arg.split(':'): + if len(x) == 0: + bits.append(None) + else: + bits.append(int(x)) + return value[slice(*bits)] + except (ValueError, TypeError): return value # Fail silently. diff --git a/django/utils/timesince.py b/django/utils/timesince.py index b9edb12554..0d1ab470e5 100644 --- a/django/utils/timesince.py +++ b/django/utils/timesince.py @@ -24,14 +24,16 @@ def timesince(d, now=None): else: tz = None now = datetime.datetime(t[0], t[1], t[2], t[3], t[4], t[5], tzinfo=tz) - delta = now - d + + # ignore microsecond part of 'd' since we removed it from 'now' + delta = now - (d - datetime.timedelta(0, 0, d.microsecond)) since = delta.days * 24 * 60 * 60 + delta.seconds for i, (seconds, name) in enumerate(chunks): count = since / seconds if count != 0: break if count < 0: - return '%d milliseconds' % math.floor(delta.microseconds / 1000) + return '%d milliseconds' % math.floor((now - d).microseconds / 1000) s = '%d %s' % (count, name(count)) if i + 1 < len(chunks): # Now get the second item diff --git a/tests/othertests/defaultfilters.py b/tests/othertests/defaultfilters.py index 539994ba84..d0d5d21e58 100644 --- a/tests/othertests/defaultfilters.py +++ b/tests/othertests/defaultfilters.py @@ -11,9 +11,319 @@ '0.0' >>> floatformat(0.0) '0' + +>>> addslashes('"double quotes" and \\'single quotes\\'') +'\\\\"double quotes\\\\" and \\\\\\'single quotes\\\\\\'' + +>>> capfirst('hello world') +'Hello world' + +>>> fix_ampersands('Jack & Jill & Jeroboam') +'Jack & Jill & Jeroboam' + +>>> linenumbers('line 1\\nline 2') +'1. line 1\\n2. line 2' + +>>> linenumbers('\\n'.join(['x'] * 10)) +'01. x\\n02. x\\n03. x\\n04. x\\n05. x\\n06. x\\n07. x\\n08. x\\n09. x\\n10. x' + +>>> lower('TEST') +'test' + +>>> lower(u'\\xcb') # uppercase E umlaut +u'\\xeb' + +>>> make_list('abc') +['a', 'b', 'c'] + +>>> make_list(1234) +['1', '2', '3', '4'] + +>>> slugify(' Jack & Jill like numbers 1,2,3 and 4 and silly characters ?%.$!/') +'jack-jill-like-numbers-123-and-4-and-silly-characters' + +>>> stringformat(1, '03d') +'001' + +>>> stringformat(1, 'z') +'' + +>>> title('a nice title, isn\\'t it?') +"A Nice Title, Isn't It?" + + +>>> truncatewords('A sentence with a few words in it', 1) +'A ...' + +>>> truncatewords('A sentence with a few words in it', 5) +'A sentence with a few ...' + +>>> truncatewords('A sentence with a few words in it', 100) +'A sentence with a few words in it' + +>>> truncatewords('A sentence with a few words in it', 'not a number') +'A sentence with a few words in it' + + +>>> upper('Mixed case input') +'MIXED CASE INPUT' + +>>> upper(u'\\xeb') # lowercase e umlaut +u'\\xcb' + + +>>> urlencode('jack & jill') +'jack%20%26%20jill' + + +>>> urlizetrunc('http://short.com/', 20) +'http://short.com/' + +>>> urlizetrunc('http://www.google.co.uk/search?hl=en&q=some+long+url&btnG=Search&meta=', 20) +'http://www.google.co...' + +>>> wordcount('') +0 + +>>> wordcount('oneword') +1 + +>>> wordcount('lots of words') +3 + +>>> wordwrap('this is a long paragraph of text that really needs to be wrapped I\\'m afraid', 14) +"this is a long\\nparagraph of\\ntext that\\nreally needs\\nto be wrapped\\nI'm afraid" + +>>> ljust('test', 10) +'test ' + +>>> ljust('test', 3) +'test' + +>>> rjust('test', 10) +' test' + +>>> rjust('test', 3) +'test' + +>>> center('test', 6) +' test ' + +>>> cut('a string to be mangled', 'a') +' string to be mngled' + +>>> cut('a string to be mangled', 'ng') +'a stri to be maled' + +>>> cut('a string to be mangled', 'strings') +'a string to be mangled' + +>>> escape(' here') +'<some html & special characters > here' + +>>> linebreaks('line 1') +'

line 1

' + +>>> linebreaks('line 1\\nline 2') +'

line 1
line 2

' + +>>> removetags('some html with disallowed tags', 'script img') +'some html with alert("You smell") disallowed tags' + +>>> striptags('some html with disallowed tags') +'some html with alert("You smell") disallowed tags' + +>>> dictsort([{'age': 23, 'name': 'Barbara-Ann'},\ + {'age': 63, 'name': 'Ra Ra Rasputin'},\ + {'name': 'Jonny B Goode', 'age': 18}], 'age') +[{'age': 18, 'name': 'Jonny B Goode'},\ + {'age': 23, 'name': 'Barbara-Ann'},\ + {'age': 63, 'name': 'Ra Ra Rasputin'}] + +>>> dictsortreversed([{'age': 23, 'name': 'Barbara-Ann'},\ + {'age': 63, 'name': 'Ra Ra Rasputin'},\ + {'name': 'Jonny B Goode', 'age': 18}], 'age') +[{'age': 63, 'name': 'Ra Ra Rasputin'},\ + {'age': 23, 'name': 'Barbara-Ann'},\ + {'age': 18, 'name': 'Jonny B Goode'}] + +>>> first([0,1,2]) +0 + +>>> first('') +'' + +>>> first('test') +'t' + +>>> join([0,1,2], 'glue') +'0glue1glue2' + +>>> length('1234') +4 + +>>> length([1,2,3,4]) +4 + +>>> length_is([], 0) +True + +>>> length_is([], 1) +False + +>>> length_is('a', 1) +True + +>>> length_is('a', 10) +False + +>>> slice_('abcdefg', '0') +'' + +>>> slice_('abcdefg', '1') +'a' + +>>> slice_('abcdefg', '-1') +'abcdef' + +>>> slice_('abcdefg', '1:2') +'b' + +>>> slice_('abcdefg', '1:3') +'bc' + +>>> slice_('abcdefg', '0::2') +'aceg' + +>>> unordered_list(['item 1', []]) +'\\t
  • item 1
  • ' + +>>> unordered_list(['item 1', [['item 1.1', []]]]) +'\\t
  • item 1\\n\\t
      \\n\\t\\t
    • item 1.1
    • \\n\\t
    \\n\\t
  • ' + +>>> unordered_list(['item 1', [['item 1.1', []], ['item 1.2', []]]]) +'\\t
  • item 1\\n\\t
      \\n\\t\\t
    • item 1.1
    • \\n\\t\\t
    • item 1.2
    • \\n\\t
    \\n\\t
  • ' + +>>> add('1', '2') +3 + +>>> get_digit(123, 1) +3 + +>>> get_digit(123, 2) +2 + +>>> get_digit(123, 3) +1 + +>>> get_digit(123, 4) +0 + +>>> get_digit(123, 0) +123 + +>>> get_digit('xyz', 0) +'xyz' + +# real testing of date() is in dateformat.py +>>> date(datetime.datetime(2005, 12, 29), "d F Y") +'29 December 2005' + +# real testing of time() is done in dateformat.py +>>> time(datetime.time(13), "h") +'01' + +# real testing is done in timesince.py, where we can provide our own 'now' +>>> timesince(datetime.datetime.now() - datetime.timedelta(1)) +'1 day' + +>>> default("val", "default") +'val' + +>>> default(None, "default") +'default' + +>>> default('', "default") +'default' + +>>> default_if_none("val", "default") +'val' + +>>> default_if_none(None, "default") +'default' + +>>> default_if_none('', "default") +'' + +>>> divisibleby(4, 2) +True + +>>> divisibleby(4, 3) +False + +>>> yesno(True) +'yes' + +>>> yesno(False) +'no' + +>>> yesno(None) +'maybe' + +>>> yesno(True, 'certainly,get out of town,perhaps') +'certainly' + +>>> yesno(False, 'certainly,get out of town,perhaps') +'get out of town' + +>>> yesno(None, 'certainly,get out of town,perhaps') +'perhaps' + +>>> yesno(None, 'certainly,get out of town') +'get out of town' + +>>> filesizeformat(1023) +'1023 bytes' + +>>> filesizeformat(1024) +'1.0 KB' + +>>> filesizeformat(10*1024) +'10.0 KB' + +>>> filesizeformat(1024*1024-1) +'1024.0 KB' + +>>> filesizeformat(1024*1024) +'1.0 MB' + +>>> filesizeformat(1024*1024*50) +'50.0 MB' + +>>> filesizeformat(1024*1024*1024-1) +'1024.0 MB' + +>>> filesizeformat(1024*1024*1024) +'1.0 GB' + +>>> pluralize(1) +'' + +>>> pluralize(0) +'s' + +>>> pluralize(2) +'s' + +>>> phone2numeric('0800 flowers') +'0800 3569377' + + + """ from django.core.template.defaultfilters import * +import datetime if __name__ == '__main__': import doctest diff --git a/tests/runtests.py b/tests/runtests.py index 30e1cd403b..b336c7ff77 100755 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -206,7 +206,7 @@ if __name__ == "__main__": parser.add_option('-v', help='How verbose should the output be? Choices are 0, 1 and 2, where 2 is most verbose. Default is 0.', type='choice', choices=['0', '1', '2']) parser.add_option('--settings', - help='Python path to settings module, e.g. "myproject.settings.main". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.') + help='Python path to settings module, e.g. "myproject.settings". If this isn\'t provided, the DJANGO_SETTINGS_MODULE environment variable will be used.') options, args = parser.parse_args() verbosity_level = 0 if options.v: