blob: dd66ca023ed2cc8ca576d446aaa5ca25e51e4408 [file] [log] [blame]
Armin Ronacherab45b842007-03-18 20:47:50 +01001# -*- coding: utf-8 -*-
2"""
3 unit test for loop functions
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
Armin Ronacher62ccd1b2009-01-04 14:26:19 +01006 :copyright: (c) 2009 by the Jinja Team.
Armin Ronacherab45b842007-03-18 20:47:50 +01007 :license: BSD, see LICENSE for more details.
8"""
Armin Ronacher42979eb2009-07-26 11:08:50 +02009from jinja2 import Environment
Armin Ronacher9f258ff2008-05-24 22:28:52 +020010from jinja2.exceptions import UndefinedError, TemplateSyntaxError
Armin Ronacher10f3ba22008-04-18 11:30:37 +020011
Armin Ronacher42979eb2009-07-26 11:08:50 +020012from nose.tools import assert_raises
13
14env = Environment()
Rene Leonhardtc7e6c6d2009-04-20 23:08:53 +020015
Armin Ronacherab45b842007-03-18 20:47:50 +010016
17SIMPLE = '''{% for item in seq %}{{ item }}{% endfor %}'''
18ELSE = '''{% for item in seq %}XXX{% else %}...{% endfor %}'''
19EMPTYBLOCKS = '''<{% for item in seq %}{% else %}{% endfor %}>'''
20CONTEXTVARS = '''{% for item in seq %}\
21{{ loop.index }}|{{ loop.index0 }}|{{ loop.revindex }}|{{
22 loop.revindex0 }}|{{ loop.first }}|{{ loop.last }}|{{
Armin Ronacher10f3ba22008-04-18 11:30:37 +020023 loop.length }}###{% endfor %}'''
24CYCLING = '''{% for item in seq %}{{ loop.cycle('<1>', '<2>') }}{% endfor %}\
25{% for item in seq %}{{ loop.cycle(*through) }}{% endfor %}'''
Armin Ronacherab45b842007-03-18 20:47:50 +010026SCOPE = '''{% for item in seq %}{% endfor %}{{ item }}'''
Armin Ronacher6dba4d62007-05-21 23:41:36 +020027VARLEN = '''{% for item in iter %}{{ item }}{% endfor %}'''
Armin Ronacher9bcd4112007-05-29 14:17:24 +020028NONITER = '''{% for item in none %}...{% endfor %}'''
Armin Ronacher66a93442008-05-11 23:42:19 +020029RECURSIVE = '''{% for item in seq recursive -%}
Armin Ronacherb455c312008-05-11 23:43:07 +020030 [{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
Armin Ronacher66a93442008-05-11 23:42:19 +020031{%- endfor %}'''
Armin Ronacher24db4512008-05-19 09:28:17 +020032LOOPLOOP = '''{% for row in table %}
33 {%- set rowloop = loop -%}
34 {% for cell in row -%}
35 [{{ rowloop.index }}|{{ loop.index }}]
36 {%- endfor %}
37{%- endfor %}'''
Armin Ronacher105f0dc2008-05-23 16:12:47 +020038LOOPERROR1 = '''\
39{% for item in [1] if loop.index == 0 %}...{% endfor %}'''
40LOOPERROR2 = '''\
41{% for item in [] %}...{% else %}{{ loop }}{% endfor %}'''
Armin Ronacher6df604e2008-05-23 22:18:38 +020042LOOPFILTER = '''\
43{% for item in range(10) if item is even %}[{{ item }}]{% endfor %}'''
44EXTENDEDLOOPFILTER = '''\
45{% for item in range(10) if item is even %}[{{ loop.index
46}}:{{ item }}]{% endfor %}'''
Armin Ronacher9f258ff2008-05-24 22:28:52 +020047LOOPUNASSIGNABLE = '''\
48{% for loop in seq %}...{% endfor %}'''
Armin Ronacherab45b842007-03-18 20:47:50 +010049
50
Armin Ronacher42979eb2009-07-26 11:08:50 +020051def test_simple():
Armin Ronacherab45b842007-03-18 20:47:50 +010052 tmpl = env.from_string(SIMPLE)
53 assert tmpl.render(seq=range(10)) == '0123456789'
54
55
Armin Ronacher42979eb2009-07-26 11:08:50 +020056def test_else():
Armin Ronacherab45b842007-03-18 20:47:50 +010057 tmpl = env.from_string(ELSE)
58 assert tmpl.render() == '...'
59
60
Armin Ronacher42979eb2009-07-26 11:08:50 +020061def test_empty_blocks():
Armin Ronacherab45b842007-03-18 20:47:50 +010062 tmpl = env.from_string(EMPTYBLOCKS)
63 assert tmpl.render() == '<>'
64
65
Armin Ronacher42979eb2009-07-26 11:08:50 +020066def test_context_vars():
Armin Ronacherab45b842007-03-18 20:47:50 +010067 tmpl = env.from_string(CONTEXTVARS)
68 one, two, _ = tmpl.render(seq=[0, 1]).split('###')
69 (one_index, one_index0, one_revindex, one_revindex0, one_first,
Armin Ronacher10f3ba22008-04-18 11:30:37 +020070 one_last, one_length) = one.split('|')
Armin Ronacherab45b842007-03-18 20:47:50 +010071 (two_index, two_index0, two_revindex, two_revindex0, two_first,
Armin Ronacher10f3ba22008-04-18 11:30:37 +020072 two_last, two_length) = two.split('|')
Armin Ronacherab45b842007-03-18 20:47:50 +010073
74 assert int(one_index) == 1 and int(two_index) == 2
75 assert int(one_index0) == 0 and int(two_index0) == 1
76 assert int(one_revindex) == 2 and int(two_revindex) == 1
77 assert int(one_revindex0) == 1 and int(two_revindex0) == 0
78 assert one_first == 'True' and two_first == 'False'
79 assert one_last == 'False' and two_last == 'True'
Armin Ronacherab45b842007-03-18 20:47:50 +010080 assert one_length == two_length == '2'
81
82
Armin Ronacher42979eb2009-07-26 11:08:50 +020083def test_cycling():
Armin Ronacherab45b842007-03-18 20:47:50 +010084 tmpl = env.from_string(CYCLING)
85 output = tmpl.render(seq=range(4), through=('<1>', '<2>'))
86 assert output == '<1><2>' * 4
87
88
Armin Ronacher42979eb2009-07-26 11:08:50 +020089def test_scope():
Armin Ronacherab45b842007-03-18 20:47:50 +010090 tmpl = env.from_string(SCOPE)
91 output = tmpl.render(seq=range(10))
92 assert not output
Armin Ronacher6dba4d62007-05-21 23:41:36 +020093
94
Armin Ronacher42979eb2009-07-26 11:08:50 +020095def test_varlen():
Armin Ronacher6dba4d62007-05-21 23:41:36 +020096 def inner():
97 for item in range(5):
98 yield item
99 tmpl = env.from_string(VARLEN)
100 output = tmpl.render(iter=inner())
101 assert output == '01234'
Armin Ronacher9bcd4112007-05-29 14:17:24 +0200102
103
Armin Ronacher42979eb2009-07-26 11:08:50 +0200104def test_noniter():
Armin Ronacher9bcd4112007-05-29 14:17:24 +0200105 tmpl = env.from_string(NONITER)
Armin Ronacher42979eb2009-07-26 11:08:50 +0200106 assert_raises(TypeError, tmpl.render)
Armin Ronacher66a93442008-05-11 23:42:19 +0200107
108
Armin Ronacher42979eb2009-07-26 11:08:50 +0200109def test_recursive():
Armin Ronacher66a93442008-05-11 23:42:19 +0200110 tmpl = env.from_string(RECURSIVE)
111 assert tmpl.render(seq=[
112 dict(a=1, b=[dict(a=1), dict(a=2)]),
113 dict(a=2, b=[dict(a=1), dict(a=2)]),
114 dict(a=3, b=[dict(a='a')])
Armin Ronacherb455c312008-05-11 23:43:07 +0200115 ]) == '[1<[1][2]>][2<[1][2]>][3<[a]>]'
Armin Ronacher24db4512008-05-19 09:28:17 +0200116
117
Armin Ronacher42979eb2009-07-26 11:08:50 +0200118def test_looploop():
Armin Ronacher24db4512008-05-19 09:28:17 +0200119 tmpl = env.from_string(LOOPLOOP)
120 assert tmpl.render(table=['ab', 'cd']) == '[1|1][1|2][2|1][2|2]'
Armin Ronacher105f0dc2008-05-23 16:12:47 +0200121
122
Armin Ronacher42979eb2009-07-26 11:08:50 +0200123def test_reversed_bug():
Armin Ronacher547d0b62008-07-04 16:35:10 +0200124 tmpl = env.from_string('{% for i in items %}{{ i }}{% if not loop.last %}'
125 ',{% endif %}{% endfor %}')
126 assert tmpl.render(items=reversed([3, 2, 1])) == '1,2,3'
127
128
Armin Ronacher42979eb2009-07-26 11:08:50 +0200129def test_loop_errors():
Armin Ronacher105f0dc2008-05-23 16:12:47 +0200130 tmpl = env.from_string(LOOPERROR1)
Armin Ronacher42979eb2009-07-26 11:08:50 +0200131 assert_raises(UndefinedError, tmpl.render)
Armin Ronacher105f0dc2008-05-23 16:12:47 +0200132 tmpl = env.from_string(LOOPERROR2)
133 assert tmpl.render() == ''
Armin Ronacher6df604e2008-05-23 22:18:38 +0200134
135
Armin Ronacher42979eb2009-07-26 11:08:50 +0200136def test_loop_filter():
Armin Ronacher6df604e2008-05-23 22:18:38 +0200137 tmpl = env.from_string(LOOPFILTER)
138 assert tmpl.render() == '[0][2][4][6][8]'
139 tmpl = env.from_string(EXTENDEDLOOPFILTER)
140 assert tmpl.render() == '[1:0][2:2][3:4][4:6][5:8]'
Armin Ronacher9f258ff2008-05-24 22:28:52 +0200141
142
Armin Ronacher42979eb2009-07-26 11:08:50 +0200143def test_loop_unassignable():
144 assert_raises(TemplateSyntaxError, env.from_string, LOOPUNASSIGNABLE)
Armin Ronacherff53c782008-08-13 18:55:50 +0200145
146
Armin Ronacher42979eb2009-07-26 11:08:50 +0200147def test_scoped_special_var():
Armin Ronacherff53c782008-08-13 18:55:50 +0200148 t = env.from_string('{% for s in seq %}[{{ loop.first }}{% for c in s %}'
149 '|{{ loop.first }}{% endfor %}]{% endfor %}')
150 assert t.render(seq=('ab', 'cd')) == '[True|True|False][False|True|False]'
Armin Ronacher833a3b52008-08-14 12:31:12 +0200151
152
Armin Ronacher42979eb2009-07-26 11:08:50 +0200153def test_scoped_loop_var():
Armin Ronacher833a3b52008-08-14 12:31:12 +0200154 t = env.from_string('{% for x in seq %}{{ loop.first }}'
155 '{% for y in seq %}{% endfor %}{% endfor %}')
156 assert t.render(seq='ab') == 'TrueFalse'
157 t = env.from_string('{% for x in seq %}{% for y in seq %}'
158 '{{ loop.first }}{% endfor %}{% endfor %}')
159 assert t.render(seq='ab') == 'TrueFalseTrueFalse'
Armin Ronachercebd8382008-12-25 18:33:46 +0100160
161
Armin Ronacher42979eb2009-07-26 11:08:50 +0200162def test_recursive_empty_loop_iter():
Armin Ronachercebd8382008-12-25 18:33:46 +0100163 t = env.from_string('''
164 {%- for item in foo recursive -%}{%- endfor -%}
165 ''')
166 assert t.render(dict(foo=[])) == ''
Armin Ronacher330fbc02009-02-04 19:13:58 +0100167
168
Armin Ronacher42979eb2009-07-26 11:08:50 +0200169def test_call_in_loop():
Armin Ronacher330fbc02009-02-04 19:13:58 +0100170 t = env.from_string('''
171 {%- macro do_something() -%}
172 [{{ caller() }}]
173 {%- endmacro %}
174
175 {%- for i in [1, 2, 3] %}
176 {%- call do_something() -%}
177 {{ i }}
178 {%- endcall %}
179 {%- endfor -%}
180 ''')
181 assert t.render() == '[1][2][3]'
Armin Ronacher271a0eb2009-02-11 22:49:08 +0100182
183
Armin Ronacher42979eb2009-07-26 11:08:50 +0200184def test_scoping_bug():
Armin Ronacher271a0eb2009-02-11 22:49:08 +0100185 t = env.from_string('''
186 {%- for item in foo %}...{{ item }}...{% endfor %}
187 {%- macro item(a) %}...{{ a }}...{% endmacro %}
188 {{- item(2) -}}
189 ''')
190 assert t.render(foo=(1,)) == '...1......2...'