blob: 0c934847a264511a6e6b160d17dc1ce7548c42c2 [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 Ronacher9f258ff2008-05-24 22:28:52 +02009from jinja2.exceptions import UndefinedError, TemplateSyntaxError
Armin Ronacher10f3ba22008-04-18 11:30:37 +020010
Rene Leonhardtc7e6c6d2009-04-20 23:08:53 +020011import conftest
12if conftest.NOSE:
13 from nose.tools import assert_raises as raises
14else:
15 from py.test import raises
16
Armin Ronacherab45b842007-03-18 20:47:50 +010017
18SIMPLE = '''{% for item in seq %}{{ item }}{% endfor %}'''
19ELSE = '''{% for item in seq %}XXX{% else %}...{% endfor %}'''
20EMPTYBLOCKS = '''<{% for item in seq %}{% else %}{% endfor %}>'''
21CONTEXTVARS = '''{% for item in seq %}\
22{{ loop.index }}|{{ loop.index0 }}|{{ loop.revindex }}|{{
23 loop.revindex0 }}|{{ loop.first }}|{{ loop.last }}|{{
Armin Ronacher10f3ba22008-04-18 11:30:37 +020024 loop.length }}###{% endfor %}'''
25CYCLING = '''{% for item in seq %}{{ loop.cycle('<1>', '<2>') }}{% endfor %}\
26{% for item in seq %}{{ loop.cycle(*through) }}{% endfor %}'''
Armin Ronacherab45b842007-03-18 20:47:50 +010027SCOPE = '''{% for item in seq %}{% endfor %}{{ item }}'''
Armin Ronacher6dba4d62007-05-21 23:41:36 +020028VARLEN = '''{% for item in iter %}{{ item }}{% endfor %}'''
Armin Ronacher9bcd4112007-05-29 14:17:24 +020029NONITER = '''{% for item in none %}...{% endfor %}'''
Armin Ronacher66a93442008-05-11 23:42:19 +020030RECURSIVE = '''{% for item in seq recursive -%}
Armin Ronacherb455c312008-05-11 23:43:07 +020031 [{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}]
Armin Ronacher66a93442008-05-11 23:42:19 +020032{%- endfor %}'''
Armin Ronacher24db4512008-05-19 09:28:17 +020033LOOPLOOP = '''{% for row in table %}
34 {%- set rowloop = loop -%}
35 {% for cell in row -%}
36 [{{ rowloop.index }}|{{ loop.index }}]
37 {%- endfor %}
38{%- endfor %}'''
Armin Ronacher105f0dc2008-05-23 16:12:47 +020039LOOPERROR1 = '''\
40{% for item in [1] if loop.index == 0 %}...{% endfor %}'''
41LOOPERROR2 = '''\
42{% for item in [] %}...{% else %}{{ loop }}{% endfor %}'''
Armin Ronacher6df604e2008-05-23 22:18:38 +020043LOOPFILTER = '''\
44{% for item in range(10) if item is even %}[{{ item }}]{% endfor %}'''
45EXTENDEDLOOPFILTER = '''\
46{% for item in range(10) if item is even %}[{{ loop.index
47}}:{{ item }}]{% endfor %}'''
Armin Ronacher9f258ff2008-05-24 22:28:52 +020048LOOPUNASSIGNABLE = '''\
49{% for loop in seq %}...{% endfor %}'''
Armin Ronacherab45b842007-03-18 20:47:50 +010050
51
52def test_simple(env):
53 tmpl = env.from_string(SIMPLE)
54 assert tmpl.render(seq=range(10)) == '0123456789'
55
56
57def test_else(env):
58 tmpl = env.from_string(ELSE)
59 assert tmpl.render() == '...'
60
61
62def test_empty_blocks(env):
63 tmpl = env.from_string(EMPTYBLOCKS)
64 assert tmpl.render() == '<>'
65
66
67def test_context_vars(env):
68 tmpl = env.from_string(CONTEXTVARS)
69 one, two, _ = tmpl.render(seq=[0, 1]).split('###')
70 (one_index, one_index0, one_revindex, one_revindex0, one_first,
Armin Ronacher10f3ba22008-04-18 11:30:37 +020071 one_last, one_length) = one.split('|')
Armin Ronacherab45b842007-03-18 20:47:50 +010072 (two_index, two_index0, two_revindex, two_revindex0, two_first,
Armin Ronacher10f3ba22008-04-18 11:30:37 +020073 two_last, two_length) = two.split('|')
Armin Ronacherab45b842007-03-18 20:47:50 +010074
75 assert int(one_index) == 1 and int(two_index) == 2
76 assert int(one_index0) == 0 and int(two_index0) == 1
77 assert int(one_revindex) == 2 and int(two_revindex) == 1
78 assert int(one_revindex0) == 1 and int(two_revindex0) == 0
79 assert one_first == 'True' and two_first == 'False'
80 assert one_last == 'False' and two_last == 'True'
Armin Ronacherab45b842007-03-18 20:47:50 +010081 assert one_length == two_length == '2'
82
83
84def test_cycling(env):
85 tmpl = env.from_string(CYCLING)
86 output = tmpl.render(seq=range(4), through=('<1>', '<2>'))
87 assert output == '<1><2>' * 4
88
89
90def test_scope(env):
91 tmpl = env.from_string(SCOPE)
92 output = tmpl.render(seq=range(10))
93 assert not output
Armin Ronacher6dba4d62007-05-21 23:41:36 +020094
95
96def test_varlen(env):
97 def inner():
98 for item in range(5):
99 yield item
100 tmpl = env.from_string(VARLEN)
101 output = tmpl.render(iter=inner())
102 assert output == '01234'
Armin Ronacher9bcd4112007-05-29 14:17:24 +0200103
104
105def test_noniter(env):
106 tmpl = env.from_string(NONITER)
Armin Ronacher10f3ba22008-04-18 11:30:37 +0200107 raises(TypeError, tmpl.render)
Armin Ronacher66a93442008-05-11 23:42:19 +0200108
109
110def test_recursive(env):
111 tmpl = env.from_string(RECURSIVE)
112 assert tmpl.render(seq=[
113 dict(a=1, b=[dict(a=1), dict(a=2)]),
114 dict(a=2, b=[dict(a=1), dict(a=2)]),
115 dict(a=3, b=[dict(a='a')])
Armin Ronacherb455c312008-05-11 23:43:07 +0200116 ]) == '[1<[1][2]>][2<[1][2]>][3<[a]>]'
Armin Ronacher24db4512008-05-19 09:28:17 +0200117
118
119def test_looploop(env):
120 tmpl = env.from_string(LOOPLOOP)
121 assert tmpl.render(table=['ab', 'cd']) == '[1|1][1|2][2|1][2|2]'
Armin Ronacher105f0dc2008-05-23 16:12:47 +0200122
123
Armin Ronacher547d0b62008-07-04 16:35:10 +0200124def test_reversed_bug(env):
125 tmpl = env.from_string('{% for i in items %}{{ i }}{% if not loop.last %}'
126 ',{% endif %}{% endfor %}')
127 assert tmpl.render(items=reversed([3, 2, 1])) == '1,2,3'
128
129
Armin Ronacher105f0dc2008-05-23 16:12:47 +0200130def test_loop_errors(env):
131 tmpl = env.from_string(LOOPERROR1)
132 raises(UndefinedError, tmpl.render)
133 tmpl = env.from_string(LOOPERROR2)
134 assert tmpl.render() == ''
Armin Ronacher6df604e2008-05-23 22:18:38 +0200135
136
137def test_loop_filter(env):
138 tmpl = env.from_string(LOOPFILTER)
139 assert tmpl.render() == '[0][2][4][6][8]'
140 tmpl = env.from_string(EXTENDEDLOOPFILTER)
141 assert tmpl.render() == '[1:0][2:2][3:4][4:6][5:8]'
Armin Ronacher9f258ff2008-05-24 22:28:52 +0200142
143
144def test_loop_unassignable(env):
145 raises(TemplateSyntaxError, env.from_string, LOOPUNASSIGNABLE)
Armin Ronacherff53c782008-08-13 18:55:50 +0200146
147
148def test_scoped_special_var(env):
149 t = env.from_string('{% for s in seq %}[{{ loop.first }}{% for c in s %}'
150 '|{{ loop.first }}{% endfor %}]{% endfor %}')
151 assert t.render(seq=('ab', 'cd')) == '[True|True|False][False|True|False]'
Armin Ronacher833a3b52008-08-14 12:31:12 +0200152
153
154def test_scoped_loop_var(env):
155 t = env.from_string('{% for x in seq %}{{ loop.first }}'
156 '{% for y in seq %}{% endfor %}{% endfor %}')
157 assert t.render(seq='ab') == 'TrueFalse'
158 t = env.from_string('{% for x in seq %}{% for y in seq %}'
159 '{{ loop.first }}{% endfor %}{% endfor %}')
160 assert t.render(seq='ab') == 'TrueFalseTrueFalse'
Armin Ronachercebd8382008-12-25 18:33:46 +0100161
162
163def test_recursive_empty_loop_iter(env):
164 t = env.from_string('''
165 {%- for item in foo recursive -%}{%- endfor -%}
166 ''')
167 assert t.render(dict(foo=[])) == ''
Armin Ronacher330fbc02009-02-04 19:13:58 +0100168
169
170def test_call_in_loop(env):
171 t = env.from_string('''
172 {%- macro do_something() -%}
173 [{{ caller() }}]
174 {%- endmacro %}
175
176 {%- for i in [1, 2, 3] %}
177 {%- call do_something() -%}
178 {{ i }}
179 {%- endcall %}
180 {%- endfor -%}
181 ''')
182 assert t.render() == '[1][2][3]'
Armin Ronacher271a0eb2009-02-11 22:49:08 +0100183
184
185def test_scoping_bug(env):
186 t = env.from_string('''
187 {%- for item in foo %}...{{ item }}...{% endfor %}
188 {%- macro item(a) %}...{{ a }}...{% endmacro %}
189 {{- item(2) -}}
190 ''')
191 assert t.render(foo=(1,)) == '...1......2...'