#![allow(clippy::disallowed_names)] // For the use of `foo` in test cases use askama::Template; use std::collections::HashMap; #[derive(Template)] #[template(path = "simple.html")] struct VariablesTemplate<'a> { strvar: &'a str, num: i64, i18n: String, } #[test] fn test_variables() { let s = VariablesTemplate { strvar: "foo", num: 42, i18n: "Iñtërnâtiônàlizætiøn".to_string(), }; assert_eq!( s.render().unwrap(), "\nhello world, foo\n\ with number: 42\n\ Iñtërnâtiônàlizætiøn is important\n\ in vars too: Iñtërnâtiônàlizætiøn" ); assert_eq!(VariablesTemplate::EXTENSION, Some("html")); } #[derive(Template)] #[template(path = "hello.html")] struct EscapeTemplate<'a> { name: &'a str, } #[test] fn test_escape() { let s = EscapeTemplate { name: "<>&\"'" }; assert_eq!(s.render().unwrap(), "Hello, <>&"'!"); } #[derive(Template)] #[template(path = "simple-no-escape.txt")] struct VariablesTemplateNoEscape<'a> { strvar: &'a str, num: i64, i18n: String, } #[test] fn test_variables_no_escape() { let s = VariablesTemplateNoEscape { strvar: "foo", num: 42, i18n: "Iñtërnâtiônàlizætiøn".to_string(), }; assert_eq!( s.render().unwrap(), "\nhello world, foo\n\ with number: 42\n\ Iñtërnâtiônàlizætiøn is important\n\ in vars too: Iñtërnâtiônàlizætiøn" ); } #[derive(Template)] #[template( source = "{{ foo }} {{ foo_bar }} {{ FOO }} {{ FOO_BAR }} {{ self::FOO }} {{ self::FOO_BAR }} {{ Self::BAR }} {{ Self::BAR_BAZ }}", ext = "txt" )] struct ConstTemplate { foo: &'static str, foo_bar: &'static str, } impl ConstTemplate { const BAR: &'static str = "BAR"; const BAR_BAZ: &'static str = "BAR BAZ"; } #[test] fn test_constants() { let t = ConstTemplate { foo: "foo", foo_bar: "foo bar", }; assert_eq!( t.render().unwrap(), "foo foo bar FOO FOO BAR FOO FOO BAR BAR BAR BAZ" ); } const FOO: &str = "FOO"; const FOO_BAR: &str = "FOO BAR"; #[derive(Template)] #[template(path = "if.html")] struct IfTemplate { cond: bool, } #[test] fn test_if() { let s = IfTemplate { cond: true }; assert_eq!(s.render().unwrap(), "true"); } #[derive(Template)] #[template(path = "else.html")] struct ElseTemplate { cond: bool, } #[test] fn test_else() { let s = ElseTemplate { cond: false }; assert_eq!(s.render().unwrap(), "false"); } #[derive(Template)] #[template(path = "else-if.html")] struct ElseIfTemplate { cond: bool, check: bool, } #[test] fn test_else_if() { let s = ElseIfTemplate { cond: false, check: true, }; assert_eq!(s.render().unwrap(), "checked"); } #[derive(Template)] #[template(path = "literals.html")] struct LiteralsTemplate {} #[test] fn test_literals() { let s = LiteralsTemplate {}; assert_eq!(s.render().unwrap(), "a\na\ntrue\nfalse"); } #[derive(Template)] #[template(path = "literals-escape.html")] struct LiteralsEscapeTemplate {} #[test] fn test_literals_escape() { let s = LiteralsEscapeTemplate {}; assert_eq!( s.render().unwrap(), "A\n\r\t\\\0♥'""\nA\n\r\t\\\0♥'"'" ); } struct Holder { a: usize, } #[derive(Template)] #[template(path = "attr.html")] struct AttrTemplate { inner: Holder, } #[test] fn test_attr() { let t = AttrTemplate { inner: Holder { a: 5 }, }; assert_eq!(t.render().unwrap(), "5"); } #[derive(Template)] #[template(path = "tuple-attr.html")] struct TupleAttrTemplate<'a> { tuple: (&'a str, &'a str), } #[test] fn test_tuple_attr() { let t = TupleAttrTemplate { tuple: ("foo", "bar"), }; assert_eq!(t.render().unwrap(), "foobar"); } struct NestedHolder { holder: Holder, } #[derive(Template)] #[template(path = "nested-attr.html")] struct NestedAttrTemplate { inner: NestedHolder, } #[test] fn test_nested_attr() { let t = NestedAttrTemplate { inner: NestedHolder { holder: Holder { a: 5 }, }, }; assert_eq!(t.render().unwrap(), "5"); } #[derive(Template)] #[template(path = "option.html")] struct OptionTemplate<'a> { var: Option<&'a str>, } #[test] fn test_option() { let some = OptionTemplate { var: Some("foo") }; assert_eq!(some.render().unwrap(), "some: foo"); let none = OptionTemplate { var: None }; assert_eq!(none.render().unwrap(), "none"); } #[derive(Template)] #[template(source = "{{ Self::foo(None) }} {{ Self::foo(Some(1)) }}", ext = "txt")] struct OptionNoneSomeTemplate; impl OptionNoneSomeTemplate { fn foo(x: Option) -> i32 { x.unwrap_or_default() } } #[test] fn test_option_none_some() { let t = OptionNoneSomeTemplate; assert_eq!(t.render().unwrap(), "0 1"); } #[derive(Template)] #[template(path = "generics.html")] struct GenericsTemplate where T: std::fmt::Display, U: std::fmt::Display, { t: T, u: U, } #[test] fn test_generics() { let t = GenericsTemplate { t: "a", u: 42 }; assert_eq!(t.render().unwrap(), "a42"); } #[derive(Template)] #[template(path = "composition.html")] struct CompositionTemplate { foo: IfTemplate, } #[test] fn test_composition() { let t = CompositionTemplate { foo: IfTemplate { cond: true }, }; assert_eq!(t.render().unwrap(), "composed: true"); } #[derive(PartialEq, Eq)] enum Alphabet { Alpha, } #[derive(Template)] #[template(source = "{% if x == Alphabet::Alpha %}true{% endif %}", ext = "txt")] struct PathCompareTemplate { x: Alphabet, } #[test] fn test_path_compare() { let t = PathCompareTemplate { x: Alphabet::Alpha }; assert_eq!(t.render().unwrap(), "true"); } #[derive(Template)] #[template( source = "{% for i in [\"a\", \"\"] %}{{ i }}{% endfor %}", ext = "txt" )] struct ArrayTemplate {} #[test] fn test_slice_literal() { let t = ArrayTemplate {}; assert_eq!(t.render().unwrap(), "a"); } #[derive(Template)] #[template(source = "Hello, {{ world(\"123\", 4) }}!", ext = "txt")] struct FunctionRefTemplate { world: fn(s: &str, v: u8) -> String, } #[test] fn test_func_ref_call() { let t = FunctionRefTemplate { world: |s, r| format!("world({s}, {r})"), }; assert_eq!(t.render().unwrap(), "Hello, world(123, 4)!"); } #[allow(clippy::trivially_copy_pass_by_ref)] fn world2(s: &str, v: u8) -> String { format!("world{v}{s}") } #[derive(Template)] #[template(source = "Hello, {{ self::world2(\"123\", 4) }}!", ext = "txt")] struct PathFunctionTemplate; #[test] fn test_path_func_call() { assert_eq!(PathFunctionTemplate.render().unwrap(), "Hello, world4123!"); } #[derive(Template)] #[template(source = "{{ ::std::string::String::from(\"123\") }}", ext = "txt")] struct RootPathFunctionTemplate; #[test] fn test_root_path_func_call() { assert_eq!(RootPathFunctionTemplate.render().unwrap(), "123"); } #[derive(Template)] #[template(source = "Hello, {{ Self::world3(self, \"123\", 4) }}!", ext = "txt")] struct FunctionTemplate; impl FunctionTemplate { #[allow(clippy::trivially_copy_pass_by_ref)] fn world3(&self, s: &str, v: u8) -> String { format!("world{s}{v}") } } #[derive(Template)] #[template(source = " {# foo -#} ", ext = "txt")] struct CommentTemplate {} #[test] fn test_comment() { let t = CommentTemplate {}; assert_eq!(t.render().unwrap(), " "); } #[derive(Template)] #[template(source = "{% if !foo %}Hello{% endif %}", ext = "txt")] struct NegationTemplate { foo: bool, } #[test] fn test_negation() { let t = NegationTemplate { foo: false }; assert_eq!(t.render().unwrap(), "Hello"); } #[derive(Template)] #[template(source = "{% if foo > -2 %}Hello{% endif %}", ext = "txt")] struct MinusTemplate { foo: i8, } #[test] fn test_minus() { let t = MinusTemplate { foo: 1 }; assert_eq!(t.render().unwrap(), "Hello"); } #[derive(Template)] #[template(source = "{{ foo[\"bar\"] }}", ext = "txt")] struct IndexTemplate { foo: HashMap, } #[test] fn test_index() { let mut foo = HashMap::new(); foo.insert("bar".into(), "baz".into()); let t = IndexTemplate { foo }; assert_eq!(t.render().unwrap(), "baz"); } #[derive(Template)] #[template(source = "foo", ext = "txt")] struct Empty; #[test] fn test_empty() { assert_eq!(Empty.render().unwrap(), "foo"); } #[derive(Template)] #[template(path = "raw-simple.html")] struct RawTemplate; #[test] fn test_raw_simple() { let template = RawTemplate; assert_eq!(template.render().unwrap(), "\n{{ name }}\n"); } #[derive(Template)] #[template(path = "raw-complex.html")] struct RawTemplateComplex; #[test] fn test_raw_complex() { let template = RawTemplateComplex; assert_eq!( template.render().unwrap(), "\n{% block name %}\n {{ name }}\n{% endblock %}\n" ); } #[derive(Template)] #[template(path = "raw-ws.html")] struct RawTemplateWs; #[test] fn test_raw_ws() { let template = RawTemplateWs; assert_eq!(template.render().unwrap(), "<{{hello}}>\n<{{bye}}>"); } mod without_import_on_derive { #[derive(askama::Template)] #[template(source = "foo", ext = "txt")] struct WithoutImport; #[test] fn test_without_import() { use askama::Template; assert_eq!(WithoutImport.render().unwrap(), "foo"); } } #[derive(askama::Template)] #[template(source = "{% let s = String::new() %}{{ s }}", ext = "txt")] struct DefineStringVar; #[test] fn test_define_string_var() { let template = DefineStringVar; assert_eq!(template.render().unwrap(), ""); } #[derive(askama::Template)] #[template(source = "{% let x = 4.5 %}{{ x }}", ext = "html")] struct SimpleFloat; #[test] fn test_simple_float() { let template = SimpleFloat; assert_eq!(template.render().unwrap(), "4.5"); } #[derive(askama::Template)] #[template(path = "num-literals.html")] struct NumLiterals; #[test] fn test_num_literals() { let template = NumLiterals; assert_eq!( template.render().unwrap(), "[90, -90, 90, 2, 56, 240, 10.5, 10.5, 100000000000, 105000000000]", ); } #[allow(non_snake_case)] #[derive(askama::Template)] #[template(source = "{{ xY }}", ext = "txt")] struct MixedCase { xY: &'static str, } /// Test that we can use mixed case in variable names /// /// We use some heuristics to distinguish paths (`std::str::String`) from /// variable names (`foo`). Previously, this test would fail because any /// name containing uppercase characters would be considered a path. /// /// https://github.com/djc/askama/issues/924 #[test] fn test_mixed_case() { let template = MixedCase { xY: "foo" }; assert_eq!(template.render().unwrap(), "foo"); }