live up to @mitsuhiko's ridiculous expectations
[jinja2.git] / jinja2 / testsuite / filters.py
1 # -*- coding: utf-8 -*-
2 """
3     jinja2.testsuite.filters
4     ~~~~~~~~~~~~~~~~~~~~~~~~
5
6     Tests for the jinja filters.
7
8     :copyright: (c) 2010 by the Jinja Team.
9     :license: BSD, see LICENSE for more details.
10 """
11 import unittest
12 from jinja2.testsuite import JinjaTestCase
13
14 from jinja2 import Markup, Environment
15
16 env = Environment()
17
18
19 class FilterTestCase(JinjaTestCase):
20
21     def test_capitalize(self):
22         tmpl = env.from_string('{{ "foo bar"|capitalize }}')
23         assert tmpl.render() == 'Foo bar'
24
25     def test_center(self):
26         tmpl = env.from_string('{{ "foo"|center(9) }}')
27         assert tmpl.render() == '   foo   '
28
29     def test_default(self):
30         tmpl = env.from_string(
31             "{{ missing|default('no') }}|{{ false|default('no') }}|"
32             "{{ false|default('no', true) }}|{{ given|default('no') }}"
33         )
34         assert tmpl.render(given='yes') == 'no|False|no|yes'
35
36     def test_dictsort(self):
37         tmpl = env.from_string(
38             '{{ foo|dictsort }}|'
39             '{{ foo|dictsort(true) }}|'
40             '{{ foo|dictsort(false, "value") }}'
41         )
42         out = tmpl.render(foo={"aa": 0, "b": 1, "c": 2, "AB": 3})
43         assert out == ("[('aa', 0), ('AB', 3), ('b', 1), ('c', 2)]|"
44                        "[('AB', 3), ('aa', 0), ('b', 1), ('c', 2)]|"
45                        "[('aa', 0), ('b', 1), ('c', 2), ('AB', 3)]")
46
47     def test_batch(self):
48         tmpl = env.from_string("{{ foo|batch(3)|list }}|"
49                                "{{ foo|batch(3, 'X')|list }}")
50         out = tmpl.render(foo=range(10))
51         assert out == ("[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]|"
52                        "[[0, 1, 2], [3, 4, 5], [6, 7, 8], [9, 'X', 'X']]")
53
54     def test_slice(self):
55         tmpl = env.from_string('{{ foo|slice(3)|list }}|'
56                                '{{ foo|slice(3, "X")|list }}')
57         out = tmpl.render(foo=range(10))
58         assert out == ("[[0, 1, 2, 3], [4, 5, 6], [7, 8, 9]]|"
59                        "[[0, 1, 2, 3], [4, 5, 6, 'X'], [7, 8, 9, 'X']]")
60
61     def test_escape(self):
62         tmpl = env.from_string('''{{ '<">&'|escape }}''')
63         out = tmpl.render()
64         assert out == '&lt;&#34;&gt;&amp;'
65
66     def test_striptags(self):
67         tmpl = env.from_string('''{{ foo|striptags }}''')
68         out = tmpl.render(foo='  <p>just a small   \n <a href="#">'
69                           'example</a> link</p>\n<p>to a webpage</p> '
70                           '<!-- <p>and some commented stuff</p> -->')
71         assert out == 'just a small example link to a webpage'
72
73     def test_filesizeformat(self):
74         tmpl = env.from_string(
75             '{{ 100|filesizeformat }}|'
76             '{{ 1000|filesizeformat }}|'
77             '{{ 1000000|filesizeformat }}|'
78             '{{ 1000000000|filesizeformat }}|'
79             '{{ 1000000000000|filesizeformat }}|'
80             '{{ 100|filesizeformat(true) }}|'
81             '{{ 1000|filesizeformat(true) }}|'
82             '{{ 1000000|filesizeformat(true) }}|'
83             '{{ 1000000000|filesizeformat(true) }}|'
84             '{{ 1000000000000|filesizeformat(true) }}'
85         )
86         out = tmpl.render()
87         self.assert_equal(out, (
88             '100 Bytes|1.0 kB|1.0 MB|1.0 GB|1.0 TB|100 Bytes|'
89             '1000 Bytes|976.6 KiB|953.7 MiB|931.3 GiB'
90         ))
91
92     def test_filesizeformat_issue59(self):
93         tmpl = env.from_string(
94             '{{ 300|filesizeformat }}|'
95             '{{ 3000|filesizeformat }}|'
96             '{{ 3000000|filesizeformat }}|'
97             '{{ 3000000000|filesizeformat }}|'
98             '{{ 3000000000000|filesizeformat }}|'
99             '{{ 300|filesizeformat(true) }}|'
100             '{{ 3000|filesizeformat(true) }}|'
101             '{{ 3000000|filesizeformat(true) }}'
102         )
103         out = tmpl.render()
104         self.assert_equal(out, (
105             '300 Bytes|3.0 kB|3.0 MB|3.0 GB|3.0 TB|300 Bytes|'
106             '2.9 KiB|2.9 MiB'
107         ))
108
109
110     def test_first(self):
111         tmpl = env.from_string('{{ foo|first }}')
112         out = tmpl.render(foo=range(10))
113         assert out == '0'
114
115     def test_float(self):
116         tmpl = env.from_string('{{ "42"|float }}|'
117                                '{{ "ajsghasjgd"|float }}|'
118                                '{{ "32.32"|float }}')
119         out = tmpl.render()
120         assert out == '42.0|0.0|32.32'
121
122     def test_format(self):
123         tmpl = env.from_string('''{{ "%s|%s"|format("a", "b") }}''')
124         out = tmpl.render()
125         assert out == 'a|b'
126
127     def test_indent(self):
128         tmpl = env.from_string('{{ foo|indent(2) }}|{{ foo|indent(2, true) }}')
129         text = '\n'.join([' '.join(['foo', 'bar'] * 2)] * 2)
130         out = tmpl.render(foo=text)
131         assert out == ('foo bar foo bar\n  foo bar foo bar|  '
132                        'foo bar foo bar\n  foo bar foo bar')
133
134     def test_int(self):
135         tmpl = env.from_string('{{ "42"|int }}|{{ "ajsghasjgd"|int }}|'
136                                '{{ "32.32"|int }}')
137         out = tmpl.render()
138         assert out == '42|0|32'
139
140     def test_join(self):
141         tmpl = env.from_string('{{ [1, 2, 3]|join("|") }}')
142         out = tmpl.render()
143         assert out == '1|2|3'
144
145         env2 = Environment(autoescape=True)
146         tmpl = env2.from_string('{{ ["<foo>", "<span>foo</span>"|safe]|join }}')
147         assert tmpl.render() == '&lt;foo&gt;<span>foo</span>'
148
149     def test_join_attribute(self):
150         class User(object):
151             def __init__(self, username):
152                 self.username = username
153         tmpl = env.from_string('''{{ users|join(', ', 'username') }}''')
154         assert tmpl.render(users=map(User, ['foo', 'bar'])) == 'foo, bar'
155
156     def test_last(self):
157         tmpl = env.from_string('''{{ foo|last }}''')
158         out = tmpl.render(foo=range(10))
159         assert out == '9'
160
161     def test_length(self):
162         tmpl = env.from_string('''{{ "hello world"|length }}''')
163         out = tmpl.render()
164         assert out == '11'
165
166     def test_lower(self):
167         tmpl = env.from_string('''{{ "FOO"|lower }}''')
168         out = tmpl.render()
169         assert out == 'foo'
170
171     def test_pprint(self):
172         from pprint import pformat
173         tmpl = env.from_string('''{{ data|pprint }}''')
174         data = range(1000)
175         assert tmpl.render(data=data) == pformat(data)
176
177     def test_random(self):
178         tmpl = env.from_string('''{{ seq|random }}''')
179         seq = range(100)
180         for _ in range(10):
181             assert int(tmpl.render(seq=seq)) in seq
182
183     def test_reverse(self):
184         tmpl = env.from_string('{{ "foobar"|reverse|join }}|'
185                                '{{ [1, 2, 3]|reverse|list }}')
186         assert tmpl.render() == 'raboof|[3, 2, 1]'
187
188     def test_string(self):
189         x = [1, 2, 3, 4, 5]
190         tmpl = env.from_string('''{{ obj|string }}''')
191         assert tmpl.render(obj=x) == unicode(x)
192
193     def test_title(self):
194         tmpl = env.from_string('''{{ "foo bar"|title }}''')
195         assert tmpl.render() == "Foo Bar"
196
197     def test_truncate(self):
198         tmpl = env.from_string(
199             '{{ data|truncate(15, true, ">>>") }}|'
200             '{{ data|truncate(15, false, ">>>") }}|'
201             '{{ smalldata|truncate(15) }}'
202         )
203         out = tmpl.render(data='foobar baz bar' * 1000,
204                           smalldata='foobar baz bar')
205         assert out == 'foobar baz barf>>>|foobar baz >>>|foobar baz bar'
206
207     def test_upper(self):
208         tmpl = env.from_string('{{ "foo"|upper }}')
209         assert tmpl.render() == 'FOO'
210
211     def test_urlize(self):
212         tmpl = env.from_string('{{ "foo http://www.example.com/ bar"|urlize }}')
213         assert tmpl.render() == 'foo <a href="http://www.example.com/">'\
214                                 'http://www.example.com/</a> bar'
215
216     def test_wordcount(self):
217         tmpl = env.from_string('{{ "foo bar baz"|wordcount }}')
218         assert tmpl.render() == '3'
219
220     def test_block(self):
221         tmpl = env.from_string('{% filter lower|escape %}<HEHE>{% endfilter %}')
222         assert tmpl.render() == '&lt;hehe&gt;'
223
224     def test_chaining(self):
225         tmpl = env.from_string('''{{ ['<foo>', '<bar>']|first|upper|escape }}''')
226         assert tmpl.render() == '&lt;FOO&gt;'
227
228     def test_sum(self):
229         tmpl = env.from_string('''{{ [1, 2, 3, 4, 5, 6]|sum }}''')
230         assert tmpl.render() == '21'
231
232     def test_sum_attributes(self):
233         tmpl = env.from_string('''{{ values|sum('value') }}''')
234         assert tmpl.render(values=[
235             {'value': 23},
236             {'value': 1},
237             {'value': 18},
238         ]) == '42'
239
240     def test_sum_attributes_nested(self):
241         tmpl = env.from_string('''{{ values|sum('real.value') }}''')
242         assert tmpl.render(values=[
243             {'real': {'value': 23}},
244             {'real': {'value': 1}},
245             {'real': {'value': 18}},
246         ]) == '42'
247
248     def test_abs(self):
249         tmpl = env.from_string('''{{ -1|abs }}|{{ 1|abs }}''')
250         assert tmpl.render() == '1|1', tmpl.render()
251
252     def test_round_positive(self):
253         tmpl = env.from_string('{{ 2.7|round }}|{{ 2.1|round }}|'
254                                "{{ 2.1234|round(3, 'floor') }}|"
255                                "{{ 2.1|round(0, 'ceil') }}")
256         assert tmpl.render() == '3.0|2.0|2.123|3.0', tmpl.render()
257
258     def test_round_negative(self):
259         tmpl = env.from_string('{{ 21.3|round(-1)}}|'
260                                "{{ 21.3|round(-1, 'ceil')}}|"
261                                "{{ 21.3|round(-1, 'floor')}}")
262         assert tmpl.render() == '20.0|30.0|20.0',tmpl.render()
263
264     def test_xmlattr(self):
265         tmpl = env.from_string("{{ {'foo': 42, 'bar': 23, 'fish': none, "
266                                "'spam': missing, 'blub:blub': '<?>'}|xmlattr }}")
267         out = tmpl.render().split()
268         assert len(out) == 3
269         assert 'foo="42"' in out
270         assert 'bar="23"' in out
271         assert 'blub:blub="&lt;?&gt;"' in out
272
273     def test_sort1(self):
274         tmpl = env.from_string('{{ [2, 3, 1]|sort }}|{{ [2, 3, 1]|sort(true) }}')
275         assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]'
276
277     def test_sort2(self):
278         tmpl = env.from_string('{{ "".join(["c", "A", "b", "D"]|sort) }}')
279         assert tmpl.render() == 'AbcD'
280
281     def test_sort3(self):
282         tmpl = env.from_string('''{{ ['foo', 'Bar', 'blah']|sort }}''')
283         assert tmpl.render() == "['Bar', 'blah', 'foo']"
284
285     def test_sort4(self):
286         class Magic(object):
287             def __init__(self, value):
288                 self.value = value
289             def __unicode__(self):
290                 return unicode(self.value)
291         tmpl = env.from_string('''{{ items|sort(attribute='value')|join }}''')
292         assert tmpl.render(items=map(Magic, [3, 2, 4, 1])) == '1234'
293
294     def test_groupby(self):
295         tmpl = env.from_string('''
296         {%- for grouper, list in [{'foo': 1, 'bar': 2},
297                                   {'foo': 2, 'bar': 3},
298                                   {'foo': 1, 'bar': 1},
299                                   {'foo': 3, 'bar': 4}]|groupby('foo') -%}
300             {{ grouper }}{% for x in list %}: {{ x.foo }}, {{ x.bar }}{% endfor %}|
301         {%- endfor %}''')
302         assert tmpl.render().split('|') == [
303             "1: 1, 2: 1, 1",
304             "2: 2, 3",
305             "3: 3, 4",
306             ""
307         ]
308
309     def test_groupby_tuple_index(self):
310         tmpl = env.from_string('''
311         {%- for grouper, list in [('a', 1), ('a', 2), ('b', 1)]|groupby(0) -%}
312             {{ grouper }}{% for x in list %}:{{ x.1 }}{% endfor %}|
313         {%- endfor %}''')
314         assert tmpl.render() == 'a:1:2|b:1|'
315
316     def test_groupby_multidot(self):
317         class Date(object):
318             def __init__(self, day, month, year):
319                 self.day = day
320                 self.month = month
321                 self.year = year
322         class Article(object):
323             def __init__(self, title, *date):
324                 self.date = Date(*date)
325                 self.title = title
326         articles = [
327             Article('aha', 1, 1, 1970),
328             Article('interesting', 2, 1, 1970),
329             Article('really?', 3, 1, 1970),
330             Article('totally not', 1, 1, 1971)
331         ]
332         tmpl = env.from_string('''
333         {%- for year, list in articles|groupby('date.year') -%}
334             {{ year }}{% for x in list %}[{{ x.title }}]{% endfor %}|
335         {%- endfor %}''')
336         assert tmpl.render(articles=articles).split('|') == [
337             '1970[aha][interesting][really?]',
338             '1971[totally not]',
339             ''
340         ]
341
342     def test_filtertag(self):
343         tmpl = env.from_string("{% filter upper|replace('FOO', 'foo') %}"
344                                "foobar{% endfilter %}")
345         assert tmpl.render() == 'fooBAR'
346
347     def test_replace(self):
348         env = Environment()
349         tmpl = env.from_string('{{ string|replace("o", 42) }}')
350         assert tmpl.render(string='<foo>') == '<f4242>'
351         env = Environment(autoescape=True)
352         tmpl = env.from_string('{{ string|replace("o", 42) }}')
353         assert tmpl.render(string='<foo>') == '&lt;f4242&gt;'
354         tmpl = env.from_string('{{ string|replace("<", 42) }}')
355         assert tmpl.render(string='<foo>') == '42foo&gt;'
356         tmpl = env.from_string('{{ string|replace("o", ">x<") }}')
357         assert tmpl.render(string=Markup('foo')) == 'f&gt;x&lt;&gt;x&lt;'
358
359     def test_forceescape(self):
360         tmpl = env.from_string('{{ x|forceescape }}')
361         assert tmpl.render(x=Markup('<div />')) == u'&lt;div /&gt;'
362
363     def test_safe(self):
364         env = Environment(autoescape=True)
365         tmpl = env.from_string('{{ "<div>foo</div>"|safe }}')
366         assert tmpl.render() == '<div>foo</div>'
367         tmpl = env.from_string('{{ "<div>foo</div>" }}')
368         assert tmpl.render() == '&lt;div&gt;foo&lt;/div&gt;'
369     
370     def test_urlescape(self):
371         env = Environment(autoescape=True)
372         tmpl = env.from_string('{{ "Hello, world!"|urlescape }}')
373         assert tmpl.render() == 'Hello%2C%20world%21'
374         tmpl = env.from_string('{{ o|urlescape }}')
375         assert tmpl.render(o=u"Hello, world\u203d") == "Hello%2C%20world%E2%80%BD"
376         assert tmpl.render(o=(("f", 1),)) == "f=1"
377         assert tmpl.render(o=(('f', 1), ("z", 2))) == "f=1&amp;z=2"
378         assert tmpl.render(o=((u"\u203d", 1),)) == "%E2%80%BD=1"
379         assert tmpl.render(o={u"\u203d": 1}) == "%E2%80%BD=1"
380
381 def suite():
382     suite = unittest.TestSuite()
383     suite.addTest(unittest.makeSuite(FilterTestCase))
384     return suite