Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | """ |
| 3 | unit test for loop functions |
| 4 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 5 | |
Armin Ronacher | 62ccd1b | 2009-01-04 14:26:19 +0100 | [diff] [blame] | 6 | :copyright: (c) 2009 by the Jinja Team. |
Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 7 | :license: BSD, see LICENSE for more details. |
| 8 | """ |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 9 | from jinja2 import Environment |
Armin Ronacher | 9f258ff | 2008-05-24 22:28:52 +0200 | [diff] [blame] | 10 | from jinja2.exceptions import UndefinedError, TemplateSyntaxError |
Armin Ronacher | 10f3ba2 | 2008-04-18 11:30:37 +0200 | [diff] [blame] | 11 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 12 | from nose.tools import assert_raises |
| 13 | |
| 14 | env = Environment() |
Rene Leonhardt | c7e6c6d | 2009-04-20 23:08:53 +0200 | [diff] [blame] | 15 | |
Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 16 | |
| 17 | SIMPLE = '''{% for item in seq %}{{ item }}{% endfor %}''' |
| 18 | ELSE = '''{% for item in seq %}XXX{% else %}...{% endfor %}''' |
| 19 | EMPTYBLOCKS = '''<{% for item in seq %}{% else %}{% endfor %}>''' |
| 20 | CONTEXTVARS = '''{% for item in seq %}\ |
| 21 | {{ loop.index }}|{{ loop.index0 }}|{{ loop.revindex }}|{{ |
| 22 | loop.revindex0 }}|{{ loop.first }}|{{ loop.last }}|{{ |
Armin Ronacher | 10f3ba2 | 2008-04-18 11:30:37 +0200 | [diff] [blame] | 23 | loop.length }}###{% endfor %}''' |
| 24 | CYCLING = '''{% for item in seq %}{{ loop.cycle('<1>', '<2>') }}{% endfor %}\ |
| 25 | {% for item in seq %}{{ loop.cycle(*through) }}{% endfor %}''' |
Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 26 | SCOPE = '''{% for item in seq %}{% endfor %}{{ item }}''' |
Armin Ronacher | 6dba4d6 | 2007-05-21 23:41:36 +0200 | [diff] [blame] | 27 | VARLEN = '''{% for item in iter %}{{ item }}{% endfor %}''' |
Armin Ronacher | 9bcd411 | 2007-05-29 14:17:24 +0200 | [diff] [blame] | 28 | NONITER = '''{% for item in none %}...{% endfor %}''' |
Armin Ronacher | 66a9344 | 2008-05-11 23:42:19 +0200 | [diff] [blame] | 29 | RECURSIVE = '''{% for item in seq recursive -%} |
Armin Ronacher | b455c31 | 2008-05-11 23:43:07 +0200 | [diff] [blame] | 30 | [{{ item.a }}{% if item.b %}<{{ loop(item.b) }}>{% endif %}] |
Armin Ronacher | 66a9344 | 2008-05-11 23:42:19 +0200 | [diff] [blame] | 31 | {%- endfor %}''' |
Armin Ronacher | 24db451 | 2008-05-19 09:28:17 +0200 | [diff] [blame] | 32 | LOOPLOOP = '''{% for row in table %} |
| 33 | {%- set rowloop = loop -%} |
| 34 | {% for cell in row -%} |
| 35 | [{{ rowloop.index }}|{{ loop.index }}] |
| 36 | {%- endfor %} |
| 37 | {%- endfor %}''' |
Armin Ronacher | 105f0dc | 2008-05-23 16:12:47 +0200 | [diff] [blame] | 38 | LOOPERROR1 = '''\ |
| 39 | {% for item in [1] if loop.index == 0 %}...{% endfor %}''' |
| 40 | LOOPERROR2 = '''\ |
| 41 | {% for item in [] %}...{% else %}{{ loop }}{% endfor %}''' |
Armin Ronacher | 6df604e | 2008-05-23 22:18:38 +0200 | [diff] [blame] | 42 | LOOPFILTER = '''\ |
| 43 | {% for item in range(10) if item is even %}[{{ item }}]{% endfor %}''' |
| 44 | EXTENDEDLOOPFILTER = '''\ |
| 45 | {% for item in range(10) if item is even %}[{{ loop.index |
| 46 | }}:{{ item }}]{% endfor %}''' |
Armin Ronacher | 9f258ff | 2008-05-24 22:28:52 +0200 | [diff] [blame] | 47 | LOOPUNASSIGNABLE = '''\ |
| 48 | {% for loop in seq %}...{% endfor %}''' |
Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 49 | |
| 50 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 51 | def test_simple(): |
Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 52 | tmpl = env.from_string(SIMPLE) |
| 53 | assert tmpl.render(seq=range(10)) == '0123456789' |
| 54 | |
| 55 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 56 | def test_else(): |
Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 57 | tmpl = env.from_string(ELSE) |
| 58 | assert tmpl.render() == '...' |
| 59 | |
| 60 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 61 | def test_empty_blocks(): |
Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 62 | tmpl = env.from_string(EMPTYBLOCKS) |
| 63 | assert tmpl.render() == '<>' |
| 64 | |
| 65 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 66 | def test_context_vars(): |
Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 67 | 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 Ronacher | 10f3ba2 | 2008-04-18 11:30:37 +0200 | [diff] [blame] | 70 | one_last, one_length) = one.split('|') |
Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 71 | (two_index, two_index0, two_revindex, two_revindex0, two_first, |
Armin Ronacher | 10f3ba2 | 2008-04-18 11:30:37 +0200 | [diff] [blame] | 72 | two_last, two_length) = two.split('|') |
Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 73 | |
| 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 Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 80 | assert one_length == two_length == '2' |
| 81 | |
| 82 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 83 | def test_cycling(): |
Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 84 | tmpl = env.from_string(CYCLING) |
| 85 | output = tmpl.render(seq=range(4), through=('<1>', '<2>')) |
| 86 | assert output == '<1><2>' * 4 |
| 87 | |
| 88 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 89 | def test_scope(): |
Armin Ronacher | ab45b84 | 2007-03-18 20:47:50 +0100 | [diff] [blame] | 90 | tmpl = env.from_string(SCOPE) |
| 91 | output = tmpl.render(seq=range(10)) |
| 92 | assert not output |
Armin Ronacher | 6dba4d6 | 2007-05-21 23:41:36 +0200 | [diff] [blame] | 93 | |
| 94 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 95 | def test_varlen(): |
Armin Ronacher | 6dba4d6 | 2007-05-21 23:41:36 +0200 | [diff] [blame] | 96 | 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 Ronacher | 9bcd411 | 2007-05-29 14:17:24 +0200 | [diff] [blame] | 102 | |
| 103 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 104 | def test_noniter(): |
Armin Ronacher | 9bcd411 | 2007-05-29 14:17:24 +0200 | [diff] [blame] | 105 | tmpl = env.from_string(NONITER) |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 106 | assert_raises(TypeError, tmpl.render) |
Armin Ronacher | 66a9344 | 2008-05-11 23:42:19 +0200 | [diff] [blame] | 107 | |
| 108 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 109 | def test_recursive(): |
Armin Ronacher | 66a9344 | 2008-05-11 23:42:19 +0200 | [diff] [blame] | 110 | 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 Ronacher | b455c31 | 2008-05-11 23:43:07 +0200 | [diff] [blame] | 115 | ]) == '[1<[1][2]>][2<[1][2]>][3<[a]>]' |
Armin Ronacher | 24db451 | 2008-05-19 09:28:17 +0200 | [diff] [blame] | 116 | |
| 117 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 118 | def test_looploop(): |
Armin Ronacher | 24db451 | 2008-05-19 09:28:17 +0200 | [diff] [blame] | 119 | tmpl = env.from_string(LOOPLOOP) |
| 120 | assert tmpl.render(table=['ab', 'cd']) == '[1|1][1|2][2|1][2|2]' |
Armin Ronacher | 105f0dc | 2008-05-23 16:12:47 +0200 | [diff] [blame] | 121 | |
| 122 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 123 | def test_reversed_bug(): |
Armin Ronacher | 547d0b6 | 2008-07-04 16:35:10 +0200 | [diff] [blame] | 124 | 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 Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 129 | def test_loop_errors(): |
Armin Ronacher | 105f0dc | 2008-05-23 16:12:47 +0200 | [diff] [blame] | 130 | tmpl = env.from_string(LOOPERROR1) |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 131 | assert_raises(UndefinedError, tmpl.render) |
Armin Ronacher | 105f0dc | 2008-05-23 16:12:47 +0200 | [diff] [blame] | 132 | tmpl = env.from_string(LOOPERROR2) |
| 133 | assert tmpl.render() == '' |
Armin Ronacher | 6df604e | 2008-05-23 22:18:38 +0200 | [diff] [blame] | 134 | |
| 135 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 136 | def test_loop_filter(): |
Armin Ronacher | 6df604e | 2008-05-23 22:18:38 +0200 | [diff] [blame] | 137 | 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 Ronacher | 9f258ff | 2008-05-24 22:28:52 +0200 | [diff] [blame] | 141 | |
| 142 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 143 | def test_loop_unassignable(): |
| 144 | assert_raises(TemplateSyntaxError, env.from_string, LOOPUNASSIGNABLE) |
Armin Ronacher | ff53c78 | 2008-08-13 18:55:50 +0200 | [diff] [blame] | 145 | |
| 146 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 147 | def test_scoped_special_var(): |
Armin Ronacher | ff53c78 | 2008-08-13 18:55:50 +0200 | [diff] [blame] | 148 | 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 Ronacher | 833a3b5 | 2008-08-14 12:31:12 +0200 | [diff] [blame] | 151 | |
| 152 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 153 | def test_scoped_loop_var(): |
Armin Ronacher | 833a3b5 | 2008-08-14 12:31:12 +0200 | [diff] [blame] | 154 | 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 Ronacher | cebd838 | 2008-12-25 18:33:46 +0100 | [diff] [blame] | 160 | |
| 161 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 162 | def test_recursive_empty_loop_iter(): |
Armin Ronacher | cebd838 | 2008-12-25 18:33:46 +0100 | [diff] [blame] | 163 | t = env.from_string(''' |
| 164 | {%- for item in foo recursive -%}{%- endfor -%} |
| 165 | ''') |
| 166 | assert t.render(dict(foo=[])) == '' |
Armin Ronacher | 330fbc0 | 2009-02-04 19:13:58 +0100 | [diff] [blame] | 167 | |
| 168 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 169 | def test_call_in_loop(): |
Armin Ronacher | 330fbc0 | 2009-02-04 19:13:58 +0100 | [diff] [blame] | 170 | 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 Ronacher | 271a0eb | 2009-02-11 22:49:08 +0100 | [diff] [blame] | 182 | |
| 183 | |
Armin Ronacher | 42979eb | 2009-07-26 11:08:50 +0200 | [diff] [blame] | 184 | def test_scoping_bug(): |
Armin Ronacher | 271a0eb | 2009-02-11 22:49:08 +0100 | [diff] [blame] | 185 | 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...' |