]>
git.proxmox.com Git - rustc.git/blob - vendor/tera/src/renderer/tests/basic.rs
1 use std
::collections
::{BTreeMap, HashMap}
;
3 use std
::sync
::atomic
::{AtomicUsize, Ordering}
;
6 use lazy_static
::lazy_static
;
7 use serde_derive
::Serialize
;
8 use serde_json
::{json, Value}
;
10 use crate::builtins
::functions
::Function
;
11 use crate::context
::Context
;
12 use crate::errors
::Result
;
13 use crate::tera
::Tera
;
17 fn render_template(content
: &str, context
: &Context
) -> Result
<String
> {
18 let mut tera
= Tera
::default();
19 tera
.add_raw_template("hello.html", content
).unwrap();
20 tera
.register_function("get_number", |_
: &HashMap
<String
, Value
>| Ok(Value
::Number(10.into
())));
21 tera
.register_function("get_true", |_
: &HashMap
<String
, Value
>| Ok(Value
::Bool(true.into())));
22 tera
.register_function("get_string", |_
: &HashMap
<String
, Value
>| {
23 Ok(Value
::String("Hello".to_string()))
26 tera
.render("hello.html", context
)
30 fn render_simple_string() {
31 let result
= render_template("<h1>Hello world</h1>", &Context
::new());
32 assert_eq
!(result
.unwrap(), "<h1>Hello world</h1>".to_owned());
36 fn render_variable_block_lit_expr() {
39 ("{{ 3.14 }}", "3.14"),
40 ("{{ \"hey\" }}", "hey"),
41 (r
#"{{ "{{ hey }}" }}"#, "{{ hey }}"),
42 ("{{ true }}", "true"),
43 ("{{ false }}", "false"),
44 ("{{ false and true or true }}", "true"),
46 ("{{ 1 + 1.1 }}", "2.1"),
48 ("{{ 3 - 1.1 }}", "1.9"),
49 ("{{ 2 * 5 }}", "10"),
50 ("{{ 10 / 5 }}", "2"),
51 ("{{ 2.1 * 5 }}", "10.5"),
52 ("{{ 2.1 * 5.05 }}", "10.605"),
53 ("{{ 2 / 0.5 }}", "4"),
54 ("{{ 2.1 / 0.5 }}", "4.2"),
55 ("{{ 2 + 1 * 2 }}", "4"),
56 ("{{ (2 + 1) * 2 }}", "6"),
57 ("{{ 2 * 4 % 8 }}", "0"),
58 ("{{ 2.8 * 2 | round }}", "6"),
59 ("{{ 1 / 0 }}", "NaN"),
60 ("{{ true and 10 }}", "true"),
61 ("{{ true and not 10 }}", "false"),
62 ("{{ not true }}", "false"),
63 ("{{ [1, 2, 3] }}", "[1, 2, 3]"),
66 for (input, expected) in inputs {
67 println!("{:?} -> {:?}", input, expected);
68 assert_eq!(render_template(input, &Context::new()).unwrap(), expected);
73 fn render_variable_block_ident() {
74 let mut context = Context::new();
75 context.insert("name", &"john");
76 context.insert("malicious", &"<html>");
77 context.insert("a", &2);
78 context.insert("b", &3);
79 context.insert("numbers", &vec![1, 2, 3]);
80 context.insert("tuple_list", &vec![(1, 2, 3), (1, 2, 3)]);
81 context.insert("review", &Review::new());
82 context.insert("with_newline", &"Animal Alphabets\nB is for Bee-Eater");
85 ("{{ name }}", "john"),
86 ("{{ malicious }}", "<html>"),
87 ("{{ \"<html>\" }}", "<html>"),
88 ("{{ \" html \" | upper | trim }}", "HTML"),
89 ("{{ 'html' }}", "html"),
90 ("{{ `html` }}", "html"),
91 // https://github.com/Keats/tera/issues/273
93 r#"{{ 'hangar new "Will Smoth <will_s@example.com>"' | safe }}"#,
94 r#"hangar new "Will Smoth <will_s@example.com>""#,
96 ("{{ malicious | safe }}", "<html>"),
97 ("{{ malicious | upper }}", "<HTML>"),
98 ("{{ malicious | upper | safe }}", "<HTML>"),
99 ("{{ malicious | safe | upper }}", "<HTML>"),
100 ("{{ review | length }}", "2"),
101 ("{{ review.paragraphs.1 }}", "B"),
102 ("{{ numbers }}", "[1, 2, 3]"),
103 ("{{ numbers.0 }}", "1"),
104 ("{{ tuple_list.1.1 }}", "2"),
105 ("{{ name and true }}", "true"),
106 ("{{ name | length }}", "4"),
107 ("{{ name is defined }}", "true"),
108 ("{{ not name is defined }}", "false"),
109 ("{{ name is not defined }}", "false"),
110 ("{{ not name is not defined }}", "true"),
111 ("{{ a is odd }}", "false"),
112 ("{{ a is odd or b is odd }}", "true"),
113 ("{{ range(start=1, end=4) }}", "[1, 2, 3]"),
114 ("{{ a + b }}", "5"),
115 ("{{ a + 1.5 }}", "3.5"),
116 ("{{ 1 + 1 + 1 }}", "3"),
117 ("{{ 2 - 2 - 1 }}", "-1"),
118 ("{{ 1 - 1 + 1 }}", "1"),
119 ("{{ 1 + get_number() }}", "11"),
120 ("{{ get_number() + 1 }}", "11"),
121 ("{{ (1.9 + a) | round }}", "4"),
122 ("{{ 1.9 + a | round }}", "4"),
123 ("{{ numbers | length - 1 }}", "2"),
124 ("{{ 1.9 + a | round - 1 }}", "3"),
125 ("{{ 1.9 + a | round - 1.8 + a | round }}", "0"),
126 ("{{ 1.9 + a | round - 1.8 + a | round - 1 }}", "-1"),
127 // https://github.com/Keats/tera/issues/435
129 "{{ with_newline | replace(from='\n', to='<br>') | safe }}",
130 "Animal Alphabets<br>B is for Bee-Eater",
134 for (input, expected) in inputs {
135 println!("{:?} -> {:?}", input, expected);
136 assert_eq!(render_template(input, &context).unwrap(), expected);
141 fn render_variable_block_logic_expr() {
142 let mut context = Context::new();
143 context.insert("name", &"john");
144 context.insert("malicious", &"<html>");
145 context.insert("a", &2);
146 context.insert("b", &3);
147 context.insert("numbers", &vec![1, 2, 3]);
148 context.insert("tuple_list", &vec![(1, 2, 3), (1, 2, 3)]);
149 let mut hashmap = HashMap::new();
150 hashmap.insert("a", 1);
151 hashmap.insert("b", 10);
152 hashmap.insert("john", 100);
153 context.insert("object", &hashmap);
156 ("{{ (1.9 + a) | round > 10 }}", "false"),
157 ("{{ (1.9 + a) | round > 10 or b > a }}", "true"),
158 ("{{ 1.9 + a | round == 4 and numbers | length == 3}}", "true"),
159 ("{{ numbers | length > 1 }}", "true"),
160 ("{{ numbers | length == 1 }}", "false"),
161 ("{{ numbers | length - 2 == 1 }}", "true"),
162 ("{{ not name }}", "false"),
163 ("{{ not true }}", "false"),
164 ("{{ not undefined }}", "true"),
165 ("{{ name == 'john' }}", "true"),
166 ("{{ name != 'john' }}", "false"),
167 ("{{ name == 'john' | capitalize }}", "false"),
168 ("{{ name != 'john' | capitalize }}", "true"),
169 ("{{ 1 in numbers }}", "true"),
170 ("{{ 1 not in numbers }}", "false"),
171 ("{{ 40 not in numbers }}", "true"),
172 ("{{ 'e' in 'hello' }}", "true"),
173 ("{{ 'e' not in 'hello' }}", "false"),
174 ("{{ 'x' not in 'hello' }}", "true"),
175 ("{{ name in 'hello john' }}", "true"),
176 ("{{ name not in 'hello john' }}", "false"),
177 ("{{ name not in 'hello' }}", "true"),
178 ("{{ name in ['bob', 2, 'john'] }}", "true"),
179 ("{{ a in ['bob', 2, 'john'] }}", "true"),
180 ("{{ 'n' in name }}", "true"),
181 ("{{ '<' in malicious }}", "true"),
182 ("{{ 'a' in object }}", "true"),
183 ("{{ name in object }}", "true"),
186 for (input, expected) in inputs {
187 println!("{:?} -> {:?}", input, expected);
188 assert_eq!(render_template(input, &context).unwrap(), expected);
193 fn render_variable_block_autoescaping_disabled() {
194 let mut context = Context::new();
195 context.insert("name", &"john");
196 context.insert("malicious", &"<html>");
199 ("{{ name }}", "john"),
200 ("{{ malicious }}", "<html>"),
201 ("{{ malicious | safe }}", "<html>"),
202 ("{{ malicious | upper }}", "<HTML>"),
203 ("{{ malicious | upper | safe }}", "<HTML>"),
204 ("{{ malicious | safe | upper }}", "<HTML>"),
207 for (input, expected) in inputs {
208 let mut tera = Tera::default();
209 tera.add_raw_template("hello.sql", input).unwrap();
210 assert_eq!(tera.render("hello.sql", &context).unwrap(), expected);
215 fn comments_are_ignored() {
217 ("Hello {# comment #}world", "Hello world"),
218 ("Hello {# comment {# nested #}world", "Hello world"),
219 ("My name {# was {{ name }} #}is No One.", "My name is No One."),
222 for (input, expected) in inputs {
223 println!("{:?} -> {:?}", input, expected);
224 assert_eq!(render_template(input, &Context::new()).unwrap(), expected);
229 fn escaping_happens_at_the_end() {
231 #[cfg(feature = "builtins")]
232 ("{{ url | urlencode | safe }}", "https%3A//www.example.org/apples-%26-oranges/"),
233 ("{{ '<html>' }}", "<html>"),
234 ("{{ '<html>' | safe }}", "<html>"),
235 ("{{ 'hello' | safe | replace(from='h', to='&') }}", "&ello"),
236 ("{{ 'hello' | replace(from='h', to='&') | safe }}", "&ello"),
239 for (input
, expected
) in inputs
{
240 let mut context
= Context
::new();
241 context
.insert("url", "https://www.example.org/apples-&-oranges/");
242 assert_eq
!(render_template(input
, &context
).unwrap(), expected
);
247 fn filter_args_are_not_escaped() {
248 let mut context
= Context
::new();
249 context
.insert("my_var", &"hey");
250 context
.insert("to", &"&");
251 let input
= r
#"{{ my_var | replace(from="h", to=to) }}"#;
253 assert_eq
!(render_template(input
, &context
).unwrap(), "&ey");
257 fn render_include_tag() {
258 let mut tera
= Tera
::default();
259 tera
.add_raw_templates(vec
![
261 ("hello", "<h1>Hello {% include \"world\" %}</h1>"),
264 let result
= tera
.render("hello", &Context
::new()).unwrap();
265 assert_eq
!(result
, "<h1>Hello world</h1>".to_owned());
269 fn render_include_array_tag() {
270 let mut tera
= Tera
::default();
271 tera
.add_raw_templates(vec
![
273 ("hello", "<h1>Hello {% include [\"custom/world\", \"world\"] %}</h1>"),
276 let result
= tera
.render("hello", &Context
::new()).unwrap();
277 assert_eq
!(result
, "<h1>Hello world</h1>".to_owned());
279 tera
.add_raw_template("custom/world", "custom world").unwrap();
280 let result
= tera
.render("hello", &Context
::new()).unwrap();
281 assert_eq
!(result
, "<h1>Hello custom world</h1>".to_owned());
285 fn render_include_tag_missing() {
286 let mut tera
= Tera
::default();
287 tera
.add_raw_template("hello", "<h1>Hello {% include \"world\" %}</h1>").unwrap();
288 let result
= tera
.render("hello", &Context
::new());
289 assert
!(result
.is_err());
291 let mut tera
= Tera
::default();
292 tera
.add_raw_template("hello", "<h1>Hello {% include \"world\" ignore missing %}</h1>")
294 let result
= tera
.render("hello", &Context
::new()).unwrap();
295 assert_eq
!(result
, "<h1>Hello </h1>".to_owned());
299 fn can_set_variables_in_included_templates() {
300 let mut tera
= Tera
::default();
301 tera
.add_raw_templates(vec
![
302 ("world", r
#"{% set a = "world" %}{{a}}"#),
303 ("hello", "<h1>Hello {% include \"world\" %}</h1>"),
306 let result
= tera
.render("hello", &Context
::new()).unwrap();
307 assert_eq
!(result
, "<h1>Hello world</h1>".to_owned());
311 fn render_raw_tag() {
313 ("{% raw %}hey{% endraw %}", "hey"),
314 ("{% raw %}{{hey}}{% endraw %}", "{{hey}}"),
315 ("{% raw %}{% if true %}{% endraw %}", "{% if true %}"),
318 for (input
, expected
) in inputs
{
319 println
!("{:?} -> {:?}", input
, expected
);
320 assert_eq
!(render_template(input
, &Context
::new()).unwrap(), expected
);
325 fn add_set_values_in_context() {
326 let mut context
= Context
::new();
327 context
.insert("my_var", &"hey");
328 context
.insert("malicious", &"<html>");
329 context
.insert("admin", &true);
330 context
.insert("num", &1);
333 ("{% set i = 1 %}{{ i }}", "1"),
334 ("{% set i = 1 + 2 %}{{ i }}", "3"),
335 (r
#"{% set i = "hey" %}{{ i }}"#, "hey"),
336 (r
#"{% set i = "<html>" %}{{ i | safe }}"#, "<html>"),
337 (r
#"{% set i = "<html>" %}{{ i }}"#, "<html>"),
338 ("{% set i = my_var %}{{ i }}", "hey"),
339 ("{% set i = malicious %}{{ i | safe }}", "<html>"),
340 ("{% set i = malicious %}{{ i }}", "<html>"),
341 ("{% set i = my_var | upper %}{{ i }}", "HEY"),
342 ("{% set i = range(end=3) %}{{ i }}", "[0, 1, 2]"),
343 ("{% set i = admin or true %}{{ i }}", "true"),
344 ("{% set i = admin and num > 0 %}{{ i }}", "true"),
345 ("{% set i = 0 / 0 %}{{ i }}", "NaN"),
346 ("{% set i = [1,2] %}{{ i }}", "[1, 2]"),
349 for (input
, expected
) in inputs
{
350 println
!("{:?} -> {:?}", input
, expected
);
351 assert_eq
!(render_template(input
, &context
).unwrap(), expected
);
356 fn render_filter_section() {
358 ("{% filter upper %}Hello{% endfilter %}", "HELLO"),
359 ("{% filter upper %}Hello{% if true %} world{% endif %}{% endfilter %}", "HELLO WORLD"),
360 ("{% filter upper %}Hello {% for i in range(end=3) %}i{% endfor %}{% endfilter %}", "HELLO III"),
362 "{% filter upper %}Hello {% for i in range(end=3) %}{% if i == 1 %}{% break %} {% endif %}i{% endfor %}{% endfilter %}",
365 ("{% filter title %}Hello {% if true %}{{ 'world' | upper | safe }}{% endif %}{% endfilter %}", "Hello World"),
366 ("{% filter safe %}{% filter upper %}<Hello>{% endfilter %}{% endfilter%}", "<HELLO>")
369 let context
= Context
::new();
370 for (input
, expected
) in inputs
{
371 println
!("{:?} -> {:?}", input
, expected
);
372 assert_eq
!(render_template(input
, &context
).unwrap(), expected
);
378 let mut context
= Context
::new();
379 context
.insert("is_true", &true);
380 context
.insert("is_false", &false);
381 context
.insert("age", &18);
382 context
.insert("name", &"john");
383 let mut map
= HashMap
::new();
385 context
.insert("map", &map
);
386 context
.insert("numbers", &vec
![1, 2, 3]);
387 context
.insert
::<Option
<usize>, _
>("maybe", &None
);
390 ("{% if is_true is defined %}Admin{% endif %}", "Admin"),
391 ("{% if hello is undefined %}Admin{% endif %}", "Admin"),
392 ("{% if name is string %}Admin{% endif %}", "Admin"),
393 ("{% if age is number %}Admin{% endif %}", "Admin"),
394 ("{% if age is even %}Admin{% endif %}", "Admin"),
395 ("{% if age is odd %}Admin{%else%}even{% endif %}", "even"),
396 ("{% if age is divisibleby(2) %}Admin{% endif %}", "Admin"),
397 ("{% if numbers is iterable %}Admin{% endif %}", "Admin"),
398 ("{% if map is iterable %}Admin{% endif %}", "Admin"),
399 ("{% if map is object %}Admin{% endif %}", "Admin"),
400 ("{% if name is starting_with('j') %}Admin{% endif %}", "Admin"),
401 ("{% if name is ending_with('n') %}Admin{% endif %}", "Admin"),
402 ("{% if numbers is containing(2) %}Admin{% endif %}", "Admin"),
403 ("{% if name is matching('^j.*') %}Admin{% endif %}", "Admin"),
404 ("{% if maybe is defined %}Admin{% endif %}", "Admin"),
407 for (input
, expected
) in inputs
{
408 println
!("{:?} -> {:?}", input
, expected
);
409 assert_eq
!(render_template(input
, &context
).unwrap(), expected
);
414 fn render_if_elif_else() {
415 let mut context
= Context
::new();
416 context
.insert("is_true", &true);
417 context
.insert("is_false", &false);
418 context
.insert("age", &18);
419 context
.insert("name", &"john");
420 context
.insert("empty_string", &"");
421 context
.insert("numbers", &vec
![1, 2, 3]);
424 ("{% if is_true %}Admin{% endif %}", "Admin"),
425 ("{% if is_true or age + 1 > 18 %}Adult{% endif %}", "Adult"),
426 ("{% if is_true and age == 18 %}Adult{% endif %}", "Adult"),
427 // https://github.com/Keats/tera/issues/187
428 ("{% if 1 <= 2 %}a{% endif %}", "a"),
429 ("{% if 2 >= 1 %}a{% endif %}", "a"),
430 ("{% if 1 < 2 %}a{% endif %}", "a"),
431 ("{% if 2 > 1 %}a{% endif %}", "a"),
432 ("{% if 1 == 1 %}a{% endif %}", "a"),
433 ("{% if 1 != 2 %}a{% endif %}", "a"),
434 // testing string conditions
435 ("{% if 'true' %}a{% endif %}", "a"),
436 ("{% if name %}a{% endif %}", "a"),
437 ("{% if '' %}a{% endif %}", ""),
438 ("{% if empty_string %}a{% endif %}", ""),
439 ("{% if '' ~ name %}a{% endif %}", "a"),
440 ("{% if '' ~ empty_string %}a{% endif %}", ""),
441 // some not conditions
442 ("{% if not is_false %}a{% endif %}", "a"),
443 ("{% if not is_true %}a{% endif %}", ""),
444 ("{% if undefined %}a{% endif %}", ""),
445 ("{% if not undefined %}a{% endif %}", "a"),
446 ("{% if not is_false and is_true %}a{% endif %}", "a"),
447 ("{% if not is_false or numbers | length > 0 %}a{% endif %}", "a"),
448 // doesn't panic with NaN results
449 ("{% if 0 / 0 %}a{% endif %}", ""),
451 ("{% if is_true %}Admin{% else %}User{% endif %}", "Admin"),
452 ("{% if is_false %}Admin{% else %}User{% endif %}", "User"),
454 ("{% if is_true %}Admin{% elif is_false %}User{% endif %}", "Admin"),
455 ("{% if is_true %}Admin{% elif is_true %}User{% endif %}", "Admin"),
456 ("{% if is_true %}Admin{% elif numbers | length > 0 %}User{% endif %}", "Admin"),
457 // if, elifs and else
458 ("{% if is_true %}Admin{% elif is_false %}User{% else %}Hmm{% endif %}", "Admin"),
459 ("{% if false %}Admin{% elif is_false %}User{% else %}Hmm{% endif %}", "Hmm"),
460 // doesn't fallthrough elifs
461 // https://github.com/Keats/tera/issues/188
462 ("{% if 1 < 4 %}a{% elif 2 < 4 %}b{% elif 3 < 4 %}c{% else %}d{% endif %}", "a"),
465 "{% if 1 in numbers %}Admin{% elif 100 in numbers %}User{% else %}Hmm{% endif %}",
468 ("{% if 100 in numbers %}Admin{% elif 1 in numbers %}User{% else %}Hmm{% endif %}", "User"),
469 ("{% if 'n' in name %}Admin{% else %}Hmm{% endif %}", "Admin"),
471 ("{% if get_true() %}Truth{% endif %}", "Truth"),
474 for (input
, expected
) in inputs
{
475 println
!("{:?} -> {:?}", input
, expected
);
476 assert_eq
!(render_template(input
, &context
).unwrap(), expected
);
482 let mut context
= Context
::new();
483 let mut map
= BTreeMap
::new();
484 map
.insert("name", "bob");
485 map
.insert("age", "18");
487 context
.insert("data", &vec
![1, 2, 3]);
488 context
.insert("notes", &vec
![1, 2, 3]);
489 context
.insert("vectors", &vec
![vec
![0, 3, 6], vec
![1, 4, 7]]);
490 context
.insert("vectors_some_empty", &vec
![vec
![0, 3, 6], vec
![], vec
![1, 4, 7]]);
491 context
.insert("map", &map
);
492 context
.insert("truthy", &2);
495 ("{% for i in data %}{{i}}{% endfor %}", "123"),
496 ("{% for key, val in map %}{{key}}:{{val}} {% endfor %}", "age:18 name:bob "),
498 "{% for i in data %}{{loop.index}}{{loop.index0}}{{loop.first}}{{loop.last}}{% endfor %}",
499 "10truefalse21falsefalse32falsetrue"
502 "{% for vector in vectors %}{% for j in vector %}{{ j }}{% endfor %}{% endfor %}",
506 "{% for vector in vectors_some_empty %}{% for j in vector %}{{ j }}{% endfor %}{% endfor %}",
510 "{% for val in data %}{% if val == truthy %}on{% else %}off{% endif %}{% endfor %}",
513 ("{% for i in range(end=5) %}{{i}}{% endfor %}", "01234"),
514 ("{% for i in range(end=5) | reverse %}{{i}}{% endfor %}", "43210"),
516 "{% set looped = 0 %}{% for i in range(end=5) %}{% set looped = i %}{{looped}}{% endfor%}{{looped}}",
519 // https://github.com/Keats/tera/issues/184
520 ("{% for note in notes %}{{ note }}{% endfor %}", "123"),
521 ("{% for note in notes | reverse %}{{ note }}{% endfor %}", "321"),
522 ("{% for v in vectors %}{{ v.0 }}{% endfor %}", "01"),
523 // Loop control (`break` and `continue`)
524 // https://github.com/Keats/tera/issues/267
526 "{% for i in data %}{{ i }}{% if i == 2 %}{% break %}{% endif %}{% endfor %}",
530 "{% for i in data %}{% if i == 2 %}{% continue %}{% endif %}{{ i }}{% endfor %}",
534 "{% for v in vectors %}{% for i in v %}{% if i == 3 %}{% break %}{% endif %}{{ i }}{% endfor %}{% endfor %}",
538 "{% for v in vectors %}{% for i in v %}{% if i == 3 %}{% continue %}{% endif %}{{ i }}{% endfor %}{% endfor %}",
542 "{% for a in [1, true, 1.1, 'hello'] %}{{a}}{% endfor %}",
545 // https://github.com/Keats/tera/issues/301
547 "{% set start = 0 %}{% set end = start + 3 %}{% for i in range(start=start, end=end) %}{{ i }}{% endfor%}",
550 // https://github.com/Keats/tera/issues/395
552 "{% for a in [] %}{{a}}{% else %}hello{% endfor %}",
556 "{% for a in undefined_variable | default(value=[]) %}{{a}}{% else %}hello{% endfor %}",
560 "{% for a in [] %}{{a}}{% else %}{% if 1 == 2 %}A{% else %}B{% endif %}{% endfor %}",
565 for (input
, expected
) in inputs
{
566 println
!("{:?} -> {:?}", input
, expected
);
567 assert_eq
!(render_template(input
, &context
).unwrap(), expected
);
572 fn render_magic_variable_isnt_escaped() {
573 let mut context
= Context
::new();
574 context
.insert("html", &"<html>");
576 let result
= render_template("{{ __tera_context }}", &context
);
587 // https://github.com/Keats/tera/issues/185
589 fn ok_many_variable_blocks() {
590 let mut context
= Context
::new();
591 context
.insert("username", &"bob");
593 let mut tpl
= String
::new();
595 tpl
.push_str("{{ username }}")
597 let mut expected
= String
::new();
599 expected
.push_str("bob")
601 assert_eq
!(render_template(&tpl
, &context
).unwrap(), expected
);
605 fn can_set_variable_in_global_context_in_forloop() {
606 let mut context
= Context
::new();
607 context
.insert("tags", &vec
![1, 2, 3]);
608 context
.insert("default", &"default");
610 let result
= render_template(
612 {%- for i in tags -%}
613 {%- set default = 1 -%}
614 {%- set_global global_val = i -%}
616 {{ default }}{{ global_val }}"#,
620 assert_eq
!(result
.unwrap(), "default3");
624 fn default_filter_works() {
625 let mut context
= Context
::new();
626 let i
: Option
<usize> = None
;
627 context
.insert("existing", "hello");
628 context
.insert("null", &i
);
631 (r
#"{{ existing | default(value="hey") }}"#, "hello"),
632 (r
#"{{ val | default(value=1) }}"#, "1"),
633 (r
#"{{ val | default(value="hey") | capitalize }}"#, "Hey"),
634 (r
#"{{ obj.val | default(value="hey") | capitalize }}"#, "Hey"),
635 (r
#"{{ obj.val | default(value="hey") | capitalize }}"#, "Hey"),
636 (r
#"{{ not admin | default(value=false) }}"#, "true"),
637 (r
#"{{ not admin | default(value=true) }}"#, "false"),
638 (r
#"{{ null | default(value=true) }}"#, "true"),
639 (r
#"{{ null | default(value="hey") | capitalize }}"#, "Hey"),
642 for (input
, expected
) in inputs
{
643 println
!("{:?} -> {:?}", input
, expected
);
644 assert_eq
!(render_template(input
, &context
).unwrap(), expected
);
649 fn filter_filter_works() {
650 #[derive(Debug, Serialize)]
655 let mut context
= Context
::new();
656 context
.insert("authors", &vec
![Author { id: 1 }
, Author { id: 2 }
, Author { id: 3 }
]);
659 vec
![(r
#"{{ authors | filter(attribute="id", value=1) | first | get(key="id") }}"#, "1")];
661 for (input
, expected
) in inputs
{
662 println
!("{:?} -> {:?}", input
, expected
);
663 assert_eq
!(render_template(input
, &context
).unwrap(), expected
);
668 fn filter_on_array_literal_works() {
669 let mut context
= Context
::new();
670 let i
: Option
<usize> = None
;
671 context
.insert("existing", "hello");
672 context
.insert("null", &i
);
675 (r
#"{{ [1, 2, 3] | length }}"#, "3"),
676 (r
#"{% set a = [1, 2, 3] | length %}{{ a }}"#, "3"),
677 (r
#"{% for a in [1, 2, 3] | slice(start=1) %}{{ a }}{% endfor %}"#, "23"),
680 for (input
, expected
) in inputs
{
681 println
!("{:?} -> {:?}", input
, expected
);
682 assert_eq
!(render_template(input
, &context
).unwrap(), expected
);
687 fn can_do_string_concat() {
688 let mut context
= Context
::new();
689 context
.insert("a_string", "hello");
690 context
.insert("another_string", "xXx");
691 context
.insert("an_int", &1);
692 context
.insert("a_float", &3.14);
695 (r
#"{{ "hello" ~ " world" }}"#, "hello world"),
696 (r
#"{{ "hello" ~ 1 }}"#, "hello1"),
697 (r
#"{{ "hello" ~ 3.14 }}"#, "hello3.14"),
698 (r
#"{{ 3.14 ~ "hello"}}"#, "3.14hello"),
699 (r
#"{{ "hello" ~ get_string() }}"#, "helloHello"),
700 (r
#"{{ get_string() ~ "hello" }}"#, "Hellohello"),
701 (r
#"{{ get_string() ~ 3.14 }}"#, "Hello3.14"),
702 (r
#"{{ a_string ~ " world" }}"#, "hello world"),
703 (r
#"{{ a_string ~ ' world ' ~ another_string }}"#, "hello world xXx"),
704 (r
#"{{ a_string ~ another_string }}"#, "helloxXx"),
705 (r
#"{{ a_string ~ an_int }}"#, "hello1"),
706 (r
#"{{ a_string ~ a_float }}"#, "hello3.14"),
709 for (input
, expected
) in inputs
{
710 println
!("{:?} -> {:?}", input
, expected
);
711 assert_eq
!(render_template(input
, &context
).unwrap(), expected
);
716 fn can_fail_rendering_from_template() {
717 let mut context
= Context
::new();
718 context
.insert("title", "hello");
720 let res
= render_template(
721 r
#"{{ throw(message="Error: " ~ title ~ " did not include a summary") }}"#,
725 let err
= res
.expect_err("This should always fail to render");
726 let source
= err
.source().expect("Must have a source");
727 assert_eq
!(source
.to_string(), "Function call 'throw' failed");
729 let source
= source
.source().expect("Should have a nested error");
730 assert_eq
!(source
.to_string(), "Error: hello did not include a summary");
734 fn does_render_owned_for_loop_with_objects() {
735 let mut context
= Context
::new();
737 {"id": 1, "year": 2015}
,
738 {"id": 2, "year": 2015}
,
739 {"id": 3, "year": 2016}
,
740 {"id": 4, "year": 2017}
,
741 {"id": 5, "year": 2017}
,
742 {"id": 6, "year": 2017}
,
743 {"id": 7, "year": 2018}
,
745 {"id": 9, "year": null}
,
747 context
.insert("something", &data
);
750 r
#"{% for year, things in something | group_by(attribute="year") %}{{year}},{% endfor %}"#;
751 let expected
= "2015,2016,2017,2018,";
752 assert_eq
!(render_template(tpl
, &context
).unwrap(), expected
);
756 fn does_render_owned_for_loop_with_objects_string_keys() {
757 let mut context
= Context
::new();
759 {"id": 1, "group": "a"}
,
760 {"id": 2, "group": "b"}
,
761 {"id": 3, "group": "c"}
,
762 {"id": 4, "group": "a"}
,
763 {"id": 5, "group": "b"}
,
764 {"id": 6, "group": "c"}
,
765 {"id": 7, "group": "a"}
,
767 {"id": 9, "year": null}
,
769 context
.insert("something", &data
);
771 let tpl
= r
#"{% for group, things in something | group_by(attribute="group") %}{{group}},{% endfor %}"#;
772 let expected
= "a,b,c,";
773 assert_eq
!(render_template(tpl
, &context
).unwrap(), expected
);
777 fn render_magic_variable_gets_all_contexts() {
778 let mut context
= Context
::new();
779 context
.insert("html", &"<html>");
780 context
.insert("num", &1);
781 context
.insert("i", &10);
783 let result
= render_template(
784 "{% set some_val = 1 %}{% for i in range(start=0, end=1) %}{% set for_val = i %}{{ __tera_context }}{% endfor %}",
802 fn render_magic_variable_macro_doesnt_leak() {
803 let mut context
= Context
::new();
804 context
.insert("html", &"<html>");
805 context
.insert("num", &1);
806 context
.insert("i", &10);
808 let mut tera
= Tera
::default();
809 tera
.add_raw_templates(vec
![
810 ("macros", "{% macro hello(arg=1) %}{{ __tera_context }}{% endmacro hello %}"),
811 ("tpl", "{% import \"macros\" as macros %}{{macros::hello()}}"),
814 let result
= tera
.render("tpl", &context
);
825 // https://github.com/Keats/tera/issues/342
827 fn redefining_loop_value_doesnt_break_loop() {
828 let mut tera
= Tera
::default();
829 tera
.add_raw_template(
832 {%- set string = "abcdefghdijklm" | split(pat="d") -%}
833 {% for i in string -%}
834 {%- set j = i ~ "lol" ~ " " -%}
840 let context
= Context
::new();
841 let result
= tera
.render("tpl", &context
);
843 assert_eq
!(result
.unwrap(), "abclol efghlol ijklmlol ");
847 fn can_use_concat_to_push_to_array() {
848 let mut tera
= Tera
::default();
849 tera
.add_raw_template(
853 {% for i in range(end=5) -%}
854 {%- set_global ids = ids | concat(with=i) -%}
859 let context
= Context
::new();
860 let result
= tera
.render("tpl", &context
);
862 assert_eq
!(result
.unwrap(), "[0, 1, 2, 3, 4]");
865 struct Next(AtomicUsize
);
867 impl Function
for Next
{
868 fn call(&self, _args
: &HashMap
<String
, Value
>) -> Result
<Value
> {
869 Ok(Value
::Number(self.0.fetch_add
(1, Ordering
::Relaxed
).into()))
874 struct SharedNext(Arc
<Next
>);
876 impl Function
for SharedNext
{
877 fn call(&self, args
: &HashMap
<String
, Value
>) -> Result
<Value
> {
883 static ref NEXT_GLOBAL
: SharedNext
= SharedNext(Arc
::new(Next(AtomicUsize
::new(1))));
887 fn stateful_global_fn() {
888 fn make_tera() -> Tera
{
889 let mut tera
= Tera
::default();
890 tera
.add_raw_template(
892 "<h1>{{ get_next() }}, {{ get_next_shared() }}, {{ get_next() }}...</h1>",
896 tera
.register_function("get_next", Next(AtomicUsize
::new(1)));
897 tera
.register_function("get_next_shared", NEXT_GLOBAL
.clone());
902 make_tera().render("fn.html", &Context
::new()).unwrap(),
903 "<h1>1, 1, 2...</h1>".to_owned()
906 make_tera().render("fn.html", &Context
::new()).unwrap(),
907 "<h1>1, 2, 2...</h1>".to_owned()
911 // https://github.com/Keats/tera/issues/373
913 fn split_on_context_value() {
914 let mut tera
= Tera
::default();
915 tera
.add_raw_template("split.html", r
#"{{ body | split(pat="\n") }}"#).unwrap();
916 let mut context
= Context
::new();
917 context
.insert("body", "multi\nple\nlines");
918 let res
= tera
.render("split.html", &context
);
919 assert_eq
!(res
.unwrap(), "[multi, ple, lines]");
922 // https://github.com/Keats/tera/issues/422
924 fn default_filter_works_in_condition() {
925 let mut tera
= Tera
::default();
926 tera
.add_raw_template("test.html", r
#"{% if frobnicate|default(value=True) %}here{% endif %}"#)
928 let res
= tera
.render("test.html", &Context
::new());
929 assert_eq
!(res
.unwrap(), "here");
933 fn safe_filter_works() {
935 impl crate::Filter
for Safe
{
936 fn filter(&self, value
: &Value
, _args
: &HashMap
<String
, Value
>) -> Result
<Value
> {
937 Ok(Value
::String(format
!("<div>{}</div>", value
.as_str().unwrap())))
940 fn is_safe(&self) -> bool
{
945 let mut tera
= Tera
::default();
946 tera
.register_filter("safe_filter", Safe
);
947 tera
.add_raw_template("test.html", r
#"{{ "Hello" | safe_filter }}"#).unwrap();
949 let res
= tera
.render("test.html", &Context
::new());
950 assert_eq
!(res
.unwrap(), "<div>Hello</div>");
954 fn safe_function_works() {
956 impl crate::Function
for Safe
{
957 fn call(&self, _args
: &HashMap
<String
, Value
>) -> Result
<Value
> {
958 Ok(Value
::String("<div>Hello</div>".to_owned()))
961 fn is_safe(&self) -> bool
{
966 let mut tera
= Tera
::default();
967 tera
.register_function("safe_function", Safe
);
968 tera
.add_raw_template("test.html", "{{ safe_function() }}").unwrap();
970 let res
= tera
.render("test.html", &Context
::new());
971 assert_eq
!(res
.unwrap(), "<div>Hello</div>");